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