309 lines
9.4 KiB
Go
309 lines
9.4 KiB
Go
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,
|
||
})
|
||
}
|