package api import ( "fmt" "net/http" "git.jnss.me/joakim/opal/internal/api/handlers" "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" ) // Server represents the API server type Server struct { router chi.Router addr string devMode bool } // NewServer creates a new API server func NewServer(addr string, devMode bool) *Server { s := &Server{ router: chi.NewRouter(), addr: addr, devMode: devMode, } s.setupRoutes() return s } // setupRoutes configures all API routes func (s *Server) setupRoutes() { r := s.router // Global middleware r.Use(middleware.Logger) r.Use(middleware.Recoverer) r.Use(CORSMiddleware()) // Health check (no auth required) r.Get("/health", func(w http.ResponseWriter, r *http.Request) { JSON(w, http.StatusOK, map[string]string{"status": "ok"}) }) if s.devMode { // Dev mode: fake session endpoint so the frontend can "log in" r.Get("/auth/dev-session", func(w http.ResponseWriter, r *http.Request) { JSON(w, http.StatusOK, map[string]interface{}{ "access_token": "dev-token", "refresh_token": "", "expires_at": 9999999999, "token_type": "bearer", "user": map[string]interface{}{ "id": 1, "username": "dev", "email": "dev@localhost", }, }) }) } else { // OAuth endpoints (no auth required) r.Get("/auth/login", handlers.GetLoginURL) r.Get("/auth/callback", handlers.OAuthCallback) r.Post("/auth/callback", handlers.OAuthCallback) r.Post("/auth/refresh", handlers.RefreshToken) r.Post("/auth/logout", handlers.Logout) } // Protected routes r.Group(func(r chi.Router) { if s.devMode { r.Use(DevAuthMiddleware()) } else { r.Use(AuthMiddleware()) } // Tasks r.Route("/tasks", func(r chi.Router) { r.Get("/", handlers.ListTasks) r.Post("/", handlers.CreateTask) r.Post("/parse", handlers.ParseTask) r.Get("/{uuid}", handlers.GetTask) r.Put("/{uuid}", handlers.UpdateTask) r.Delete("/{uuid}", handlers.DeleteTask) r.Post("/{uuid}/complete", handlers.CompleteTask) r.Post("/{uuid}/start", handlers.StartTask) r.Post("/{uuid}/stop", handlers.StopTask) // Tags r.Get("/{uuid}/tags", handlers.GetTaskTags) r.Post("/{uuid}/tags", handlers.AddTaskTag) r.Delete("/{uuid}/tags/{tag}", handlers.RemoveTaskTag) }) // Metadata r.Get("/tags", handlers.ListAllTags) r.Get("/projects", handlers.ListProjects) // Sync endpoints r.Post("/sync/changes", handlers.GetChanges) r.Post("/sync/push", handlers.PushChanges) // Key management r.Get("/auth/keys", handlers.ListAPIKeys) r.Delete("/auth/keys/{id}", handlers.RevokeAPIKey) }) } // Start starts the HTTP server func (s *Server) Start() error { fmt.Printf("Starting opal API server on %s\n", s.addr) return http.ListenAndServe(s.addr, s.router) }