- Implement a token-authenticated admin console at `/admin` with overview metrics and file management. - Add a background worker to periodically clean up expired boxes based on `WARPBOX_CLEANUP_EVERY`. - Add a background worker to generate image and video thumbnails based on `WARPBOX_THUMBNAIL_EVERY`. - Update file storage paths to use `@each@` and `@thumb@` prefixes to separate original files from thumbnails. - Add severity fields to startup logs and update configuration templates.
127 lines
3.2 KiB
Go
127 lines
3.2 KiB
Go
package httpserver
|
|
|
|
import (
|
|
"log/slog"
|
|
"net/http"
|
|
"time"
|
|
|
|
"warpbox.dev/backend/libs/config"
|
|
"warpbox.dev/backend/libs/handlers"
|
|
"warpbox.dev/backend/libs/middleware"
|
|
"warpbox.dev/backend/libs/services"
|
|
"warpbox.dev/backend/libs/web"
|
|
)
|
|
|
|
func New(cfg config.Config, logger *slog.Logger) (*http.Server, error) {
|
|
renderer, err := web.NewRenderer(cfg.TemplateDir, cfg.AppName, cfg.BaseURL)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
uploadService, err := services.NewUploadService(cfg.MaxUploadSize, cfg.DataDir, cfg.BaseURL, logger)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
stopCleanup := startCleanup(uploadService, cfg.CleanupEvery, logger)
|
|
stopThumbnails := startThumbnails(uploadService, cfg.ThumbnailEvery, logger)
|
|
app := handlers.NewApp(cfg, logger, renderer, uploadService)
|
|
|
|
router := http.NewServeMux()
|
|
app.RegisterRoutes(router)
|
|
|
|
handler := middleware.Chain(
|
|
router,
|
|
middleware.Recoverer(logger),
|
|
middleware.RequestID,
|
|
middleware.SecurityHeaders,
|
|
middleware.Gzip,
|
|
middleware.Logger(logger),
|
|
)
|
|
|
|
server := &http.Server{
|
|
Addr: cfg.Addr,
|
|
Handler: handler,
|
|
ReadTimeout: cfg.ReadTimeout,
|
|
WriteTimeout: cfg.WriteTimeout,
|
|
IdleTimeout: cfg.IdleTimeout,
|
|
}
|
|
server.RegisterOnShutdown(func() {
|
|
stopCleanup()
|
|
stopThumbnails()
|
|
if err := uploadService.Close(); err != nil {
|
|
logger.Error("failed to close upload service", "source", "shutdown", "severity", "error", "error", err.Error())
|
|
}
|
|
})
|
|
|
|
return server, nil
|
|
}
|
|
|
|
func startCleanup(uploadService *services.UploadService, interval time.Duration, logger *slog.Logger) func() {
|
|
if interval <= 0 {
|
|
return func() {}
|
|
}
|
|
|
|
stop := make(chan struct{})
|
|
go func() {
|
|
if cleaned, err := uploadService.CleanupExpired(); err != nil {
|
|
logger.Warn("initial cleanup failed", "source", "housekeeping", "severity", "warn", "code", 4201, "error", err.Error())
|
|
} else if cleaned > 0 {
|
|
logger.Info("initial cleanup complete", "source", "housekeeping", "severity", "user_activity", "code", 2202, "cleaned", cleaned)
|
|
}
|
|
|
|
ticker := time.NewTicker(interval)
|
|
defer ticker.Stop()
|
|
for {
|
|
select {
|
|
case <-ticker.C:
|
|
if _, err := uploadService.CleanupExpired(); err != nil {
|
|
logger.Warn("scheduled cleanup failed", "source", "housekeeping", "severity", "warn", "code", 4202, "error", err.Error())
|
|
}
|
|
case <-stop:
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
|
|
return func() {
|
|
close(stop)
|
|
}
|
|
}
|
|
|
|
func startThumbnails(uploadService *services.UploadService, interval time.Duration, logger *slog.Logger) func() {
|
|
if interval <= 0 {
|
|
return func() {}
|
|
}
|
|
|
|
stop := make(chan struct{})
|
|
run := func(source string) {
|
|
result, err := uploadService.GenerateMissingThumbnails()
|
|
if err != nil {
|
|
logger.Warn("thumbnail job failed", "source", "thumbnail", "severity", "warn", "code", 4203, "error", err.Error())
|
|
return
|
|
}
|
|
if result.Generated > 0 || result.Failed > 0 {
|
|
logger.Info("thumbnail job run", "source", source, "severity", "user_activity", "code", 2204, "generated", result.Generated, "failed", result.Failed)
|
|
}
|
|
}
|
|
|
|
go func() {
|
|
run("thumbnail")
|
|
|
|
ticker := time.NewTicker(interval)
|
|
defer ticker.Stop()
|
|
for {
|
|
select {
|
|
case <-ticker.C:
|
|
run("thumbnail")
|
|
case <-stop:
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
|
|
return func() {
|
|
close(stop)
|
|
}
|
|
}
|