openapi: 3.1.0 info: title: Volunteer Coordination API description: | API для системы координации волонтёрской помощи маломобильным людям. Система позволяет: - Маломобильным гражданам создавать заявки на помощь - Волонтёрам находить заявки рядом с собой и откликаться на них - Модераторам управлять заявками и пользователями - Администраторам управлять пользователями и ролями version: 1.0.1 contact: name: API Support email: api@volontery.example.com license: name: MIT url: https://opensource.org/licenses/MIT servers: - url: http://localhost:8080/api/v1 description: Development server - url: https://api.volontery.example.com/api/v1 description: Production server tags: - name: health description: Проверка здоровья сервиса - name: auth description: Аутентификация и авторизация - name: users description: Управление пользователями - name: requests description: Управление заявками на помощь - name: responses description: Отклики волонтёров на заявки - name: moderation description: Модерация заявок (требуется роль moderator или admin) - name: admin description: Административные функции (требуется роль admin) security: - BearerAuth: [] paths: /health: get: tags: [health] summary: Проверка здоровья сервиса security: [] responses: '200': description: Сервис работает content: text/plain: schema: type: string example: OK # ==================== AUTH ==================== /auth/register: post: tags: [auth] summary: Регистрация нового пользователя description: Создает нового пользователя в системе security: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/RegisterRequest' responses: '201': description: Пользователь успешно зарегистрирован content: application/json: schema: $ref: '#/components/schemas/AuthResponse' '400': $ref: '#/components/responses/ErrorResponse' /auth/login: post: tags: [auth] summary: Вход в систему description: Аутентификация пользователя по email и паролю security: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/LoginRequest' responses: '200': description: Успешная аутентификация content: application/json: schema: $ref: '#/components/schemas/AuthResponse' '401': $ref: '#/components/responses/ErrorResponse' /auth/refresh: post: tags: [auth] summary: Обновление токенов description: Получение новой пары access/refresh токенов security: [] requestBody: required: true content: application/json: schema: type: object required: [refresh_token] properties: refresh_token: type: string description: Refresh токен responses: '200': description: Токены успешно обновлены content: application/json: schema: $ref: '#/components/schemas/AuthResponse' '401': $ref: '#/components/responses/ErrorResponse' /auth/me: get: tags: [auth] summary: Информация о текущем пользователе description: Возвращает базовую информацию об аутентифицированном пользователе responses: '200': description: Информация о пользователе content: application/json: schema: type: object properties: id: type: integer format: int64 email: type: string format: email '401': $ref: '#/components/responses/ErrorResponse' /auth/logout: post: tags: [auth] summary: Выход из системы description: Отзывает все refresh токены пользователя responses: '200': description: Успешный выход content: application/json: schema: type: object properties: message: type: string example: logged out successfully '401': $ref: '#/components/responses/ErrorResponse' # ==================== USERS ==================== /users/me: get: tags: [users] summary: Получение профиля текущего пользователя description: Возвращает детальную информацию о профиле responses: '200': description: Профиль пользователя content: application/json: schema: $ref: '#/components/schemas/UserProfile' '401': $ref: '#/components/responses/ErrorResponse' '404': $ref: '#/components/responses/ErrorResponse' patch: tags: [users] summary: Обновление профиля description: Обновляет информацию в профиле пользователя requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateProfileInput' responses: '200': description: Профиль успешно обновлен content: application/json: schema: type: object properties: message: type: string example: profile updated successfully '400': $ref: '#/components/responses/ErrorResponse' '401': $ref: '#/components/responses/ErrorResponse' /users/me/location: post: tags: [users] summary: Обновление местоположения description: Обновляет координаты домашнего адреса пользователя requestBody: required: true content: application/json: schema: type: object required: [latitude, longitude] properties: latitude: type: number format: double minimum: -90 maximum: 90 example: 55.751244 longitude: type: number format: double minimum: -180 maximum: 180 example: 37.618423 responses: '200': description: Местоположение обновлено content: application/json: schema: type: object properties: message: type: string example: location updated successfully '400': $ref: '#/components/responses/ErrorResponse' '401': $ref: '#/components/responses/ErrorResponse' /users/me/verify-email: post: tags: [users] summary: Подтверждение email адреса description: | Подтверждает email адрес текущего пользователя. В полноценной системе должен принимать токен подтверждения из письма. responses: '200': description: Email успешно подтвержден content: application/json: schema: type: object properties: message: type: string example: email verified successfully '401': $ref: '#/components/responses/ErrorResponse' '500': $ref: '#/components/responses/ErrorResponse' /users/me/roles: get: tags: [users] summary: Получение ролей пользователя description: Возвращает список ролей текущего пользователя responses: '200': description: Список ролей content: application/json: schema: type: array items: $ref: '#/components/schemas/Role' '401': $ref: '#/components/responses/ErrorResponse' /users/me/permissions: get: tags: [users] summary: Получение разрешений пользователя description: Возвращает список всех разрешений текущего пользователя responses: '200': description: Список разрешений content: application/json: schema: type: array items: $ref: '#/components/schemas/Permission' '401': $ref: '#/components/responses/ErrorResponse' /users/me/permissions/{permission_name}/check: get: tags: [users] summary: Проверка наличия разрешения description: Проверяет, есть ли у текущего пользователя указанное разрешение parameters: - name: permission_name in: path required: true description: Название разрешения для проверки schema: type: string example: create_request examples: create_request: value: create_request summary: Разрешение на создание заявок moderate_request: value: moderate_request summary: Разрешение на модерацию заявок manage_users: value: manage_users summary: Разрешение на управление пользователями responses: '200': description: Результат проверки разрешения content: application/json: schema: $ref: '#/components/schemas/PermissionCheckResult' '401': $ref: '#/components/responses/ErrorResponse' '500': $ref: '#/components/responses/ErrorResponse' /users/{id}: get: tags: [users] summary: Получение профиля пользователя по ID description: Возвращает публичную информацию о пользователе parameters: - name: id in: path required: true schema: type: integer format: int64 responses: '200': description: Профиль пользователя content: application/json: schema: $ref: '#/components/schemas/UserProfile' '401': $ref: '#/components/responses/ErrorResponse' '404': $ref: '#/components/responses/ErrorResponse' # ==================== REQUEST TYPES ==================== /request-types: get: tags: [requests] summary: Получение списка типов заявок description: Возвращает справочник типов помощи security: [] responses: '200': description: Список типов заявок content: application/json: schema: type: array items: $ref: '#/components/schemas/RequestType' # ==================== REQUESTS ==================== /requests: post: tags: [requests] summary: Создание новой заявки description: Создает заявку на помощь от имени текущего пользователя requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateRequestInput' responses: '201': description: Заявка успешно создана content: application/json: schema: $ref: '#/components/schemas/Request' '400': $ref: '#/components/responses/ErrorResponse' '401': $ref: '#/components/responses/ErrorResponse' /requests/my: get: tags: [requests] summary: Получение заявок пользователя description: Возвращает список заявок, созданных текущим пользователем parameters: - $ref: '#/components/parameters/Limit' - $ref: '#/components/parameters/Offset' responses: '200': description: Список заявок content: application/json: schema: type: array items: $ref: '#/components/schemas/RequestListItem' '401': $ref: '#/components/responses/ErrorResponse' /requests/nearby: get: tags: [requests] summary: Поиск заявок рядом с точкой description: Геопространственный поиск заявок в радиусе от указанной точки parameters: - name: lat in: query required: true description: Широта schema: type: number format: double minimum: -90 maximum: 90 - name: lon in: query required: true description: Долгота schema: type: number format: double minimum: -180 maximum: 180 - name: radius in: query required: false description: Радиус поиска в метрах (по умолчанию 5000) schema: type: number format: double default: 5000 minimum: 100 maximum: 50000 - $ref: '#/components/parameters/Limit' - $ref: '#/components/parameters/Offset' responses: '200': description: Список найденных заявок content: application/json: schema: type: array items: $ref: '#/components/schemas/RequestWithDistance' '400': $ref: '#/components/responses/ErrorResponse' '401': $ref: '#/components/responses/ErrorResponse' /requests/bounds: get: tags: [requests] summary: Поиск заявок в прямоугольной области description: Возвращает заявки в заданной прямоугольной области (для отображения на карте) parameters: - name: min_lon in: query required: true description: Минимальная долгота (левый нижний угол) schema: type: number format: double - name: min_lat in: query required: true description: Минимальная широта (левый нижний угол) schema: type: number format: double - name: max_lon in: query required: true description: Максимальная долгота (правый верхний угол) schema: type: number format: double - name: max_lat in: query required: true description: Максимальная широта (правый верхний угол) schema: type: number format: double - $ref: '#/components/parameters/Limit' - $ref: '#/components/parameters/Offset' responses: '200': description: Список заявок в области content: application/json: schema: type: array items: $ref: '#/components/schemas/RequestListItem' '400': $ref: '#/components/responses/ErrorResponse' '401': $ref: '#/components/responses/ErrorResponse' /requests/{id}: get: tags: [requests] summary: Получение заявки по ID description: Возвращает детальную информацию о заявке parameters: - name: id in: path required: true schema: type: integer format: int64 responses: '200': description: Детальная информация о заявке content: application/json: schema: $ref: '#/components/schemas/RequestDetail' '401': $ref: '#/components/responses/ErrorResponse' '404': $ref: '#/components/responses/ErrorResponse' /requests/{id}/complete: post: tags: [requests] summary: Завершение заявки с оценкой description: Создатель заявки завершает выполненную заявку и оценивает волонтёра parameters: - name: id in: path required: true schema: type: integer format: int64 requestBody: required: true content: application/json: schema: type: object required: - rating properties: rating: type: integer format: int32 minimum: 1 maximum: 5 description: Оценка работы волонтёра (от 1 до 5) example: 5 comment: type: string nullable: true description: Комментарий к оценке example: Отличная работа, спасибо! responses: '200': description: Заявка завершена успешно content: application/json: schema: type: object properties: success: type: boolean example: true message: type: string example: request completed successfully rating_id: type: integer format: int64 '400': $ref: '#/components/responses/ErrorResponse' '401': $ref: '#/components/responses/ErrorResponse' '404': $ref: '#/components/responses/ErrorResponse' # ==================== RESPONSES ==================== /requests/{id}/responses: get: tags: [responses] summary: Получение откликов на заявку description: Возвращает список откликов волонтёров на заявку parameters: - name: id in: path required: true schema: type: integer format: int64 responses: '200': description: Список откликов content: application/json: schema: type: array items: $ref: '#/components/schemas/VolunteerResponse' '401': $ref: '#/components/responses/ErrorResponse' post: tags: [responses] summary: Создание отклика на заявку description: Волонтёр откликается на заявку о помощи parameters: - name: id in: path required: true schema: type: integer format: int64 requestBody: required: true content: application/json: schema: type: object properties: message: type: string description: Сообщение волонтёра example: Готов помочь завтра после 15:00 responses: '201': description: Отклик успешно создан content: application/json: schema: $ref: '#/components/schemas/VolunteerResponse' '400': $ref: '#/components/responses/ErrorResponse' '401': $ref: '#/components/responses/ErrorResponse' /requests/{id}/responses/{response_id}/accept: post: tags: [responses] summary: Принятие отклика волонтёра description: Создатель заявки принимает отклик волонтёра и назначает его на заявку parameters: - name: id in: path required: true schema: type: integer format: int64 - name: response_id in: path required: true schema: type: integer format: int64 responses: '200': description: Отклик принят, волонтёр назначен content: application/json: schema: type: object properties: success: type: boolean example: true message: type: string example: volunteer assigned successfully request_id: type: integer format: int64 volunteer_id: type: integer format: int64 '400': $ref: '#/components/responses/ErrorResponse' '401': $ref: '#/components/responses/ErrorResponse' '404': $ref: '#/components/responses/ErrorResponse' # ==================== MODERATION ==================== /moderation/requests/pending: get: tags: [moderation] summary: Получение заявок на модерацию description: Возвращает заявки со статусом pending_moderation (требуется роль moderator или admin) parameters: - $ref: '#/components/parameters/Limit' - $ref: '#/components/parameters/Offset' responses: '200': description: Список заявок на модерацию content: application/json: schema: type: array items: $ref: '#/components/schemas/RequestListItem' '401': $ref: '#/components/responses/ErrorResponse' '403': $ref: '#/components/responses/ErrorResponse' /moderation/requests/my: get: tags: [moderation] summary: Получение моих промодерированных заявок description: Возвращает заявки, которые были промодерированы текущим пользователем parameters: - $ref: '#/components/parameters/Limit' - $ref: '#/components/parameters/Offset' responses: '200': description: Список промодерированных заявок content: application/json: schema: type: array items: $ref: '#/components/schemas/RequestListItem' '401': $ref: '#/components/responses/ErrorResponse' '403': $ref: '#/components/responses/ErrorResponse' /moderation/requests/{id}/approve: post: tags: [moderation] summary: Одобрение заявки description: Модератор одобряет заявку, меняет её статус на approved parameters: - name: id in: path required: true schema: type: integer format: int64 requestBody: content: application/json: schema: type: object properties: comment: type: string nullable: true description: Комментарий модератора (необязательно) example: Заявка соответствует требованиям responses: '200': description: Заявка одобрена content: application/json: schema: type: object properties: status: type: string example: approved '400': $ref: '#/components/responses/ErrorResponse' '401': $ref: '#/components/responses/ErrorResponse' '403': $ref: '#/components/responses/ErrorResponse' '404': $ref: '#/components/responses/ErrorResponse' /moderation/requests/{id}/reject: post: tags: [moderation] summary: Отклонение заявки description: Модератор отклоняет заявку, меняет её статус на rejected parameters: - name: id in: path required: true schema: type: integer format: int64 requestBody: required: true content: application/json: schema: type: object required: - comment properties: comment: type: string description: Причина отклонения (обязательно) example: Недостаточно информации для выполнения заявки responses: '200': description: Заявка отклонена content: application/json: schema: type: object properties: status: type: string example: rejected '400': $ref: '#/components/responses/ErrorResponse' '401': $ref: '#/components/responses/ErrorResponse' '403': $ref: '#/components/responses/ErrorResponse' '404': $ref: '#/components/responses/ErrorResponse' /moderation/requests/{id}/moderate: post: tags: [moderation] summary: Модерация заявки (альтернативный метод) description: | Модерирует заявку через stored procedure в БД. Альтернатива отдельным эндпоинтам approve/reject. Выполняет approve или reject в одной транзакции через БД процедуру. parameters: - name: id in: path required: true schema: type: integer format: int64 requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ModerationAction' responses: '200': description: Заявка успешно промодерирована content: application/json: schema: $ref: '#/components/schemas/ModerationResult' '400': description: Некорректные данные запроса content: application/json: schema: type: object properties: error: type: string examples: - value: action must be 'approve' or 'reject' - value: comment is required when rejecting - value: request is not in pending_moderation status '401': $ref: '#/components/responses/ErrorResponse' '403': $ref: '#/components/responses/ErrorResponse' '404': $ref: '#/components/responses/ErrorResponse' # ==================== ADMIN ==================== /admin/users/{user_id}/roles/{role_id}: post: tags: [admin] summary: Назначение роли пользователю description: | Назначает роль пользователю (требуется роль admin или разрешение manage_users). Только администраторы могут управлять ролями пользователей. parameters: - name: user_id in: path required: true description: ID пользователя schema: type: integer format: int64 - name: role_id in: path required: true description: ID роли для назначения schema: type: integer format: int64 responses: '200': description: Роль успешно назначена content: application/json: schema: $ref: '#/components/schemas/RoleAssignmentResult' '400': $ref: '#/components/responses/ErrorResponse' '401': $ref: '#/components/responses/ErrorResponse' '403': description: Недостаточно прав доступа content: application/json: schema: type: object properties: error: type: string example: admin role required '404': $ref: '#/components/responses/ErrorResponse' components: securitySchemes: BearerAuth: type: http scheme: bearer bearerFormat: JWT description: 'JWT токен в формате: Bearer {token}' parameters: Limit: name: limit in: query description: Количество результатов на странице schema: type: integer format: int32 minimum: 1 maximum: 100 default: 20 Offset: name: offset in: query description: Смещение для пагинации schema: type: integer format: int32 minimum: 0 default: 0 schemas: # ==================== AUTH SCHEMAS ==================== RegisterRequest: type: object required: - email - password - first_name - last_name properties: email: type: string format: email example: user@example.com password: type: string format: password minLength: 8 example: SecureP@ssw0rd first_name: type: string minLength: 1 maxLength: 100 example: Иван last_name: type: string minLength: 1 maxLength: 100 example: Иванов phone: type: string pattern: '^\+?[1-9]\d{1,14}$' example: '+79001234567' latitude: type: number format: double minimum: -90 maximum: 90 example: 55.751244 longitude: type: number format: double minimum: -180 maximum: 180 example: 37.618423 address: type: string example: Красная площадь, 1 city: type: string example: Москва bio: type: string maxLength: 500 example: Рад помочь пожилым людям с покупками LoginRequest: type: object required: - email - password properties: email: type: string format: email example: user@example.com password: type: string format: password example: SecureP@ssw0rd AuthResponse: type: object properties: access_token: type: string description: JWT access токен refresh_token: type: string description: Refresh токен expires_in: type: integer format: int64 description: Время жизни access токена в секундах user: $ref: '#/components/schemas/UserInfo' UserInfo: type: object properties: id: type: integer format: int64 email: type: string format: email first_name: type: string last_name: type: string email_verified: type: boolean # ==================== USER SCHEMAS ==================== UserProfile: type: object properties: id: type: integer format: int64 email: type: string format: email first_name: type: string last_name: type: string phone: type: string nullable: true bio: type: string nullable: true avatar_url: type: string nullable: true address: type: string nullable: true city: type: string nullable: true volunteer_rating: type: number format: double nullable: true completed_requests_count: type: integer nullable: true is_verified: type: boolean is_blocked: type: boolean email_verified: type: boolean created_at: type: string format: date-time updated_at: type: string format: date-time UpdateProfileInput: type: object properties: first_name: type: string minLength: 1 maxLength: 100 last_name: type: string minLength: 1 maxLength: 100 phone: type: string bio: type: string maxLength: 500 address: type: string city: type: string Role: type: object properties: id: type: integer format: int64 name: type: string example: volunteer description: type: string nullable: true Permission: type: object properties: id: type: integer format: int64 name: type: string example: create_request resource: type: string example: request action: type: string example: create description: type: string nullable: true PermissionCheckResult: type: object properties: has_permission: type: boolean description: Есть ли у пользователя это разрешение permission_name: type: string description: Название проверяемого разрешения # ==================== REQUEST SCHEMAS ==================== RequestType: type: object properties: id: type: integer format: int64 name: type: string example: Покупка продуктов description: type: string nullable: true icon: type: string nullable: true is_active: type: boolean CreateRequestInput: type: object required: - request_type_id - title - description - latitude - longitude - address - urgency properties: request_type_id: type: integer format: int64 title: type: string minLength: 5 maxLength: 200 example: Нужна помощь с покупкой продуктов description: type: string minLength: 10 maxLength: 2000 example: Прошу помочь купить продукты в ближайшем магазине latitude: type: number format: double minimum: -90 maximum: 90 longitude: type: number format: double minimum: -180 maximum: 180 address: type: string example: ул. Ленина, д. 10, кв. 5 city: type: string example: Москва desired_completion_date: type: string format: date-time nullable: true urgency: type: string enum: [low, medium, high, urgent] example: high contact_phone: type: string example: '+79001234567' contact_notes: type: string example: Код домофона 123, 3 этаж Request: type: object properties: id: type: integer format: int64 requester_id: type: integer format: int64 request_type_id: type: integer format: int64 assigned_volunteer_id: type: integer format: int64 nullable: true title: type: string description: type: string address: type: string city: type: string nullable: true desired_completion_date: type: string format: date-time nullable: true urgency: type: string nullable: true status: $ref: '#/components/schemas/RequestStatus' contact_phone: type: string nullable: true contact_notes: type: string nullable: true created_at: type: string format: date-time updated_at: type: string format: date-time RequestListItem: type: object properties: id: type: integer format: int64 title: type: string description: type: string address: type: string city: type: string urgency: type: string status: $ref: '#/components/schemas/RequestStatus' requester_name: type: string request_type_name: type: string created_at: type: string format: date-time RequestWithDistance: allOf: - $ref: '#/components/schemas/RequestListItem' - type: object properties: distance_meters: type: number format: double description: Расстояние до заявки в метрах RequestDetail: allOf: - $ref: '#/components/schemas/Request' - type: object properties: requester: $ref: '#/components/schemas/UserInfo' request_type: $ref: '#/components/schemas/RequestType' assigned_volunteer: $ref: '#/components/schemas/UserInfo' nullable: true RequestStatus: type: string enum: - pending_moderation - approved - in_progress - completed - cancelled - rejected # ==================== RESPONSE SCHEMAS ==================== VolunteerResponse: type: object properties: id: type: integer format: int64 request_id: type: integer format: int64 volunteer_id: type: integer format: int64 volunteer_name: type: string status: $ref: '#/components/schemas/ResponseStatus' message: type: string nullable: true responded_at: type: string format: date-time accepted_at: type: string format: date-time nullable: true rejected_at: type: string format: date-time nullable: true ResponseStatus: type: string enum: - pending - accepted - rejected - cancelled # ==================== MODERATION SCHEMAS ==================== ModerationAction: type: object required: - action properties: action: type: string enum: [approve, reject] description: Действие модерации example: approve comment: type: string nullable: true description: Комментарий модератора (обязателен при reject) example: Заявка соответствует требованиям ModerationResult: type: object properties: success: type: boolean example: true message: type: string example: request moderated successfully new_status: type: string enum: [approved, rejected] example: approved # ==================== ADMIN SCHEMAS ==================== RoleAssignmentResult: type: object properties: message: type: string example: role assigned successfully user_id: type: integer format: int64 role_id: type: integer format: int64 # ==================== ERROR SCHEMAS ==================== responses: ErrorResponse: description: Ошибка content: application/json: schema: type: object properties: error: type: string description: Описание ошибки