113 lines
3.7 KiB
Go
113 lines
3.7 KiB
Go
package httpserver
|
|
|
|
import (
|
|
"log/slog"
|
|
"net/http"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/go-chi/chi/v5/middleware"
|
|
|
|
"update_server/internal/apikeys"
|
|
authservice "update_server/internal/auth"
|
|
"update_server/internal/config"
|
|
"update_server/internal/db"
|
|
"update_server/internal/releases"
|
|
)
|
|
|
|
type handler struct {
|
|
config config.Config
|
|
store *db.Store
|
|
logger *slog.Logger
|
|
renderer *Renderer
|
|
auth *authservice.Service
|
|
apiKeys *apikeys.Service
|
|
releases *releases.Service
|
|
loginRateLimiter *rateLimitStore
|
|
clientRateLimiter *rateLimitStore
|
|
}
|
|
|
|
func NewRouter(cfg config.Config, logger *slog.Logger, renderer *Renderer, store *db.Store, auth *authservice.Service, apiKeyService *apikeys.Service, releaseService *releases.Service) http.Handler {
|
|
h := &handler{
|
|
config: cfg,
|
|
store: store,
|
|
logger: logger,
|
|
renderer: renderer,
|
|
auth: auth,
|
|
apiKeys: apiKeyService,
|
|
releases: releaseService,
|
|
loginRateLimiter: newRateLimitStore(cfg.LoginRateLimitPerMinute, cfg.LoginRateLimitBurst),
|
|
clientRateLimiter: newRateLimitStore(cfg.ClientRateLimitPerMinute, cfg.ClientRateLimitBurst),
|
|
}
|
|
|
|
router := chi.NewRouter()
|
|
router.Use(middleware.RequestID)
|
|
if cfg.TrustProxyHeaders {
|
|
router.Use(middleware.RealIP)
|
|
}
|
|
router.Use(requestLogger(logger))
|
|
router.Use(middleware.Recoverer)
|
|
router.Use(middleware.StripSlashes)
|
|
router.Use(securityHeaders(cfg))
|
|
|
|
router.Get("/healthz", h.health)
|
|
router.Handle("/static/*", http.StripPrefix("/static/", http.FileServer(http.Dir(cfg.StaticDir))))
|
|
router.Get("/", h.home)
|
|
|
|
router.Route("/admin", func(r chi.Router) {
|
|
r.Use(adminResponseHeaders)
|
|
r.Use(h.adminCSRF)
|
|
|
|
r.Get("/login", h.adminLoginForm)
|
|
r.With(h.loginRateLimit).Post("/login", h.adminLogin)
|
|
|
|
r.Group(func(r chi.Router) {
|
|
r.Use(h.requireAuthenticatedSession)
|
|
r.Use(h.requireRole(db.UserRoleAdmin))
|
|
|
|
r.Get("/", h.adminHome)
|
|
r.Post("/logout", h.adminLogout)
|
|
r.Get("/projects", h.adminProjects)
|
|
r.Get("/projects/new", h.adminProjectNew)
|
|
r.Post("/projects", h.adminProjectCreate)
|
|
r.Get("/projects/{projectID}", h.adminProjectDetail)
|
|
r.Post("/projects/{projectID}", h.adminProjectUpdate)
|
|
r.Post("/projects/{projectID}/archive", h.adminProjectArchive)
|
|
r.Post("/projects/{projectID}/tags", h.adminProjectAttachTag)
|
|
r.Post("/projects/{projectID}/tags/{tagID}/detach", h.adminProjectDetachTag)
|
|
r.Post("/projects/{projectID}/releases", h.adminProjectUploadRelease)
|
|
r.Get("/tags", h.adminTags)
|
|
r.Get("/tags/new", h.adminTagNew)
|
|
r.Post("/tags", h.adminTagCreate)
|
|
r.Get("/tags/{tagID}", h.adminTagDetail)
|
|
r.Post("/tags/{tagID}", h.adminTagUpdate)
|
|
r.Post("/tags/{tagID}/delete", h.adminTagDelete)
|
|
r.Get("/api-keys", h.adminAPIKeys)
|
|
r.Get("/api-keys/new", h.adminAPIKeyNew)
|
|
r.Post("/api-keys", h.adminAPIKeyCreate)
|
|
r.Get("/api-keys/{apiKeyID}", h.adminAPIKeyDetail)
|
|
r.Post("/api-keys/{apiKeyID}", h.adminAPIKeyUpdate)
|
|
r.Post("/api-keys/{apiKeyID}/activate", h.adminAPIKeyToggleActive)
|
|
})
|
|
})
|
|
|
|
router.Route("/api", func(r chi.Router) {
|
|
r.Route("/v1", func(r chi.Router) {
|
|
r.Use(apiResponseHeaders)
|
|
r.Get("/", h.apiIndex)
|
|
|
|
r.Group(func(r chi.Router) {
|
|
r.Use(protectedAPIResponseHeaders)
|
|
r.Use(h.clientAPIRateLimit)
|
|
r.Use(h.requireAPIKey)
|
|
r.Use(h.requireAPIKeyPermission(apikeys.PermissionDownload))
|
|
|
|
r.Get("/projects", h.apiAccessibleProjects)
|
|
r.Get("/projects/{projectSlug}/releases/latest", h.apiLatestRelease)
|
|
r.Get("/releases/{releaseID}", h.apiReleaseMetadata)
|
|
r.Get("/releases/{releaseID}/download", h.apiReleaseDownload)
|
|
})
|
|
})
|
|
})
|
|
|
|
return router
|
|
}
|