package api import ( "database/sql" "encoding/json" "net/http" "github.com/syncserver/internal/auth" ) type AuthHandler struct { db *sql.DB } func NewAuthHandler(db *sql.DB) *AuthHandler { return &AuthHandler{db: db} } type LoginRequest struct { Username string `json:"username"` Password string `json:"password"` } type LoginResponse struct { User UserResponse `json:"user"` ExpiresAt string `json:"expires_at"` } type UserResponse struct { ID int64 `json:"id"` Username string `json:"username"` Role string `json:"role"` } func (h *AuthHandler) Login(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 } row := h.db.QueryRow( "SELECT id, username, password_hash, role FROM users WHERE username = ?", req.Username, ) var u struct { ID int64 Username string PasswordHash string Role string } if err := row.Scan(&u.ID, &u.Username, &u.PasswordHash, &u.Role); err != nil { http.Error(w, `{"error":"invalid credentials"}`, http.StatusUnauthorized) return } if !auth.VerifyPassword([]byte(u.PasswordHash), req.Password) { http.Error(w, `{"error":"invalid credentials"}`, http.StatusUnauthorized) return } jwtMgr := auth.GetJWTManager() if jwtMgr == nil { http.Error(w, `{"error":"server misconfigured"}`, http.StatusInternalServerError) return } token, expiresAt, err := jwtMgr.Generate(u.ID, u.Username, u.Role) if err != nil { http.Error(w, `{"error":"failed to generate token"}`, http.StatusInternalServerError) return } auth.SetAuthCookie(w, token, expiresAt) resp := LoginResponse{ User: UserResponse{ ID: u.ID, Username: u.Username, Role: u.Role, }, ExpiresAt: expiresAt.Format("2006-01-02T15:04:05Z07:00"), } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(resp) } func (h *AuthHandler) Logout(w http.ResponseWriter, r *http.Request) { auth.ClearAuthCookie(w) w.WriteHeader(http.StatusNoContent) } func (h *AuthHandler) Me(w http.ResponseWriter, r *http.Request) { claims := auth.GetClaims(r.Context()) if claims == nil { http.Error(w, `{"error":"unauthorized"}`, http.StatusUnauthorized) return } resp := UserResponse{ ID: claims.UserID, Username: claims.Username, Role: claims.Role, } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{"user": resp}) }