package handler import ( "encoding/json" "errors" "net/http" "strings" "time" "5803025020/w04/internal/auth" "5803025020/w04/internal/httpjson" "5803025020/w04/internal/models" "5803025020/w04/internal/store" "github.com/google/uuid" ) type AuthHandler struct { userStore UserStore sessionStore SessionStore } func NewAuthHandler(userStore UserStore, sessionStore SessionStore) *AuthHandler { return &AuthHandler{ userStore: userStore, sessionStore: sessionStore, } } type authRequest struct { Username string `json:"username"` Password string `json:"password"` } func (h *AuthHandler) Register(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { httpjson.WriteError(w, http.StatusMethodNotAllowed, "Method Not Allowed") return } var req authRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { httpjson.WriteError(w, http.StatusBadRequest, "Bad Request") return } req.Username = strings.TrimSpace(req.Username) if len(req.Username) < 3 || len(req.Username) > 32 || req.Password == "" { httpjson.WriteError(w, http.StatusBadRequest, "Invalid username or password") return } hash, err := auth.HashPassword(req.Password) if err != nil { httpjson.WriteError(w, http.StatusInternalServerError, "Internal Server Error") return } user := &models.User{ ID: uuid.New().String(), Username: req.Username, PasswordHash: hash, } if err := h.userStore.Create(r.Context(), user); err != nil { if errors.Is(err, store.ErrUsernameTaken) { httpjson.WriteError(w, http.StatusConflict, "Username already exists") return } httpjson.WriteError(w, http.StatusInternalServerError, "Internal Server Error") return } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) _ = json.NewEncoder(w).Encode(user) } func (h *AuthHandler) Login(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { httpjson.WriteError(w, http.StatusMethodNotAllowed, "Method Not Allowed") return } var req authRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { httpjson.WriteError(w, http.StatusBadRequest, "Bad Request") return } user, err := h.userStore.GetByUsername(r.Context(), req.Username) if err != nil || !auth.CheckPassword(req.Password, user.PasswordHash) { httpjson.WriteError(w, http.StatusUnauthorized, "Invalid credentials") return } token, err := auth.GenerateToken() if err != nil { httpjson.WriteError(w, http.StatusInternalServerError, "Internal Server Error") return } session := &models.Session{ Token: token, UserID: user.ID, CreatedAt: time.Now(), } if err := h.sessionStore.Create(r.Context(), session); err != nil { httpjson.WriteError(w, http.StatusInternalServerError, "Internal Server Error") return } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) _ = json.NewEncoder(w).Encode(map[string]string{"session_token": session.Token}) } func (h *AuthHandler) Logout(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { httpjson.WriteError(w, http.StatusMethodNotAllowed, "Method Not Allowed") return } authHeader := r.Header.Get("Authorization") if authHeader == "" { httpjson.WriteError(w, http.StatusUnauthorized, "Unauthorized") return } token := authHeader if strings.HasPrefix(strings.ToLower(authHeader), "bearer ") { token = authHeader[len("Bearer "):] } token = strings.TrimSpace(token) if token == "" { httpjson.WriteError(w, http.StatusUnauthorized, "Unauthorized") return } if err := h.sessionStore.Delete(r.Context(), token); err != nil { if errors.Is(err, store.ErrSessionNotFound) { httpjson.WriteError(w, http.StatusUnauthorized, "Unauthorized") return } httpjson.WriteError(w, http.StatusInternalServerError, "Internal Server Error") return } w.WriteHeader(http.StatusNoContent) }