summaryrefslogtreecommitdiffstats
path: root/docs/superpowers/specs/2026-04-03-gin-migration-design.md
diff options
context:
space:
mode:
authorivar <i@oiee.no>2026-04-04 16:34:46 +0200
committerivar <i@oiee.no>2026-04-04 16:34:46 +0200
commita6355e7a6530af3335c4cd8af05f1e9c8b978169 (patch)
treec9d920d1e996ef1c42d3455825731598df6b56c2 /docs/superpowers/specs/2026-04-03-gin-migration-design.md
parent8a093aacd162d3fd9f142b53aab9edfa061fd66a (diff)
downloadnebbet.no-a6355e7a6530af3335c4cd8af05f1e9c8b978169.tar.xz
nebbet.no-a6355e7a6530af3335c4cd8af05f1e9c8b978169.zip
.
Diffstat (limited to 'docs/superpowers/specs/2026-04-03-gin-migration-design.md')
-rw-r--r--docs/superpowers/specs/2026-04-03-gin-migration-design.md198
1 files changed, 0 insertions, 198 deletions
diff --git a/docs/superpowers/specs/2026-04-03-gin-migration-design.md b/docs/superpowers/specs/2026-04-03-gin-migration-design.md
deleted file mode 100644
index 41163a3..0000000
--- a/docs/superpowers/specs/2026-04-03-gin-migration-design.md
+++ /dev/null
@@ -1,198 +0,0 @@
----
-name: Gin Migration Design
-description: Migrate admin server from net/http to Gin framework with RESTful routes and middleware-based auth
-type: implementation
-date: 2026-04-03
----
-
-# Gin Migration Design
-
-## Overview
-
-Migrate the nebbet admin server from Go's `net/http` package to the Gin web framework. The migration will:
-- Replace manual route handling with Gin's declarative routing
-- Reorganize routes to follow RESTful conventions
-- Extract auth into reusable middleware
-- Preserve all existing functionality (auth, template rendering, post management)
-- Keep UI behavior identical from the user's perspective
-
-## Goals
-
-1. **Cleaner routing:** Replace manual `ServeHTTP` switch with Gin's route declarations
-2. **Better framework:** Leverage Gin for middleware, error handling, and future extensibility
-3. **RESTful design:** Modernize route structure while keeping templates server-rendered
-
-## Non-Goals
-
-- Rewriting the frontend to a JS framework
-- Adding new features beyond the migration
-- Changing auth format or password file structure
-- Changing how posts are stored or built
-
-## Architecture
-
-### Current State
-
-The `admin.Server` implements `http.Handler` with manual routing in `ServeHTTP()`:
-```
-GET/POST /admin → list posts
-GET/POST /admin/new → create form / create post
-GET/POST /admin/{slug}/edit → edit form / update post
-POST /admin/{slug}/delete → delete post
-```
-
-Auth is checked manually via `checkAuth()` for every request.
-
-### Target State
-
-Replace with Gin routing and middleware:
-- `Server` struct holds a `*gin.Engine`
-- `NewServer()` initializes Gin with routes and middleware
-- Auth middleware wraps all admin routes
-- Handlers call `gin.Context` instead of `http.ResponseWriter`
-
-**New route structure (RESTful, under `/admin` namespace):**
-```
-GET /admin/ → list posts
-GET /admin/new → create form
-POST /admin/ → create post
-GET /admin/:slug → edit form
-POST /admin/:slug → update post
-DELETE /admin/:slug → delete post
-```
-
-## Implementation Details
-
-### File: `internal/admin/server.go`
-
-#### Server Struct
-- Add field: `engine *gin.Engine`
-- Keep existing fields: `PostsDir`, `AuthFile`, `Builder`, `tmpl`
-
-#### Constructor: `NewServer(postsDir, authFile string, builder *builder.Builder) *Server`
-- Create and configure Gin engine
-- Register middleware (auth)
-- Register all routes
-- Return `*Server`
-
-#### Auth Middleware
-- Extracted from `checkAuth()` into a middleware function
-- Signature: `func (s *Server) authMiddleware() gin.HandlerFunc`
-- Logic:
- - Skip auth if `AuthFile` is empty or doesn't exist
- - Extract Basic Auth credentials
- - Call `auth.Verify(username, password)`
- - Send 401 + `WWW-Authenticate` header on failure
- - Call `c.Next()` to proceed on success
-
-#### Route Handlers
-Keep existing handler functions, update signatures:
-- `handleList(c *gin.Context)` — render post list
-- `handleNew(c *gin.Context)` — GET shows form, POST creates post
-- `handleNewPost(c *gin.Context)` — handle POST /new (merge with `handleNew` or keep separate)
-- `handleEdit(c *gin.Context)` — GET shows form, POST updates post
-- `handleDelete(c *gin.Context)` — DELETE removes post
-
-For GET requests that show forms, continue using `c.HTML()` with the template and data map.
-For POST requests, validate form data, write to disk, rebuild, and redirect to `/`.
-
-#### Helper Functions
-Keep existing:
-- `listPosts()` — read `.md` files from PostsDir
-- `render(c *gin.Context, name string, data map[string]any)` — render template (adapt to use `c.HTML()`)
-- `renderError(c *gin.Context, msg string)` — render error template
-- `postFromForm(c *gin.Context) Post` — extract form values from `c.PostForm()`
-- `readPostFile()`, `writePostFile()`, `slugify()` — unchanged
-- `mustParseTemplates()` — unchanged
-
-### File: `cmd/nebbet/main.go`
-
-#### Changes in `cmdServe()`
-Instead of:
-```go
-adminSrv := &admin.Server{...}
-mux := http.NewServeMux()
-mux.Handle("/admin/", http.StripPrefix("/admin", adminSrv))
-mux.Handle("/lib/", ...)
-mux.Handle("/", ...)
-http.ListenAndServe(addr, mux)
-```
-
-Change to:
-```go
-adminSrv := admin.NewServer(postsDir, passwordFile, b)
-engine := adminSrv.Engine() // or keep internal, add to main router
-
-// Either:
-// 1. Use adminSrv.Engine directly (if it handles all routes)
-// 2. Create a main Gin router and nest adminSrv's routes
-
-// Handle /lib/ and static files as Gin routes or separate handlers
-```
-
-Exact approach depends on whether we keep `/lib` and public site serving separate or consolidate into one Gin router. **Recommended:** single Gin router for consistency.
-
-## Route Mapping
-
-| Old Route | New Route | Method | Action |
-|-----------|-----------|--------|--------|
-| `/admin/` | `/admin/` | GET | List posts |
-| `/admin/new` | `/admin/new` | GET | Show create form |
-| `/admin/new` | `/admin/` | POST | Create post |
-| `/admin/{slug}/edit` | `/admin/{slug}` | GET | Show edit form |
-| `/admin/{slug}/edit` | `/admin/{slug}` | POST | Update post |
-| `/admin/{slug}/delete` | `/admin/{slug}` | DELETE | Delete post |
-
-**HTML Form Handling:** Standard HTML forms only support GET/POST. For DELETE:
-- **Option A:** Keep POST with custom handling (check method override or hidden field)
-- **Option B:** Use POST `/admin/{slug}?method=delete` and parse in handler
-
-Recommended: keep as POST for form compatibility, or use POST with Gin's `c.PostForm("_method")` check.
-
-## Dependencies
-
-- Add `github.com/gin-gonic/gin` to `go.mod`
-- No changes to other dependencies
-
-## Backwards Compatibility
-
-- **URLs:** Admin routes change (listed above). Bookmarks will break, but it's an internal admin interface.
-- **Auth:** No changes. Password file format and Basic Auth remain the same.
-- **Posts:** No changes. Markdown files, frontmatter, build process unchanged.
-- **Public site:** No changes. Static files served the same way.
-
-## Testing Strategy
-
-- **Manual:** Create/edit/delete posts via the admin UI, verify rebuilds work
-- **Auth:** Test Basic Auth with valid/invalid credentials
-- **Forms:** Verify all form fields (title, date, tags, content) are captured and saved correctly
-- **Errors:** Verify error handling for missing posts, invalid input, file write failures
-
-## Implementation Order
-
-1. Add Gin dependency to `go.mod`
-2. Create `NewServer()` constructor and auth middleware in `server.go`
-3. Register routes in `NewServer()`
-4. Convert handler signatures to `*gin.Context`
-5. Update `render()` and helper functions to work with Gin
-6. Update `cmd/nebbet/main.go` to use `NewServer()`
-7. Verify all routes work locally
-8. Test admin UI end-to-end
-
-## Risks & Mitigation
-
-| Risk | Mitigation |
-|------|-----------|
-| Breaking the admin UI during migration | Test each route in a browser after updating |
-| Form submission issues (e.g., multipart/form-data) | Use `c.PostForm()` and test file uploads if used |
-| Auth middleware interfering with static files | Apply middleware only to admin routes, not `/lib` or `/` |
-| Template rendering errors | Keep template loading and execution the same |
-
-## Success Criteria
-
-- ✓ All admin routes work with Gin
-- ✓ Auth middleware blocks unauthorized access
-- ✓ Create/edit/delete posts works end-to-end
-- ✓ Posts rebuild after create/edit/delete
-- ✓ No changes to password file format or post storage
-- ✓ Static file serving (`/lib`, public site) unchanged