package handler import ( "encoding/json" "errors" "net/http" "strings" "time" "5803025015/w04/internal/auth" "5803025015/w04/internal/middleware" "5803025015/w04/internal/models" "5803025015/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"` } // ---------- shared JSON helpers (dipakai juga oleh snippet_handler.go) ---------- func writeError(w http.ResponseWriter, status int, msg string) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) _ = json.NewEncoder(w).Encode(map[string]string{"error": msg}) } func writeJSON(w http.ResponseWriter, status int, body any) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) _ = json.NewEncoder(w).Encode(body) } // ---------- handlers ---------- func (h *AuthHandler) Register(w http.ResponseWriter, r *http.Request) { var req authRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, "invalid request body") return } req.Username = strings.TrimSpace(req.Username) if len(req.Username) < 3 || len(req.Username) > 32 || req.Password == "" { writeError(w, http.StatusBadRequest, "invalid username or password") return } hash, err := auth.HashPassword(req.Password) if err != nil { writeError(w, http.StatusInternalServerError, "internal 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.ErrDuplicate) { writeError(w, http.StatusConflict, "username already exists") return } writeError(w, http.StatusInternalServerError, "internal error") return } writeJSON(w, http.StatusCreated, map[string]any{ "id": user.ID, "username": user.Username, }) } func (h *AuthHandler) Login(w http.ResponseWriter, r *http.Request) { var req authRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, "invalid request body") return } user, err := h.userStore.GetByUsername(r.Context(), req.Username) if err != nil || user == nil || !auth.CheckPassword(req.Password, user.PasswordHash) { writeError(w, http.StatusUnauthorized, "invalid credentials") return } token, err := auth.GenerateToken() if err != nil { writeError(w, http.StatusInternalServerError, "internal error") return } session := &models.Session{ Token: token, UserID: user.ID, CreatedAt: time.Now(), } if err := h.sessionStore.Create(r.Context(), session); err != nil { writeError(w, http.StatusInternalServerError, "internal error") return } writeJSON(w, http.StatusOK, map[string]any{ "session_token": token, }) } func (h *AuthHandler) Logout(w http.ResponseWriter, r *http.Request) { authHeader := r.Header.Get("Authorization") if authHeader == "" { writeError(w, http.StatusUnauthorized, "unauthorized") return } const prefix = "bearer " if !strings.HasPrefix(strings.ToLower(authHeader), prefix) { writeError(w, http.StatusUnauthorized, "unauthorized") return } token := strings.TrimSpace(authHeader[len(prefix):]) if token == "" { writeError(w, http.StatusUnauthorized, "unauthorized") return } if _, err := h.sessionStore.Get(r.Context(), token); err != nil { writeError(w, http.StatusUnauthorized, "unauthorized") return } if err := h.sessionStore.Delete(r.Context(), token); err != nil { writeError(w, http.StatusInternalServerError, "internal error") return } w.WriteHeader(http.StatusNoContent) } func (h *AuthHandler) GetProfile(w http.ResponseWriter, r *http.Request) { userID, _ := r.Context().Value(middleware.UserIDKey).(string) if userID == "" { writeError(w, http.StatusUnauthorized, "unauthorized") return } writeJSON(w, http.StatusOK, map[string]any{ "message": "Berhasil mengambil profil", "user_id": userID, }) }