Compare commits
2 Commits
c1585ed09a
...
a7ac8c85f3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a7ac8c85f3 | ||
|
|
ca35c2a6a4 |
@ -75,7 +75,29 @@
|
|||||||
return `${base}${ext}`;
|
return `${base}${ext}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const api = { normalizeLogMode, resolveLogFileName, formatDateStamp, formatSessionStamp, VALID_MODES };
|
/**
|
||||||
|
* Reverse of resolveLogFileName: given a full filename like
|
||||||
|
* "fileuploader-2026-06-03.log" or
|
||||||
|
* "fileuploader-session-2026-06-03_18-16-20-8132.log", strip the mode-stamp
|
||||||
|
* so the bare base ("fileuploader.log") remains. Used when persisting an
|
||||||
|
* auto-resolved fallback path back into config — otherwise the saved path
|
||||||
|
* would keep growing a new stamp on every reload.
|
||||||
|
*/
|
||||||
|
function stripModeStampFromFileName(fileName) {
|
||||||
|
if (!fileName || typeof fileName !== 'string') return fileName;
|
||||||
|
// Order matters: session first (longer, more specific) before daily.
|
||||||
|
// Both regexes are anchored to $ with no nested/ambiguous quantifiers, so
|
||||||
|
// matching is linear — the eslint security warning is precautionary.
|
||||||
|
// eslint-disable-next-line security/detect-unsafe-regex
|
||||||
|
const sessionRe = /-session-\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}(?:-\d+)?(\.[^.]+)?$/;
|
||||||
|
// eslint-disable-next-line security/detect-unsafe-regex
|
||||||
|
const dailyRe = /-\d{4}-\d{2}-\d{2}(\.[^.]+)?$/;
|
||||||
|
let out = fileName.replace(sessionRe, (m, ext) => ext || '');
|
||||||
|
out = out.replace(dailyRe, (m, ext) => ext || '');
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
const api = { normalizeLogMode, resolveLogFileName, formatDateStamp, formatSessionStamp, stripModeStampFromFileName, VALID_MODES };
|
||||||
|
|
||||||
if (typeof module !== 'undefined' && module.exports) {
|
if (typeof module !== 'undefined' && module.exports) {
|
||||||
module.exports = api;
|
module.exports = api;
|
||||||
|
|||||||
18
main.js
18
main.js
@ -245,7 +245,7 @@ function getBaseLogFilePath() {
|
|||||||
// given session lands in the same file. A close→reopen of the app starts a new
|
// given session lands in the same file. A close→reopen of the app starts a new
|
||||||
// main process, so a new SESSION_ID, so a new session file. PID is appended as
|
// main process, so a new SESSION_ID, so a new session file. PID is appended as
|
||||||
// a cheap hedge against same-second restart collisions.
|
// a cheap hedge against same-second restart collisions.
|
||||||
const { resolveLogFileName, formatSessionStamp, formatDateStamp } = require('./lib/log-mode');
|
const { resolveLogFileName, formatSessionStamp, formatDateStamp, stripModeStampFromFileName } = require('./lib/log-mode');
|
||||||
const SESSION_ID = formatSessionStamp(new Date(), process.pid);
|
const SESSION_ID = formatSessionStamp(new Date(), process.pid);
|
||||||
let _activeLogKey = null; // remembers (mode + date-or-session) so cache rolls correctly
|
let _activeLogKey = null; // remembers (mode + date-or-session) so cache rolls correctly
|
||||||
let _activeLogPath = null;
|
let _activeLogPath = null;
|
||||||
@ -398,16 +398,18 @@ function _persistFallbackLogPath(workingPath) {
|
|||||||
try {
|
try {
|
||||||
const cfg = configStore.load();
|
const cfg = configStore.load();
|
||||||
const gs = cfg.globalSettings || {};
|
const gs = cfg.globalSettings || {};
|
||||||
// If daily-log is on, workingPath has a date suffix (fileuploader-YYYY-MM-DD.log).
|
const mode = gs.logMode || 'single';
|
||||||
// Strip that before saving so the base path rolls forward to tomorrow's
|
// Strip the mode-specific suffix so logFilePath stores the BARE base path.
|
||||||
// file correctly — otherwise the next day's getLogFilePath would append
|
// Otherwise daily would compound into "...-2026-06-03-2026-06-04.log" and
|
||||||
// another date onto the already-dated base.
|
// session would compound a second session-stamp onto the first — which split
|
||||||
|
// a session's lines across two files (the first few before _persistFallback
|
||||||
|
// ran, the rest after, into the doubly-stamped path). gated on logMode (the
|
||||||
|
// legacy `sessionLog` field is no longer the source of truth).
|
||||||
let toSave = workingPath;
|
let toSave = workingPath;
|
||||||
if (gs.sessionLog) {
|
if (mode === 'daily' || mode === 'session') {
|
||||||
const dir = path.dirname(workingPath);
|
const dir = path.dirname(workingPath);
|
||||||
const base = path.basename(workingPath);
|
const base = path.basename(workingPath);
|
||||||
const stripped = base.replace(/-\d{4}-\d{2}-\d{2}(\.[^.]+)$/, '$1');
|
toSave = path.join(dir, stripModeStampFromFileName(base));
|
||||||
toSave = path.join(dir, stripped);
|
|
||||||
}
|
}
|
||||||
if (gs.logFilePath === toSave) return;
|
if (gs.logFilePath === toSave) return;
|
||||||
gs.logFilePath = toSave;
|
gs.logFilePath = toSave;
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "multi-hoster-uploader",
|
"name": "multi-hoster-uploader",
|
||||||
"version": "3.3.36",
|
"version": "3.3.37",
|
||||||
"description": "Upload files to doodstream, voe, vidmoly, byse simultaneously",
|
"description": "Upload files to doodstream, voe, vidmoly, byse simultaneously",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@ -78,6 +78,50 @@ test('resolveLogFileName: unknown mode is treated as single', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// --- stripModeStampFromFileName ---
|
||||||
|
|
||||||
|
const { stripModeStampFromFileName } = require('../lib/log-mode');
|
||||||
|
|
||||||
|
test('stripModeStampFromFileName: leaves bare names alone', () => {
|
||||||
|
assert.equal(stripModeStampFromFileName('fileuploader.log'), 'fileuploader.log');
|
||||||
|
assert.equal(stripModeStampFromFileName('fileuploader'), 'fileuploader');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('stripModeStampFromFileName: strips a daily YYYY-MM-DD suffix', () => {
|
||||||
|
assert.equal(stripModeStampFromFileName('fileuploader-2026-06-03.log'), 'fileuploader.log');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('stripModeStampFromFileName: strips a session-stamp suffix (with and without pid)', () => {
|
||||||
|
assert.equal(
|
||||||
|
stripModeStampFromFileName('fileuploader-session-2026-06-03_18-16-20-8132.log'),
|
||||||
|
'fileuploader.log'
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
stripModeStampFromFileName('fileuploader-session-2026-06-03_18-16-20.log'),
|
||||||
|
'fileuploader.log'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('regression: resolveLogFileName(stripModeStampFromFileName(...)) is idempotent — persisting then re-resolving never compounds stamps', () => {
|
||||||
|
// This is the exact bug shape: persist the resolved path, then on next call
|
||||||
|
// re-resolve from the saved base — must produce the same file, not a doubled
|
||||||
|
// session-stamped one. The fix is the strip; this test guards against
|
||||||
|
// regressing _persistFallbackLogPath into the 3.3.35 bug.
|
||||||
|
const sessionId = '2026-06-03_18-16-20-8132';
|
||||||
|
const dailyDate = new Date(2026, 5, 3);
|
||||||
|
for (const mode of ['daily', 'session']) {
|
||||||
|
const date = mode === 'daily' ? dailyDate : new Date();
|
||||||
|
const initial = resolveLogFileName({ baseName: 'fileuploader', ext: '.log', mode, date, sessionId });
|
||||||
|
const stripped = stripModeStampFromFileName(initial);
|
||||||
|
// After strip, the base should be back to the bare name.
|
||||||
|
assert.equal(stripped, 'fileuploader.log', `${mode}: strip should produce bare base`);
|
||||||
|
// Re-resolving from the bare base gives the same final filename — no doubling.
|
||||||
|
const reBase = stripped.replace(/\.log$/, '');
|
||||||
|
const second = resolveLogFileName({ baseName: reBase, ext: '.log', mode, date, sessionId });
|
||||||
|
assert.equal(second, initial, `${mode}: round-trip must be idempotent`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// --- format helpers ---
|
// --- format helpers ---
|
||||||
|
|
||||||
test('formatDateStamp: zero-pads month and day', () => {
|
test('formatDateStamp: zero-pads month and day', () => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user