Files
Multi-Hoster-Upload/lib/upload-audit.js
T
Sucukdeluxe 28a475401e fix: preserve rotation logs during quit fallback
Route synchronous account-rotation shutdown flushes through the internal log writer so failures under userData/logs retry userData/internal-logs in the same order as runtime writes.

Keep buffered lines until a target accepts the full chunk, report the successful fallback as the active path, and retain the buffer when every internal target fails.

Cover the real EISDIR primary-path case, complete fallback failure, and main-process quit delegation without launching Electron.
2026-08-26 12:37:17 +02:00

113 lines
4.3 KiB
JavaScript

const nodePath = require('path');
function createInternalLogPathResolver(options) {
const source = options && typeof options === 'object' ? options : {};
const fs = source.fs;
const path = source.path || nodePath;
const userDataPath = typeof source.userDataPath === 'string' ? source.userDataPath.trim() : '';
if (!fs || typeof fs.mkdirSync !== 'function' || !userDataPath) {
throw new TypeError('createInternalLogPathResolver requires fs and userDataPath');
}
const directories = [path.join(userDataPath, 'logs'), path.join(userDataPath, 'internal-logs')];
return function resolveInternalLogPath(fileName, excludedPaths = new Set()) {
if (typeof fileName !== 'string' || !fileName || path.basename(fileName) !== fileName) return null;
const excluded = excludedPaths instanceof Set
? excludedPaths
: new Set(Array.isArray(excludedPaths) ? excludedPaths : [excludedPaths]);
for (const directory of directories) {
const targetPath = path.join(directory, fileName);
if (excluded.has(targetPath)) continue;
try {
fs.mkdirSync(directory, { recursive: true });
return targetPath;
} catch {}
}
return null;
};
}
function createInternalLogWriter(options) {
const source = options && typeof options === 'object' ? options : {};
const fs = source.fs;
const path = source.path || nodePath;
const fileName = source.fileName;
const resolveInternalLogPath = source.resolveInternalLogPath;
const rotateLogFile = typeof source.rotateLogFile === 'function' ? source.rotateLogFile : () => {};
const reportError = typeof source.reportError === 'function' ? source.reportError : () => {};
const retryDelays = Array.isArray(source.retryDelays) && source.retryDelays.length > 0 ? source.retryDelays : [0, 100, 250];
const maxBytes = Number.isFinite(source.maxBytes) ? source.maxBytes : 10 * 1024 * 1024;
const maxBackups = Number.isFinite(source.maxBackups) ? source.maxBackups : 2;
let activePath = null;
if (!fs || !fs.promises || typeof fs.promises.appendFile !== 'function' || typeof fs.appendFileSync !== 'function' || typeof resolveInternalLogPath !== 'function' || typeof fileName !== 'string') {
throw new TypeError('createInternalLogWriter requires fs, fileName and resolveInternalLogPath');
}
async function append(line, label) {
const excludedPaths = new Set();
for (const delay of retryDelays) {
if (delay) await new Promise(resolve => setTimeout(resolve, delay));
const targetPath = resolveInternalLogPath(fileName, excludedPaths);
if (!targetPath) break;
excludedPaths.add(targetPath);
try {
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
rotateLogFile(targetPath, maxBytes, maxBackups);
await fs.promises.appendFile(targetPath, line, 'utf-8');
activePath = targetPath;
return true;
} catch (error) {
reportError(label, error);
}
}
return false;
}
function appendSync(line, label) {
const excludedPaths = new Set();
for (let attempt = 0; attempt < retryDelays.length; attempt++) {
const targetPath = resolveInternalLogPath(fileName, excludedPaths);
if (!targetPath) break;
excludedPaths.add(targetPath);
try {
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
rotateLogFile(targetPath, maxBytes, maxBackups);
fs.appendFileSync(targetPath, line, 'utf-8');
activePath = targetPath;
return true;
} catch (error) {
reportError(label, error);
}
}
return false;
}
function flushSync(buffer, label) {
if (!Array.isArray(buffer) || buffer.length === 0) return true;
if (!appendSync(buffer.join(''), label)) return false;
buffer.length = 0;
return true;
}
return {
append,
appendSync,
flushSync,
getActivePath: () => activePath,
getPath: () => activePath || resolveInternalLogPath(fileName)
};
}
function createUploadAuditWriter(options) {
return createInternalLogWriter({ ...options, fileName: 'upload-audit.log' });
}
function getLogOpenDirectory(targetPath, fallbackDirectory, pathApi = nodePath) {
return typeof targetPath === 'string' && targetPath ? pathApi.dirname(targetPath) : fallbackDirectory;
}
module.exports = { createInternalLogPathResolver, createInternalLogWriter, createUploadAuditWriter, getLogOpenDirectory };