feat: base go mkiso
This commit is contained in:
122
internal/route/route.go
Normal file
122
internal/route/route.go
Normal file
@@ -0,0 +1,122 @@
|
||||
package route
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"runtime/debug"
|
||||
"time"
|
||||
|
||||
"mkiso-server/internal/config"
|
||||
"mkiso-server/internal/handler"
|
||||
"mkiso-server/internal/middleware"
|
||||
"mkiso-server/internal/service"
|
||||
)
|
||||
|
||||
// Setup creates the HTTP handler with all routes wired.
|
||||
// Stage 1: no auth.
|
||||
func Setup(cfg *config.Config, isoSvc *service.ISOService, relaySvc *service.RelayService) http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
|
||||
// Health check — always public
|
||||
mux.HandleFunc("GET /api/health", handler.Health(cfg))
|
||||
|
||||
// ISO download endpoints
|
||||
mux.HandleFunc("GET /api/iso/download", handler.DownloadSingle(isoSvc))
|
||||
mux.HandleFunc("GET /api/iso/download-multiple", handler.DownloadMultiple(isoSvc))
|
||||
|
||||
// Print/relay endpoint (single endpoint handles both single and multi via comma detection)
|
||||
mux.HandleFunc("GET /api/iso/print", handler.PrintISO(relaySvc))
|
||||
|
||||
// Wrap with middleware chain: Recovery → RequestID → Logging
|
||||
return middleware.Chain(
|
||||
mux,
|
||||
recoveryMiddleware,
|
||||
requestIDMiddleware,
|
||||
loggingMiddleware,
|
||||
)
|
||||
}
|
||||
|
||||
// SetupSecure creates the HTTP handler with API key authentication on /api/iso/*.
|
||||
// Stage 2: auth enabled.
|
||||
func SetupSecure(cfg *config.Config, isoSvc *service.ISOService, relaySvc *service.RelayService) http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
|
||||
// Health check — always public
|
||||
mux.HandleFunc("GET /api/health", handler.Health(cfg))
|
||||
|
||||
// Protected routes
|
||||
apiKeyMw := middleware.APIKey(cfg.Auth.APIKey)
|
||||
mux.Handle("GET /api/iso/download", apiKeyMw(http.HandlerFunc(handler.DownloadSingle(isoSvc))))
|
||||
mux.Handle("GET /api/iso/download-multiple", apiKeyMw(http.HandlerFunc(handler.DownloadMultiple(isoSvc))))
|
||||
mux.Handle("GET /api/iso/print", apiKeyMw(http.HandlerFunc(handler.PrintISO(relaySvc))))
|
||||
|
||||
return middleware.Chain(
|
||||
mux,
|
||||
recoveryMiddleware,
|
||||
requestIDMiddleware,
|
||||
loggingMiddleware,
|
||||
)
|
||||
}
|
||||
|
||||
// recoveryMiddleware catches panics, logs the stack trace, and returns 500.
|
||||
func recoveryMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
defer func() {
|
||||
if rec := recover(); rec != nil {
|
||||
slog.Error("handler panic",
|
||||
"method", r.Method,
|
||||
"path", r.URL.Path,
|
||||
"panic", rec,
|
||||
"stack", string(debug.Stack()),
|
||||
)
|
||||
http.Error(w, `{"error":"internal server error"}`, http.StatusInternalServerError)
|
||||
}
|
||||
}()
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
// requestIDMiddleware adds a unique request ID to each request context.
|
||||
func requestIDMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
rid := r.Header.Get("X-Request-ID")
|
||||
if rid == "" {
|
||||
rid = generateRequestID()
|
||||
}
|
||||
w.Header().Set("X-Request-ID", rid)
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
// loggingMiddleware logs each request with method, path, status, and duration.
|
||||
func loggingMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
start := time.Now()
|
||||
lrw := &loggingResponseWriter{ResponseWriter: w, statusCode: http.StatusOK}
|
||||
next.ServeHTTP(lrw, r)
|
||||
slog.Info("request",
|
||||
"method", r.Method,
|
||||
"path", r.URL.Path,
|
||||
"query", r.URL.RawQuery,
|
||||
"status", lrw.statusCode,
|
||||
"duration_ms", time.Since(start).Milliseconds(),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// loggingResponseWriter wraps http.ResponseWriter to capture the status code.
|
||||
type loggingResponseWriter struct {
|
||||
http.ResponseWriter
|
||||
statusCode int
|
||||
}
|
||||
|
||||
func (lrw *loggingResponseWriter) WriteHeader(code int) {
|
||||
lrw.statusCode = code
|
||||
lrw.ResponseWriter.WriteHeader(code)
|
||||
}
|
||||
|
||||
// generateRequestID returns a simple unique request ID.
|
||||
func generateRequestID() string {
|
||||
return fmt.Sprintf("req-%d", time.Now().UnixNano())
|
||||
}
|
||||
Reference in New Issue
Block a user