3344 lines
114 KiB
YAML
3344 lines
114 KiB
YAML
basePath: /
|
||
definitions:
|
||
handlers.BindCodeResponse:
|
||
properties:
|
||
code:
|
||
type: string
|
||
expires_at:
|
||
type: string
|
||
link:
|
||
type: string
|
||
type: object
|
||
handlers.ErrorResponse:
|
||
properties:
|
||
error:
|
||
type: string
|
||
type: object
|
||
handlers.InvitationListResponse:
|
||
properties:
|
||
items:
|
||
items:
|
||
$ref: '#/definitions/models.ProjectInvitation'
|
||
type: array
|
||
limit:
|
||
type: integer
|
||
offset:
|
||
type: integer
|
||
total:
|
||
type: integer
|
||
type: object
|
||
handlers.InvitationResponse:
|
||
properties:
|
||
expires_at:
|
||
description: До какого срока мастер может ответить (см. repository.InvitationTTL).
|
||
type: string
|
||
ok:
|
||
type: boolean
|
||
status:
|
||
description: |-
|
||
Всегда "invited": стать участником мастер может только сам, через
|
||
POST /projects/{id}/invitation/accept.
|
||
type: string
|
||
type: object
|
||
handlers.OKResponse:
|
||
properties:
|
||
ok:
|
||
type: boolean
|
||
revoked:
|
||
description: Revoked — сколько сессий закрыто; заполняется только в logout-all.
|
||
type: integer
|
||
type: object
|
||
handlers.ObjectListResponse:
|
||
properties:
|
||
items:
|
||
items:
|
||
$ref: '#/definitions/models.Object'
|
||
type: array
|
||
limit:
|
||
type: integer
|
||
offset:
|
||
type: integer
|
||
total:
|
||
type: integer
|
||
type: object
|
||
handlers.PasswordResetResponse:
|
||
properties:
|
||
expires_at:
|
||
description: См. repository.ResetTTL (24 часа).
|
||
type: string
|
||
ok:
|
||
type: boolean
|
||
token:
|
||
type: string
|
||
type: object
|
||
handlers.PhotoListResponse:
|
||
properties:
|
||
items:
|
||
items:
|
||
$ref: '#/definitions/models.TicketPhoto'
|
||
type: array
|
||
limit:
|
||
type: integer
|
||
max_per_ticket:
|
||
description: |-
|
||
MaxPerTicket — лимит файлов на заявку (PHOTO_MAX_PER_TICKET):
|
||
UI рисует «n / N», не зная настройки окружения.
|
||
type: integer
|
||
offset:
|
||
type: integer
|
||
total:
|
||
type: integer
|
||
type: object
|
||
handlers.ProfileListResponse:
|
||
properties:
|
||
items:
|
||
items:
|
||
$ref: '#/definitions/models.Profile'
|
||
type: array
|
||
limit:
|
||
type: integer
|
||
offset:
|
||
type: integer
|
||
total:
|
||
type: integer
|
||
type: object
|
||
handlers.ProjectListResponse:
|
||
properties:
|
||
items:
|
||
items:
|
||
$ref: '#/definitions/models.Project'
|
||
type: array
|
||
limit:
|
||
type: integer
|
||
offset:
|
||
type: integer
|
||
total:
|
||
type: integer
|
||
type: object
|
||
handlers.ProjectWorkerListResponse:
|
||
properties:
|
||
items:
|
||
items:
|
||
$ref: '#/definitions/models.ProjectWorker'
|
||
type: array
|
||
limit:
|
||
type: integer
|
||
offset:
|
||
type: integer
|
||
total:
|
||
type: integer
|
||
type: object
|
||
handlers.RecurringRuleListResponse:
|
||
properties:
|
||
items:
|
||
items:
|
||
$ref: '#/definitions/models.RecurringRule'
|
||
type: array
|
||
limit:
|
||
type: integer
|
||
offset:
|
||
type: integer
|
||
total:
|
||
type: integer
|
||
type: object
|
||
handlers.RecurringRunListResponse:
|
||
properties:
|
||
items:
|
||
items:
|
||
$ref: '#/definitions/models.RecurringRun'
|
||
type: array
|
||
limit:
|
||
type: integer
|
||
offset:
|
||
type: integer
|
||
total:
|
||
type: integer
|
||
type: object
|
||
handlers.RecurringRunResponse:
|
||
properties:
|
||
ok:
|
||
type: boolean
|
||
ticket_id:
|
||
type: integer
|
||
type: object
|
||
handlers.TelegramStatusResponse:
|
||
properties:
|
||
bot_username:
|
||
description: |-
|
||
BotUsername — @имя бота, через который придут уведомления и
|
||
ссылка привязки; пусто, пока getMe не удался.
|
||
type: string
|
||
bound:
|
||
description: Bound — есть ли уже цель уведомлений.
|
||
type: boolean
|
||
enabled:
|
||
description: |-
|
||
Enabled — может ли этот пользователь получать уведомления вообще:
|
||
есть свой токен бота либо настроен глобальный бот сервера.
|
||
type: boolean
|
||
has_bot_token:
|
||
description: HasBotToken — подключён ли пользователь свой бот (токен в БД).
|
||
type: boolean
|
||
target:
|
||
description: 'Target — текущая цель: числовой chat_id либо @группа.'
|
||
type: string
|
||
token_configurable:
|
||
description: |-
|
||
TokenConfigurable — можно ли ввести токен прямо сейчас: false,
|
||
если на сервере не задан DATA_ENCRYPTION_KEY.
|
||
type: boolean
|
||
type: object
|
||
handlers.TicketEventListResponse:
|
||
properties:
|
||
items:
|
||
items:
|
||
$ref: '#/definitions/models.TicketEvent'
|
||
type: array
|
||
limit:
|
||
type: integer
|
||
offset:
|
||
type: integer
|
||
total:
|
||
type: integer
|
||
type: object
|
||
handlers.TicketListResponse:
|
||
properties:
|
||
items:
|
||
items:
|
||
$ref: '#/definitions/models.Ticket'
|
||
type: array
|
||
limit:
|
||
type: integer
|
||
offset:
|
||
type: integer
|
||
total:
|
||
type: integer
|
||
type: object
|
||
handlers.TokenResponse:
|
||
properties:
|
||
expires_in:
|
||
description: Срок жизни access-токена в секундах.
|
||
type: integer
|
||
token:
|
||
description: 'Access-токен: JWT на auth.AccessTTL.'
|
||
type: string
|
||
user:
|
||
$ref: '#/definitions/models.Profile'
|
||
type: object
|
||
handlers.addWorkerReq:
|
||
properties:
|
||
worker_id:
|
||
type: string
|
||
required:
|
||
- worker_id
|
||
type: object
|
||
handlers.assignReq:
|
||
properties:
|
||
worker_id:
|
||
type: string
|
||
required:
|
||
- worker_id
|
||
type: object
|
||
handlers.createObjectReq:
|
||
properties:
|
||
address:
|
||
minLength: 5
|
||
type: string
|
||
name:
|
||
minLength: 2
|
||
type: string
|
||
required:
|
||
- address
|
||
- name
|
||
type: object
|
||
handlers.createProjectReq:
|
||
properties:
|
||
client_id:
|
||
description: |-
|
||
ClientID — только для админа: для какого клиента создаётся проект.
|
||
У клиента владелец берётся из токена, поле игнорируется.
|
||
type: string
|
||
name:
|
||
minLength: 2
|
||
type: string
|
||
required:
|
||
- name
|
||
type: object
|
||
handlers.createRecurringReq:
|
||
properties:
|
||
anchor_at:
|
||
description: |-
|
||
Расписание: «каждые PeriodDays дней/недель от AnchorAt» —
|
||
AnchorAt (RFC3339) — дата-время первого запуска, PeriodDays —
|
||
период в днях (1..3650; недели кратны 7).
|
||
type: string
|
||
category:
|
||
enum:
|
||
- electric
|
||
- plumbing
|
||
- climate
|
||
- elevator
|
||
- other
|
||
type: string
|
||
description:
|
||
minLength: 5
|
||
type: string
|
||
floor:
|
||
type: string
|
||
object_id:
|
||
type: integer
|
||
period_days:
|
||
maximum: 3650
|
||
minimum: 1
|
||
type: integer
|
||
priority:
|
||
enum:
|
||
- low
|
||
- normal
|
||
- high
|
||
type: string
|
||
required:
|
||
- anchor_at
|
||
- category
|
||
- description
|
||
- object_id
|
||
- period_days
|
||
type: object
|
||
handlers.createTicketReq:
|
||
properties:
|
||
category:
|
||
enum:
|
||
- electric
|
||
- plumbing
|
||
- climate
|
||
- elevator
|
||
- other
|
||
type: string
|
||
description:
|
||
minLength: 5
|
||
type: string
|
||
floor:
|
||
type: string
|
||
object_id:
|
||
description: 'Объект, на который создаётся заявка: адрес копируется из объекта.'
|
||
type: integer
|
||
priority:
|
||
enum:
|
||
- low
|
||
- normal
|
||
- high
|
||
type: string
|
||
recurring:
|
||
allOf:
|
||
- $ref: '#/definitions/handlers.recurringSchedule'
|
||
description: Recurring — задан, если заявку делают повторяющейся.
|
||
required:
|
||
- category
|
||
- description
|
||
- object_id
|
||
type: object
|
||
handlers.createUserReq:
|
||
properties:
|
||
email:
|
||
type: string
|
||
name:
|
||
type: string
|
||
password:
|
||
type: string
|
||
phone:
|
||
type: string
|
||
role:
|
||
enum:
|
||
- worker
|
||
- admin
|
||
- client
|
||
type: string
|
||
required:
|
||
- email
|
||
- name
|
||
- password
|
||
- role
|
||
type: object
|
||
handlers.forgotPasswordReq:
|
||
properties:
|
||
email:
|
||
type: string
|
||
required:
|
||
- email
|
||
type: object
|
||
handlers.loginReq:
|
||
properties:
|
||
email:
|
||
type: string
|
||
password:
|
||
type: string
|
||
required:
|
||
- email
|
||
- password
|
||
type: object
|
||
handlers.reasonReq:
|
||
properties:
|
||
reason:
|
||
minLength: 5
|
||
type: string
|
||
required:
|
||
- reason
|
||
type: object
|
||
handlers.recurringSchedule:
|
||
properties:
|
||
anchor_at:
|
||
description: |-
|
||
AnchorAt — дата-время первого запуска (RFC3339): точка отсчёта
|
||
сетки сроков, от неё идут «каждые period_days».
|
||
type: string
|
||
period_days:
|
||
description: PeriodDays — период в днях (1..3650; недели кратны 7).
|
||
maximum: 3650
|
||
minimum: 1
|
||
type: integer
|
||
required:
|
||
- anchor_at
|
||
- period_days
|
||
type: object
|
||
handlers.resetPasswordReq:
|
||
properties:
|
||
password:
|
||
type: string
|
||
token:
|
||
type: string
|
||
required:
|
||
- password
|
||
- token
|
||
type: object
|
||
handlers.setBotReq:
|
||
properties:
|
||
token:
|
||
description: Token — токен бота от @BotFather; пусто невалидно (нужен явный
|
||
ввод).
|
||
type: string
|
||
type: object
|
||
handlers.setPasswordReq:
|
||
properties:
|
||
password:
|
||
type: string
|
||
required:
|
||
- password
|
||
type: object
|
||
handlers.setTelegramReq:
|
||
properties:
|
||
target:
|
||
description: Target — @группа/@канал или числовой chat_id; пусто — отключить.
|
||
type: string
|
||
type: object
|
||
handlers.setUserReq:
|
||
properties:
|
||
is_active:
|
||
description: |-
|
||
*bool обязателен: на bool с binding:"required" запрос
|
||
«деактивировать» (is_active=false) не прошёл бы валидацию.
|
||
type: boolean
|
||
required:
|
||
- is_active
|
||
type: object
|
||
handlers.updateObjectReq:
|
||
properties:
|
||
address:
|
||
minLength: 5
|
||
type: string
|
||
is_active:
|
||
type: boolean
|
||
name:
|
||
minLength: 2
|
||
type: string
|
||
type: object
|
||
handlers.updateProjectReq:
|
||
properties:
|
||
is_active:
|
||
type: boolean
|
||
name:
|
||
minLength: 2
|
||
type: string
|
||
type: object
|
||
handlers.updateRecurringReq:
|
||
properties:
|
||
anchor_at:
|
||
type: string
|
||
category:
|
||
type: string
|
||
description:
|
||
type: string
|
||
floor:
|
||
type: string
|
||
is_active:
|
||
type: boolean
|
||
period_days:
|
||
type: integer
|
||
priority:
|
||
type: string
|
||
type: object
|
||
models.Object:
|
||
properties:
|
||
address:
|
||
type: string
|
||
created_at:
|
||
type: string
|
||
id:
|
||
type: integer
|
||
is_active:
|
||
type: boolean
|
||
name:
|
||
type: string
|
||
project_id:
|
||
type: integer
|
||
project_name:
|
||
description: Денормализации для UI.
|
||
type: string
|
||
type: object
|
||
models.Profile:
|
||
properties:
|
||
confirmed_at:
|
||
description: |-
|
||
ConfirmedAt — момент подтверждения аккаунта; nil (null в JSON)
|
||
— ожидает подтверждения, вход до этого запрещён (403
|
||
account not confirmed). Существующие пользователи подтверждены
|
||
бэкфиллом миграции 0010.
|
||
type: string
|
||
created_at:
|
||
type: string
|
||
email:
|
||
type: string
|
||
id:
|
||
type: string
|
||
is_active:
|
||
type: boolean
|
||
name:
|
||
type: string
|
||
phone:
|
||
type: string
|
||
role:
|
||
type: string
|
||
type: object
|
||
models.Project:
|
||
properties:
|
||
client_id:
|
||
type: string
|
||
client_name:
|
||
description: 'Денормализация для UI: чей проект (админ видит все сразу).'
|
||
type: string
|
||
created_at:
|
||
type: string
|
||
id:
|
||
type: integer
|
||
is_active:
|
||
type: boolean
|
||
name:
|
||
type: string
|
||
type: object
|
||
models.ProjectInvitation:
|
||
properties:
|
||
client_name:
|
||
type: string
|
||
expires_at:
|
||
type: string
|
||
invited_at:
|
||
type: string
|
||
invited_by:
|
||
description: id пригласившего
|
||
type: string
|
||
invited_by_name:
|
||
description: его имя для UI
|
||
type: string
|
||
project_id:
|
||
type: integer
|
||
project_name:
|
||
type: string
|
||
type: object
|
||
models.ProjectWorker:
|
||
properties:
|
||
confirmed_at:
|
||
description: |-
|
||
ConfirmedAt — момент подтверждения аккаунта; nil (null в JSON)
|
||
— ожидает подтверждения, вход до этого запрещён (403
|
||
account not confirmed). Существующие пользователи подтверждены
|
||
бэкфиллом миграции 0010.
|
||
type: string
|
||
created_at:
|
||
type: string
|
||
email:
|
||
type: string
|
||
expires_at:
|
||
description: ExpiresAt — крайний срок ответа; заполнен у приглашений.
|
||
type: string
|
||
id:
|
||
type: string
|
||
is_active:
|
||
type: boolean
|
||
name:
|
||
type: string
|
||
phone:
|
||
type: string
|
||
responded_at:
|
||
description: RespondedAt — когда мастер принял/отклонил; пусто, пока не ответил.
|
||
type: string
|
||
role:
|
||
type: string
|
||
status:
|
||
description: invited | active | declined (см. 0005_project_invitations.sql).
|
||
type: string
|
||
type: object
|
||
models.RecurringRule:
|
||
properties:
|
||
address:
|
||
description: 'Адрес объекта — снимок, как у заявки: правило показывает, куда
|
||
едут.'
|
||
type: string
|
||
anchor_at:
|
||
description: |-
|
||
Расписание: «каждые PeriodDays дней/недель от AnchorAt» —
|
||
AnchorAt — дата-время первого запуска (точка отсчёта сетки
|
||
сроков), PeriodDays — период в днях (1..3650, недели кратны 7).
|
||
type: string
|
||
category:
|
||
description: Шаблон заявки — те же поля, что у tickets.
|
||
type: string
|
||
client_id:
|
||
description: ClientID — владелец будущих заявок (клиент проекта).
|
||
type: string
|
||
client_name:
|
||
type: string
|
||
created_at:
|
||
type: string
|
||
created_by:
|
||
description: |-
|
||
CreatedBy — кто завёл правило (админ или клиент); в журнале
|
||
созданной заявки это же лицо будет автором события «created».
|
||
type: string
|
||
created_by_name:
|
||
description: Кто завёл правило (админ или сам клиент) — для аудита, в JSON
|
||
идёт имя.
|
||
type: string
|
||
description:
|
||
type: string
|
||
floor:
|
||
type: string
|
||
id:
|
||
type: integer
|
||
is_active:
|
||
type: boolean
|
||
last_run_at:
|
||
description: |-
|
||
LastRunAt — когда последний раз срабатывали; null (в JSON — null)
|
||
означает «ещё ни разу», следующий запуск считается от created_at.
|
||
type: string
|
||
next_run_at:
|
||
description: |-
|
||
NextRunAt — ближайший запуск после последнего срабатывания
|
||
(вычисляется при чтении; у выключенного правила — когда включат).
|
||
type: string
|
||
object_id:
|
||
type: integer
|
||
object_name:
|
||
type: string
|
||
period_days:
|
||
type: integer
|
||
priority:
|
||
type: string
|
||
project_id:
|
||
type: integer
|
||
project_name:
|
||
description: Денормализации для UI.
|
||
type: string
|
||
type: object
|
||
models.RecurringRun:
|
||
properties:
|
||
address:
|
||
description: Адрес и категория — из заявки на момент создания (для списка
|
||
истории).
|
||
type: string
|
||
category:
|
||
type: string
|
||
id:
|
||
type: integer
|
||
rule_id:
|
||
type: integer
|
||
run_at:
|
||
type: string
|
||
ticket_id:
|
||
description: |-
|
||
TicketID — созданная заявка (её номер = id); null, если её удалили
|
||
каскадом вместе с проектом.
|
||
type: integer
|
||
type: object
|
||
models.Ticket:
|
||
properties:
|
||
address:
|
||
type: string
|
||
assigned_at:
|
||
type: string
|
||
assigned_by:
|
||
type: string
|
||
cancel_reason:
|
||
type: string
|
||
category:
|
||
type: string
|
||
client_id:
|
||
type: string
|
||
client_name:
|
||
description: Денормализованные поля для UI — подтягиваются JOIN'ом
|
||
type: string
|
||
closed_at:
|
||
type: string
|
||
closed_by:
|
||
type: string
|
||
confirmed_at:
|
||
type: string
|
||
created_at:
|
||
type: string
|
||
description:
|
||
type: string
|
||
done_at:
|
||
type: string
|
||
floor:
|
||
type: string
|
||
id:
|
||
type: integer
|
||
object_id:
|
||
type: integer
|
||
object_name:
|
||
type: string
|
||
priority:
|
||
type: string
|
||
project_id:
|
||
description: |-
|
||
Принадлежность: заявка живёт в объекте проекта клиента.
|
||
Проверяется составными FK в БД (миграция 0004).
|
||
type: integer
|
||
project_name:
|
||
type: string
|
||
recurring:
|
||
allOf:
|
||
- $ref: '#/definitions/models.TicketRecurring'
|
||
description: |-
|
||
Recurring — правило повторения, если заявка связана с ним:
|
||
создана вместе с правилом (чекбокс «Новая заявка») или самим
|
||
планировщиком. Заполняется только в детали (GetByID) — спискам
|
||
блок на странице заявки не нужен.
|
||
reject_reason:
|
||
type: string
|
||
rejected_at:
|
||
description: 'Закрытие цикла: rejected — это доработка, closed — финал.'
|
||
type: string
|
||
rework_count:
|
||
description: |-
|
||
Сколько раз клиент отклонял работу — чтобы доработки не бесконечны
|
||
были незаметны.
|
||
type: integer
|
||
started_at:
|
||
type: string
|
||
status:
|
||
type: string
|
||
worker_id:
|
||
type: string
|
||
worker_name:
|
||
type: string
|
||
type: object
|
||
models.TicketEvent:
|
||
properties:
|
||
action:
|
||
description: |-
|
||
created | assigned | started | done | confirmed | rejected |
|
||
canceled | photo_added | photo_removed.
|
||
type: string
|
||
actor_id:
|
||
description: '"" — автор удалён (событие сохранится, имя нет).'
|
||
type: string
|
||
actor_name:
|
||
type: string
|
||
comment:
|
||
description: Причина для rejected/canceled, имя файла для photo_*; иначе пусто.
|
||
type: string
|
||
created_at:
|
||
type: string
|
||
from_status:
|
||
description: |-
|
||
"" — у события «создана» не было предыдущего статуса.
|
||
У photo_added/photo_removed статус не менялся: оба равны текущему.
|
||
type: string
|
||
id:
|
||
type: integer
|
||
ticket_id:
|
||
type: integer
|
||
to_status:
|
||
type: string
|
||
type: object
|
||
models.TicketPhoto:
|
||
properties:
|
||
content_type:
|
||
type: string
|
||
created_at:
|
||
type: string
|
||
id:
|
||
type: integer
|
||
original_name:
|
||
description: 'Имя, которое дал файл при загрузке: лежит и в журнале (comment).'
|
||
type: string
|
||
size_bytes:
|
||
type: integer
|
||
ticket_id:
|
||
type: integer
|
||
uploader_id:
|
||
description: '"" — аккаунт загрузившего удалён, файл остаётся.'
|
||
type: string
|
||
uploader_name:
|
||
type: string
|
||
url:
|
||
description: |-
|
||
URL — presigned GET (срок жизни ограничен); заполняется только
|
||
в ответах API. ObjectKey — внутренняя деталь, в JSON не уходит.
|
||
type: string
|
||
type: object
|
||
models.TicketRecurring:
|
||
properties:
|
||
anchor_at:
|
||
description: |-
|
||
Расписание: «каждые PeriodDays дней/недель от AnchorAt» —
|
||
точка отсчёта плюс период в днях.
|
||
type: string
|
||
is_active:
|
||
type: boolean
|
||
next_run_at:
|
||
description: |-
|
||
NextRunAt — ближайший запуск, вычисляется при чтении
|
||
(см. repository.nextDue); у выключенного правила — когда включат.
|
||
type: string
|
||
period_days:
|
||
type: integer
|
||
rule_id:
|
||
type: integer
|
||
type: object
|
||
host: localhost:8080
|
||
info:
|
||
contact: {}
|
||
description: 'Управление заявками на обслуживание: админ создаёт клиента, клиент
|
||
заводит проект, объекты в нём и мастеров, назначает заявку мастеру проекта, мастер
|
||
выполняет, клиент подтверждает работу или отклоняет её на доработку. Финальный
|
||
статус — closed.'
|
||
title: Building Management API
|
||
version: "1.0"
|
||
paths:
|
||
/auth/forgot-password:
|
||
post:
|
||
consumes:
|
||
- application/json
|
||
description: 'Отправляет на email письмо со ссылкой формы сброса (токен живёт
|
||
24 часа, одноразовый). Ответ всегда одинаковый — 200 с ok:true, независимо
|
||
от того, существует ящик, подтверждён он и активен: эндпоинт не перечисляет
|
||
аккаунты. Письмо уходит только подтверждённым активным аккаунтам. Лимиты:
|
||
10 запросов в минуту с одного IP; 3 письма в сутки на один ящик (счётчик тикает
|
||
при любом запросе — и для несуществующих ящиков тоже), при превышении — 429.'
|
||
parameters:
|
||
- description: Email для восстановления
|
||
in: body
|
||
name: body
|
||
required: true
|
||
schema:
|
||
$ref: '#/definitions/handlers.forgotPasswordReq'
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: всегда ok:true, даже если ящик не найден
|
||
schema:
|
||
$ref: '#/definitions/handlers.OKResponse'
|
||
"400":
|
||
description: некорректное тело запроса
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"429":
|
||
description: слишком много попыток
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
summary: Запрос письма для сброса пароля
|
||
tags:
|
||
- auth
|
||
/auth/login:
|
||
post:
|
||
consumes:
|
||
- application/json
|
||
description: 'Возвращает access-токен; refresh-токен — только в httpOnly-куке
|
||
(Path=/auth, SameSite=Lax, Secure — через COOKIE_SECURE), в JSON его нет.
|
||
Email ищется без учёта регистра, пароль — bcrypt. Неподтверждённый аккаунт
|
||
— 403 account not confirmed (отличайте от account disabled). Лимит: 10 попыток
|
||
в минуту с одного IP.'
|
||
parameters:
|
||
- description: Учётные данные
|
||
in: body
|
||
name: body
|
||
required: true
|
||
schema:
|
||
$ref: '#/definitions/handlers.loginReq'
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/handlers.TokenResponse'
|
||
"400":
|
||
description: некорректное тело запроса
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: неверный логин или пароль
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: аккаунт отключён либо не подтверждён
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"429":
|
||
description: слишком много попыток
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
summary: Вход
|
||
tags:
|
||
- auth
|
||
/auth/logout:
|
||
post:
|
||
description: 'Гасит refresh-сессию из куки refresh_token и очищает куку. Тело
|
||
запроса не нужно. Auth-middleware намеренно не используется: выйти должен
|
||
пользователь и с протухшим access-токеном.'
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/handlers.OKResponse'
|
||
"429":
|
||
description: слишком много попыток
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
summary: Выйти (одно устройство)
|
||
tags:
|
||
- auth
|
||
/auth/logout-all:
|
||
post:
|
||
description: Закрывает все сессии текущего пользователя.
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/handlers.OKResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Выйти со всех устройств
|
||
tags:
|
||
- auth
|
||
/auth/me:
|
||
get:
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/models.Profile'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Текущий пользователь
|
||
tags:
|
||
- auth
|
||
/auth/refresh:
|
||
post:
|
||
description: 'Refresh-токен читается из httpOnly-куки refresh_token (Path=/auth),
|
||
тело запроса не требуется. Ротация: на выходе новый access-токен и новая кука,
|
||
старая сессия гасится. Повторное использование отозванного токена закрывает
|
||
все сессии пользователя — признак того, что секретом владеет кто-то второй.'
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/handlers.TokenResponse'
|
||
"401":
|
||
description: куки нет, токен неизвестен, отозван или истёк
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: аккаунт отключён
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"429":
|
||
description: слишком много попыток
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
summary: Обновить пару токенов
|
||
tags:
|
||
- auth
|
||
/auth/reset-password:
|
||
post:
|
||
consumes:
|
||
- application/json
|
||
description: 'Токен одноразовый и живёт 24 часа: приходит письмом после POST
|
||
/auth/forgot-password либо выдаётся админом через POST /users/{id}/password-reset.
|
||
Новый пароль — по политике (10..72 символа, буквы и цифры). Успех гасит токен
|
||
и все refresh-сессии пользователя. Лимит: 30 попыток в минуту с одного IP.'
|
||
parameters:
|
||
- description: Токен и новый пароль
|
||
in: body
|
||
name: body
|
||
required: true
|
||
schema:
|
||
$ref: '#/definitions/handlers.resetPasswordReq'
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/handlers.OKResponse'
|
||
"400":
|
||
description: некорректное тело или токен неизвестен, использован либо истёк;
|
||
слабый пароль
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"429":
|
||
description: слишком много попыток
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
summary: Сброс пароля по токену
|
||
tags:
|
||
- auth
|
||
/auth/telegram:
|
||
delete:
|
||
description: Очищает цель уведомлений текущего пользователя (то же, что PUT
|
||
с пустым значением). Токен бота при этом не трогается — он живёт на своём
|
||
эндпоинте.
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/handlers.OKResponse'
|
||
"401":
|
||
description: нет входа
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
summary: Отключить уведомления в Telegram
|
||
tags:
|
||
- auth
|
||
get:
|
||
description: 'Куда сейчас приходят уведомления о заявках: цель (чат или группа),
|
||
имя бота (свой или глобальный), подключён ли собственный токен бота (has_bot_token)
|
||
и можно ли его ввести (token_configurable — зависит от DATA_ENCRYPTION_KEY
|
||
на сервере). Сам токен наружу не отдаётся.'
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/handlers.TelegramStatusResponse'
|
||
"401":
|
||
description: нет входа
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: профиль не найден
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
summary: Статус уведомлений в Telegram
|
||
tags:
|
||
- auth
|
||
put:
|
||
consumes:
|
||
- application/json
|
||
description: Принимает @имя группы/канала (бот обязан быть в нём) или числовой
|
||
chat_id; пустое значение отключает уведомления. Сначала проверяется формат,
|
||
затем бот проверяет чат через getChat — чужой или недоступный чат не сохраняется.
|
||
parameters:
|
||
- description: 'Цель: @группа, chat_id или пусто'
|
||
in: body
|
||
name: body
|
||
required: true
|
||
schema:
|
||
$ref: '#/definitions/handlers.setTelegramReq'
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/handlers.OKResponse'
|
||
"400":
|
||
description: invalid telegram target
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет входа
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
summary: Указать цель уведомлений вручную
|
||
tags:
|
||
- auth
|
||
/auth/telegram/bot:
|
||
delete:
|
||
description: Стирает сохранённый токен текущего пользователя и останавливает
|
||
его поллер. Повторный вызов безопасен — ответ тот же 200.
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/handlers.OKResponse'
|
||
"401":
|
||
description: нет входа
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: профиль не найден
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
summary: Отключить своего бота
|
||
tags:
|
||
- auth
|
||
post:
|
||
consumes:
|
||
- application/json
|
||
description: 'Принимает токен Bot API (формат «id:secret» от @BotFather), проверяет
|
||
его через getMe, шифрует и сохраняет. Дальше уведомления пользователя идут
|
||
через этого бота, его поллер поднимается сразу. Лимит: 5 попыток за 5 минут.
|
||
Без DATA_ENCRYPTION_KEY на сервере — 400.'
|
||
parameters:
|
||
- description: Токен бота
|
||
in: body
|
||
name: body
|
||
required: true
|
||
schema:
|
||
$ref: '#/definitions/handlers.setBotReq'
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/handlers.OKResponse'
|
||
"400":
|
||
description: токен не сохранён (формат, getMe или фича выключена)
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет входа
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: профиль не найден
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"429":
|
||
description: слишком много попыток
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
summary: Подключить своего бота
|
||
tags:
|
||
- auth
|
||
/auth/telegram/code:
|
||
post:
|
||
consumes:
|
||
- application/json
|
||
description: 'Одноразовый код и deep-link t.me/{bot}?start={code}: откройте
|
||
ссылку в Telegram и отправьте боту код — этот чат начнёт получать уведомления
|
||
о заявках. Бот — ваш собственный токен, а если его нет — глобальный бот сервера.
|
||
Код живёт 15 минут, прежние коды гаснут. Лимит: 5 выпусков за 5 минут на пользователя.'
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/handlers.BindCodeResponse'
|
||
"400":
|
||
description: ни свой бот, ни глобальный не настроены
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: профиль не найден
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"429":
|
||
description: слишком много кодов
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
summary: Выдать код привязки Telegram
|
||
tags:
|
||
- auth
|
||
/invitations:
|
||
get:
|
||
description: 'Список приглашений для текущего мастера: по каждому — проект,
|
||
кто пригласил и до какого срока. Просроченные сюда не попадают — принять их
|
||
уже нельзя.'
|
||
parameters:
|
||
- default: 20
|
||
description: Строк на странице, 1..100
|
||
in: query
|
||
name: limit
|
||
type: integer
|
||
- default: 0
|
||
description: Сдвиг от начала выборки
|
||
in: query
|
||
name: offset
|
||
type: integer
|
||
- default: invited_at
|
||
description: Поле сортировки
|
||
enum:
|
||
- invited_at
|
||
- expires_at
|
||
- project_name
|
||
in: query
|
||
name: sort
|
||
type: string
|
||
- description: Направление сортировки
|
||
enum:
|
||
- asc
|
||
- desc
|
||
in: query
|
||
name: order
|
||
type: string
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/handlers.InvitationListResponse'
|
||
"400":
|
||
description: некорректные параметры страницы
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: нужна роль worker
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Мои приглашения в проекты
|
||
tags:
|
||
- projects
|
||
/objects/{id}:
|
||
patch:
|
||
consumes:
|
||
- application/json
|
||
description: Поля опциональны. У уже созданных заявок адрес остаётся тем, что
|
||
был на момент создания — это осознанно.
|
||
parameters:
|
||
- description: ID объекта
|
||
in: path
|
||
name: id
|
||
required: true
|
||
type: integer
|
||
- description: Что изменить
|
||
in: body
|
||
name: body
|
||
required: true
|
||
schema:
|
||
$ref: '#/definitions/handlers.updateObjectReq'
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/models.Object'
|
||
"400":
|
||
description: некорректный id или пустое тело
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: нужна роль client или admin
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: объект не найден
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"409":
|
||
description: имя уже занято в проекте
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Изменить объект
|
||
tags:
|
||
- projects
|
||
/projects:
|
||
get:
|
||
description: 'Клиент видит только свои проекты, админ — все (можно сузить через
|
||
client_id). Мастерам проекты не отдаются. Ответ — страница: items плюс total/limit/offset.'
|
||
parameters:
|
||
- description: Фильтр по клиенту (только admin)
|
||
in: query
|
||
name: client_id
|
||
type: string
|
||
- default: 20
|
||
description: Строк на странице, 1..100
|
||
in: query
|
||
name: limit
|
||
type: integer
|
||
- default: 0
|
||
description: Сдвиг от начала выборки
|
||
in: query
|
||
name: offset
|
||
type: integer
|
||
- default: created_at
|
||
description: Поле сортировки
|
||
enum:
|
||
- created_at
|
||
- name
|
||
- id
|
||
in: query
|
||
name: sort
|
||
type: string
|
||
- description: Направление сортировки
|
||
enum:
|
||
- asc
|
||
- desc
|
||
in: query
|
||
name: order
|
||
type: string
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/handlers.ProjectListResponse'
|
||
"400":
|
||
description: некорректные параметры страницы
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: нужна роль client или admin
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Список проектов
|
||
tags:
|
||
- projects
|
||
post:
|
||
consumes:
|
||
- application/json
|
||
description: Клиент создаёт проект себе; админ — для любого клиента (в этом
|
||
случае обязателен client_id). Проект — контур, внутри которого живут объекты
|
||
и мастера.
|
||
parameters:
|
||
- description: Данные проекта
|
||
in: body
|
||
name: body
|
||
required: true
|
||
schema:
|
||
$ref: '#/definitions/handlers.createProjectReq'
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"201":
|
||
description: Created
|
||
schema:
|
||
$ref: '#/definitions/models.Project'
|
||
"400":
|
||
description: некорректное тело; client_id обязателен для admin; владелец
|
||
не клиент
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: нужна роль client или admin
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"409":
|
||
description: у клиента уже есть проект с таким именем
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Создать проект
|
||
tags:
|
||
- projects
|
||
/projects/{id}:
|
||
delete:
|
||
description: 'Действие необратимо: вместе с проектом удаляются его объекты,
|
||
участия мастеров и все заявки (вместе с событиями и фото). Объекты фото в
|
||
хранилище убираются после успешного удаления в БД. Клиент своих проектов не
|
||
удаляет — только админ.'
|
||
parameters:
|
||
- description: ID проекта
|
||
in: path
|
||
name: id
|
||
required: true
|
||
type: integer
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/handlers.OKResponse'
|
||
"400":
|
||
description: некорректный id
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: нужна роль admin
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: проект не найден
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Удалить проект
|
||
tags:
|
||
- projects
|
||
get:
|
||
description: Админ — любой проект, клиент — только свой; чужой проект неотличим
|
||
от несуществующего (404).
|
||
parameters:
|
||
- description: ID проекта
|
||
in: path
|
||
name: id
|
||
required: true
|
||
type: integer
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/models.Project'
|
||
"400":
|
||
description: некорректный id
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: нужна роль client или admin
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: проект не найден
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Детали проекта
|
||
tags:
|
||
- projects
|
||
patch:
|
||
consumes:
|
||
- application/json
|
||
description: Поля опциональны, меняется только переданное. Выключенный проект
|
||
не принимает новые заявки и объекты.
|
||
parameters:
|
||
- description: ID проекта
|
||
in: path
|
||
name: id
|
||
required: true
|
||
type: integer
|
||
- description: Что изменить
|
||
in: body
|
||
name: body
|
||
required: true
|
||
schema:
|
||
$ref: '#/definitions/handlers.updateProjectReq'
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/models.Project'
|
||
"400":
|
||
description: некорректный id или пустое тело
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: нужна роль client или admin
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: проект не найден
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"409":
|
||
description: имя уже занято у этого клиента
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Изменить проект
|
||
tags:
|
||
- projects
|
||
/projects/{id}/invitation/accept:
|
||
post:
|
||
description: 'invited → active: с этого момента мастер состоит в проекте и ему
|
||
можно назначать заявки. Действует только на своё непросроченное приглашение
|
||
(срок — 7 дней, см. repository.InvitationTTL).'
|
||
parameters:
|
||
- description: ID проекта
|
||
in: path
|
||
name: id
|
||
required: true
|
||
type: integer
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/handlers.OKResponse'
|
||
"400":
|
||
description: некорректный id
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: нужна роль worker
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: приглашения нет
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"409":
|
||
description: срок вышел или приглашение уже закрыто
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Принять приглашение в проект
|
||
tags:
|
||
- projects
|
||
/projects/{id}/invitation/decline:
|
||
post:
|
||
description: 'invited → declined: заявок мастер не получает, клиент видит отказ
|
||
в составе проекта и может пригласить повторно (строка та же, история ответа
|
||
обновляется).'
|
||
parameters:
|
||
- description: ID проекта
|
||
in: path
|
||
name: id
|
||
required: true
|
||
type: integer
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/handlers.OKResponse'
|
||
"400":
|
||
description: некорректный id
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: нужна роль worker
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: приглашения нет
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"409":
|
||
description: приглашение уже закрыто
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Отклонить приглашение в проект
|
||
tags:
|
||
- projects
|
||
/projects/{id}/objects:
|
||
get:
|
||
description: Здания/корпуса, входящие в проект. Адрес заявки берётся из объекта.
|
||
parameters:
|
||
- description: ID проекта
|
||
in: path
|
||
name: id
|
||
required: true
|
||
type: integer
|
||
- default: 20
|
||
description: Строк на странице, 1..100
|
||
in: query
|
||
name: limit
|
||
type: integer
|
||
- default: 0
|
||
description: Сдвиг от начала выборки
|
||
in: query
|
||
name: offset
|
||
type: integer
|
||
- default: name
|
||
description: Поле сортировки
|
||
enum:
|
||
- name
|
||
- created_at
|
||
- id
|
||
in: query
|
||
name: sort
|
||
type: string
|
||
- description: Направление сортировки
|
||
enum:
|
||
- asc
|
||
- desc
|
||
in: query
|
||
name: order
|
||
type: string
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/handlers.ObjectListResponse'
|
||
"400":
|
||
description: некорректный id
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: нужна роль client или admin
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: проект не найден
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Объекты проекта
|
||
tags:
|
||
- projects
|
||
post:
|
||
consumes:
|
||
- application/json
|
||
description: Объект — здание/корпус с адресом; заявки создаются именно на объект.
|
||
Выключенный проект объектов не принимает.
|
||
parameters:
|
||
- description: ID проекта
|
||
in: path
|
||
name: id
|
||
required: true
|
||
type: integer
|
||
- description: Данные объекта
|
||
in: body
|
||
name: body
|
||
required: true
|
||
schema:
|
||
$ref: '#/definitions/handlers.createObjectReq'
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"201":
|
||
description: Created
|
||
schema:
|
||
$ref: '#/definitions/models.Object'
|
||
"400":
|
||
description: некорректный id или тело
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: нужна роль client или admin
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: проект не найден
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"409":
|
||
description: проект выключен или имя объекта занято
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Создать объект
|
||
tags:
|
||
- projects
|
||
/projects/{id}/workers:
|
||
get:
|
||
description: 'Кто приглашён в проект и кем принят: статус invited — приглашение
|
||
ждёт решения, active — мастер работает с заявками, declined — отказал. Хеши
|
||
паролей в ответ не попадают.'
|
||
parameters:
|
||
- description: ID проекта
|
||
in: path
|
||
name: id
|
||
required: true
|
||
type: integer
|
||
- default: 20
|
||
description: Строк на странице, 1..100
|
||
in: query
|
||
name: limit
|
||
type: integer
|
||
- default: 0
|
||
description: Сдвиг от начала выборки
|
||
in: query
|
||
name: offset
|
||
type: integer
|
||
- default: name
|
||
description: Поле сортировки
|
||
enum:
|
||
- name
|
||
- created_at
|
||
- email
|
||
- id
|
||
in: query
|
||
name: sort
|
||
type: string
|
||
- description: Направление сортировки
|
||
enum:
|
||
- asc
|
||
- desc
|
||
in: query
|
||
name: order
|
||
type: string
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/handlers.ProjectWorkerListResponse'
|
||
"400":
|
||
description: некорректный id или параметры страницы
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: нужна роль client или admin
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: проект не найден
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Мастера проекта
|
||
tags:
|
||
- projects
|
||
post:
|
||
consumes:
|
||
- application/json
|
||
description: 'С 0005 это приглашение, а не готовое членство: мастер становится
|
||
участником только сам, приняв приглашение (POST /projects/{id}/invitation/accept)
|
||
— до этого заявки проекта ему не назначаются. Приглашение живёт 7 дней. Мастер
|
||
может состоять в нескольких проектах; аккаунт должен быть активным, с ролью
|
||
worker и подтверждённым (0010) — неподтверждённого сначала подтверждает тот,
|
||
кто его создал.'
|
||
parameters:
|
||
- description: ID проекта
|
||
in: path
|
||
name: id
|
||
required: true
|
||
type: integer
|
||
- description: Мастер
|
||
in: body
|
||
name: body
|
||
required: true
|
||
schema:
|
||
$ref: '#/definitions/handlers.addWorkerReq'
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/handlers.InvitationResponse'
|
||
"400":
|
||
description: некорректный id/тело; не мастер, неактивен или не подтверждён
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: нужна роль client или admin
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: проект или мастер не найден
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"409":
|
||
description: мастер уже состоит в проекте / приглашение уже ждёт ответа
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Пригласить мастера в проект
|
||
tags:
|
||
- projects
|
||
/projects/{id}/workers/{worker_id}:
|
||
delete:
|
||
description: Нельзя убрать, пока за ним закреплены незакрытые заявки проекта
|
||
(409) — иначе работа осталась бы без исполнителя. Заявки из истории при этом
|
||
не теряются.
|
||
parameters:
|
||
- description: ID проекта
|
||
in: path
|
||
name: id
|
||
required: true
|
||
type: integer
|
||
- description: ID мастера (uuid)
|
||
in: path
|
||
name: worker_id
|
||
required: true
|
||
type: string
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/handlers.OKResponse'
|
||
"400":
|
||
description: некорректный id
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: нужна роль client или admin
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: проект не найден или мастер не состоит в проекте
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"409":
|
||
description: за мастером есть незакрытые заявки
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Убрать мастера из проекта
|
||
tags:
|
||
- projects
|
||
/recurring:
|
||
get:
|
||
description: 'Клиент видит только свои правила, админ — все. Ответ — страница:
|
||
items плюс total/limit/offset.'
|
||
parameters:
|
||
- description: Сузить до одного проекта
|
||
in: query
|
||
name: project_id
|
||
type: integer
|
||
- default: 20
|
||
description: Строк на странице, 1..100
|
||
in: query
|
||
name: limit
|
||
type: integer
|
||
- default: 0
|
||
description: Сдвиг от начала выборки
|
||
in: query
|
||
name: offset
|
||
type: integer
|
||
- default: created_at
|
||
description: Поле сортировки
|
||
enum:
|
||
- created_at
|
||
- id
|
||
- date
|
||
in: query
|
||
name: sort
|
||
type: string
|
||
- description: Направление сортировки
|
||
enum:
|
||
- asc
|
||
- desc
|
||
in: query
|
||
name: order
|
||
type: string
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/handlers.RecurringRuleListResponse'
|
||
"400":
|
||
description: некорректные параметры страницы
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: нужна роль client или admin
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Список правил повторения
|
||
tags:
|
||
- recurring
|
||
post:
|
||
consumes:
|
||
- application/json
|
||
description: 'Правило — шаблон заявки (объект, категория, описание, приоритет)
|
||
плюс расписание: anchor_at (дата-время первого запуска) и period_days — «каждые
|
||
N дней/недель». Фоновый планировщик сам создаёт заявку, когда срок наступил,
|
||
— но только если прошлая по правилу уже закрыта (без дублей). Клиент создаёт
|
||
правило для своего проекта, админ — для любого. Обычно правило заводится чекбоксом
|
||
в POST /tickets; этот эндпоинт — для управления без заявки.'
|
||
parameters:
|
||
- description: Данные правила
|
||
in: body
|
||
name: body
|
||
required: true
|
||
schema:
|
||
$ref: '#/definitions/handlers.createRecurringReq'
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"201":
|
||
description: Created
|
||
schema:
|
||
$ref: '#/definitions/models.RecurringRule'
|
||
"400":
|
||
description: некорректное тело запроса
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: нужна роль client или admin
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: объект не найден (или чужой — неотличим)
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"409":
|
||
description: проект или объект выключен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Создать правило повторяющейся заявки
|
||
tags:
|
||
- recurring
|
||
/recurring/{id}:
|
||
delete:
|
||
description: Удаляется само правило, его история срабатываний (какая заявка
|
||
создавалась и когда) и — каскадом — будущие заявки ещё не созданы. Созданные
|
||
ранее заявки не трогаются.
|
||
parameters:
|
||
- description: ID правила
|
||
in: path
|
||
name: id
|
||
required: true
|
||
type: integer
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/handlers.OKResponse'
|
||
"400":
|
||
description: некорректный id
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: нужна роль client или admin
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: правило не найдено
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Удалить правило повторения
|
||
tags:
|
||
- recurring
|
||
get:
|
||
description: Админ — любое правило, клиент — только своё; чужое неотличимо от
|
||
несуществующего (404).
|
||
parameters:
|
||
- description: ID правила
|
||
in: path
|
||
name: id
|
||
required: true
|
||
type: integer
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/models.RecurringRule'
|
||
"400":
|
||
description: некорректный id
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: нужна роль client или admin
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: правило не найдено
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Детали правила повторения
|
||
tags:
|
||
- recurring
|
||
patch:
|
||
consumes:
|
||
- application/json
|
||
description: 'Частичное обновление: применяются только присланные поля, рамки
|
||
те же, что при создании (anchor_at — любая дата-время, period_days 1..3650).
|
||
object_id не меняется (перенос — через удаление и создание). Срок не сдвигается
|
||
вручную: он пересчитывается от последнего срабатывания с новым расписанием.'
|
||
parameters:
|
||
- description: ID правила
|
||
in: path
|
||
name: id
|
||
required: true
|
||
type: integer
|
||
- description: Что изменить (хотя бы одно поле)
|
||
in: body
|
||
name: body
|
||
required: true
|
||
schema:
|
||
$ref: '#/definitions/handlers.updateRecurringReq'
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/models.RecurringRule'
|
||
"400":
|
||
description: некорректный id, пустое тело или значение вне рамок
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: нужна роль client или admin
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: правило не найдено
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Изменить правило повторения
|
||
tags:
|
||
- recurring
|
||
/recurring/{id}/run:
|
||
post:
|
||
description: 'Заявка создаётся сразу, без ожидания интервала. Остальные рубежи
|
||
как у планировщика: правило, проект и объект включены, прошлая заявка по правилу
|
||
закрыта — иначе 409, дубль не создаём. Срок сдвигается на момент запуска (следующий
|
||
фоновый тик отсчитает интервал от него), уведомление уходит тем же текстом,
|
||
что при фоновом срабатывании.'
|
||
parameters:
|
||
- description: ID правила
|
||
in: path
|
||
name: id
|
||
required: true
|
||
type: integer
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"201":
|
||
description: Created
|
||
schema:
|
||
$ref: '#/definitions/handlers.RecurringRunResponse'
|
||
"400":
|
||
description: некорректный id
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: нужна роль client или admin
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: правило не найдено (или чужое — неотличим)
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"409":
|
||
description: правило, проект или объект выключены, либо есть открытая заявка
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Запустить правило сейчас
|
||
tags:
|
||
- recurring
|
||
/recurring/{id}/runs:
|
||
get:
|
||
description: 'Какие заявки создавались по правилу и когда (аудит). Сначала свежие.
|
||
Строка живёт вместе со своей заявкой: когда заявку удаляет автоочистка (TICKET_RETENTION_DAYS),
|
||
строка истории уходит следом.'
|
||
parameters:
|
||
- description: ID правила
|
||
in: path
|
||
name: id
|
||
required: true
|
||
type: integer
|
||
- default: 20
|
||
description: Строк на странице, 1..100
|
||
in: query
|
||
name: limit
|
||
type: integer
|
||
- default: 0
|
||
description: Сдвиг от начала выборки
|
||
in: query
|
||
name: offset
|
||
type: integer
|
||
- default: run_at
|
||
description: Поле сортировки
|
||
enum:
|
||
- run_at
|
||
- id
|
||
in: query
|
||
name: sort
|
||
type: string
|
||
- description: Направление сортировки
|
||
enum:
|
||
- asc
|
||
- desc
|
||
in: query
|
||
name: order
|
||
type: string
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/handlers.RecurringRunListResponse'
|
||
"400":
|
||
description: некорректный id или параметры страницы
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: нужна роль client или admin
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: правило не найдено
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: История срабатываний правила
|
||
tags:
|
||
- recurring
|
||
/tickets:
|
||
get:
|
||
description: 'Выборка режется по роли: клиент видит свои, мастер — назначенные
|
||
ему, админ — всё. Клиенту и мастеру параметры client_id/worker_id игнорируются:
|
||
подсмотреть чужое нельзя. Фильтры накладываются поверх выборки по роли. Ответ
|
||
— страница: items плюс total/limit/offset.'
|
||
parameters:
|
||
- description: Фильтр по статусу
|
||
enum:
|
||
- new
|
||
- assigned
|
||
- in_progress
|
||
- done
|
||
- confirmed
|
||
- rejected
|
||
- closed
|
||
in: query
|
||
name: status
|
||
type: string
|
||
- description: Фильтр по категории
|
||
enum:
|
||
- electric
|
||
- plumbing
|
||
- climate
|
||
- elevator
|
||
- other
|
||
in: query
|
||
name: category
|
||
type: string
|
||
- description: Фильтр по приоритету
|
||
enum:
|
||
- low
|
||
- normal
|
||
- high
|
||
in: query
|
||
name: priority
|
||
type: string
|
||
- description: Фильтр по клиенту (только admin)
|
||
in: query
|
||
name: client_id
|
||
type: string
|
||
- description: Фильтр по мастеру (только admin)
|
||
in: query
|
||
name: worker_id
|
||
type: string
|
||
- description: Фильтр по проекту
|
||
in: query
|
||
name: project_id
|
||
type: integer
|
||
- description: Фильтр по объекту
|
||
in: query
|
||
name: object_id
|
||
type: integer
|
||
- description: created_at с, YYYY-MM-DD (включительно, с полуночи UTC)
|
||
in: query
|
||
name: date_from
|
||
type: string
|
||
- description: created_at по, YYYY-MM-DD (включительно, до полуночи UTC следующего
|
||
дня)
|
||
in: query
|
||
name: date_to
|
||
type: string
|
||
- default: 20
|
||
description: Строк на странице, 1..100
|
||
in: query
|
||
name: limit
|
||
type: integer
|
||
- default: 0
|
||
description: Сдвиг от начала выборки
|
||
in: query
|
||
name: offset
|
||
type: integer
|
||
- default: created_at
|
||
description: Поле сортировки
|
||
enum:
|
||
- created_at
|
||
- priority
|
||
- id
|
||
in: query
|
||
name: sort
|
||
type: string
|
||
- description: Направление сортировки
|
||
enum:
|
||
- asc
|
||
- desc
|
||
in: query
|
||
name: order
|
||
type: string
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/handlers.TicketListResponse'
|
||
"400":
|
||
description: некорректный фильтр или параметры страницы
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: неизвестная роль
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Список заявок
|
||
tags:
|
||
- tickets
|
||
post:
|
||
consumes:
|
||
- application/json
|
||
description: 'Клиент создаёт заявку на объект своего проекта. Адрес берётся
|
||
из объекта на момент создания, статус всегда new, мастер не назначен. Если
|
||
передан recurring — той же операцией создаётся правило повторения (anchor_at
|
||
— первый запуск, period_days — период в днях), связанное с заявкой: оно видно
|
||
в GET /tickets/{id} в поле recurring, управление — через PATCH /recurring/{id}.'
|
||
parameters:
|
||
- description: Данные заявки
|
||
in: body
|
||
name: body
|
||
required: true
|
||
schema:
|
||
$ref: '#/definitions/handlers.createTicketReq'
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"201":
|
||
description: Created
|
||
schema:
|
||
$ref: '#/definitions/models.Ticket'
|
||
"400":
|
||
description: некорректное тело запроса
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: нужна роль client
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: объект не найден (или чужой — неотличим)
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"409":
|
||
description: проект или объект выключен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Создать заявку
|
||
tags:
|
||
- tickets
|
||
/tickets/{id}:
|
||
get:
|
||
description: Админ — любая заявка, клиент — только своя, мастер — назначенная
|
||
ему либо свободная (new) из его проектов.
|
||
parameters:
|
||
- description: ID заявки
|
||
in: path
|
||
name: id
|
||
required: true
|
||
type: integer
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/models.Ticket'
|
||
"400":
|
||
description: некорректный id
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: заявка не принадлежит вам
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: заявка не найдена
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Детали заявки
|
||
tags:
|
||
- tickets
|
||
/tickets/{id}/assign:
|
||
patch:
|
||
consumes:
|
||
- application/json
|
||
description: new → assigned. Доступно админу и владельцу-клиенту. Мастер должен
|
||
состоять в проекте заявки и быть активным — иначе 400/409.
|
||
parameters:
|
||
- description: ID заявки
|
||
in: path
|
||
name: id
|
||
required: true
|
||
type: integer
|
||
- description: Мастер
|
||
in: body
|
||
name: body
|
||
required: true
|
||
schema:
|
||
$ref: '#/definitions/handlers.assignReq'
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/models.Ticket'
|
||
"400":
|
||
description: некорректный id/тело, не-мастер, неактивен, приглашение не
|
||
принято или не состоит в проекте заявки
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: нужна роль admin или client; чужая заявка
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: заявка не найдена
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"409":
|
||
description: заявка уже назначена или закрыта
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Назначить мастера
|
||
tags:
|
||
- tickets
|
||
/tickets/{id}/cancel:
|
||
patch:
|
||
consumes:
|
||
- application/json
|
||
description: Закрывает заявку с cancel_reason и closed_by. Клиент — только свои
|
||
в new/assigned/done/rejected; админ — любую кроме уже закрытой, включая in_progress.
|
||
Причина обязательна.
|
||
parameters:
|
||
- description: ID заявки
|
||
in: path
|
||
name: id
|
||
required: true
|
||
type: integer
|
||
- description: Причина отмены, минимум 5 символов
|
||
in: body
|
||
name: body
|
||
required: true
|
||
schema:
|
||
$ref: '#/definitions/handlers.reasonReq'
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/models.Ticket'
|
||
"400":
|
||
description: 'некорректный id или тело: причина обязательна'
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: чужая заявка или роль не client/admin
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: заявка не найдена
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"409":
|
||
description: нельзя закрыть из текущего статуса
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Отменить заявку (финал)
|
||
tags:
|
||
- tickets
|
||
/tickets/{id}/confirm:
|
||
patch:
|
||
description: done → closed с confirmed_at. Доступно только владельцу-клиенту.
|
||
parameters:
|
||
- description: ID заявки
|
||
in: path
|
||
name: id
|
||
required: true
|
||
type: integer
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/models.Ticket'
|
||
"400":
|
||
description: некорректный id
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: заявка не принадлежит вам
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: заявка не найдена
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"409":
|
||
description: заявка не в статусе done
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Подтвердить работу (финал)
|
||
tags:
|
||
- tickets
|
||
/tickets/{id}/done:
|
||
patch:
|
||
description: in_progress → done. Дальше клиент подтверждает или отклоняет.
|
||
parameters:
|
||
- description: ID заявки
|
||
in: path
|
||
name: id
|
||
required: true
|
||
type: integer
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/models.Ticket'
|
||
"400":
|
||
description: некорректный id
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: нужна роль worker
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: заявка не найдена
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"409":
|
||
description: не тот статус или заявка назначена другому
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Завершить работу
|
||
tags:
|
||
- tickets
|
||
/tickets/{id}/events:
|
||
get:
|
||
description: 'Хронология переходов заявки с авторами: created, assigned, started,
|
||
done, confirmed, rejected, canceled. from_status у события «создана» нет.
|
||
События пишутся той же SQL-операцией, что и переход, поэтому история не расходится
|
||
со статусом.'
|
||
parameters:
|
||
- description: ID заявки
|
||
in: path
|
||
name: id
|
||
required: true
|
||
type: integer
|
||
- default: 20
|
||
description: Строк на странице, 1..100
|
||
in: query
|
||
name: limit
|
||
type: integer
|
||
- default: 0
|
||
description: Сдвиг от начала выборки
|
||
in: query
|
||
name: offset
|
||
type: integer
|
||
- default: id
|
||
description: Поле сортировки
|
||
enum:
|
||
- id
|
||
- created_at
|
||
in: query
|
||
name: sort
|
||
type: string
|
||
- description: Направление сортировки
|
||
enum:
|
||
- asc
|
||
- desc
|
||
in: query
|
||
name: order
|
||
type: string
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/handlers.TicketEventListResponse'
|
||
"400":
|
||
description: некорректный id или параметры страницы
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: чужая заявка
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: заявка не найдена
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Журнал событий заявки
|
||
tags:
|
||
- tickets
|
||
/tickets/{id}/photos:
|
||
get:
|
||
description: 'Файлы заявки в хронологии (id ASC по умолчанию). В каждом элементе
|
||
url — временная подписанная ссылка на чтение: бакет приватный, сам ключ наружу
|
||
не уходит.'
|
||
parameters:
|
||
- description: ID заявки
|
||
in: path
|
||
name: id
|
||
required: true
|
||
type: integer
|
||
- default: 20
|
||
description: Строк на странице, 1..100
|
||
in: query
|
||
name: limit
|
||
type: integer
|
||
- default: 0
|
||
description: Сдвиг от начала выборки
|
||
in: query
|
||
name: offset
|
||
type: integer
|
||
- default: id
|
||
description: Поле сортировки
|
||
enum:
|
||
- id
|
||
- created_at
|
||
in: query
|
||
name: sort
|
||
type: string
|
||
- description: Направление сортировки
|
||
enum:
|
||
- asc
|
||
- desc
|
||
in: query
|
||
name: order
|
||
type: string
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/handlers.PhotoListResponse'
|
||
"400":
|
||
description: некорректный id или параметры страницы
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: чужая заявка
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: заявка не найдена
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Список фото отчёта
|
||
tags:
|
||
- tickets
|
||
post:
|
||
consumes:
|
||
- multipart/form-data
|
||
description: 'multipart/form-data, поле file: только фото (jpeg/png/webp, определяется
|
||
по сигнатуре байтов), не больше PHOTO_MAX_BYTES на файл и PHOTO_MAX_PER_TICKET
|
||
файлов на заявку. Доступно мастеру (заявка in_progress — после «Работа выполнена»
|
||
отчёт заморожен, мастер — активный участник проекта) или админу. Каждая загрузка
|
||
оставляет событие photo_added в журнале заявки.'
|
||
parameters:
|
||
- description: ID заявки
|
||
in: path
|
||
name: id
|
||
required: true
|
||
type: integer
|
||
- description: Файл фотографии (jpeg/png/webp)
|
||
in: formData
|
||
name: file
|
||
required: true
|
||
type: file
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"201":
|
||
description: фото загружено, url — временная ссылка на чтение
|
||
schema:
|
||
$ref: '#/definitions/models.TicketPhoto'
|
||
"400":
|
||
description: некорректный id, файл отсутствует, не фото или больше лимита
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: нужна роль worker или admin; чужая заявка
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: заявка не найдена
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"409":
|
||
description: лимит файлов исчерпан или заявка не в статусе in_progress
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"500":
|
||
description: хранилище недоступно
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Загрузить фото отчёта
|
||
tags:
|
||
- tickets
|
||
/tickets/{id}/photos/{photo_id}:
|
||
delete:
|
||
description: 'Своё фото удаляет автор, пока заявка в работе (in_progress): после
|
||
«Работа выполнена» отчёт заморожен; админу — любое фото. Событие photo_removed
|
||
пишется той же операцией, что и удаление.'
|
||
parameters:
|
||
- description: ID заявки
|
||
in: path
|
||
name: id
|
||
required: true
|
||
type: integer
|
||
- description: ID фото
|
||
in: path
|
||
name: photo_id
|
||
required: true
|
||
type: integer
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/handlers.OKResponse'
|
||
"400":
|
||
description: некорректный id или photo_id
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: нужна роль worker или admin; чужая заявка
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: заявка или фото не найдены
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"409":
|
||
description: чужое фото или заявка уже не в работе
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Удалить фото
|
||
tags:
|
||
- tickets
|
||
/tickets/{id}/reject:
|
||
patch:
|
||
consumes:
|
||
- application/json
|
||
description: 'done → rejected. Не финал: мастер берёт заявку в работу повторно,
|
||
rework_count увеличивается. Причина обязательна — без неё мастер не поймёт,
|
||
что переделывать.'
|
||
parameters:
|
||
- description: ID заявки
|
||
in: path
|
||
name: id
|
||
required: true
|
||
type: integer
|
||
- description: Причина отклонения, минимум 5 символов
|
||
in: body
|
||
name: body
|
||
required: true
|
||
schema:
|
||
$ref: '#/definitions/handlers.reasonReq'
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/models.Ticket'
|
||
"400":
|
||
description: 'некорректный id или тело: причина обязательна'
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: заявка не принадлежит вам
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: заявка не найдена
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"409":
|
||
description: заявка не в статусе done
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Отклонить работу (на доработку)
|
||
tags:
|
||
- tickets
|
||
/tickets/{id}/start:
|
||
patch:
|
||
description: assigned → in_progress, а после отклонения (rejected) — повторный
|
||
запуск. started_at первого круга сохраняется.
|
||
parameters:
|
||
- description: ID заявки
|
||
in: path
|
||
name: id
|
||
required: true
|
||
type: integer
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/models.Ticket'
|
||
"400":
|
||
description: некорректный id
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: нужна роль worker
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: заявка не найдена
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"409":
|
||
description: не тот статус или заявка назначена другому
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Взять в работу
|
||
tags:
|
||
- tickets
|
||
/tickets/{id}/take:
|
||
patch:
|
||
description: 'new → assigned: мастер назначает себя на свободную заявку своего
|
||
проекта. Дальше — «Взять в работу» (start) как обычно.'
|
||
parameters:
|
||
- description: ID заявки
|
||
in: path
|
||
name: id
|
||
required: true
|
||
type: integer
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/models.Ticket'
|
||
"400":
|
||
description: некорректный id; приглашение не принято или отклонено; не состоит
|
||
в проекте заявки
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: нужна роль worker
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: заявка не найдена
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"409":
|
||
description: заявка уже взята или не в статусе new
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Взять заявку самому
|
||
tags:
|
||
- tickets
|
||
/users:
|
||
get:
|
||
description: Админ видит любую роль и всех; клиенту доступна только роль worker
|
||
и только его собственные мастера (чужие организации не видны). С параметром
|
||
q (минимум 2 символа) возвращается поиск по имени/email среди активных — так
|
||
клиент пригласит в проект существующего мастера, не видя при этом полного
|
||
списка. include_inactive=1 дополняет список отключёнными аккаунтами — только
|
||
для админа.
|
||
parameters:
|
||
- default: worker
|
||
description: Роль
|
||
enum:
|
||
- worker
|
||
- admin
|
||
- client
|
||
in: query
|
||
name: role
|
||
type: string
|
||
- description: Поиск по имени или email, минимум 2 символа
|
||
in: query
|
||
name: q
|
||
type: string
|
||
- default: "0"
|
||
description: Показать и отключённых (только для админа)
|
||
enum:
|
||
- "0"
|
||
- "1"
|
||
in: query
|
||
name: include_inactive
|
||
type: string
|
||
- default: 20
|
||
description: Строк на странице, 1..100
|
||
in: query
|
||
name: limit
|
||
type: integer
|
||
- default: 0
|
||
description: Сдвиг от начала выборки
|
||
in: query
|
||
name: offset
|
||
type: integer
|
||
- default: name
|
||
description: Поле сортировки
|
||
enum:
|
||
- name
|
||
- created_at
|
||
- email
|
||
- id
|
||
in: query
|
||
name: sort
|
||
type: string
|
||
- description: Направление сортировки
|
||
enum:
|
||
- asc
|
||
- desc
|
||
in: query
|
||
name: order
|
||
type: string
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/handlers.ProfileListResponse'
|
||
"400":
|
||
description: неизвестная роль, параметры страницы или короткий q
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: клиент запрашивает не мастеров
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Список пользователей по роли
|
||
tags:
|
||
- users
|
||
post:
|
||
consumes:
|
||
- application/json
|
||
description: 'Админ создаёт мастеров, админов и клиентов; клиент — только мастеров
|
||
(для себя, далее мастер добавляется в проект через POST /projects/{id}/workers).
|
||
Роли admin и client клиентом недоступны. Пароль — по политике (10..72 символа,
|
||
буквы и цифры). Подтверждение: аккаунт, созданный админом, подтверждён сразу;
|
||
созданный клиентом — ждёт подтверждения этим же клиентом, после чего мастеру
|
||
уходит письмо.'
|
||
parameters:
|
||
- description: Данные пользователя
|
||
in: body
|
||
name: body
|
||
required: true
|
||
schema:
|
||
$ref: '#/definitions/handlers.createUserReq'
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"201":
|
||
description: Created
|
||
schema:
|
||
$ref: '#/definitions/models.Profile'
|
||
"400":
|
||
description: некорректное тело запроса или слабый пароль
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: клиент пытается создать админа или клиента
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"409":
|
||
description: email уже занят
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Создать пользователя
|
||
tags:
|
||
- users
|
||
/users/{id}:
|
||
delete:
|
||
description: 'Админ удаляет любой «чистый» аккаунт: refresh-сессии и токены
|
||
сброса удаляются каскадом, но наличие заявок, проектов или участий блокирует
|
||
удаление (409 user has related records) — в этом случае отключите аккаунт
|
||
через PATCH /users/{id}, историю не стираем. Клиент удаляет только мастера,
|
||
состоящего в одном из его проектов: членства клиента в проектах удаляются
|
||
в той же транзакции, но если на мастера ссылаются заявки либо чужие проекты
|
||
— тоже 409, ничего не меняется. Чужой мастер и несуществующий id неразличимы
|
||
(404). Свой id удалить нельзя.'
|
||
parameters:
|
||
- description: ID пользователя (uuid)
|
||
in: path
|
||
name: id
|
||
required: true
|
||
type: string
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/handlers.OKResponse'
|
||
"400":
|
||
description: некорректный id; нельзя удалить себя
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: нужна роль admin или client
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: пользователь не найден
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"409":
|
||
description: у пользователя есть связанные записи
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Удалить пользователя
|
||
tags:
|
||
- users
|
||
patch:
|
||
consumes:
|
||
- application/json
|
||
description: Админ меняет любой аккаунт; клиент — только мастера, который активный
|
||
участник одного из его проектов (иного клиента или чужого мастера не видит
|
||
— 404). При деактивации все refresh-сессии пользователя закрываются сразу,
|
||
мастер перестаёт работать во всех проектах. Выключить самого себя нельзя —
|
||
вы остались бы без входа.
|
||
parameters:
|
||
- description: ID пользователя (uuid)
|
||
in: path
|
||
name: id
|
||
required: true
|
||
type: string
|
||
- description: Новый флаг
|
||
in: body
|
||
name: body
|
||
required: true
|
||
schema:
|
||
$ref: '#/definitions/handlers.setUserReq'
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/models.Profile'
|
||
"400":
|
||
description: некорректный id или тело; попытка выключить себя
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: нужна роль admin или client
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: пользователь не найден (для клиента — не его мастер)
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Включить/выключить аккаунт
|
||
tags:
|
||
- users
|
||
/users/{id}/confirm:
|
||
post:
|
||
consumes:
|
||
- application/json
|
||
description: 'Подтверждает неподтверждённый аккаунт и шлёт пользователю письмо
|
||
«аккаунт подтверждён» со ссылкой на вход. Право — в SQL: админ подтверждает
|
||
любой аккаунт, клиент — только созданного им мастера (иной и несуществующий
|
||
не видны — 404). Повторное подтверждение — 409. Письмо отправляется после
|
||
подтверждения; недоступный SMTP не откатывает подтверждение (письмо просто
|
||
не уйдёт).'
|
||
parameters:
|
||
- description: ID пользователя (uuid)
|
||
in: path
|
||
name: id
|
||
required: true
|
||
type: string
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/handlers.OKResponse'
|
||
"400":
|
||
description: некорректный id
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: нужна роль admin или client
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: пользователь не найден (для клиента — не созданный им мастер)
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"409":
|
||
description: аккаунт уже подтверждён
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Подтвердить аккаунт
|
||
tags:
|
||
- users
|
||
/users/{id}/password-reset:
|
||
post:
|
||
description: 'Токен возвращается в ответе — админ передаёт его пользователю
|
||
сам, любым каналом. Для пользователя есть почта: POST /auth/forgot-password
|
||
шлёт письмо со ссылкой. Токен одноразовый, живёт 24 часа (repository.ResetTTL);
|
||
выпуск нового гасит прежние неиспользованные токены пользователя (включая
|
||
свежие из писем). В БД хранится только sha256 токена.'
|
||
parameters:
|
||
- description: ID пользователя (uuid)
|
||
in: path
|
||
name: id
|
||
required: true
|
||
type: string
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/handlers.PasswordResetResponse'
|
||
"400":
|
||
description: некорректный id
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: нужна роль admin
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: пользователь не найден
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Выдать токен сброса пароля
|
||
tags:
|
||
- users
|
||
/users/{id}/password-set:
|
||
post:
|
||
consumes:
|
||
- application/json
|
||
description: Админ ставит пароль пользователю сам, без ссылки. Политика та же,
|
||
что при создании и при сбросе по токену (10..72 символа, буквы и цифры). Успех
|
||
закрывает все refresh-сессии пользователя и гасит неиспользованные токены
|
||
сброса — со старым паролем в системе остаться нельзя, старые ссылки перестают
|
||
работать. Альтернатива — одноразовая ссылка через POST /users/{id}/password-reset.
|
||
parameters:
|
||
- description: ID пользователя (uuid)
|
||
in: path
|
||
name: id
|
||
required: true
|
||
type: string
|
||
- description: Новый пароль
|
||
in: body
|
||
name: body
|
||
required: true
|
||
schema:
|
||
$ref: '#/definitions/handlers.setPasswordReq'
|
||
produces:
|
||
- application/json
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
schema:
|
||
$ref: '#/definitions/handlers.OKResponse'
|
||
"400":
|
||
description: некорректный id, тело или слабый пароль
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"401":
|
||
description: нет или неверный токен
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"403":
|
||
description: нужна роль admin
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
"404":
|
||
description: пользователь не найден
|
||
schema:
|
||
$ref: '#/definitions/handlers.ErrorResponse'
|
||
security:
|
||
- Bearer: []
|
||
summary: Задать пароль напрямую
|
||
tags:
|
||
- users
|
||
schemes:
|
||
- http
|
||
securityDefinitions:
|
||
Bearer:
|
||
description: JWT формата Bearer, получить через POST /auth/login. Срок жизни —
|
||
1 час, дальше POST /auth/refresh.
|
||
in: header
|
||
name: Authorization
|
||
type: apiKey
|
||
swagger: "2.0"
|