{"openapi":"3.0.0","paths":{"/ask-gpt-proxy":{"post":{"operationId":"AppController_askGPTProxy","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AskGptProxyReqDTO"}}}},"responses":{"201":{"description":""}},"security":[{"bearer":[]}],"tags":["temp for test"]}},"/ask-gpt":{"post":{"operationId":"AppController_askGPT","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AskGptReqDTO"}}}},"responses":{"201":{"description":""}},"security":[{"bearer":[]}],"tags":["temp for test"]}},"/kafka-test":{"get":{"operationId":"AppController_kafkaTest","parameters":[{"name":"racs_id","required":true,"in":"query","description":"ID РАЦ (racs_id)","schema":{"example":"123","type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"tags":["temp for test"]}},"/kafka-change-broker":{"get":{"operationId":"AppController_kafkaChangeBroker","parameters":[{"name":"host","required":true,"in":"query","description":"Host of the Kafka broker","schema":{"default":"172.16.128.5","type":"string"}},{"name":"port","required":true,"in":"query","description":"Port of the Kafka broker","schema":{"default":"29092","type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"tags":["temp for test"]}},"/racs-change-address":{"get":{"operationId":"AppController_racsChangeAddress","parameters":[{"name":"proto","required":true,"in":"query","schema":{"default":"http","type":"string"}},{"name":"host","required":true,"in":"query","schema":{"default":"172.16.128.3","type":"string"}},{"name":"port","required":true,"in":"query","schema":{"default":"5005","type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"tags":["temp for test"]}},"/racs-test-set-job":{"get":{"operationId":"AppController_racsTestSetJob","parameters":[{"name":"theme_counter","required":true,"in":"query","schema":{"nullable":false,"example":69,"type":"number"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"tags":["temp for test"]}},"/health":{"get":{"operationId":"AppController_getHealth","parameters":[],"responses":{"200":{"description":""}},"tags":["temp for test"]}},"/send-email":{"post":{"operationId":"AppController_sendEmail","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendEmailRequestDTO"},"examples":{"sample":{"summary":"Пример запроса","value":{"subject":"привет","html":"<p>Здравствуйте!</p>","from":"primary@example.com","to":"empreview@mail.ru"}}}}}},"responses":{"201":{"description":""}},"security":[{"bearer":[]}],"tags":["temp for test"]}},"/auth/login":{"post":{"operationId":"AuthController_login","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthDTO"}}}},"responses":{"200":{"description":"Успешная авторизация, возвращает JWT токен"},"401":{"description":"Неверные учетные данные"}},"summary":"Вход в систему","tags":["auth"]}},"/auth/profile":{"get":{"operationId":"AuthController_getProfile","parameters":[],"responses":{"200":{"description":"Профиль пользователя"},"401":{"description":"Требуется авторизация"}},"security":[{"bearer":[]}],"summary":"Получить профиль текущего пользователя","tags":["auth"]}},"/job":{"get":{"description":"Возвращает массив задач с фильтрацией по периоду/теме и пагинацией. Элементы могут быть в полном (ViewJobUserResDTO) или сокращённом (ViewJobUserResShortDTO) формате.","operationId":"JobController_viewJobs","parameters":[{"name":"from","required":true,"in":"query","schema":{"format":"date-time","nullable":true,"example":"2024-04-18T13:09:40","type":"string"}},{"name":"to","required":true,"in":"query","schema":{"format":"date-time","nullable":true,"example":"2024-05-12T23:59:40","type":"string"}},{"name":"theme","required":true,"in":"query","schema":{"nullable":true,"example":"тест","type":"string"}},{"name":"page_size","required":true,"in":"query","schema":{"nullable":true,"example":20,"type":"number"}},{"name":"page","required":true,"in":"query","schema":{"nullable":true,"example":1,"type":"number"}}],"responses":{"200":{"description":"Массив задач (полный или сокращённый формат)","content":{"application/json":{"schema":{"type":"array","items":{"oneOf":[{"$ref":"#/components/schemas/ViewJobUserResDTO"},{"$ref":"#/components/schemas/ViewJobUserResShortDTO"}]}}}}}},"summary":"Список задач","tags":["job"]},"post":{"description":"Принимает запрос с фронта, ставит задачу в очередь и возвращает её ID.","operationId":"JobController_createJob","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateJobUserReqDTO"}}}},"responses":{"201":{"description":"Задача успешно создана","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateJobUserResDTO"}}}},"400":{"description":"Не передан html или devices не является массивом / ошибка валидации","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"Validation failed"},"errors":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","example":"html"},"errors":{"type":"array","items":{"type":"string"},"example":["html should not be empty"]}}}}}}}}},"401":{"description":"Unauthorized when JOB_API_AUTH_ENABLED=true"},"503":{"description":"БД недоступна: счётчик тем ещё не получен на старте"}},"security":[{"bearer":[]}],"summary":"Создать задачу","tags":["job"]},"delete":{"description":"ВНИМАНИЕ: удаляет все задачи без фильтра. Операция необратима.","operationId":"JobController_deleteAllJobs","parameters":[],"responses":{"200":{"description":"Задачи удалены","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteJobReqDTO"}}}},"401":{"description":"Unauthorized when JOB_API_AUTH_ENABLED=true"}},"security":[{"bearer":[]}],"summary":"Удалить ВСЕ задачи","tags":["job"]},"patch":{"operationId":"JobController_changeJobStatus","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangeJobStatusDTO"}}}},"responses":{"200":{"description":"Статус обновлён"},"400":{"description":"Ошибка валидации тела запроса","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"Validation failed"},"errors":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","example":"html"},"errors":{"type":"array","items":{"type":"string"},"example":["html should not be empty"]}}}}}}}}},"401":{"description":"Unauthorized when JOB_API_AUTH_ENABLED=true"}},"security":[{"bearer":[]}],"summary":"Изменить статус задачи","tags":["job"]}},"/job/online-status":{"get":{"description":"Возвращает текущие статусы задач, которые находятся в работе (in-memory коллекция).","operationId":"JobController_onlineStatus","parameters":[],"responses":{"200":{"description":"Массив объектов статусов активных задач","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","additionalProperties":true}}}}}},"summary":"Онлайн-статусы активных задач","tags":["job"]}},"/job/unaccomplished":{"get":{"description":"Возвращает задачи, которые ещё не доведены до финального статуса.","operationId":"JobController_getUnaccomplishedJobs","parameters":[],"responses":{"200":{"description":"Массив незавершённых задач","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","additionalProperties":true}}}}}},"summary":"Незавершённые задачи","tags":["job"]}},"/job/{job_id}":{"get":{"operationId":"JobController_viewJob","parameters":[{"name":"job_id","required":true,"in":"path","description":"Уникальный ID задачи","schema":{"example":"6ec0783fd733cee35b43de1e","type":"string"}}],"responses":{"200":{"description":"Задача найдена","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ViewJobUserResDTO"}}}},"404":{"description":"Задача с указанным ID не найдена"}},"summary":"Получить задачу по ID","tags":["job"]},"delete":{"operationId":"JobController_deleteJob","parameters":[{"name":"job_id","required":true,"in":"path","description":"Уникальный ID задачи","schema":{"example":"6ec0783fd733cee35b43de1e","type":"string"}}],"responses":{"200":{"description":"Задача удалена","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteJobReqDTO"}}}},"401":{"description":"Unauthorized when JOB_API_AUTH_ENABLED=true"}},"security":[{"bearer":[]}],"summary":"Удалить задачу по ID","tags":["job"]}},"/job/fake":{"post":{"description":"Тестовый эндпоинт: отправляет задачу напрямую в Kafka, минуя email и БД.","operationId":"JobController_sendFakeJob","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateJobUserReqDTO"}}}},"responses":{"200":{"description":"Результат отправки фейковой задачи","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","example":true},"message":{"type":"string","example":"Фейковая задача отправлена в Kafka. ID: fake_1700000000000, тема: fake0"}}}}}},"400":{"description":"Ошибка валидации тела запроса","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"Validation failed"},"errors":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","example":"html"},"errors":{"type":"array","items":{"type":"string"},"example":["html should not be empty"]}}}}}}}}}},"summary":"Отправить фейковую задачу","tags":["job"]}}},"info":{"title":"EmCoTe Application","description":"The EmCoTe API description.","version":"1.0","contact":{}},"tags":[{"name":"job","description":"application main purpose"}],"servers":[],"components":{"securitySchemes":{"bearer":{"scheme":"bearer","bearerFormat":"JWT","type":"http"}},"schemas":{"ChatMessageDTO":{"type":"object","properties":{"role":{"type":"string","example":"user","description":"Роль сообщения (system | user | assistant)"},"content":{"type":"string","example":"Вакансия:\n...\nРезюме кандидата:\n...","description":"Содержимое сообщения"}},"required":["role","content"]},"AskGptProxyReqDTO":{"type":"object","properties":{"model":{"type":"string","example":"gpt-4o-mini","description":"Модель OpenAI"},"messages":{"description":"Сообщения в формате OpenAI Chat Completions","type":"array","items":{"$ref":"#/components/schemas/ChatMessageDTO"}}},"required":["messages"]},"AskGptReqDTO":{"type":"object","properties":{"vacancy":{"type":"string","example":"Backend-разработчик, Node.js, 3+ года","description":"Текст вакансии"},"resume":{"type":"string","example":"Опыт 5 лет на Node.js, NestJS, PostgreSQL","description":"Резюме кандидата"},"ai_req_enrich":{"type":"string","example":"","description":"Доп. уточнения для ИИ (можно пустой строкой)"}},"required":["vacancy","resume"]},"SendEmailRequestDTO":{"type":"object","properties":{"to":{"type":"string","example":"empreview@mail.ru","description":"comma-separated recipients"},"html":{"type":"string","example":"<p>Здравствуйте!</p>","description":"HTML content of the email"},"subject":{"type":"string","example":"привет"},"from":{"type":"string","example":"emailmakerserg@gmail.com","description":"Sender email; server restricts to two configured addresses"}},"required":["to","html"]},"AuthDTO":{"type":"object","properties":{"username":{"type":"string","default":"username"},"password":{"type":"string","default":"password"}},"required":["username","password"]},"ViewJobUserResDTO":{"type":"object","properties":{"task_status":{"type":"string","description":"Агрегированный статус задачи (включая partially_success)","enum":["set","waiting","progress","done","canceled","failed","partially_success"]},"theme":{"type":"string"},"html":{"type":"string"},"devices":{"type":"object","description":"Map of devices and their statuses","additionalProperties":{"type":"string","enum":["set","waiting","progress","done","canceled","failed"],"description":"Status of the device"},"example":{"yandex":"progress","mailru":"failed","android":"canceled","windows_outlook":"done"}},"_created":{"type":"string","description":"record creation date","example":"2021-01-01T00:00:00.000Z","format":"date-time"},"_modified":{"type":"string","description":"record modification date","example":"2021-01-01T00:00:00.000Z","format":"date-time"},"_ip":{"type":"string","description":"ip address","example":"172.20.200.1"},"_id":{"type":"string","description":"unique Job ID","example":"6ec0783fd733cee35b43de1e"},"status_detailed":{"type":"object","description":"Детализация по устройствам"},"queue_info":{"type":"object","description":"Оценка очереди (при waiting — по выборке; иначе дефолт position_estimate: 1)"}},"required":["task_status","theme","html","devices","_created","_modified","_ip","_id","queue_info"]},"ViewJobUserResShortDTO":{"type":"object","properties":{"id":{"type":"string","example":"230fbce1d733cee35b42b33c","nullable":false},"status":{"type":"string","example":"done","nullable":false},"theme":{"type":"string","example":"Приветствие","nullable":true}},"required":["id","status","theme"]},"CreateJobUserReqDTO":{"type":"object","properties":{"theme":{"type":"string","default":"тест"},"html":{"type":"string","default":"<p>Привет</p>"},"devices":{"type":"array","description":"List of devices","default":["yandex_chrome","mailru_chrome","gmail_chrome","outlook_chrome","gmail_android11","outlook16_windows11","mail_macos15","mail_iphone14"],"items":{"type":"string","enum":["yandex_chrome","mailru_chrome","gmail_chrome","outlook_chrome","gmail_android11","outlook16_windows11","mail_macos15","mail_iphone14"]}},"user_id":{"type":"string","default":"1","nullable":true},"project_id":{"type":"string","default":"1","nullable":true},"html_preprocess":{"type":"string","enum":["inline-css"],"description":"Опциональная предобработка HTML перед сохранением и отправкой. inline-css — встроить CSS в style-атрибуты (как в production ESP). Без поля — HTML отправляется как есть.","example":"inline-css"}},"required":["html","user_id","project_id"]},"CreateJobUserResDTO":{"type":"object","properties":{"theme_counter":{"type":"number","example":175,"description":"Глобальный счётчик задач, прикрученный к теме письма"},"id":{"type":"string","example":"6ec0783fd733cee35b43de1e","description":"Уникальный ID созданной задачи"},"error":{"type":"string","description":"Текст ошибки, если задача не создана"},"chain":{"type":"string","description":"Служебная цепочка обработки"}},"required":["theme_counter","id"]},"DeleteJobReqDTO":{"type":"object","properties":{"id":{"type":"string","example":"230fbce1d733cee35b42b33c","nullable":true}},"required":["id"]},"ChangeJobStatusDTO":{"type":"object","properties":{"task_id":{"type":"string","example":"230fbce1d733cee35b42b33c","description":"ID задачи (task_id)"},"status":{"type":"string","example":"done","description":"Новый статус задачи"}},"required":["task_id","status"]}}},"externalDocs":{"description":"For more information","url":"http://swagger.io"}}