sp3-solo-1770327084/services/chat-api/internal/api/routes.go
rdev-worker 82c41e819b
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
build: /implement-feature websocket-chat --requirements 'GET /ws upgrades to...
2026-02-05 21:58:16 +00:00

82 lines
2.7 KiB
Go

// Package api provides HTTP routing and handlers for the chat-api service.
package api
import (
"net/http"
"git.threesix.ai/jordan/sp3-solo-1770327084/pkg/app"
"git.threesix.ai/jordan/sp3-solo-1770327084/pkg/auth"
"git.threesix.ai/jordan/sp3-solo-1770327084/pkg/httpresponse"
"git.threesix.ai/jordan/sp3-solo-1770327084/pkg/realtime"
"git.threesix.ai/jordan/sp3-solo-1770327084/services/chat-api/internal/api/handlers"
"git.threesix.ai/jordan/sp3-solo-1770327084/services/chat-api/internal/config"
"git.threesix.ai/jordan/sp3-solo-1770327084/services/chat-api/internal/service"
)
// RegisterRoutes registers all HTTP routes for the service.
// Routes are mounted under /api/chat-api to match the ingress path routing.
// This allows the monorepo to expose multiple services under a single domain:
// - https://domain/api/chat-api/health
// - https://domain/api/chat-api/examples
// - https://domain/api/chat-api/ws (WebSocket)
func RegisterRoutes(
application *app.App,
exampleService *service.ExampleService,
hub realtime.Hub,
broadcaster realtime.Broadcaster,
) {
logger := application.Logger()
cfg := config.Load()
// Initialize handlers with injected services
healthHandler := handlers.NewHealth(logger)
exampleHandler := handlers.NewExample(exampleService, logger)
// Initialize WebSocket handler
wsHandler := realtime.NewHandler(hub, logger, realtime.HandlerConfig{
Broadcaster: broadcaster,
AuthRequired: cfg.AuthEnabled,
})
// Build and mount OpenAPI spec
spec := NewServiceSpec()
application.EnableDocs(spec)
// Register API routes under /api/{service-name} to match ingress path routing.
// The ingress routes /api/chat-api/* to this service.
application.Route("/api/chat-api", func(r app.Router) {
r.Get("/health", healthHandler.Check)
// Public routes (no auth required)
r.Get("/examples", app.Wrap(exampleHandler.List))
r.Get("/examples/{id}", app.Wrap(exampleHandler.Get))
// WebSocket routes
// GET /ws - connect to global channel
// GET /ws/{room} - connect and join a room
r.Mount("/ws", wsHandler.Routes())
// WebSocket stats endpoint
r.Get("/ws/stats", func(w http.ResponseWriter, r *http.Request) {
stats := wsHandler.GetStats()
httpresponse.OK(w, r, stats)
})
// Protected routes (auth required when enabled)
r.Group(func(r app.Router) {
if cfg.AuthEnabled {
r.Use(auth.Middleware(auth.MiddlewareConfig{
Validator: auth.NewJWTValidator(auth.JWTConfig{
Secret: []byte(cfg.JWTSecret),
Issuer: "sp3-solo-1770327084",
}),
}))
}
r.Post("/examples", app.Wrap(exampleHandler.Create))
r.Put("/examples/{id}", app.Wrap(exampleHandler.Update))
r.Delete("/examples/{id}", app.Wrap(exampleHandler.Delete))
})
})
}