Files
photo-editor/public/js/main.js
Coder 401969cfdb
Some checks failed
test / test (push) Failing after 24s
v2: модульный монолит — очередь, персистентность, безопасность, SSE, тесты
Реализация целевого дизайна (docs/architecture/target-design.md):
- src/: модульная структура (config, logger, errors, http, core: auth/history/jobs/engines/comfy/codex/images)
- Очередь заданий: FIFO, кониурентность codex=1 / comfy-upscale=2, MAX_QUEUED_JOBS=5, JSONL-журнал и восстановление после рестарта (running → interrupted, upscale requeue)
- Персистентные сессии (sha256-хеши токенов), scrypt-хеш пароля, rate-limit входа, Origin-проверка, magic-byte валидация аплоадов
- Атомарная запись манифеста истории, каскадное удаление, URL с фактическим расширением + 302-алиас /image.jpg, миграция history/ → data/history
- Даунскейл sharp до 1024px перед Codex + масштабирование области референса
- SSE /api/events с фолбэком на polling, фронтенд переведён на ES-модули (public/js/)
- Тесты node:test + supertest (27), CI workflow test.yml, Docker/деплой: том kadr-data
- Документация: README, docs/architecture/
2026-08-27 12:52:27 +07:00

78 lines
2.3 KiB
JavaScript

/**
* main.js — Главная точка входа клиентского приложения «Кадр» v2
*/
import { checkAuth, submitLogin, logout } from "./auth.js";
import { startHealthPolling } from "./health.js";
import { initUpload } from "./upload.js";
import { initReference } from "./reference.js";
import { initSuggest } from "./suggest.js";
import { initJobsEvents, runJob } from "./jobs.js";
import { refreshHistory } from "./history-tree.js";
import { subscribe, getState } from "./state.js";
function setupButtonsAndInputs() {
const loginForm = document.querySelector("#login-form");
const logoutBtn = document.querySelector("#logout-button");
const editBtn = document.querySelector("#edit-button");
const upscaleBtn = document.querySelector("#upscale-button");
const promptInput = document.querySelector("#prompt");
const promptCounter = document.querySelector("#prompt-counter");
if (loginForm) {
loginForm.addEventListener("submit", submitLogin);
}
if (logoutBtn) {
logoutBtn.addEventListener("click", logout);
}
if (promptInput) {
promptInput.addEventListener("input", () => {
const val = promptInput.value;
if (promptCounter) {
promptCounter.textContent = `${val.length} / 1000`;
}
const state = getState();
if (editBtn) {
editBtn.disabled = !state.currentVersionId || !val.trim() || state.isBusy;
}
});
}
if (editBtn) {
editBtn.addEventListener("click", () => runJob("edit"));
}
if (upscaleBtn) {
upscaleBtn.addEventListener("click", () => runJob("upscale"));
}
// Subscribe to state changes to update button states
subscribe((state, changeKey) => {
if (changeKey === "isBusy" || changeKey === "currentVersionId") {
const hasVersion = Boolean(state.currentVersionId);
const promptVal = promptInput?.value?.trim() || "";
if (upscaleBtn) upscaleBtn.disabled = !hasVersion || state.isBusy;
if (editBtn) editBtn.disabled = !hasVersion || !promptVal || state.isBusy;
}
});
}
async function init() {
setupButtonsAndInputs();
initUpload();
initReference();
initSuggest();
startHealthPolling();
const authenticated = await checkAuth();
if (authenticated) {
initJobsEvents();
await refreshHistory(true);
}
}
document.addEventListener("DOMContentLoaded", init);