forked from admin/zeropost-engine
feat: auto-retry SVG covers every 30 min
coverRetry.js: сканирует articles с cover < 30KB (SVG-заглушки), перегенерирует через covers.generateCover(). При недоступности провайдера (timeout/502) прерывает цикл до следующего запуска. Первый запуск через 5 мин после старта engine, далее каждые 30 мин.
This commit is contained in:
@@ -91,6 +91,10 @@ const start = async () => {
|
||||
await migrate();
|
||||
await config.reloadAi();
|
||||
console.log('[Engine] AI config loaded from app_settings: text=' + config.ai.baseUrl + ', images=' + config.ai.imageBaseUrl);
|
||||
|
||||
// Автоматический ретрай SVG-заглушек
|
||||
require('./src/services/coverRetry').start();
|
||||
|
||||
app.listen(config.port, () => {
|
||||
console.log(`[Engine] Running on port ${config.port}`);
|
||||
});
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ const covers = require('./src/services/covers');
|
||||
const config = require('./src/config');
|
||||
const { query } = require('./src/config/db');
|
||||
|
||||
const DELAY_MS = 8000;
|
||||
const DELAY_MS = 20000;
|
||||
const sleep = ms => new Promise(r => setTimeout(r, ms));
|
||||
|
||||
(async () => {
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
// Фоновый ретрай SVG-обложек.
|
||||
// Запускается каждые 30 минут, ищет статьи с маленькими обложками (< 30KB),
|
||||
// перегенерирует их. Если провайдер недоступен — пропускает до следующего запуска.
|
||||
|
||||
const { query } = require('../config/db');
|
||||
const covers = require('../services/covers');
|
||||
const config = require('../config');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
const UPLOADS_DIR = process.env.UPLOADS_DIR || '/var/www/zeropost-uploads';
|
||||
const SVG_MAX_BYTES = 30 * 1024; // < 30KB = скорее всего SVG-заглушка
|
||||
const DELAY_MS = 25000; // пауза между генерациями
|
||||
|
||||
const sleep = ms => new Promise(r => setTimeout(r, ms));
|
||||
|
||||
async function findSvgCovers() {
|
||||
const { rows } = await query(
|
||||
`SELECT id, title, tags, cover_url FROM articles
|
||||
WHERE cover_url IS NOT NULL AND cover_url != ''
|
||||
ORDER BY id DESC`
|
||||
);
|
||||
const candidates = [];
|
||||
for (const a of rows) {
|
||||
const file = path.join(UPLOADS_DIR, a.cover_url.replace('/uploads/', ''));
|
||||
try {
|
||||
const stat = fs.statSync(file);
|
||||
if (stat.size < SVG_MAX_BYTES) candidates.push(a);
|
||||
} catch (_) { /* файл не найден — пропускаем */ }
|
||||
}
|
||||
return candidates;
|
||||
}
|
||||
|
||||
async function retryCovers() {
|
||||
let candidates;
|
||||
try { candidates = await findSvgCovers(); }
|
||||
catch (err) { console.warn('[CoverRetry] DB error:', err.message); return; }
|
||||
|
||||
if (candidates.length === 0) {
|
||||
console.log('[CoverRetry] no SVG covers found');
|
||||
return;
|
||||
}
|
||||
console.log(`[CoverRetry] found ${candidates.length} SVG cover(s), retrying...`);
|
||||
|
||||
let ok = 0, fail = 0;
|
||||
for (const a of candidates) {
|
||||
try {
|
||||
await covers.generateCover({ articleId: a.id, title: a.title, tags: a.tags || [], channelId: 1 });
|
||||
// Проверяем что новый файл реально больше
|
||||
const { rows } = await query('SELECT cover_url FROM articles WHERE id=$1', [a.id]);
|
||||
const newFile = path.join(UPLOADS_DIR, (rows[0]?.cover_url || '').replace('/uploads/', ''));
|
||||
const newSize = fs.existsSync(newFile) ? fs.statSync(newFile).size : 0;
|
||||
if (newSize > SVG_MAX_BYTES) {
|
||||
console.log(`[CoverRetry] ✓ article=${a.id} new size=${Math.round(newSize/1024)}KB`);
|
||||
ok++;
|
||||
} else {
|
||||
console.log(`[CoverRetry] ✗ article=${a.id} still small (${Math.round(newSize/1024)}KB)`);
|
||||
fail++;
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(`[CoverRetry] ✗ article=${a.id} error:`, err.message.slice(0, 80));
|
||||
fail++;
|
||||
// Если провайдер не отвечает — прерываем цикл до следующего запуска
|
||||
if (err.message.includes('timeout') || err.message.includes('502')) {
|
||||
console.warn('[CoverRetry] provider unavailable, stopping until next run');
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ok + fail < candidates.length) await sleep(DELAY_MS);
|
||||
}
|
||||
console.log(`[CoverRetry] done: ${ok} fixed, ${fail} failed`);
|
||||
}
|
||||
|
||||
// Запуск каждые 30 минут
|
||||
function start() {
|
||||
// Первый запуск через 5 минут после старта engine (чтобы не грузить при рестарте)
|
||||
setTimeout(async () => {
|
||||
await retryCovers();
|
||||
setInterval(retryCovers, 30 * 60 * 1000);
|
||||
}, 5 * 60 * 1000);
|
||||
console.log('[CoverRetry] Auto-retry started (every 30 min, first run in 5 min)');
|
||||
}
|
||||
|
||||
module.exports = { start, retryCovers };
|
||||
Reference in New Issue
Block a user