initial commit

This commit is contained in:
2025-11-29 00:28:21 +05:00
parent 46229acc82
commit ec3b03a935
76 changed files with 13492 additions and 0 deletions

View File

@@ -0,0 +1,308 @@
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,
})
}