axe/docs/swagger.yaml

3344 lines
114 KiB
YAML
Raw Permalink Normal View History

2026-10-18 19:07:25 +00:00
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"