From b86ee549343b7da305afef6918fd4eea4627d52f Mon Sep 17 00:00:00 2001 From: omigamedev Date: Sun, 18 Jan 2026 20:18:51 +0100 Subject: [PATCH] update M05 frontend with htmx + Go templates for NAS deployment --- DEV_PORTAL_M05_FRONTEND.md | 348 +++++++++++++++++++++++++------------ 1 file changed, 238 insertions(+), 110 deletions(-) diff --git a/DEV_PORTAL_M05_FRONTEND.md b/DEV_PORTAL_M05_FRONTEND.md index 746f02e..a99befe 100644 --- a/DEV_PORTAL_M05_FRONTEND.md +++ b/DEV_PORTAL_M05_FRONTEND.md @@ -1,8 +1,48 @@ # Milestone 5: Developer Portal Frontend -**Status**: Planning +**Status**: Decided **Goal**: Web interface for developer account and app management. +## Decision + +**htmx + Go Templates** for server-rendered UI from the single Go container: + +``` +Rendering: Go html/template +Interactivity: htmx (partial page updates) +Styling: Tailwind CSS (compiled at build time) +Charts: Chart.js (lightweight) +Icons: Heroicons or Lucide +Build: Embed static assets in Go binary +``` + +### Rationale + +1. **Single container** - No separate Node.js server needed +2. **Server-rendered** - All HTML generated by Go templates +3. **htmx for interactivity** - AJAX without JavaScript framework +4. **Embedded assets** - CSS/JS bundled into Go binary via `embed` +5. **Simple deployment** - Just the Go binary, nothing else +6. **Low resource usage** - Perfect for Synology NAS + +### Architecture + +``` +┌─────────────────────────────────────────┐ +│ mosis-portal container │ +│ ┌────────────────────────────────────┐ │ +│ │ Go binary │ │ +│ │ ├── Chi router │ │ +│ │ ├── html/template rendering │ │ +│ │ ├── Embedded static assets │ │ +│ │ │ ├── tailwind.css (built) │ │ +│ │ │ ├── htmx.min.js │ │ +│ │ │ └── chart.min.js │ │ +│ │ └── SQLite database │ │ +│ └────────────────────────────────────┘ │ +└─────────────────────────────────────────┘ +``` + --- ## Overview @@ -246,136 +286,224 @@ Approach: npm package --- -## Key Features +## Key Features (htmx Implementation) -### File Upload +### File Upload with Progress -```typescript -// Drag and drop with progress - - -

Drop your package here

-
+```html + +
+ +
+ + +
+ +
+ Uploading... +
+ + +
+ +
``` -### Real-time Validation +### Dynamic App List -```typescript -// Validate package client-side before upload -async function validatePackage(file: File) { - const zip = await JSZip.loadAsync(file); - - // Check manifest - const manifest = await zip.file('manifest.json')?.async('text'); - if (!manifest) throw new Error('Missing manifest.json'); - - const parsed = JSON.parse(manifest); - ManifestSchema.parse(parsed); - - // Check required files - const entry = parsed.entry; - if (!zip.file(entry)) { - throw new Error(`Entry file not found: ${entry}`); - } - - return parsed; -} +```html + +
+ {{range .Apps}} +
+ {{.Name}} +

{{.Name}}

+ {{.Status}} + View → +
+ {{end}} +
``` -### Analytics Dashboard +### Form with Validation -```typescript -// Recharts example - - - - - - +```html + +
+
+ + + +
+ +
+ + + +
+ + +
+``` + +### Analytics Chart + +```html + +
+ +
+ + ``` --- -## State Management +## Go Template Structure -### Server State (React Query) +### Base Layout -```typescript -// Fetch apps -const { data: apps, isLoading } = useQuery({ - queryKey: ['apps'], - queryFn: () => api.get('/apps'), -}); - -// Create app mutation -const createApp = useMutation({ - mutationFn: (data) => api.post('/apps', data), - onSuccess: () => { - queryClient.invalidateQueries(['apps']); - }, -}); +```go +// templates/layouts/base.html +{{define "base"}} + + + + + + {{.Title}} - Mosis Developer Portal + + + + + {{template "navbar" .}} +
+ {{template "content" .}} +
+ + +{{end}} ``` -### Client State (Zustand) +### Page Template -```typescript -// UI state -const useStore = create((set) => ({ - sidebarOpen: true, - toggleSidebar: () => set((s) => ({ sidebarOpen: !s.sidebarOpen })), -})); +```go +// templates/pages/dashboard.html +{{define "content"}} +

Dashboard

+ +
+ {{template "stat-card" dict "Label" "Total Apps" "Value" .Stats.TotalApps}} + {{template "stat-card" dict "Label" "Downloads" "Value" .Stats.Downloads}} + {{template "stat-card" dict "Label" "Active Users" "Value" .Stats.ActiveUsers}} +
+ +
+

Your Apps

+ + New App +
+ +
+ Loading... +
+{{end}} ``` --- ## Authentication Flow -### Login Page +### Login Page (Go Template) -```tsx -export default function LoginPage() { - return ( -
- - -

Sign in to Mosis

-
- - - - -
- - - -
-
-
+```html +{{define "content"}} +
+
+

Sign in to Mosis

+ + - ); -} +
+
+{{end}} ``` -### Protected Routes +### Auth Middleware (Go) -```tsx -// Middleware (Next.js) -export function middleware(request: NextRequest) { - const token = request.cookies.get('token'); - - if (!token && request.nextUrl.pathname.startsWith('/dashboard')) { - return NextResponse.redirect(new URL('/login', request.url)); - } +```go +// middleware/auth.go +func RequireAuth(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + session, err := sessionStore.Get(r, "session") + if err != nil || session.Values["developer_id"] == nil { + http.Redirect(w, r, "/login", http.StatusSeeOther) + return + } + next.ServeHTTP(w, r) + }) } + +// Usage in router +r.Group(func(r chi.Router) { + r.Use(RequireAuth) + r.Get("/dashboard", handlers.Dashboard) + r.Get("/apps", handlers.AppList) + r.Get("/apps/{id}", handlers.AppDetail) +}) ``` --- @@ -433,11 +561,11 @@ npm install @axe-core/react ## Deliverables -- [ ] Framework selection -- [ ] UI component library selection +- [x] Framework selection (htmx + Go Templates) +- [x] UI component library selection (Tailwind CSS + Chart.js) - [ ] Design system (colors, typography) -- [ ] Page wireframes -- [ ] Authentication flow +- [x] Page wireframes (see above) +- [x] Authentication flow (server sessions + OAuth) - [ ] Dashboard implementation - [ ] App management pages - [ ] Version submission flow @@ -449,10 +577,10 @@ npm install @axe-core/react ## Open Questions -1. Dark mode support? -2. Internationalization (i18n)? -3. Custom domain for docs vs integrated? -4. Email notifications UI? +1. ~~Dark mode support?~~ → Defer to post-MVP (Tailwind makes it easy to add later) +2. ~~Internationalization (i18n)?~~ → English only for MVP +3. ~~Custom domain for docs vs integrated?~~ → Integrated into same Go server at /docs +4. Email notifications UI? → Consider for v1.1 ---