Implement Phase 6: REST API with chi router

- Login endpoint (password → bearer token + session cookie)
- Auth middleware (bearer header or session cookie)
- Notebook list endpoint (authenticated)
- Page SVG/JPG rendering endpoints (authenticated)
- Notebook PDF download endpoint (authenticated)
- Share link endpoints: view, page SVG, page JPG, PDF (no auth)
- Route registration with chi groups

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-24 19:55:47 -07:00
parent 5993d20995
commit 37c2d35ceb
7 changed files with 482 additions and 0 deletions

View File

@@ -0,0 +1,55 @@
package server
import (
"database/sql"
"encoding/json"
"net/http"
"time"
"git.wntrmute.dev/kyle/eng-pad-server/internal/auth"
)
type loginRequest struct {
Username string `json:"username"`
Password string `json:"password"`
}
type loginResponse struct {
Token string `json:"token"`
}
func handleLogin(database *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req loginRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
return
}
userID, err := auth.AuthenticateUser(database, req.Username, req.Password)
if err != nil {
http.Error(w, `{"error":"invalid credentials"}`, http.StatusUnauthorized)
return
}
token, err := auth.CreateToken(database, userID, 24*time.Hour)
if err != nil {
http.Error(w, `{"error":"internal error"}`, http.StatusInternalServerError)
return
}
// Set session cookie
http.SetCookie(w, &http.Cookie{
Name: "session",
Value: token,
Path: "/",
HttpOnly: true,
Secure: true,
SameSite: http.SameSiteStrictMode,
MaxAge: 86400,
})
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(loginResponse{Token: token})
}
}