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/)
117 lines
3.6 KiB
Go
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")
|
|
}
|