Update frame work and workspace codebase

This commit is contained in:
2025-12-13 17:44:42 +00:00
parent 8100f87686
commit 72b5c18f29
16 changed files with 1499 additions and 0 deletions

View File

@@ -0,0 +1,125 @@
package http
import (
"encoding/json"
"net/http"
"github.com/example/storage-appliance/internal/auth"
)
// LoginHandler handles user login
func (a *App) LoginHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
// Show login page
data := map[string]interface{}{
"Title": "Login",
}
if err := templates.ExecuteTemplate(w, "login", data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
return
}
// Handle POST login
var req struct {
Username string `json:"username"`
Password string `json:"password"`
}
if r.Header.Get("Content-Type") == "application/json" {
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "invalid request", http.StatusBadRequest)
return
}
} else {
req.Username = r.FormValue("username")
req.Password = r.FormValue("password")
}
// Authenticate user
userStore := auth.NewUserStore(a.DB)
user, err := userStore.Authenticate(r.Context(), req.Username, req.Password)
if err != nil {
if r.Header.Get("HX-Request") == "true" {
w.Header().Set("Content-Type", "text/html")
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte(`<div class="text-red-600">Invalid username or password</div>`))
} else {
http.Error(w, "invalid credentials", http.StatusUnauthorized)
}
return
}
// Create session
sessionStore := auth.NewSessionStore(a.DB)
session, err := sessionStore.CreateSession(r.Context(), user.ID)
if err != nil {
http.Error(w, "failed to create session", http.StatusInternalServerError)
return
}
// Set session cookie
http.SetCookie(w, &http.Cookie{
Name: auth.SessionCookieName,
Value: session.Token,
Path: "/",
HttpOnly: true,
Secure: false, // Set to true in production with HTTPS
SameSite: http.SameSiteStrictMode,
MaxAge: int(auth.SessionDuration.Seconds()),
})
// Set CSRF token cookie
csrfToken := generateCSRFToken()
http.SetCookie(w, &http.Cookie{
Name: "csrf_token",
Value: csrfToken,
Path: "/",
HttpOnly: false, // Needed for HTMX to read it
Secure: false,
SameSite: http.SameSiteStrictMode,
MaxAge: int(auth.SessionDuration.Seconds()),
})
// Redirect or return success
if r.Header.Get("HX-Request") == "true" {
w.Header().Set("HX-Redirect", "/dashboard")
w.WriteHeader(http.StatusOK)
} else {
http.Redirect(w, r, "/dashboard", http.StatusFound)
}
}
// LogoutHandler handles user logout
func (a *App) LogoutHandler(w http.ResponseWriter, r *http.Request) {
// Get session token from cookie
cookie, err := r.Cookie(auth.SessionCookieName)
if err == nil {
// Delete session
sessionStore := auth.NewSessionStore(a.DB)
sessionStore.DeleteSession(r.Context(), cookie.Value)
}
// Clear cookies
http.SetCookie(w, &http.Cookie{
Name: auth.SessionCookieName,
Value: "",
Path: "/",
HttpOnly: true,
MaxAge: -1,
})
http.SetCookie(w, &http.Cookie{
Name: "csrf_token",
Value: "",
Path: "/",
HttpOnly: false,
MaxAge: -1,
})
if r.Header.Get("HX-Request") == "true" {
w.Header().Set("HX-Redirect", "/login")
w.WriteHeader(http.StatusOK)
} else {
http.Redirect(w, r, "/login", http.StatusFound)
}
}