package handlers import ( "encoding/json" "net/http" "strconv" "git.kirlllll.ru/volontery/backend/internal/api/middleware" "git.kirlllll.ru/volontery/backend/internal/service" "github.com/go-chi/chi/v5" ) // UserHandler обрабатывает запросы пользователей type UserHandler struct { userService *service.UserService } // NewUserHandler создает новый UserHandler func NewUserHandler(userService *service.UserService) *UserHandler { return &UserHandler{ userService: userService, } } // GetProfile возвращает профиль пользователя // GET /api/v1/users/{id} func (h *UserHandler) GetProfile(w http.ResponseWriter, r *http.Request) { idStr := chi.URLParam(r, "id") userID, err := strconv.ParseInt(idStr, 10, 64) if err != nil { respondError(w, http.StatusBadRequest, "invalid user id") return } profile, err := h.userService.GetUserProfile(r.Context(), userID) if err != nil { respondError(w, http.StatusNotFound, "user not found") return } respondJSON(w, http.StatusOK, profile) } // GetMyProfile возвращает профиль текущего пользователя // GET /api/v1/users/me func (h *UserHandler) GetMyProfile(w http.ResponseWriter, r *http.Request) { userID, ok := middleware.GetUserIDFromContext(r.Context()) if !ok { respondError(w, http.StatusUnauthorized, "unauthorized") return } profile, err := h.userService.GetUserProfile(r.Context(), userID) if err != nil { respondError(w, http.StatusNotFound, "user not found") return } respondJSON(w, http.StatusOK, profile) } // UpdateProfile обновляет профиль текущего пользователя // PATCH /api/v1/users/me func (h *UserHandler) UpdateProfile(w http.ResponseWriter, r *http.Request) { userID, ok := middleware.GetUserIDFromContext(r.Context()) if !ok { respondError(w, http.StatusUnauthorized, "unauthorized") return } var input service.UpdateProfileInput if err := json.NewDecoder(r.Body).Decode(&input); err != nil { respondError(w, http.StatusBadRequest, "invalid request body") return } if err := h.userService.UpdateUserProfile(r.Context(), userID, input); err != nil { respondError(w, http.StatusInternalServerError, "failed to update profile") return } respondJSON(w, http.StatusOK, map[string]string{"message": "profile updated successfully"}) } // UpdateLocation обновляет местоположение пользователя // POST /api/v1/users/me/location func (h *UserHandler) UpdateLocation(w http.ResponseWriter, r *http.Request) { userID, ok := middleware.GetUserIDFromContext(r.Context()) if !ok { respondError(w, http.StatusUnauthorized, "unauthorized") return } var input struct { Latitude float64 `json:"latitude"` Longitude float64 `json:"longitude"` } if err := json.NewDecoder(r.Body).Decode(&input); err != nil { respondError(w, http.StatusBadRequest, "invalid request body") return } if err := h.userService.UpdateUserLocation(r.Context(), userID, input.Latitude, input.Longitude); err != nil { respondError(w, http.StatusBadRequest, err.Error()) return } respondJSON(w, http.StatusOK, map[string]string{"message": "location updated successfully"}) } // GetMyRoles возвращает роли текущего пользователя // GET /api/v1/users/me/roles func (h *UserHandler) GetMyRoles(w http.ResponseWriter, r *http.Request) { userID, ok := middleware.GetUserIDFromContext(r.Context()) if !ok { respondError(w, http.StatusUnauthorized, "unauthorized") return } roles, err := h.userService.GetUserRoles(r.Context(), userID) if err != nil { respondError(w, http.StatusInternalServerError, "failed to get roles") return } respondJSON(w, http.StatusOK, roles) } // GetMyPermissions возвращает разрешения текущего пользователя // GET /api/v1/users/me/permissions func (h *UserHandler) GetMyPermissions(w http.ResponseWriter, r *http.Request) { userID, ok := middleware.GetUserIDFromContext(r.Context()) if !ok { respondError(w, http.StatusUnauthorized, "unauthorized") return } permissions, err := h.userService.GetUserPermissions(r.Context(), userID) if err != nil { respondError(w, http.StatusInternalServerError, "failed to get permissions") return } respondJSON(w, http.StatusOK, permissions) } // VerifyEmail подтверждает email пользователя // POST /api/v1/users/me/verify-email func (h *UserHandler) VerifyEmail(w http.ResponseWriter, r *http.Request) { userID, ok := middleware.GetUserIDFromContext(r.Context()) if !ok { respondError(w, http.StatusUnauthorized, "unauthorized") return } // В реальной системе здесь должна быть проверка токена из письма // Сейчас просто подтверждаем email для текущего пользователя if err := h.userService.VerifyEmail(r.Context(), userID); err != nil { respondError(w, http.StatusInternalServerError, "failed to verify email") return } respondJSON(w, http.StatusOK, map[string]string{"message": "email verified successfully"}) } // CheckPermission проверяет наличие разрешения у пользователя // GET /api/v1/users/me/permissions/{permission_name}/check func (h *UserHandler) CheckPermission(w http.ResponseWriter, r *http.Request) { userID, ok := middleware.GetUserIDFromContext(r.Context()) if !ok { respondError(w, http.StatusUnauthorized, "unauthorized") return } permissionName := chi.URLParam(r, "permission_name") if permissionName == "" { respondError(w, http.StatusBadRequest, "permission_name is required") return } hasPermission, err := h.userService.HasPermission(r.Context(), userID, permissionName) if err != nil { respondError(w, http.StatusInternalServerError, "failed to check permission") return } respondJSON(w, http.StatusOK, map[string]interface{}{ "has_permission": hasPermission, "permission_name": permissionName, }) } // ========== AdminHandler - новый хендлер для административных функций ========== // AdminHandler обрабатывает административные запросы type AdminHandler struct { userService *service.UserService } // NewAdminHandler создает новый AdminHandler func NewAdminHandler(userService *service.UserService) *AdminHandler { return &AdminHandler{ userService: userService, } } // AssignRole назначает роль пользователю // POST /api/v1/admin/users/{user_id}/roles/{role_id} func (h *AdminHandler) AssignRole(w http.ResponseWriter, r *http.Request) { adminID, ok := middleware.GetUserIDFromContext(r.Context()) if !ok { respondError(w, http.StatusUnauthorized, "unauthorized") return } // Проверка, что текущий пользователь является администратором // В реальной системе это должно быть в middleware isAdmin, err := h.userService.HasPermission(r.Context(), adminID, "manage_users") if err != nil { respondError(w, http.StatusInternalServerError, "failed to check permissions") return } if !isAdmin { respondError(w, http.StatusForbidden, "admin role required") return } userIDStr := chi.URLParam(r, "user_id") userID, err := strconv.ParseInt(userIDStr, 10, 64) if err != nil { respondError(w, http.StatusBadRequest, "invalid user_id") return } roleIDStr := chi.URLParam(r, "role_id") roleID, err := strconv.ParseInt(roleIDStr, 10, 64) if err != nil { respondError(w, http.StatusBadRequest, "invalid role_id") return } if err := h.userService.AssignRole(r.Context(), userID, roleID, adminID); err != nil { respondError(w, http.StatusBadRequest, err.Error()) return } respondJSON(w, http.StatusOK, map[string]interface{}{ "message": "role assigned successfully", "user_id": userID, "role_id": roleID, }) } // ========== RequestHandler - дополнительный метод ========== // ModerateRequestProcedure модерирует заявку через stored procedure // POST /api/v1/moderation/requests/{id}/moderate func (h *RequestHandler) ModerateRequestProcedure(w http.ResponseWriter, r *http.Request) { moderatorID, ok := middleware.GetUserIDFromContext(r.Context()) if !ok { respondError(w, http.StatusUnauthorized, "unauthorized") return } idStr := chi.URLParam(r, "id") requestID, err := strconv.ParseInt(idStr, 10, 64) if err != nil { respondError(w, http.StatusBadRequest, "invalid request id") return } var input struct { Action string `json:"action"` Comment *string `json:"comment"` } if err := json.NewDecoder(r.Body).Decode(&input); err != nil { respondError(w, http.StatusBadRequest, "invalid request body") return } if input.Action != "approve" && input.Action != "reject" { respondError(w, http.StatusBadRequest, "action must be 'approve' or 'reject'") return } if input.Action == "reject" && (input.Comment == nil || *input.Comment == "") { respondError(w, http.StatusBadRequest, "comment is required when rejecting") return } result, err := h.requestService.ModerateRequestProcedure(r.Context(), requestID, moderatorID, input.Action, input.Comment) if err != nil { respondError(w, http.StatusBadRequest, err.Error()) return } if !result.Success { respondError(w, http.StatusBadRequest, result.Message) return } respondJSON(w, http.StatusOK, map[string]interface{}{ "success": result.Success, "message": result.Message, }) }