Незакоммиченная работа сессии 2026-09-05 (уже на проде): - lib/owner.js — реестр владельцев файлов-результатов, owner-check во всех download-роутах (IDOR) - lib/limits.js — createLimiter(): skip для админа, ipKeyGenerator (фикс ERR_ERL_KEY_GEN_IPV6) - video: обработчики 'error' у ffmpeg/ffprobe (spawn ENOENT больше не роняет сервер), таймауты - pdf: runGhostscript/gsUnavailable, водяной знак с кириллицей (fontkit + TTF) - снятие лимитов размера/количества для админа в pdf/video - журнал сессии docs/sessions/2026-09-05-0231.md
59 lines
2.0 KiB
JavaScript
59 lines
2.0 KiB
JavaScript
/**
|
||
* Реестр владельцев файлов-результатов (RESULTS_DIR).
|
||
* Имена результатов строятся из времени и потому угадываемы, поэтому скачивание
|
||
* проверяется по владельцу, а не по знанию имени.
|
||
* Живёт в той же SQLite, что и очередь задач, — переживает рестарт сервиса.
|
||
*/
|
||
|
||
const { db } = require('./queue');
|
||
|
||
db.exec(`
|
||
CREATE TABLE IF NOT EXISTS file_owners (
|
||
filename TEXT PRIMARY KEY,
|
||
user_id TEXT,
|
||
display_name TEXT,
|
||
created_at INTEGER NOT NULL
|
||
)
|
||
`);
|
||
|
||
const stmts = {
|
||
claim: db.prepare(`INSERT OR REPLACE INTO file_owners (filename, user_id, display_name, created_at) VALUES (?, ?, ?, ?)`),
|
||
get: db.prepare(`SELECT user_id, display_name FROM file_owners WHERE filename = ?`),
|
||
cleanup: db.prepare(`DELETE FROM file_owners WHERE created_at < ?`),
|
||
};
|
||
|
||
function claim(filename, userId, displayName) {
|
||
try {
|
||
stmts.claim.run(String(filename), userId != null ? String(userId) : null, displayName ? String(displayName) : null, Date.now());
|
||
} catch {}
|
||
}
|
||
|
||
// Записи живут дольше самих файлов (24 часа против 30 минут), поэтому
|
||
// отсутствие записи означает чужой/просроченный файл — доступ закрыт.
|
||
function isOwner(filename, userId) {
|
||
try {
|
||
const row = stmts.get.get(String(filename));
|
||
if (!row || userId == null) return false;
|
||
return String(row.user_id) === String(userId);
|
||
} catch {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
function displayName(filename) {
|
||
try {
|
||
const row = stmts.get.get(String(filename));
|
||
return (row && row.display_name) || null;
|
||
} catch {
|
||
return null;
|
||
}
|
||
}
|
||
|
||
function cleanup() {
|
||
try { stmts.cleanup.run(Date.now() - 24 * 3600 * 1000); } catch {}
|
||
}
|
||
|
||
setInterval(cleanup, 3600 * 1000);
|
||
|
||
module.exports = { claim, isOwner, displayName, cleanup };
|