summaryrefslogtreecommitdiff
path: root/cmd/dashboard
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-01-13 08:57:04 -1000
committerPeter Stone <thepeterstone@gmail.com>2026-01-13 08:57:04 -1000
commitb112927937c4441acc7922d40beb147cd9cdd2ea (patch)
treebb919745a56053fa1809c2529e6c62d370dc36c2 /cmd/dashboard
parent2292dff2d8d6f4b43dad8dffd3d559f7c1e5bb35 (diff)
Implement 4-Tab Architecture with unified Atom model
Diffstat (limited to 'cmd/dashboard')
-rw-r--r--cmd/dashboard/main.go118
1 files changed, 118 insertions, 0 deletions
diff --git a/cmd/dashboard/main.go b/cmd/dashboard/main.go
new file mode 100644
index 0000000..3f46e8d
--- /dev/null
+++ b/cmd/dashboard/main.go
@@ -0,0 +1,118 @@
+package main
+
+import (
+ "context"
+ "log"
+ "net/http"
+ "os"
+ "os/signal"
+ "syscall"
+ "time"
+
+ "github.com/go-chi/chi/v5"
+ "github.com/go-chi/chi/v5/middleware"
+ "github.com/joho/godotenv"
+
+ "task-dashboard/internal/api"
+ "task-dashboard/internal/config"
+ "task-dashboard/internal/handlers"
+ "task-dashboard/internal/store"
+)
+
+func main() {
+ // Load .env file (ignore error if file doesn't exist - env vars might be set directly)
+ _ = godotenv.Load()
+
+ // Load configuration
+ cfg, err := config.Load()
+ if err != nil {
+ log.Fatalf("Failed to load configuration: %v", err)
+ }
+
+ // Initialize database
+ db, err := store.New(cfg.DatabasePath)
+ if err != nil {
+ log.Fatalf("Failed to initialize database: %v", err)
+ }
+ defer db.Close()
+
+ // Initialize API clients
+ todoistClient := api.NewTodoistClient(cfg.TodoistAPIKey)
+ trelloClient := api.NewTrelloClient(cfg.TrelloAPIKey, cfg.TrelloToken)
+
+ var obsidianClient api.ObsidianAPI
+ if cfg.HasObsidian() {
+ obsidianClient = api.NewObsidianClient(cfg.ObsidianVaultPath)
+ }
+
+ var planToEatClient api.PlanToEatAPI
+ if cfg.HasPlanToEat() {
+ planToEatClient = api.NewPlanToEatClient(cfg.PlanToEatAPIKey)
+ }
+
+ // Initialize handlers
+ h := handlers.New(db, todoistClient, trelloClient, obsidianClient, planToEatClient, cfg)
+ tabsHandler := handlers.NewTabsHandler(db)
+
+ // Set up router
+ r := chi.NewRouter()
+
+ // Middleware
+ r.Use(middleware.Logger)
+ r.Use(middleware.Recoverer)
+ r.Use(middleware.Timeout(60 * time.Second))
+
+ // Routes
+ r.Get("/", h.HandleDashboard)
+ r.Post("/api/refresh", h.HandleRefresh)
+ r.Get("/api/tasks", h.HandleGetTasks)
+ r.Get("/api/notes", h.HandleGetNotes)
+ r.Get("/api/meals", h.HandleGetMeals)
+ r.Get("/api/boards", h.HandleGetBoards)
+
+ // Tab routes for HTMX (using new TabsHandler)
+ r.Get("/tabs/tasks", tabsHandler.HandleTasks)
+ r.Get("/tabs/planning", tabsHandler.HandlePlanning)
+ r.Get("/tabs/notes", tabsHandler.HandleNotes)
+ r.Get("/tabs/meals", tabsHandler.HandleMeals)
+ r.Post("/tabs/refresh", h.HandleRefreshTab)
+
+ // Serve static files
+ fileServer := http.FileServer(http.Dir("web/static"))
+ r.Handle("/static/*", http.StripPrefix("/static/", fileServer))
+
+ // Start server
+ addr := ":" + cfg.Port
+ srv := &http.Server{
+ Addr: addr,
+ Handler: r,
+ ReadTimeout: 15 * time.Second,
+ WriteTimeout: 15 * time.Second,
+ IdleTimeout: 60 * time.Second,
+ }
+
+ // Graceful shutdown
+ go func() {
+ log.Printf("Starting server on http://localhost%s", addr)
+ if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
+ log.Fatalf("Server failed: %v", err)
+ }
+ }()
+
+ // Wait for interrupt signal
+ quit := make(chan os.Signal, 1)
+ signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
+ <-quit
+
+ log.Println("Shutting down server...")
+
+ // Graceful shutdown with timeout
+ ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ defer cancel()
+
+ if err := srv.Shutdown(ctx); err != nil {
+ log.Fatalf("Server forced to shutdown: %v", err)
+ }
+
+ log.Println("Server exited")
+}