Initial commit: Phase 1+2 prototype

- Go backend with SQLite, JWT auth, file CRUD
- Vue 3 frontend with split/raw/WYSIWYG editor modes
- Markdown preview (marked, GFM)
- Formatting toolbar + keyboard shortcuts
- File tree with search, create, delete
- Light/dark theme toggle
- Admin panel (user management)
- Preferences (timezone, theme, default mode)
- Shared documents section (placeholder)
- Export: PDF, HTML, MD
- Build daemon (Python, stdlib only)
- Build job queue API
- Docker deployment
This commit is contained in:
2026-05-22 19:48:48 +02:00
commit 0c1047d390
26 changed files with 6206 additions and 0 deletions
+63
View File
@@ -0,0 +1,63 @@
package api
import (
"context"
"net/http"
"strings"
"markdownhub/internal/auth"
)
type contextKey string
const (
ctxUserID contextKey = "userID"
ctxIsAdmin contextKey = "isAdmin"
)
func (s *Server) requireAuth(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
tokenStr := extractToken(r)
if tokenStr == "" {
http.Error(w, `{"error":"unauthorized"}`, http.StatusUnauthorized)
return
}
userID, isAdmin, err := auth.ValidateToken(tokenStr, s.secret)
if err != nil || userID == "" {
http.Error(w, `{"error":"unauthorized"}`, http.StatusUnauthorized)
return
}
ctx := context.WithValue(r.Context(), ctxUserID, userID)
ctx = context.WithValue(ctx, ctxIsAdmin, isAdmin)
next(w, r.WithContext(ctx))
}
}
func (s *Server) requireAdmin(next http.HandlerFunc) http.HandlerFunc {
return s.requireAuth(func(w http.ResponseWriter, r *http.Request) {
isAdmin, _ := r.Context().Value(ctxIsAdmin).(bool)
if !isAdmin {
http.Error(w, `{"error":"forbidden"}`, http.StatusForbidden)
return
}
next(w, r)
})
}
func extractToken(r *http.Request) string {
// Cookie first
if c, err := r.Cookie("authToken"); err == nil && c.Value != "" {
return c.Value
}
// Bearer header
h := r.Header.Get("Authorization")
if strings.HasPrefix(h, "Bearer ") {
return h[7:]
}
return ""
}
func getUserID(r *http.Request) string {
v, _ := r.Context().Value(ctxUserID).(string)
return v
}