diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-01-13 08:57:04 -1000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-01-13 08:57:04 -1000 |
| commit | b112927937c4441acc7922d40beb147cd9cdd2ea (patch) | |
| tree | bb919745a56053fa1809c2529e6c62d370dc36c2 /cmd/dashboard | |
| parent | 2292dff2d8d6f4b43dad8dffd3d559f7c1e5bb35 (diff) | |
Implement 4-Tab Architecture with unified Atom model
Diffstat (limited to 'cmd/dashboard')
| -rw-r--r-- | cmd/dashboard/main.go | 118 |
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") +} |
