chore: auto-commit (28 archivos)
- app.md - auth.go - chat.go - chat.log - db.go - frontend/package.json - frontend/pnpm-lock.yaml - frontend/src/App.tsx - frontend/src/Root.tsx - frontend/src/api.ts - ... Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"fn-registry/functions/infra"
|
||||
)
|
||||
|
||||
const (
|
||||
cookieName = "kanban_session"
|
||||
sessionTTL = 7 * 24 * time.Hour
|
||||
)
|
||||
|
||||
type ctxKey string
|
||||
|
||||
const userCtxKey ctxKey = "kanban_user_id"
|
||||
|
||||
func setSessionCookie(w http.ResponseWriter, token string, expiresAt int64) {
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: cookieName,
|
||||
Value: token,
|
||||
Path: "/",
|
||||
HttpOnly: true,
|
||||
SameSite: http.SameSiteLaxMode,
|
||||
Expires: time.Unix(expiresAt, 0),
|
||||
})
|
||||
}
|
||||
|
||||
func clearSessionCookie(w http.ResponseWriter) {
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: cookieName,
|
||||
Value: "",
|
||||
Path: "/",
|
||||
HttpOnly: true,
|
||||
SameSite: http.SameSiteLaxMode,
|
||||
MaxAge: -1,
|
||||
})
|
||||
}
|
||||
|
||||
func tokenFromRequest(r *http.Request) string {
|
||||
if c, err := r.Cookie(cookieName); err == nil && c.Value != "" {
|
||||
return c.Value
|
||||
}
|
||||
auth := r.Header.Get("Authorization")
|
||||
if len(auth) > 7 && auth[:7] == "Bearer " {
|
||||
return auth[7:]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// POST /api/auth/register {username, password, display_name?}
|
||||
func handleRegister(db *DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var body struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
DisplayName string `json:"display_name"`
|
||||
}
|
||||
if err := infra.HTTPParseBody(r, &body, maxBodyBytes); err != nil {
|
||||
badRequest(w, err.Error())
|
||||
return
|
||||
}
|
||||
u, err := db.CreateUser(body.Username, body.Password, body.DisplayName)
|
||||
if err != nil {
|
||||
if errors.Is(err, errUserAlreadyExists) {
|
||||
infra.HTTPErrorResponse(w, infra.HTTPError{Status: http.StatusConflict, Code: "user_exists", Message: err.Error()})
|
||||
return
|
||||
}
|
||||
badRequest(w, err.Error())
|
||||
return
|
||||
}
|
||||
infra.HTTPJSONResponse(w, http.StatusCreated, u)
|
||||
}
|
||||
}
|
||||
|
||||
// POST /api/auth/login {username, password}
|
||||
func handleLogin(db *DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var body struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
if err := infra.HTTPParseBody(r, &body, maxBodyBytes); err != nil {
|
||||
badRequest(w, err.Error())
|
||||
return
|
||||
}
|
||||
u, err := db.Authenticate(body.Username, body.Password)
|
||||
if err != nil {
|
||||
infra.HTTPErrorResponse(w, infra.HTTPError{Status: http.StatusUnauthorized, Code: "invalid_credentials", Message: "invalid username or password"})
|
||||
return
|
||||
}
|
||||
sess, err := infra.SessionCreate(db.conn, u.ID, sessionTTL, map[string]any{"username": u.Username})
|
||||
if err != nil {
|
||||
serverError(w, err)
|
||||
return
|
||||
}
|
||||
setSessionCookie(w, sess.Token, sess.ExpiresAt)
|
||||
infra.HTTPJSONResponse(w, http.StatusOK, u)
|
||||
}
|
||||
}
|
||||
|
||||
// POST /api/auth/logout
|
||||
func handleLogout(db *DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
token := tokenFromRequest(r)
|
||||
if token != "" {
|
||||
_ = db.DeleteSessionByToken(token)
|
||||
}
|
||||
clearSessionCookie(w)
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
}
|
||||
|
||||
// GET /api/me
|
||||
func handleMe(db *DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
uid, ok := infra.UserIDFromContext(r.Context(), userCtxKey)
|
||||
if !ok {
|
||||
infra.HTTPErrorResponse(w, infra.HTTPError{Status: http.StatusUnauthorized, Code: "unauthorized", Message: "no session"})
|
||||
return
|
||||
}
|
||||
u, err := db.GetUserByID(uid)
|
||||
if err != nil {
|
||||
serverError(w, err)
|
||||
return
|
||||
}
|
||||
infra.HTTPJSONResponse(w, http.StatusOK, u)
|
||||
}
|
||||
}
|
||||
|
||||
// GET /api/users
|
||||
func handleListUsers(db *DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
users, err := db.ListUsers()
|
||||
if err != nil {
|
||||
serverError(w, err)
|
||||
return
|
||||
}
|
||||
infra.HTTPJSONResponse(w, http.StatusOK, users)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user