1076 lines
37 KiB
TypeScript
1076 lines
37 KiB
TypeScript
import { config } from './config.js';
|
|
|
|
/**
|
|
* OpenAPI popis API.
|
|
*
|
|
* `servers` MUSI obsahovat prefix reverse proxy, jinak Swagger "Try it out"
|
|
* vola endpointy na root domene a dostane 404 (viz AGENTS.md).
|
|
* Prefix se bere z ROOT_PATH, nikdy se nehardcoduje.
|
|
*/
|
|
export function buildOpenApiDocument() {
|
|
const server = config.rootPath === '' ? '/' : config.rootPath;
|
|
|
|
return {
|
|
openapi: '3.0.3',
|
|
info: {
|
|
title: 'Automia - portal a API',
|
|
version: '1.0.0',
|
|
description:
|
|
'Webova prezentace a klientsky portal. Automatizace, voiceboti, integrace, ' +
|
|
'tickety a incidenty. Aplikace bezi za reverse proxy AppFactory.',
|
|
},
|
|
servers: [{ url: server, description: 'Verejna adresa vcetne prefixu proxy' }],
|
|
tags: [
|
|
{ name: 'Provoz', description: 'Health a zakladni informace' },
|
|
{ name: 'Autentizace', description: 'Prihlaseni do portalu' },
|
|
{ name: 'Dashboard', description: 'Data klientskeho portalu' },
|
|
{ name: 'Tickety', description: 'Pozadavky, jejich resitele a log prubehu' },
|
|
{ name: 'Automatizace', description: 'Sprava automatizaci a stromu akci' },
|
|
{ name: 'Simulace', description: 'Vyvolani provoznich udalosti pro nahled' },
|
|
{ name: 'Webhook', description: 'Verejny prijem dat do automatizace' },
|
|
{ name: 'Kontakt', description: 'Poptavkovy formular z webu' },
|
|
],
|
|
components: {
|
|
securitySchemes: {
|
|
bearerAuth: {
|
|
type: 'http',
|
|
scheme: 'bearer',
|
|
bearerFormat: 'JWT',
|
|
description: 'Token z POST /api/auth/login. Vlozte samotny token bez slova Bearer.',
|
|
},
|
|
},
|
|
schemas: {
|
|
Error: {
|
|
type: 'object',
|
|
properties: {
|
|
error: { type: 'string', example: 'validation_error' },
|
|
message: { type: 'string', example: 'Zadejte platny e-mail.' },
|
|
},
|
|
},
|
|
User: {
|
|
type: 'object',
|
|
description:
|
|
'Uzivatel muze patrit do vic firem. Role je vzdy az uvnitr firmy, ' +
|
|
'pristup napric firmami je zvlast jako platformAdmin.',
|
|
properties: {
|
|
id: { type: 'string', example: 'usr_1' },
|
|
email: { type: 'string', example: 'admin@automia.cz' },
|
|
name: { type: 'string', example: 'Jiri Uhlir' },
|
|
platformAdmin: {
|
|
type: 'boolean',
|
|
description: 'Vidi napric vsemi firmami a muze mezi nimi prepinat.',
|
|
},
|
|
memberships: {
|
|
type: 'array',
|
|
items: {
|
|
type: 'object',
|
|
properties: {
|
|
tenantId: { type: 'string', example: 'tnt_automia' },
|
|
role: { type: 'string', enum: ['admin', 'agent'] },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Access: {
|
|
type: 'object',
|
|
description: 'Co uzivatel smi. Klient podle toho kresli prepinac pohledu.',
|
|
properties: {
|
|
scopes: {
|
|
type: 'array',
|
|
items: { type: 'string', enum: ['all', 'tenant', 'mine'] },
|
|
},
|
|
tenants: {
|
|
type: 'array',
|
|
items: {
|
|
type: 'object',
|
|
properties: {
|
|
id: { type: 'string', example: 'tnt_automia' },
|
|
name: { type: 'string', example: 'Automia' },
|
|
},
|
|
},
|
|
},
|
|
defaultTenantId: { type: 'string', nullable: true },
|
|
canAssignOthers: {
|
|
type: 'boolean',
|
|
description: 'Smi prehazovat tickety mezi lidmi, ne jen brat na sebe.',
|
|
},
|
|
personId: { type: 'string', nullable: true },
|
|
},
|
|
},
|
|
LoginRequest: {
|
|
type: 'object',
|
|
required: ['email', 'password'],
|
|
properties: {
|
|
email: { type: 'string', format: 'email', example: 'admin@automia.cz' },
|
|
password: { type: 'string', format: 'password', example: 'demo1234' },
|
|
},
|
|
},
|
|
LoginResponse: {
|
|
type: 'object',
|
|
properties: {
|
|
token: { type: 'string' },
|
|
user: { $ref: '#/components/schemas/User' },
|
|
},
|
|
},
|
|
Person: {
|
|
type: 'object',
|
|
description: 'Resitel ticketu. Nemusi mit ucet v portalu, spojka je e-mail.',
|
|
properties: {
|
|
id: { type: 'string', example: 'ppl_vomacka' },
|
|
name: { type: 'string', example: 'Karel Vomacka' },
|
|
email: { type: 'string', format: 'email' },
|
|
role: { type: 'string', example: 'Servicedesk' },
|
|
capacity: {
|
|
type: 'integer',
|
|
description: 'Kolik nevyrizenych ticketu je pro nej jeste zdrava zatez.',
|
|
},
|
|
},
|
|
},
|
|
TicketCustomer: {
|
|
type: 'object',
|
|
properties: {
|
|
id: {
|
|
type: 'string',
|
|
nullable: true,
|
|
description: 'ID firmy v CRM. null = zakaznika se nepodarilo dohledat.',
|
|
example: 'crm_1042',
|
|
},
|
|
company: { type: 'string', example: 'Firma s.r.o.' },
|
|
contact: { type: 'string', example: 'Petra Klientova' },
|
|
reply: {
|
|
type: 'string',
|
|
description: 'Adresa nebo cislo, odkud pozadavek prisel a kam se odpovida.',
|
|
},
|
|
},
|
|
},
|
|
Ticket: {
|
|
type: 'object',
|
|
properties: {
|
|
id: { type: 'string', example: 'TK-4821' },
|
|
subject: { type: 'string' },
|
|
body: {
|
|
type: 'string',
|
|
description:
|
|
'Cely text pozadavku. Prazdny retezec = krok "Zalozit ticket" obsah nenaplnil.',
|
|
},
|
|
sourceRef: {
|
|
type: 'string',
|
|
nullable: true,
|
|
description: 'Odkaz na zdrojovou zpravu u poskytovatele.',
|
|
example: 'wamid.HBgLNDIwNzc0OTAyMzMx',
|
|
},
|
|
channel: {
|
|
type: 'string',
|
|
enum: ['whatsapp', 'facebook', 'instagram', 'email', 'voice', 'form', 'portal'],
|
|
description: 'Odkud pozadavek prisel.',
|
|
},
|
|
customer: { $ref: '#/components/schemas/TicketCustomer' },
|
|
status: { type: 'string', enum: ['new', 'open', 'waiting', 'resolved'] },
|
|
priority: { type: 'string', enum: ['low', 'normal', 'high', 'critical'] },
|
|
assignee: {
|
|
type: 'object',
|
|
nullable: true,
|
|
description: 'Kdo ma ticket u sebe. null = ceka ve fronte.',
|
|
properties: {
|
|
id: { type: 'string', example: 'ppl_vomacka' },
|
|
name: { type: 'string', example: 'Karel Vomacka' },
|
|
},
|
|
},
|
|
automationId: {
|
|
type: 'string',
|
|
nullable: true,
|
|
description: 'Automatizace, ktera ticket zalozila. null = zalozeno rucne.',
|
|
},
|
|
createdAt: { type: 'string', format: 'date-time' },
|
|
updatedAt: { type: 'string', format: 'date-time' },
|
|
},
|
|
},
|
|
TicketTraceEntry: {
|
|
type: 'object',
|
|
description:
|
|
'Jeden radek logu ticketu. Strom se sklada pres parentId - vetev podminky ' +
|
|
'visi na zaznamu te podminky.',
|
|
properties: {
|
|
id: { type: 'string', example: 'tr_12' },
|
|
parentId: {
|
|
type: 'string',
|
|
nullable: true,
|
|
description: 'null = zaznam v hlavni sekvenci.',
|
|
},
|
|
kind: { type: 'string', enum: ['trigger', 'action', 'condition', 'note'] },
|
|
connectorId: { type: 'string', nullable: true, example: 'raynet' },
|
|
operationId: { type: 'string', nullable: true, example: 'upsert-contact' },
|
|
label: { type: 'string' },
|
|
status: { type: 'string', enum: ['ok', 'error', 'skipped', 'info'] },
|
|
response: {
|
|
type: 'string',
|
|
nullable: true,
|
|
description: 'Co sluzba vratila. Kvuli tomuhle log existuje.',
|
|
},
|
|
durationMs: { type: 'integer', nullable: true },
|
|
at: { type: 'string', format: 'date-time' },
|
|
},
|
|
},
|
|
TicketDetail: {
|
|
allOf: [
|
|
{ $ref: '#/components/schemas/Ticket' },
|
|
{
|
|
type: 'object',
|
|
properties: {
|
|
trace: {
|
|
type: 'array',
|
|
items: { $ref: '#/components/schemas/TicketTraceEntry' },
|
|
},
|
|
},
|
|
},
|
|
],
|
|
},
|
|
Workload: {
|
|
type: 'object',
|
|
description: 'Prehled nad firmou - kdo ma kolik ticketu u sebe.',
|
|
properties: {
|
|
rows: {
|
|
type: 'array',
|
|
items: {
|
|
type: 'object',
|
|
properties: {
|
|
person: { $ref: '#/components/schemas/Person' },
|
|
open: { type: 'integer', description: 'Nevyresene tickety.' },
|
|
total: { type: 'integer' },
|
|
critical: { type: 'integer' },
|
|
oldestOpenAt: { type: 'string', format: 'date-time', nullable: true },
|
|
overloaded: { type: 'boolean' },
|
|
},
|
|
},
|
|
},
|
|
unassigned: { type: 'integer', description: 'Nevyresene tickety bez resitele.' },
|
|
openTotal: { type: 'integer' },
|
|
},
|
|
},
|
|
Incident: {
|
|
type: 'object',
|
|
properties: {
|
|
id: { type: 'string', example: 'INC-231' },
|
|
title: { type: 'string' },
|
|
service: { type: 'string' },
|
|
severity: { type: 'string', enum: ['sev1', 'sev2', 'sev3'] },
|
|
status: {
|
|
type: 'string',
|
|
enum: ['investigating', 'identified', 'monitoring', 'resolved'],
|
|
},
|
|
startedAt: { type: 'string', format: 'date-time' },
|
|
resolvedAt: { type: 'string', format: 'date-time', nullable: true },
|
|
},
|
|
},
|
|
TriggerField: {
|
|
type: 'object',
|
|
required: ['id', 'name', 'type', 'required'],
|
|
properties: {
|
|
id: { type: 'string', example: 'f_42' },
|
|
name: {
|
|
type: 'string',
|
|
example: 'score',
|
|
description: 'Klic v prichozich datech, pismena, cislice a podtrzitko.',
|
|
},
|
|
type: { type: 'string', enum: ['string', 'number', 'boolean', 'date'] },
|
|
required: { type: 'boolean' },
|
|
},
|
|
},
|
|
FlowStep: {
|
|
type: 'object',
|
|
description:
|
|
'Krok stromu. Bud akce nad konektorem, nebo podminka se dvema vetvemi.',
|
|
properties: {
|
|
id: { type: 'string' },
|
|
kind: { type: 'string', enum: ['action', 'condition'] },
|
|
connectorId: { type: 'string', example: 'email' },
|
|
operationId: { type: 'string', example: 'send' },
|
|
inputs: {
|
|
type: 'object',
|
|
additionalProperties: { type: 'string' },
|
|
description:
|
|
'Nastaveni akce. Klic je ID pole z katalogu, hodnota je sablona - ' +
|
|
'{{nazev}} se nahradi parametrem spoustece. Neznamy klic vraci 400.',
|
|
example: { subject: 'Reklamace od {{profileName}}', body: '{{text}}' },
|
|
},
|
|
fieldId: { type: 'string', example: 'f_42' },
|
|
operator: {
|
|
type: 'string',
|
|
enum: [
|
|
'eq',
|
|
'neq',
|
|
'gt',
|
|
'gte',
|
|
'lt',
|
|
'lte',
|
|
'contains',
|
|
'startsWith',
|
|
'isEmpty',
|
|
'isNotEmpty',
|
|
'isTrue',
|
|
'isFalse',
|
|
],
|
|
},
|
|
value: { type: 'string', example: '15' },
|
|
yes: { type: 'array', items: { $ref: '#/components/schemas/FlowStep' } },
|
|
no: { type: 'array', items: { $ref: '#/components/schemas/FlowStep' } },
|
|
},
|
|
},
|
|
AutomationFlow: {
|
|
type: 'object',
|
|
properties: {
|
|
trigger: {
|
|
type: 'object',
|
|
nullable: true,
|
|
properties: {
|
|
connectorId: { type: 'string', example: 'webhook' },
|
|
operationId: { type: 'string', example: 'received' },
|
|
fields: {
|
|
type: 'array',
|
|
items: { $ref: '#/components/schemas/TriggerField' },
|
|
},
|
|
webhookToken: {
|
|
type: 'string',
|
|
readOnly: true,
|
|
description: 'Generuje vyhradne server, hodnota od klienta se ignoruje.',
|
|
},
|
|
},
|
|
},
|
|
steps: { type: 'array', items: { $ref: '#/components/schemas/FlowStep' } },
|
|
},
|
|
},
|
|
Automation: {
|
|
type: 'object',
|
|
properties: {
|
|
id: { type: 'string', example: 'AUT-01' },
|
|
name: { type: 'string' },
|
|
kind: { type: 'string', enum: ['workflow', 'voicebot', 'integrace', 'report'] },
|
|
enabled: { type: 'boolean' },
|
|
runsToday: { type: 'integer' },
|
|
successRate: { type: 'number' },
|
|
avgDurationMs: { type: 'integer' },
|
|
lastRunAt: { type: 'string', format: 'date-time' },
|
|
stepCount: { type: 'integer' },
|
|
configured: { type: 'boolean' },
|
|
issues: {
|
|
type: 'array',
|
|
items: { type: 'string' },
|
|
description: 'Co chybi k zapnuti. Prazdne pole znamena hotovo.',
|
|
},
|
|
},
|
|
},
|
|
AutomationDetail: {
|
|
allOf: [
|
|
{ $ref: '#/components/schemas/Automation' },
|
|
{
|
|
type: 'object',
|
|
properties: {
|
|
flow: { $ref: '#/components/schemas/AutomationFlow' },
|
|
createdAt: { type: 'string', format: 'date-time' },
|
|
updatedAt: { type: 'string', format: 'date-time' },
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
},
|
|
paths: {
|
|
'/health': {
|
|
get: {
|
|
tags: ['Provoz'],
|
|
summary: 'Health check',
|
|
description: 'Vraci 200, pokud je aplikace schopna prijimat provoz.',
|
|
responses: {
|
|
'200': {
|
|
description: 'Aplikace bezi',
|
|
content: {
|
|
'application/json': {
|
|
schema: {
|
|
type: 'object',
|
|
properties: {
|
|
status: { type: 'string', example: 'ok' },
|
|
uptimeSec: { type: 'integer', example: 42 },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
'/api/auth/login': {
|
|
post: {
|
|
tags: ['Autentizace'],
|
|
summary: 'Prihlaseni',
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
'application/json': { schema: { $ref: '#/components/schemas/LoginRequest' } },
|
|
},
|
|
},
|
|
responses: {
|
|
'200': {
|
|
description: 'Token a udaje uzivatele',
|
|
content: {
|
|
'application/json': { schema: { $ref: '#/components/schemas/LoginResponse' } },
|
|
},
|
|
},
|
|
'401': {
|
|
description: 'Nespravny e-mail nebo heslo',
|
|
content: { 'application/json': { schema: { $ref: '#/components/schemas/Error' } } },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
'/api/auth/me': {
|
|
get: {
|
|
tags: ['Autentizace'],
|
|
summary: 'Prihlaseny uzivatel',
|
|
security: [{ bearerAuth: [] }],
|
|
responses: {
|
|
'200': {
|
|
description: 'Udaje uzivatele',
|
|
content: {
|
|
'application/json': {
|
|
schema: {
|
|
type: 'object',
|
|
properties: { user: { $ref: '#/components/schemas/User' } },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
'401': { description: 'Chybi nebo neplatny token' },
|
|
},
|
|
},
|
|
},
|
|
'/api/auth/logout': {
|
|
post: {
|
|
tags: ['Autentizace'],
|
|
summary: 'Odhlaseni',
|
|
security: [{ bearerAuth: [] }],
|
|
responses: { '204': { description: 'Odhlaseno' } },
|
|
},
|
|
},
|
|
'/api/dashboard/summary': {
|
|
get: {
|
|
tags: ['Dashboard'],
|
|
summary: 'Souhrn pro prehled',
|
|
security: [{ bearerAuth: [] }],
|
|
responses: { '200': { description: 'Souhrnne metriky a casova rada' } },
|
|
},
|
|
},
|
|
'/api/dashboard/widgets': {
|
|
get: {
|
|
tags: ['Dashboard'],
|
|
summary: 'Katalog widgetu prehledu',
|
|
description: 'Co jde polozit na dashboard vcetne povolenych sirek.',
|
|
security: [{ bearerAuth: [] }],
|
|
responses: { '200': { description: 'Widgety' } },
|
|
},
|
|
},
|
|
'/api/dashboard/layout': {
|
|
get: {
|
|
tags: ['Dashboard'],
|
|
summary: 'Rozlozeni dashboardu',
|
|
description:
|
|
'Uklada se pro dvojici uzivatel a firma. `custom: false` znamena, ' +
|
|
'ze uzivatel kouka na vychozi rozlozeni.',
|
|
security: [{ bearerAuth: [] }],
|
|
parameters: [
|
|
{
|
|
name: 'tenantId',
|
|
in: 'query',
|
|
schema: { type: 'string' },
|
|
description: 'Firma. Bez ni se pouzije prvni, do ktere uzivatel patri.',
|
|
},
|
|
],
|
|
responses: {
|
|
'200': { description: 'Rozlozeni' },
|
|
'403': { description: 'Ucet nepatri do zadne firmy' },
|
|
'404': { description: 'Firma neexistuje, nebo do ni uzivatel nepatri' },
|
|
},
|
|
},
|
|
put: {
|
|
tags: ['Dashboard'],
|
|
summary: 'Ulozit rozlozeni',
|
|
description: 'Overuje se proti katalogu. Neznamy widget nebo sirka vraci 400.',
|
|
security: [{ bearerAuth: [] }],
|
|
parameters: [{ name: 'tenantId', in: 'query', schema: { type: 'string' } }],
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
'application/json': {
|
|
schema: {
|
|
type: 'object',
|
|
required: ['items'],
|
|
properties: {
|
|
items: {
|
|
type: 'array',
|
|
items: {
|
|
type: 'object',
|
|
required: ['id', 'widgetId', 'size'],
|
|
properties: {
|
|
id: { type: 'string', example: 'w1' },
|
|
widgetId: { type: 'string', example: 'stat.openTickets' },
|
|
size: { type: 'string', enum: ['third', 'half', 'full'] },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
responses: {
|
|
'200': { description: 'Ulozeno' },
|
|
'400': { description: 'Neplatne rozlozeni' },
|
|
},
|
|
},
|
|
delete: {
|
|
tags: ['Dashboard'],
|
|
summary: 'Vratit na vychozi rozlozeni',
|
|
security: [{ bearerAuth: [] }],
|
|
parameters: [{ name: 'tenantId', in: 'query', schema: { type: 'string' } }],
|
|
responses: { '200': { description: 'Vychozi rozlozeni' } },
|
|
},
|
|
},
|
|
'/api/dashboard/access': {
|
|
get: {
|
|
tags: ['Dashboard'],
|
|
summary: 'Co uzivatel smi videt',
|
|
description:
|
|
'Povolene pohledy, firmy k prepinani a prava. Klient si to nesmi dovozovat sam.',
|
|
security: [{ bearerAuth: [] }],
|
|
responses: {
|
|
'200': {
|
|
description: 'Opravneni',
|
|
content: {
|
|
'application/json': { schema: { $ref: '#/components/schemas/Access' } },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
'/api/dashboard/people': {
|
|
get: {
|
|
tags: ['Tickety'],
|
|
summary: 'Seznam resitelu',
|
|
description:
|
|
'Lide, na ktere jde ticket priradit. `meId` je resitel odpovidajici ' +
|
|
'prihlasenemu uzivateli, nebo null, pokud zadny neni.',
|
|
security: [{ bearerAuth: [] }],
|
|
responses: {
|
|
'200': {
|
|
description: 'Resitele',
|
|
content: {
|
|
'application/json': {
|
|
schema: {
|
|
type: 'object',
|
|
properties: {
|
|
items: { type: 'array', items: { $ref: '#/components/schemas/Person' } },
|
|
meId: { type: 'string', nullable: true },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
'/api/dashboard/tickets': {
|
|
get: {
|
|
tags: ['Tickety'],
|
|
summary: 'Seznam ticketu',
|
|
description: 'Neznama hodnota filtru se ignoruje a zaloguje, seznam se nezuzi.',
|
|
security: [{ bearerAuth: [] }],
|
|
parameters: [
|
|
{
|
|
name: 'scope',
|
|
in: 'query',
|
|
schema: { type: 'string', enum: ['all', 'tenant', 'mine'] },
|
|
description:
|
|
'`all` napric firmami (jen platformni admin), `tenant` cela firma, ' +
|
|
'`mine` jen moje tickety. Nepovoleny pohled vraci 403, nikdy se tise nezuzi.',
|
|
},
|
|
{
|
|
name: 'tenantId',
|
|
in: 'query',
|
|
schema: { type: 'string' },
|
|
description: 'Firma u pohledu `tenant` a `mine`. Bez clenstvi vraci 404.',
|
|
example: 'tnt_automia',
|
|
},
|
|
{
|
|
name: 'assignee',
|
|
in: 'query',
|
|
schema: { type: 'string' },
|
|
description:
|
|
'ID resitele nebo `unassigned` pro frontu. U pohledu `mine` se ignoruje.',
|
|
},
|
|
{
|
|
name: 'status',
|
|
in: 'query',
|
|
schema: { type: 'string', enum: ['new', 'open', 'waiting', 'resolved'] },
|
|
},
|
|
{
|
|
name: 'channel',
|
|
in: 'query',
|
|
schema: { type: 'string', enum: ['whatsapp', 'email', 'voice', 'form', 'portal'] },
|
|
},
|
|
],
|
|
responses: {
|
|
'200': {
|
|
description: 'Tickety',
|
|
content: {
|
|
'application/json': {
|
|
schema: {
|
|
type: 'object',
|
|
properties: {
|
|
items: { type: 'array', items: { $ref: '#/components/schemas/Ticket' } },
|
|
meId: { type: 'string', nullable: true },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
'/api/dashboard/tickets/workload': {
|
|
get: {
|
|
tags: ['Tickety'],
|
|
summary: 'Kdo co ma u sebe',
|
|
description: 'Prehled zateze pres cely tym vcetne poctu ticketu ve fronte.',
|
|
security: [{ bearerAuth: [] }],
|
|
responses: {
|
|
'200': {
|
|
description: 'Vytizeni resitelu',
|
|
content: {
|
|
'application/json': { schema: { $ref: '#/components/schemas/Workload' } },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
'/api/dashboard/tickets/{id}': {
|
|
get: {
|
|
tags: ['Tickety'],
|
|
summary: 'Detail ticketu vcetne logu',
|
|
description: 'Log obsahuje i to, co ktera volana sluzba vratila.',
|
|
security: [{ bearerAuth: [] }],
|
|
parameters: [
|
|
{
|
|
name: 'id',
|
|
in: 'path',
|
|
required: true,
|
|
schema: { type: 'string' },
|
|
example: 'TK-4821',
|
|
},
|
|
],
|
|
responses: {
|
|
'200': {
|
|
description: 'Detail',
|
|
content: {
|
|
'application/json': { schema: { $ref: '#/components/schemas/TicketDetail' } },
|
|
},
|
|
},
|
|
'404': { description: 'Neexistuje' },
|
|
},
|
|
},
|
|
},
|
|
'/api/dashboard/tickets/{id}/assign': {
|
|
post: {
|
|
tags: ['Tickety'],
|
|
summary: 'Priradit resitele',
|
|
description: 'Poslete null pro vraceni ticketu do fronty.',
|
|
security: [{ bearerAuth: [] }],
|
|
parameters: [{ name: 'id', in: 'path', required: true, schema: { type: 'string' } }],
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
'application/json': {
|
|
schema: {
|
|
type: 'object',
|
|
required: ['assigneeId'],
|
|
properties: {
|
|
assigneeId: { type: 'string', nullable: true, example: 'ppl_vomacka' },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
responses: {
|
|
'200': {
|
|
description: 'Prirazeno',
|
|
content: {
|
|
'application/json': { schema: { $ref: '#/components/schemas/Ticket' } },
|
|
},
|
|
},
|
|
'400': { description: 'Chybi assigneeId' },
|
|
'404': { description: 'Ticket nebo resitel neexistuje' },
|
|
},
|
|
},
|
|
},
|
|
'/api/dashboard/tickets/{id}/status': {
|
|
post: {
|
|
tags: ['Tickety'],
|
|
summary: 'Zmenit stav ticketu',
|
|
security: [{ bearerAuth: [] }],
|
|
parameters: [{ name: 'id', in: 'path', required: true, schema: { type: 'string' } }],
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
'application/json': {
|
|
schema: {
|
|
type: 'object',
|
|
required: ['status'],
|
|
properties: {
|
|
status: { type: 'string', enum: ['new', 'open', 'waiting', 'resolved'] },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
responses: {
|
|
'200': {
|
|
description: 'Zmeneno',
|
|
content: {
|
|
'application/json': { schema: { $ref: '#/components/schemas/Ticket' } },
|
|
},
|
|
},
|
|
'400': { description: 'Neplatny stav' },
|
|
'404': { description: 'Neexistuje' },
|
|
},
|
|
},
|
|
},
|
|
'/api/dashboard/tickets/{id}/comment': {
|
|
post: {
|
|
tags: ['Tickety'],
|
|
summary: 'Pridat komentar',
|
|
description: 'Komentar je dalsi radek logu, aby bylo vse na jedne casove ose.',
|
|
security: [{ bearerAuth: [] }],
|
|
parameters: [{ name: 'id', in: 'path', required: true, schema: { type: 'string' } }],
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
'application/json': {
|
|
schema: {
|
|
type: 'object',
|
|
required: ['text'],
|
|
properties: { text: { type: 'string', minLength: 2 } },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
responses: {
|
|
'200': {
|
|
description: 'Zapsano',
|
|
content: {
|
|
'application/json': { schema: { $ref: '#/components/schemas/Ticket' } },
|
|
},
|
|
},
|
|
'400': { description: 'Prazdny komentar' },
|
|
'404': { description: 'Neexistuje' },
|
|
},
|
|
},
|
|
},
|
|
'/api/dashboard/incidents': {
|
|
get: {
|
|
tags: ['Dashboard'],
|
|
summary: 'Seznam incidentu',
|
|
security: [{ bearerAuth: [] }],
|
|
responses: {
|
|
'200': {
|
|
description: 'Incidenty',
|
|
content: {
|
|
'application/json': {
|
|
schema: {
|
|
type: 'object',
|
|
properties: {
|
|
items: { type: 'array', items: { $ref: '#/components/schemas/Incident' } },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
'/api/dashboard/stream': {
|
|
get: {
|
|
tags: ['Dashboard'],
|
|
summary: 'Zivy stream zmen (SSE)',
|
|
description:
|
|
'Server-Sent Events. Drzi otevrene spojeni a posila udalosti, jakmile nastanou. ' +
|
|
'Swagger UI streamovanou odpoved nezobrazi rozumne, testujte prohlizecem nebo curl.',
|
|
security: [{ bearerAuth: [] }],
|
|
responses: { '200': { description: 'Proud udalosti text/event-stream' } },
|
|
},
|
|
},
|
|
'/api/dashboard/connectors': {
|
|
get: {
|
|
tags: ['Automatizace'],
|
|
summary: 'Katalog konektoru',
|
|
security: [{ bearerAuth: [] }],
|
|
responses: { '200': { description: 'Konektory, kategorie a operatory podminek' } },
|
|
},
|
|
},
|
|
'/api/dashboard/automations': {
|
|
get: {
|
|
tags: ['Automatizace'],
|
|
summary: 'Seznam automatizaci',
|
|
security: [{ bearerAuth: [] }],
|
|
responses: {
|
|
'200': {
|
|
description: 'Automatizace',
|
|
content: {
|
|
'application/json': {
|
|
schema: {
|
|
type: 'object',
|
|
properties: {
|
|
items: { type: 'array', items: { $ref: '#/components/schemas/Automation' } },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
post: {
|
|
tags: ['Automatizace'],
|
|
summary: 'Zalozit automatizaci',
|
|
security: [{ bearerAuth: [] }],
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
'application/json': {
|
|
schema: {
|
|
type: 'object',
|
|
required: ['name'],
|
|
properties: { name: { type: 'string', minLength: 3 } },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
responses: {
|
|
'201': {
|
|
description: 'Vytvoreno',
|
|
content: {
|
|
'application/json': { schema: { $ref: '#/components/schemas/AutomationDetail' } },
|
|
},
|
|
},
|
|
'400': { description: 'Neplatny nazev' },
|
|
},
|
|
},
|
|
},
|
|
'/api/dashboard/automations/{id}': {
|
|
parameters: [
|
|
{ name: 'id', in: 'path', required: true, schema: { type: 'string' }, example: 'AUT-01' },
|
|
],
|
|
get: {
|
|
tags: ['Automatizace'],
|
|
summary: 'Detail vcetne stromu akci',
|
|
security: [{ bearerAuth: [] }],
|
|
responses: {
|
|
'200': {
|
|
description: 'Detail',
|
|
content: {
|
|
'application/json': { schema: { $ref: '#/components/schemas/AutomationDetail' } },
|
|
},
|
|
},
|
|
'404': { description: 'Neexistuje' },
|
|
},
|
|
},
|
|
put: {
|
|
tags: ['Automatizace'],
|
|
summary: 'Ulozit automatizaci',
|
|
description:
|
|
'Validuje strom proti katalogu konektoru. Nedokoncenou automatizaci server ' +
|
|
'nezapne ani pri enabled=true, duvody vraci v poli issues.',
|
|
security: [{ bearerAuth: [] }],
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
'application/json': {
|
|
schema: {
|
|
type: 'object',
|
|
properties: {
|
|
name: { type: 'string', minLength: 3 },
|
|
enabled: { type: 'boolean' },
|
|
flow: { $ref: '#/components/schemas/AutomationFlow' },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
responses: {
|
|
'200': {
|
|
description: 'Ulozeno',
|
|
content: {
|
|
'application/json': { schema: { $ref: '#/components/schemas/AutomationDetail' } },
|
|
},
|
|
},
|
|
'400': { description: 'Neplatny strom' },
|
|
'404': { description: 'Neexistuje' },
|
|
},
|
|
},
|
|
delete: {
|
|
tags: ['Automatizace'],
|
|
summary: 'Smazat automatizaci',
|
|
security: [{ bearerAuth: [] }],
|
|
responses: { '204': { description: 'Smazano' }, '404': { description: 'Neexistuje' } },
|
|
},
|
|
},
|
|
'/api/dashboard/automations/{id}/webhook/regenerate': {
|
|
post: {
|
|
tags: ['Automatizace'],
|
|
summary: 'Nova adresa webhooku',
|
|
description: 'Stara adresa okamzite prestane fungovat.',
|
|
security: [{ bearerAuth: [] }],
|
|
parameters: [{ name: 'id', in: 'path', required: true, schema: { type: 'string' } }],
|
|
responses: {
|
|
'200': {
|
|
description: 'Novy token',
|
|
content: {
|
|
'application/json': { schema: { $ref: '#/components/schemas/AutomationDetail' } },
|
|
},
|
|
},
|
|
'404': { description: 'Neexistuje nebo spoustecem neni webhook' },
|
|
},
|
|
},
|
|
},
|
|
'/api/simulate': {
|
|
post: {
|
|
tags: ['Simulace'],
|
|
summary: 'Vyvolat provozni udalost',
|
|
description:
|
|
'Meni skutecna data, aby bylo videt, jak dashboard reaguje zive. ' +
|
|
'Nevyplnena pole server doplni ukazkovou hodnotou.',
|
|
security: [{ bearerAuth: [] }],
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
'application/json': {
|
|
schema: {
|
|
type: 'object',
|
|
required: ['action'],
|
|
properties: {
|
|
action: {
|
|
type: 'string',
|
|
enum: [
|
|
'ticket.created',
|
|
'ticket.resolved',
|
|
'incident.started',
|
|
'incident.resolved',
|
|
'automation.run',
|
|
],
|
|
},
|
|
subject: { type: 'string' },
|
|
body: { type: 'string', description: 'Cely text pozadavku.' },
|
|
channel: {
|
|
type: 'string',
|
|
enum: ['whatsapp', 'facebook', 'instagram', 'email', 'voice', 'form', 'portal'],
|
|
description: 'Odkud pozadavek prisel. Podle toho se poskladá i log ticketu.',
|
|
},
|
|
contact: { type: 'string' },
|
|
knownCustomer: {
|
|
type: 'boolean',
|
|
description:
|
|
'false = CRM firmu nedohleda, ticket zustane bez zakaznika i bez resitele.',
|
|
},
|
|
priority: { type: 'string', enum: ['low', 'normal', 'high', 'critical'] },
|
|
ticketId: { type: 'string' },
|
|
title: { type: 'string' },
|
|
service: { type: 'string' },
|
|
severity: { type: 'string', enum: ['sev1', 'sev2', 'sev3'] },
|
|
incidentId: { type: 'string' },
|
|
automationId: { type: 'string' },
|
|
ok: { type: 'boolean' },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
responses: {
|
|
'200': { description: 'Udalost provedena' },
|
|
'201': { description: 'Zaznam vytvoren' },
|
|
'409': { description: 'Neni co provest' },
|
|
},
|
|
},
|
|
},
|
|
'/webhook/{token}': {
|
|
post: {
|
|
tags: ['Webhook'],
|
|
summary: 'Prijem dat do automatizace',
|
|
description:
|
|
'Verejny endpoint bez prihlaseni. Autorizuje neuhodnutelny token v adrese. ' +
|
|
'Telo se overuje proti parametrum deklarovanym u spoustece.',
|
|
parameters: [
|
|
{
|
|
name: 'token',
|
|
in: 'path',
|
|
required: true,
|
|
schema: { type: 'string' },
|
|
description: '32znakovy token vygenerovany serverem.',
|
|
},
|
|
],
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
'application/json': {
|
|
schema: { type: 'object', additionalProperties: true },
|
|
example: { customer: 'Nordis', score: 18, comment: 'vse ok' },
|
|
},
|
|
},
|
|
},
|
|
responses: {
|
|
'202': { description: 'Prijato' },
|
|
'400': { description: 'Chybi parametr nebo nesedi typ' },
|
|
'404': { description: 'Neznamy token' },
|
|
'409': { description: 'Automatizace je pozastavena' },
|
|
},
|
|
},
|
|
},
|
|
'/api/contact': {
|
|
post: {
|
|
tags: ['Kontakt'],
|
|
summary: 'Odeslat poptavku z webu',
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
'application/json': {
|
|
schema: {
|
|
type: 'object',
|
|
required: ['name', 'email', 'topic', 'message'],
|
|
properties: {
|
|
name: { type: 'string', minLength: 2 },
|
|
email: { type: 'string', format: 'email' },
|
|
company: { type: 'string' },
|
|
phone: { type: 'string' },
|
|
topic: {
|
|
type: 'string',
|
|
enum: [
|
|
'automatizace',
|
|
'voicebot',
|
|
'integrace',
|
|
'dashboard',
|
|
'podpora',
|
|
'jine',
|
|
],
|
|
},
|
|
message: { type: 'string', minLength: 10 },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
responses: {
|
|
'202': { description: 'Prijato' },
|
|
'400': { description: 'Neplatny vstup' },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
};
|
|
}
|