diff --git a/public/js/worker.js b/public/js/worker.js
index aac7db8..9717875 100644
--- a/public/js/worker.js
+++ b/public/js/worker.js
@@ -232,6 +232,7 @@ function renderPhoto() {
['error', 'Ошибка', c.error || 0],
].map(([cls, label, n]) => `${label}: ${n}`).join('');
const rows = photoState.recent || [];
+ renderPhotoPending();
const tbody = document.querySelector('#photoTable tbody');
const empty = document.getElementById('photoEmpty');
if (!rows.length) { tbody.innerHTML = ''; empty.style.display = 'block'; return; }
@@ -258,6 +259,26 @@ function renderPhoto() {
renderIcons();
}
+function renderPhotoPending() {
+ const rows = photoState.pending || [];
+ document.getElementById('photoPendingCount').textContent = rows.length;
+ const tbody = document.querySelector('#photoPendingTable tbody');
+ const empty = document.getElementById('photoPendingEmpty');
+ if (!rows.length) { tbody.innerHTML = ''; empty.style.display = 'block'; return; }
+ empty.style.display = 'none';
+ tbody.innerHTML = rows.map(r => {
+ const processing = r.status === 'processing';
+ const action = esc(PHOTO_ACTION_LABELS[r.action] || r.action);
+ const badge = `${processing ? '⏳ Обрабатывается' : 'В очереди'}`;
+ return `
+ | #${r.id} |
+ #${r.entry_id} ${esc(r.student_name || '')} (${esc(r.group_name || '')}) |
+ ${action} ${badge} |
+ ${fmtTime(r.created_at)} |
+
`;
+ }).join('');
+}
+
function findPhotoJob(id) {
return (photoState && photoState.recent || []).find(r => r.id === id) || null;
}
diff --git a/public/worker.html b/public/worker.html
index e6a0846..20dcda4 100644
--- a/public/worker.html
+++ b/public/worker.html
@@ -106,6 +106,16 @@
+ В очереди (0)
+
+
+
+ | Задание | Запись | Действие | Поставлено |
+
+
+
+
Очередь пуста.
+
diff --git a/worker.js b/worker.js
index 0bb5662..291e254 100644
--- a/worker.js
+++ b/worker.js
@@ -136,6 +136,8 @@ function createPhotoEnhanceWorker({ pool, getSetting, logAudit, invalidateEntrie
const out = Buffer.from(await resp.arrayBuffer());
const newName = crypto.randomBytes(12).toString('hex') + '.jpg';
fs.writeFileSync(path.join(uploadsDir, newName), out);
+ return `/uploads/${newName}`;
+ }
async function applyResult(job, photoPath, newPath) {
const beforePath = backupOldFile(photoPath);
await pool.query('UPDATE entries SET photo_path = $1 WHERE id = $2', [newPath, job.entry_id]);
@@ -186,6 +188,12 @@ function createPhotoEnhanceWorker({ pool, getSetting, logAudit, invalidateEntrie
`UPDATE photo_jobs SET status = 'error', attempts = $1, error = $2, finished_at = now() WHERE id = $3`,
[tries, message, job.id]
);
+ stats.errors++;
+ if (logAudit) await logAudit(null, 'photo.job.error', { entry_id: job.entry_id, job_id: job.id, error: message });
+ return true;
+ }
+ }
+
async function loop() {
while (!stopped) {
let job = null;
@@ -248,17 +256,6 @@ function createPhotoEnhanceWorker({ pool, getSetting, logAudit, invalidateEntrie
return { start, notify, getInfo };
}
- stats.errors++;
- if (logAudit) await logAudit(null, 'photo.job.error', { entry_id: job.entry_id, job_id: job.id, error: message });
- return true;
- }
- }
-
-
- return `/uploads/${newName}`;
- }
-
-
function createEntryAutoChecker({ pool, getSetting, logAudit, aiUrl, defaultPrompt, model }) {
const AI_URL = aiUrl || process.env.AI_URL || 'http://text-corrector:8080';
const MODEL = model || process.env.AI_MODEL || 'qwen2.5-1.5b-instruct-q4_k_m.gguf';
@@ -514,5 +511,3 @@ function createEntryAutoChecker({ pool, getSetting, logAudit, aiUrl, defaultProm
}
module.exports = { createEntryAutoChecker, createPhotoEnhanceWorker };
-
-(function photoEnhanceWorkerImpl() {})();