Внешние системы не могли разбудить воркер, переочередить упавшие
задания или отправить запись на повторную ИИ-проверку: все эти роуты
существовали только во внутреннем API под requireAdmin.
Добавлено на apiV1 (все под apiWrite('write')):
- POST /ai/wake, /photo-jobs/wake — пинок воркеров
- POST /ai/requeue-failed, /photo-jobs/requeue-failed — error -> pending
- POST /entries/:id/ai/recheck — повторная проверка конкретной записи
Филиальная изоляция (главное в этом изменении):
- внутренние requeue-failed делают UPDATE по всей таблице; перенос их
как есть позволил бы ключу с ограничением по филиалу переочередить
чужие задания, что ломает правило «ключ не шире выдавшего»
- добавлен хелпер apiBranchClause(user, expr, params): пустая строка
для admin, AND FALSE при пустом списке филиалов, иначе
AND <expr> = ANY($N::int[]); применён к обоим массовым UPDATE
- entries фильтруется через groups.branch_id, photo_jobs — через
photo_jobs -> entries -> groups
Аудит через apiAudit() с префиксом api., метки добавлены в
public/js/audit.js; после мутаций invalidateEntries/invalidateStats
и broadcastEntryChanged.
Воркер отчётов о занятии wake-эндпоинта не получает: он будится сам
из POST/PUT /lesson-reports при ai_check === true.
Документация: таблица эндпоинтов и раздел про воркеров в README.md,
правило apiBranchClause в AGENTS.md 3f.
Проверено: изолированный тест на двух филиалах — requeue-failed
ключом одного филиала вернул count 1 из двух ошибочных заданий,
запись и фото-джоб чужого филиала остались в error, recheck чужой
записи 403; api-keys.selftest.js 61 PASS, api.smoketest.js 76 PASS,
регрессий нет.
Замечание: server.js запечён в образ, compose монтирует только
uploads/, поэтому restart правку не подхватит — нужен
./scripts/deploy.sh или docker compose up -d --build app.