Codevault initial commit
This commit is contained in:
116
internal/httpserver/server.go
Normal file
116
internal/httpserver/server.go
Normal file
@@ -0,0 +1,116 @@
|
||||
// Package httpserver assembles the gin engine (API routes + web UI) and runs it
|
||||
// with graceful shutdown.
|
||||
package httpserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/kanootoko/codevault/internal/api"
|
||||
"github.com/kanootoko/codevault/internal/db"
|
||||
"github.com/kanootoko/codevault/internal/observability/logging"
|
||||
"github.com/kanootoko/codevault/internal/observability/metrics"
|
||||
"github.com/kanootoko/codevault/internal/ratelimit"
|
||||
"github.com/kanootoko/codevault/internal/utils/gshutdown"
|
||||
"github.com/kanootoko/codevault/internal/webui"
|
||||
)
|
||||
|
||||
const shutdownTimeout = 10 * time.Second
|
||||
|
||||
const cleanupInterval = time.Minute
|
||||
|
||||
func New(
|
||||
secrets *db.SecretsRepository,
|
||||
users *db.UsersRepository,
|
||||
limiter *ratelimit.Limiter,
|
||||
m metrics.Metrics,
|
||||
trustedProxies []string,
|
||||
) (*gin.Engine, error) {
|
||||
router := gin.New()
|
||||
// Strict-server handlers only see context.Context (via *gin.Context, which
|
||||
// implements it), not *gin.Context directly. Without this flag, gin.Context.Value
|
||||
// never falls back to c.Request.Context(), so values stashed there by middleware
|
||||
// (e.g. LoginFromContext) would be invisible to handlers.
|
||||
router.ContextWithFallback = true
|
||||
router.Use(gin.Recovery())
|
||||
if err := router.SetTrustedProxies(trustedProxies); err != nil {
|
||||
return nil, fmt.Errorf("error on setting trusted proxies: %w", err)
|
||||
}
|
||||
|
||||
router.GET("/", func(c *gin.Context) { serveStatic(c, webui.GetPagePath) })
|
||||
router.GET("/set", func(c *gin.Context) { serveStatic(c, webui.SetPagePath) })
|
||||
|
||||
if err := api.RegisterSwaggerHandlers(router); err != nil {
|
||||
return nil, fmt.Errorf("error on registering swagger handlers: %w", err)
|
||||
}
|
||||
|
||||
handler := api.NewHandler(secrets, m.Value)
|
||||
strictHandler := api.NewStrictHandler(handler, nil)
|
||||
wrapper := api.ServerInterfaceWrapper{Handler: strictHandler}
|
||||
|
||||
router.POST("/api/v1/getValue",
|
||||
api.InstrumentMiddleware("getValue", m.HTTP),
|
||||
api.RateLimitMiddleware(limiter, m.Value),
|
||||
wrapper.GetValue,
|
||||
)
|
||||
router.POST("/api/v1/setValue",
|
||||
api.InstrumentMiddleware("setValue", m.HTTP),
|
||||
api.BasicAuthMiddleware(users, m.Value),
|
||||
wrapper.SetValue,
|
||||
)
|
||||
|
||||
return router, nil
|
||||
}
|
||||
|
||||
func serveStatic(c *gin.Context, path string) {
|
||||
data, err := webui.Static.ReadFile(path)
|
||||
if err != nil {
|
||||
c.AbortWithStatus(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
c.Data(http.StatusOK, "text/html; charset=utf-8", data)
|
||||
}
|
||||
|
||||
// Run starts the HTTP server and the rate limiter's cleanup goroutine, both
|
||||
// tracked as gshutdown subtasks, and blocks until ctx is done.
|
||||
func Run(ctx context.Context, address string, router *gin.Engine, limiter *ratelimit.Limiter) error {
|
||||
logger := logging.LoggerMustFromContext(ctx)
|
||||
|
||||
server := &http.Server{
|
||||
Addr: address,
|
||||
Handler: router,
|
||||
}
|
||||
|
||||
_ = gshutdown.SubtaskStarted(ctx, "http-server")
|
||||
go func() {
|
||||
defer func() { _ = gshutdown.SubtaskFinished(ctx, "http-server") }()
|
||||
<-ctx.Done()
|
||||
shutdownCtx, cancel := context.WithTimeout(context.Background(), shutdownTimeout)
|
||||
defer cancel()
|
||||
if err := server.Shutdown(shutdownCtx); err != nil {
|
||||
logger.Error("error on http server shutdown", zap.Error(err))
|
||||
}
|
||||
}()
|
||||
|
||||
cleanupDone := make(chan struct{})
|
||||
_ = gshutdown.SubtaskStarted(ctx, "ratelimit-cleanup")
|
||||
go func() {
|
||||
defer func() { _ = gshutdown.SubtaskFinished(ctx, "ratelimit-cleanup") }()
|
||||
limiter.Run(cleanupDone, cleanupInterval)
|
||||
}()
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
close(cleanupDone)
|
||||
}()
|
||||
|
||||
logger.Info("starting http server", zap.String("address", address))
|
||||
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
return fmt.Errorf("http server error: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user