summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorivar <i@oiee.no>2026-04-04 16:51:05 +0200
committerivar <i@oiee.no>2026-04-04 16:51:05 +0200
commit87deaec8c519ee477439922f1cbecb8c90cbe5af (patch)
tree47fd4986d41141dc8d889b1b150a0e3faab61278
parent7de11c5ca03fcfda6ec3d39c5340a317ae77e2d5 (diff)
downloadnebbet.no-87deaec8c519ee477439922f1cbecb8c90cbe5af.tar.xz
nebbet.no-87deaec8c519ee477439922f1cbecb8c90cbe5af.zip
feat: wire MediaHandler routes and govips lifecycle
- Add RegisterUploadRoute to admin Server (POST /admin/upload/image, auth-protected) - Import govips/v2 and internal/media in main.go - Replace static /media/ file handler with mediaSrv.HandleServe for on-the-fly image conversion - Call vips.Startup/Shutdown around server lifetime Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
-rw-r--r--cmd/nebbet/main.go30
-rw-r--r--internal/admin/server.go8
2 files changed, 17 insertions, 21 deletions
diff --git a/cmd/nebbet/main.go b/cmd/nebbet/main.go
index 356838e..f74d0d2 100644
--- a/cmd/nebbet/main.go
+++ b/cmd/nebbet/main.go
@@ -7,9 +7,11 @@ import (
"os"
"path/filepath"
+ "github.com/davidbyttow/govips/v2/vips"
"nebbet.no/internal/admin"
"nebbet.no/internal/admin/auth"
"nebbet.no/internal/db"
+ "nebbet.no/internal/media"
"nebbet.no/internal/server"
)
@@ -43,6 +45,9 @@ func cmdServe(args []string) {
port := fs.String("port", "8080", "port to listen on")
_ = fs.Parse(args)
+ vips.Startup(nil)
+ defer vips.Shutdown()
+
meta, search := mustOpenDBs()
defer meta.Close()
defer search.Close()
@@ -50,28 +55,17 @@ func cmdServe(args []string) {
adminSrv := admin.NewServer(passwordFile, meta, search, outputDir)
engine := adminSrv.Engine()
- // Load templates for post rendering
tmpl, err := template.ParseGlob(filepath.Join("templates", "*.html"))
if err != nil {
fmt.Fprintln(os.Stderr, "template load error:", err)
os.Exit(1)
}
- // Define MIME types for each directory
libMIMEs := server.AllowedMIMEs{
".js": "application/javascript; charset=utf-8",
".css": "text/css; charset=utf-8",
".wasm": "application/wasm",
}
- mediaMIMEs := server.AllowedMIMEs{
- ".jpg": "image/jpeg",
- ".jpeg": "image/jpeg",
- ".png": "image/png",
- ".webp": "image/webp",
- ".gif": "image/gif",
- ".svg": "image/svg+xml",
- ".mp4": "video/mp4",
- }
publicMIMEs := server.AllowedMIMEs{
".html": "text/html; charset=utf-8",
".css": "text/css; charset=utf-8",
@@ -89,27 +83,21 @@ func cmdServe(args []string) {
".css": "text/css; charset=utf-8",
}
- // Serve styles directory
engine.GET("/assets/styles/*filepath", server.FileHandler("assets/styles", stylesMIMEs))
-
- // Serve lib directory (JS modules)
engine.GET("/lib/*filepath", server.FileHandler("assets/lib", libMIMEs))
-
- // Serve components directory (JS web components)
engine.GET("/assets/components/*filepath", server.FileHandler("assets/components", libMIMEs))
- // Serve media directory (images, videos)
- engine.GET("/media/*filepath", server.FileHandler("assets/media", mediaMIMEs))
+ // Image upload (admin-protected) + public image serving with on-the-fly conversion
+ mediaSrv := media.NewMediaHandler("assets/media")
+ adminSrv.RegisterUploadRoute(mediaSrv.HandleUpload)
+ engine.GET("/media/*filepath", mediaSrv.HandleServe)
- // Dynamic frontpage with post listing, tags, and search
frontpage := server.NewFrontpageHandler(meta, search, tmpl)
engine.GET("/", frontpage.Serve)
- // Serve posts dynamically with caching (single route, dispatch by extension)
postHandler := server.NewPostHandler(meta, tmpl, outputDir)
engine.GET("/:slug", postHandler.Serve)
- // Serve static site as fallback for unmatched routes
engine.NoRoute(server.PublicFileHandler(outputDir, publicMIMEs))
addr := ":" + *port
diff --git a/internal/admin/server.go b/internal/admin/server.go
index c0400de..5457372 100644
--- a/internal/admin/server.go
+++ b/internal/admin/server.go
@@ -105,6 +105,14 @@ func (s *Server) Engine() *gin.Engine {
return s.engine
}
+// RegisterUploadRoute registers handler under POST /admin/upload/image
+// behind the admin Basic Auth middleware.
+func (s *Server) RegisterUploadRoute(handler gin.HandlerFunc) {
+ admin := s.engine.Group("/admin")
+ admin.Use(s.authMiddleware())
+ admin.POST("/upload/image", handler)
+}
+
func (s *Server) authMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
if s.AuthFile == "" {