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