Files
paliad/internal/handlers/auth.go
m 40a9c927fb feat: rewrite frontend from Go templates to Bun + TSX
Replace Go HTML template rendering with a Bun + TSX build-time static
site generator. Go backend becomes API-only for auth.

Frontend:
- Custom JSX-to-HTML-string factory (zero dependencies)
- TSX components for Header, Footer, index page, login page
- Client-side login.ts handles tab switching and fetch()-based auth
- Bun bundler compiles client JS, build.ts renders pages to dist/

Backend:
- Auth handlers return JSON (POST /api/login, POST /api/register)
- Login page served as static HTML from dist/
- Static assets served from /assets/ (public)
- Auth middleware unchanged (cookie check, redirect to /login)
- Removed template parsing and renderPage

Dockerfile:
- 3-stage build: Bun frontend -> Go backend -> alpine runtime
- Frontend dist copied to /app/dist in final image

Removed: templates/, static/css/ (replaced by frontend/)
2026-04-14 16:50:27 +02:00

117 lines
3.6 KiB
Go

package handlers
import (
"encoding/json"
"log"
"net/http"
"strings"
"time"
"mgit.msbls.de/m/patholo/internal/auth"
)
func handleLoginPage(w http.ResponseWriter, r *http.Request) {
if cookie, err := r.Cookie(auth.SessionCookieName); err == nil && cookie.Value != "" {
if exp, err := auth.DecodeJWTExpiry(cookie.Value); err == nil && time.Now().Before(exp) {
http.Redirect(w, r, "/", http.StatusFound)
return
}
}
http.ServeFile(w, r, "dist/login.html")
}
func handleAPILogin(w http.ResponseWriter, r *http.Request) {
var req struct {
Email string `json:"email"`
Password string `json:"password"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "Ungültige Anfrage."})
return
}
req.Email = strings.TrimSpace(req.Email)
if req.Email == "" || req.Password == "" {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "Bitte E-Mail und Passwort eingeben."})
return
}
if !isHoganLovellsEmail(req.Email) {
writeJSON(w, http.StatusForbidden, map[string]string{"error": "Zugang nur für @hoganlovells.com E-Mail-Adressen."})
return
}
tokens, err := authClient.SignIn(req.Email, req.Password)
if err != nil {
log.Printf("sign in failed for %s: %v", req.Email, err)
errMsg := "Anmeldung fehlgeschlagen. Bitte versuchen Sie es erneut."
if strings.Contains(err.Error(), "Invalid login credentials") {
errMsg = "Ungültige E-Mail-Adresse oder Passwort."
}
writeJSON(w, http.StatusUnauthorized, map[string]string{"error": errMsg})
return
}
auth.SetAuthCookies(w, r, tokens)
writeJSON(w, http.StatusOK, map[string]string{"ok": "true"})
}
func handleAPIRegister(w http.ResponseWriter, r *http.Request) {
var req struct {
Email string `json:"email"`
Password string `json:"password"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "Ungültige Anfrage."})
return
}
req.Email = strings.TrimSpace(req.Email)
if req.Email == "" || req.Password == "" {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "Bitte alle Felder ausfüllen."})
return
}
if len(req.Password) < 8 {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "Passwort muss mindestens 8 Zeichen lang sein."})
return
}
if !isHoganLovellsEmail(req.Email) {
writeJSON(w, http.StatusForbidden, map[string]string{"error": "Registrierung nur für @hoganlovells.com E-Mail-Adressen."})
return
}
tokens, err := authClient.SignUp(req.Email, req.Password)
if err != nil {
log.Printf("sign up failed for %s: %v", req.Email, err)
errMsg := "Registrierung fehlgeschlagen. Bitte versuchen Sie es erneut."
if strings.Contains(err.Error(), "already registered") || strings.Contains(err.Error(), "already been registered") {
errMsg = "Ein Account mit dieser E-Mail existiert bereits."
}
writeJSON(w, http.StatusBadRequest, map[string]string{"error": errMsg})
return
}
if tokens != nil {
auth.SetAuthCookies(w, r, tokens)
writeJSON(w, http.StatusOK, map[string]string{"redirect": "/"})
return
}
writeJSON(w, http.StatusOK, map[string]string{"message": "Account erstellt. Bitte melden Sie sich an."})
}
func handleLogout(w http.ResponseWriter, r *http.Request) {
if cookie, err := r.Cookie(auth.SessionCookieName); err == nil {
authClient.SignOut(cookie.Value)
}
auth.ClearAuthCookies(w)
http.Redirect(w, r, "/login", http.StatusFound)
}
func isHoganLovellsEmail(email string) bool {
parts := strings.SplitN(email, "@", 2)
return len(parts) == 2 && strings.EqualFold(parts[1], "hoganlovells.com")
}