feat(api): add v2 routes, auth middleware, structured errors, and server status endpoint

Introduces incremental API improvements for custom launcher support
(mhf-iel, stratic-dev's Rust launcher):

- Standardize all error responses to JSON envelopes with error/message
- Add Bearer token auth middleware for v2 routes (legacy body-token preserved)
- Add `returning` (>90d inactive) and `courses` fields to auth response
- Add /v2/ route prefix with HTTP method enforcement
- Add GET /v2/server/status for MezFes, featured weapon, and event status
- Add APIEventRepo for read-only event data access

Closes #44
This commit is contained in:
Houmgaor
2026-02-27 12:46:23 +01:00
parent 9f43940a44
commit 7ff26f4980
11 changed files with 825 additions and 92 deletions

View File

@@ -31,6 +31,7 @@ type APIServer struct {
userRepo APIUserRepo
charRepo APICharacterRepo
sessionRepo APISessionRepo
eventRepo APIEventRepo
httpServer *http.Server
isShuttingDown bool
}
@@ -47,6 +48,7 @@ func NewAPIServer(config *Config) *APIServer {
s.userRepo = NewAPIUserRepository(config.DB)
s.charRepo = NewAPICharacterRepository(config.DB)
s.sessionRepo = NewAPISessionRepository(config.DB)
s.eventRepo = NewAPIEventRepository(config.DB)
}
return s
}
@@ -55,6 +57,8 @@ func NewAPIServer(config *Config) *APIServer {
func (s *APIServer) Start() error {
// Set up the routes responsible for serving the launcher HTML, serverlist, unique name check, and JP auth.
r := mux.NewRouter()
// Legacy routes (unchanged, no method enforcement)
r.HandleFunc("/launcher", s.Launcher)
r.HandleFunc("/login", s.Login)
r.HandleFunc("/register", s.Register)
@@ -66,7 +70,26 @@ func (s *APIServer) Start() error {
r.HandleFunc("/", s.LandingPage)
r.HandleFunc("/health", s.Health)
r.HandleFunc("/version", s.Version)
handler := handlers.CORS(handlers.AllowedHeaders([]string{"Content-Type"}))(r)
// V2 routes (with HTTP method enforcement)
v2 := r.PathPrefix("/v2").Subrouter()
v2.HandleFunc("/login", s.Login).Methods("POST")
v2.HandleFunc("/register", s.Register).Methods("POST")
v2.HandleFunc("/launcher", s.Launcher).Methods("GET")
v2.HandleFunc("/version", s.Version).Methods("GET")
v2.HandleFunc("/health", s.Health).Methods("GET")
v2.HandleFunc("/server/status", s.ServerStatus).Methods("GET")
// V2 authenticated routes
v2Auth := v2.PathPrefix("").Subrouter()
v2Auth.Use(s.AuthMiddleware)
v2Auth.HandleFunc("/characters", s.CreateCharacter).Methods("POST")
v2Auth.HandleFunc("/characters/{id}/delete", s.DeleteCharacter).Methods("POST")
v2Auth.HandleFunc("/characters/{id}/export", s.ExportSave).Methods("GET")
handler := handlers.CORS(
handlers.AllowedHeaders([]string{"Content-Type", "Authorization"}),
)(r)
s.httpServer.Handler = handlers.LoggingHandler(os.Stdout, handler)
s.httpServer.Addr = fmt.Sprintf(":%d", s.erupeConfig.API.Port)