Phase 2: auth, session management, layout, PWA manifest
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+49
-1
@@ -2,6 +2,11 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
"image/png"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -13,8 +18,10 @@ import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
|
||||
"qbank/internal/auth"
|
||||
"qbank/internal/config"
|
||||
"qbank/internal/db"
|
||||
"qbank/internal/handlers"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -40,17 +47,38 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
_ = db.New(database) // repo will be wired into handlers in later phases
|
||||
repo := db.New(database)
|
||||
authMgr := auth.NewManager(database)
|
||||
renderer := handlers.NewRenderer("web/templates")
|
||||
|
||||
ensureIcons("web/static")
|
||||
|
||||
authH := handlers.NewAuthHandler(authMgr, repo, renderer)
|
||||
homeH := handlers.NewHomeHandler(authMgr, renderer)
|
||||
|
||||
r := chi.NewRouter()
|
||||
r.Use(middleware.RequestID)
|
||||
r.Use(requestLogger(logger))
|
||||
r.Use(middleware.Recoverer)
|
||||
r.Use(authMgr.SM.LoadAndSave)
|
||||
|
||||
r.Get("/healthz", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("OK"))
|
||||
})
|
||||
r.Get("/sw.js", func(w http.ResponseWriter, r *http.Request) {
|
||||
http.ServeFile(w, r, "web/static/sw.js")
|
||||
})
|
||||
r.Handle("/static/*", http.StripPrefix("/static/", http.FileServer(http.Dir("web/static"))))
|
||||
|
||||
r.Get("/login", authH.LoginGet)
|
||||
r.Post("/login", authH.LoginPost)
|
||||
r.Post("/logout", authH.Logout)
|
||||
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(authMgr.RequireAuth)
|
||||
r.Get("/", homeH.Handle)
|
||||
})
|
||||
|
||||
srv := &http.Server{
|
||||
Addr: ":" + cfg.Port,
|
||||
@@ -80,6 +108,26 @@ func main() {
|
||||
slog.Info("server stopped")
|
||||
}
|
||||
|
||||
// ensureIcons generates simple solid-color PNG icons if they don't already exist.
|
||||
func ensureIcons(dir string) {
|
||||
for _, size := range []int{192, 512} {
|
||||
path := filepath.Join(dir, fmt.Sprintf("icon-%d.png", size))
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
continue
|
||||
}
|
||||
img := image.NewRGBA(image.Rect(0, 0, size, size))
|
||||
blue := color.RGBA{R: 37, G: 99, B: 235, A: 255} // Tailwind blue-600
|
||||
draw.Draw(img, img.Bounds(), &image.Uniform{C: blue}, image.Point{}, draw.Src)
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
slog.Warn("create icon", "path", path, "err", err)
|
||||
continue
|
||||
}
|
||||
png.Encode(f, img)
|
||||
f.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func requestLogger(logger *slog.Logger) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
Reference in New Issue
Block a user