Часовой пояс и формат времени (24h/12h) перенесены из браузера в настройки
приложения: валидация IANA-зоны, сид в db/init.sql + db/migration.sql, отдача
в GET /api/public-settings, раздел #sec-datetime в настройках с живым превью.
Границы суток в SQL переведены на tzDayStart/tzDayEnd + bindTz ($TZ$) — раньше
$n::date по TIMESTAMPTZ считал дни в UTC (у контейнера TimeZone=UTC) и молча
сдвигал выборку на день; хардкод Europe/Moscow вычищен, now() заменён на
tzWall().
Новый public/js/datetime.js: три семейства хелперов (instant / чистая
DATE-строка без Date() / чистая TIME-строка), подключён на всех страницах
включая публичные share/report/index, initDateTime() встроен в checkAuth().
Прямые toLocale*/getFullYear/toISOString().slice(0,10) в public/ выпилены.
Попутно: bindTz добавляет параметр только при наличии $TZ$ в SQL (иначе запрос
виснет вечно без global error handler) — регрессия закрыта в api.smoketest.js;
починены off-by-one месяца в report.js и группировка по дате в share.js.
Только фронтенд: server.js, worker.js, photo-ai/, compose и схема БД не тронуты.
Контракт /enhance-ai, /api/photo-ai/health и service в /api/photo-jobs/status
взят из Stage 4 как есть (I6 — новых колонок нет, метаданные из photo_jobs.params
и audit_log.target).
journal.html + journal.js:
- селект режима рядом с «🤖 ИИ»: Универсально (x2) / Быстро (x4) / Лица /
Лица + фон; PHOTO_AI_MODES — единственная таблица режим→{model, face}
- значение уходит в POST .../enhance-ai телом {model, face, face_model};
face_model подставляется только когда face !== 'off'
- дефолт селекта из photo_ai_face_mode (face=all → facesbg, face=face → faces)
- подсказка про медленный CPU: loadPhotoAiDevice() читает
GET /api/photo-ai/health (только для админа)
- текст подтверждения и надпись «ИИ обрабатывает…» различают апскейл и face-режим
- кнопка и селект скрываются вместе при photo_ai_enabled === 'false' (I3)
settings.html + settings.js:
- новый раздел «Фото-ИИ»: режим по умолчанию, модель лиц, желаемое устройство
- read-only статус из /api/photo-jobs/status: фактическое устройство,
device_name, VRAM, модели/модели лиц/загруженные; жёлтым — расхождение с
желаемым устройством, красным — недоступность и незаданный PHOTO_AI_URL
- renderStackInfo() рисует карточку «Фото-ИИ» из stack.photo_ai
- новые id в DIRTY_FIELDS и в payload PUT /api/settings
- photo_ai_device_pref документирующий: фактическое устройство задаёт
PHOTO_AI_DEVICE в контейнере
worker.html + worker.js:
- PHOTO_ACTION_LABELS: ai_face «ИИ + лица», ai_upscale «ИИ-апскейл»
- в модалке сравнения — чипы с моделью, режимом лиц, найденными лицами,
устройством, временем обработки и предупреждениями
- строка состояния учитывает service.reachable === false («задания ждут»)
и дописывает «· расчёт на <device>»
audit.js: 10 меток для кодов, которые раньше показывались сырыми (профиль и
фото ученика, главное фото, фото модуля, удаление/очистка уведомлений, логотип).
Проверено: node --check для всех четырёх js. Приёмка на живом стеке в этом
изменении не гонялась — чекбокс приёмки Stage 5 в TODO_PHOTO_FACE_AI.md
оставлен пустым, там же журнал раздела 5 с замечаниями для Stage 6.
- Fix runAiEnhance missing return/closing brace from previous commit
- Remove duplicate code fragments in worker.js
- Add renderPhotoPending() to show pending/processing jobs in worker dashboard
- New pending queue table in worker.html photo section
- Replace in-memory photoAiJobs Map with DB-backed photo_jobs table
- Background photo worker (worker.js) with retry, backoff, stale reset
- Handles both AI (Real-ESRGAN) and server-side (sharp) enhancement
- Controlled via photo_worker_enabled setting
- Photo job history in enhance modal with before/after thumbnails + rollback
- Worker dashboard: photo jobs section with status counts, recent table,
compare slider for before/after, rollback from worker UI
- New endpoints: /api/photo-jobs/status|wake|enabled|requeue-failed,
/api/entries/:id/photo/jobs (history), .../rollback
- swapEntryPhotoFiles logs every mutation to photo_jobs table
- Replace emoji icons with Lucide across admin pages; add vendor/lucide.min.js and renderIcons() helper\n- Reorder sidebar logically (Dashboard, Journal, Students, Groups, Files, Links, Trash + admin sections)\n- Groups: open photo chronology only via the Фото button; covers no longer clickable\n- Journal: show group badge over card photo, compact AI-status icon beside description, and date range in empty-state message\n- Update README (AI worker, Lucide, worker.js)