axe/docs/swagger.yaml
2026-10-18 22:07:25 +03:00

3344 lines
114 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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"