feat: 10 quick wins — account deletion, health check, debug cleanup, UX polish, graceful shutdown
- backend/handlers/user/account.go: Wire DELETE /api/user/account to call anonymize_user() for registered users and delete_guest_user() for guests, with CardDAV contact cleanup - backend/handlers/user/profile_test.go: Add TestAccount_DeleteGuest and enhance TestAccount_Delete to verify anonymization results - backend/main.go: Add GET /api/health endpoint with DB ping and S3 status check; add HSTS and Referrer-Policy security headers; replace http.ListenAndServe with http.Server + graceful SIGTERM/SIGINT shutdown - frontend/routes/+layout.svelte: Replace alert() with toast notifications for email verification flow - frontend/routes/login/+page.svelte: Replace alert() with toast.info for social login prototype buttons - frontend/booking/BookingFlow.svelte: Remove 2 console.log debug calls; add cancellation policy note in Step 3; add timezone policy comment - frontend/ImageUpload.svelte: Comment out debug console.log - init-scripts/init-script.sql: Add delete_guest_user() SQL function - docs: Update README.md and Obsidian notes to reflect completed items
This commit is contained in:
+68
-1
@@ -1,13 +1,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crussell/auth"
|
||||
"crussell/internal/dav"
|
||||
"crussell/internal/s3"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
@@ -56,6 +60,42 @@ func initS3() {
|
||||
}
|
||||
}
|
||||
|
||||
func healthCheckHandler(w http.ResponseWriter, r *http.Request) {
|
||||
status := "ok"
|
||||
services := map[string]string{
|
||||
"backend": "ok",
|
||||
"database": "ok",
|
||||
"s3_storage": "ok",
|
||||
"square_payments": "not_implemented",
|
||||
"frontend": "unknown",
|
||||
}
|
||||
|
||||
if db.DB != nil {
|
||||
if err := db.DB.Ping(r.Context()); err != nil {
|
||||
services["database"] = "error"
|
||||
status = "degraded"
|
||||
}
|
||||
} else {
|
||||
services["database"] = "error"
|
||||
status = "degraded"
|
||||
}
|
||||
|
||||
if s3.Client == nil {
|
||||
services["s3_storage"] = "not_configured"
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if status == "degraded" {
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
} else {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"status": status,
|
||||
"services": services,
|
||||
})
|
||||
}
|
||||
|
||||
func main() {
|
||||
initDB()
|
||||
initDav()
|
||||
@@ -74,6 +114,10 @@ func main() {
|
||||
w.Header().Set("X-Content-Type-Options", "nosniff")
|
||||
w.Header().Set("X-Frame-Options", "DENY")
|
||||
w.Header().Set("X-XSS-Protection", "1; mode=block")
|
||||
// TODO: Enable HSTS in production
|
||||
w.Header().Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
|
||||
// TODO: Enable Referrer-Policy in production
|
||||
w.Header().Set("Referrer-Policy", "strict-origin-when-cross-origin")
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
})
|
||||
@@ -98,6 +142,9 @@ func main() {
|
||||
r.Post("/verify/generate", authHandlers.GenerateVerificationCodeHandler)
|
||||
r.Post("/verify/check", authHandlers.VerifyCodeHandler)
|
||||
|
||||
// Health check
|
||||
r.Get("/health", healthCheckHandler)
|
||||
|
||||
// Public contact info
|
||||
r.Get("/contact", user.GetContactInfoHandler)
|
||||
|
||||
@@ -225,6 +272,26 @@ func main() {
|
||||
})
|
||||
})
|
||||
|
||||
srv := &http.Server{
|
||||
Addr: ":8080",
|
||||
Handler: r,
|
||||
}
|
||||
|
||||
quit := make(chan os.Signal, 1)
|
||||
signal.Notify(quit, syscall.SIGTERM, syscall.SIGINT)
|
||||
go func() {
|
||||
<-quit
|
||||
log.Println("Shutting down server...")
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel()
|
||||
if err := srv.Shutdown(ctx); err != nil {
|
||||
log.Printf("Server forced to shutdown: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
fmt.Println("Server is listening on :8080")
|
||||
http.ListenAndServe(":8080", r)
|
||||
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
log.Fatalf("Server failed to start: %v", err)
|
||||
}
|
||||
log.Println("Server exited")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user