summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
Diffstat (limited to 'internal')
-rw-r--r--internal/config/config.go2
-rw-r--r--internal/handlers/handlers.go25
-rw-r--r--internal/handlers/handlers_test.go2
-rw-r--r--internal/handlers/heuristic_test.go2
-rw-r--r--internal/handlers/tab_state_test.go2
-rw-r--r--internal/store/sqlite.go24
6 files changed, 46 insertions, 11 deletions
diff --git a/internal/config/config.go b/internal/config/config.go
index ba2719d..62e733a 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -20,6 +20,7 @@ type Config struct {
// Paths
DatabasePath string
+ MigrationDir string
TemplateDir string
StaticDir string
@@ -44,6 +45,7 @@ func Load() (*Config, error) {
// Paths
DatabasePath: getEnvWithDefault("DATABASE_PATH", "./dashboard.db"),
+ MigrationDir: getEnvWithDefault("MIGRATION_DIR", "migrations"),
TemplateDir: getEnvWithDefault("TEMPLATE_DIR", "web/templates"),
StaticDir: getEnvWithDefault("STATIC_DIR", "web/static"),
diff --git a/internal/handlers/handlers.go b/internal/handlers/handlers.go
index c44e771..b8fc574 100644
--- a/internal/handlers/handlers.go
+++ b/internal/handlers/handlers.go
@@ -207,6 +207,11 @@ func (h *Handler) aggregateData(ctx context.Context, forceRefresh bool) (*models
wg.Add(1)
go func() {
defer wg.Done()
+ select {
+ case <-ctx.Done():
+ return
+ default:
+ }
boards, err := h.fetchBoards(ctx, forceRefresh)
mu.Lock()
defer mu.Unlock()
@@ -221,6 +226,11 @@ func (h *Handler) aggregateData(ctx context.Context, forceRefresh bool) (*models
wg.Add(1)
go func() {
defer wg.Done()
+ select {
+ case <-ctx.Done():
+ return
+ default:
+ }
tasks, err := h.fetchTasks(ctx, forceRefresh)
mu.Lock()
defer mu.Unlock()
@@ -255,6 +265,11 @@ func (h *Handler) aggregateData(ctx context.Context, forceRefresh bool) (*models
wg.Add(1)
go func() {
defer wg.Done()
+ select {
+ case <-ctx.Done():
+ return
+ default:
+ }
projects, err := h.todoistClient.GetProjects(ctx)
mu.Lock()
defer mu.Unlock()
@@ -270,6 +285,11 @@ func (h *Handler) aggregateData(ctx context.Context, forceRefresh bool) (*models
wg.Add(1)
go func() {
defer wg.Done()
+ select {
+ case <-ctx.Done():
+ return
+ default:
+ }
meals, err := h.fetchMeals(ctx, forceRefresh)
mu.Lock()
defer mu.Unlock()
@@ -286,6 +306,11 @@ func (h *Handler) aggregateData(ctx context.Context, forceRefresh bool) (*models
wg.Add(1)
go func() {
defer wg.Done()
+ select {
+ case <-ctx.Done():
+ return
+ default:
+ }
events, err := h.googleCalendarClient.GetUpcomingEvents(ctx, 10)
mu.Lock()
defer mu.Unlock()
diff --git a/internal/handlers/handlers_test.go b/internal/handlers/handlers_test.go
index e4a9f05..5628237 100644
--- a/internal/handlers/handlers_test.go
+++ b/internal/handlers/handlers_test.go
@@ -39,7 +39,7 @@ func setupTestDB(t *testing.T) (*store.Store, func()) {
}
// Initialize store (this runs migrations)
- db, err := store.New(tmpFile.Name())
+ db, err := store.New(tmpFile.Name(), "migrations")
if err != nil {
os.Chdir(originalDir)
os.Remove(tmpFile.Name())
diff --git a/internal/handlers/heuristic_test.go b/internal/handlers/heuristic_test.go
index 2b70218..b03b664 100644
--- a/internal/handlers/heuristic_test.go
+++ b/internal/handlers/heuristic_test.go
@@ -33,7 +33,7 @@ func TestHandleTasks_Heuristic(t *testing.T) {
defer os.Chdir(originalDir)
// Initialize store (this runs migrations)
- db, err := store.New(tmpFile.Name())
+ db, err := store.New(tmpFile.Name(), "migrations")
if err != nil {
t.Fatalf("Failed to initialize store: %v", err)
}
diff --git a/internal/handlers/tab_state_test.go b/internal/handlers/tab_state_test.go
index d7bb8dd..60f1340 100644
--- a/internal/handlers/tab_state_test.go
+++ b/internal/handlers/tab_state_test.go
@@ -13,7 +13,7 @@ import (
func TestHandleDashboard_TabState(t *testing.T) {
// Create a temporary database for testing
- db, err := store.New(":memory:")
+ db, err := store.New(":memory:", "../../migrations")
if err != nil {
t.Fatalf("Failed to create test database: %v", err)
}
diff --git a/internal/store/sqlite.go b/internal/store/sqlite.go
index 069313a..a4d01a2 100644
--- a/internal/store/sqlite.go
+++ b/internal/store/sqlite.go
@@ -22,11 +22,12 @@ const (
)
type Store struct {
- db *sql.DB
+ db *sql.DB
+ migrationDir string
}
// New creates a new Store instance and runs migrations
-func New(dbPath string) (*Store, error) {
+func New(dbPath, migrationDir string) (*Store, error) {
db, err := sql.Open("sqlite3", dbPath)
if err != nil {
return nil, fmt.Errorf("failed to open database: %w", err)
@@ -42,10 +43,13 @@ func New(dbPath string) (*Store, error) {
return nil, fmt.Errorf("failed to enable WAL mode: %w", err)
}
- // Serialize writes to prevent "database is locked" errors
- db.SetMaxOpenConns(1)
+ // Configure connection pool for SQLite with WAL mode
+ // WAL allows concurrent reads, but writes still need serialization
+ db.SetMaxOpenConns(5)
+ db.SetMaxIdleConns(2)
+ db.SetConnMaxLifetime(time.Hour)
- store := &Store{db: db}
+ store := &Store{db: db, migrationDir: migrationDir}
// Run migrations
if err := store.runMigrations(); err != nil {
@@ -67,8 +71,9 @@ func (s *Store) DB() *sql.DB {
// runMigrations executes all migration files in order
func (s *Store) runMigrations() error {
- // Get migration files
- migrationFiles, err := filepath.Glob("migrations/*.sql")
+ // Get migration files from configured directory
+ pattern := filepath.Join(s.migrationDir, "*.sql")
+ migrationFiles, err := filepath.Glob(pattern)
if err != nil {
return fmt.Errorf("failed to read migration files: %w", err)
}
@@ -178,7 +183,10 @@ func (s *Store) GetTasks() ([]models.Task, error) {
task.DueDate = &dueDate.Time
}
- json.Unmarshal([]byte(labelsJSON), &task.Labels)
+ if err := json.Unmarshal([]byte(labelsJSON), &task.Labels); err != nil {
+ log.Printf("Warning: failed to unmarshal labels for task %s: %v", task.ID, err)
+ task.Labels = []string{}
+ }
tasks = append(tasks, task)
}