diff options
Diffstat (limited to 'cmd/nebbet/main.go')
| -rw-r--r-- | cmd/nebbet/main.go | 142 |
1 files changed, 63 insertions, 79 deletions
diff --git a/cmd/nebbet/main.go b/cmd/nebbet/main.go index 210ad67..356838e 100644 --- a/cmd/nebbet/main.go +++ b/cmd/nebbet/main.go @@ -1,34 +1,20 @@ -// Command nebbet is the CLI for the nebbet.no static site generator. -// -// Usage: -// -// nebbet build – build all pages once -// nebbet watch – watch for changes and rebuild -// nebbet serve [--port N] – serve with live admin UI + watch mode -// nebbet user add <name> – add a user to the passwords file -// nebbet user list – list users -// nebbet user delete <name> – remove a user -// nebbet user passwd <name> – change a user's password package main import ( "flag" "fmt" - "net/http" + "html/template" "os" - - "github.com/gin-gonic/gin" + "path/filepath" "nebbet.no/internal/admin" "nebbet.no/internal/admin/auth" - "nebbet.no/internal/builder" "nebbet.no/internal/db" + "nebbet.no/internal/server" ) const ( - contentDir = "content" outputDir = "public" - postsDir = "content/posts" dataDir = "data" metaDBPath = "data/meta.db" searchDBPath = "data/search.db" @@ -41,10 +27,6 @@ func main() { os.Exit(1) } switch os.Args[1] { - case "build": - cmdBuild(os.Args[2:]) - case "watch": - cmdWatch() case "serve": cmdServe(os.Args[2:]) case "user": @@ -56,78 +38,82 @@ func main() { } } -func cmdBuild(args []string) { - fs := flag.NewFlagSet("build", flag.ExitOnError) - watch := fs.Bool("watch", false, "watch for changes and rebuild") +func cmdServe(args []string) { + fs := flag.NewFlagSet("serve", flag.ExitOnError) + port := fs.String("port", "8080", "port to listen on") _ = fs.Parse(args) - b := mustBuilder() - defer b.MetaDB.Close() - defer b.SearchDB.Close() + meta, search := mustOpenDBs() + defer meta.Close() + defer search.Close() + + adminSrv := admin.NewServer(passwordFile, meta, search, outputDir) + engine := adminSrv.Engine() - if err := b.BuildAll(); err != nil { - fmt.Fprintln(os.Stderr, "build error:", err) + // 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) } - if *watch { - if err := b.Watch(); err != nil { - fmt.Fprintln(os.Stderr, "watch error:", err) - os.Exit(1) - } - } -} -func cmdWatch() { - b := mustBuilder() - defer b.MetaDB.Close() - defer b.SearchDB.Close() - if err := b.BuildAll(); err != nil { - fmt.Fprintln(os.Stderr, "build 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", } - if err := b.Watch(); err != nil { - fmt.Fprintln(os.Stderr, "watch error:", err) - os.Exit(1) + 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", + ".js": "application/javascript; charset=utf-8", + ".json": "application/json", + ".xml": "application/xml", + ".txt": "text/plain; charset=utf-8", + ".ico": "image/x-icon", + ".svg": "image/svg+xml", + ".png": "image/png", + ".jpg": "image/jpeg", + ".webp": "image/webp", + } + stylesMIMEs := server.AllowedMIMEs{ + ".css": "text/css; charset=utf-8", } -} -func cmdServe(args []string) { - fs := flag.NewFlagSet("serve", flag.ExitOnError) - port := fs.String("port", "8080", "port to listen on") - _ = fs.Parse(args) + // Serve styles directory + engine.GET("/assets/styles/*filepath", server.FileHandler("assets/styles", stylesMIMEs)) - b := mustBuilder() - defer b.MetaDB.Close() - defer b.SearchDB.Close() + // Serve lib directory (JS modules) + engine.GET("/lib/*filepath", server.FileHandler("assets/lib", libMIMEs)) - // Initial build. - if err := b.BuildAll(); err != nil { - fmt.Fprintln(os.Stderr, "build error:", err) - os.Exit(1) - } + // Serve components directory (JS web components) + engine.GET("/assets/components/*filepath", server.FileHandler("assets/components", libMIMEs)) - // Watch in background. - go func() { - if err := b.Watch(); err != nil { - fmt.Fprintln(os.Stderr, "watch error:", err) - } - }() + // Serve media directory (images, videos) + engine.GET("/media/*filepath", server.FileHandler("assets/media", mediaMIMEs)) - // Create admin server with Gin - adminSrv := admin.NewServer(postsDir, passwordFile, b) - engine := adminSrv.Engine() + // Dynamic frontpage with post listing, tags, and search + frontpage := server.NewFrontpageHandler(meta, search, tmpl) + engine.GET("/", frontpage.Serve) - // Serve dependencies (lib/) - engine.Static("/lib", "lib") + // 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(func(c *gin.Context) { - http.FileServer(http.Dir(outputDir)).ServeHTTP(c.Writer, c.Request) - }) + engine.NoRoute(server.PublicFileHandler(outputDir, publicMIMEs)) addr := ":" + *port fmt.Printf("listening on http://localhost%s\n", addr) - fmt.Printf(" public site: http://localhost%s/\n", addr) fmt.Printf(" admin UI: http://localhost%s/admin/\n", addr) if err := engine.Run(addr); err != nil { fmt.Fprintln(os.Stderr, err) @@ -184,7 +170,7 @@ func cmdUser(args []string) { } } -func mustBuilder() *builder.Builder { +func mustOpenDBs() (*db.MetaDB, *db.SearchDB) { if err := os.MkdirAll(dataDir, 0755); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) @@ -199,7 +185,7 @@ func mustBuilder() *builder.Builder { fmt.Fprintln(os.Stderr, "search db:", err) os.Exit(1) } - return builder.New(contentDir, outputDir, meta, search) + return meta, search } func requireArg(args []string, n int, usage string) { @@ -210,11 +196,9 @@ func requireArg(args []string, n int, usage string) { } func usage() { - fmt.Fprintln(os.Stderr, `nebbet — static site generator with admin + fmt.Fprintln(os.Stderr, `nebbet.no Commands: - build [--watch] build all pages (optionally watch for changes) - watch watch for changes and rebuild serve [--port N] serve site + admin UI (default port 8080) user add <name> add user to password file user list list users |
