d51c6da18d
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>
113 lines
2.8 KiB
Go
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)
|
|
}
|