Files
gems/opal-task/internal/api/server.go
T
joakim d51c6da18d feat: replace mock mode with real backend dev mode
Add --dev flag to `opal server start` that disables auth (injects
userID=1 for all requests) and exposes a /auth/dev-session endpoint,
so the frontend can develop against a real backend without OAuth
config. Remove VITE_MOCK_MODE and all mock data/branches from the
frontend stores. Add scripts/dev.sh to start both services locally.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 17:07:34 +01:00

113 lines
2.8 KiB
Go

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)
}