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