From f0006f8003f22fbc1b383a3b7774744218375811 Mon Sep 17 00:00:00 2001 From: Administrator Date: Sat, 27 Jun 2026 19:52:13 +0200 Subject: [PATCH] feat(logs): append a 6-digit random suffix to session log filenames (v3.3.108) Session-mode log files now end in a generated 6-digit number, e.g. 26-06-2026-mdu-session-06-02-847581.log. Dropping seconds/pid in v3.3.107 reintroduced same-minute collision risk on fast close/reopen; the random suffix restores per-launch uniqueness without leaking the process id. - formatSessionStamp(date, rand) appends - when supplied - main.js stamps SESSION_ID with a 6-digit Math.random value - stripModeStampFromFileName tolerates the optional -NNNNNN suffix - tests cover stamp-with-rand and strip-with-suffix; 411 pass Co-Authored-By: Claude Opus 4.8 (1M context) --- lib/log-mode.js | 9 +++++---- main.js | 6 +++--- package.json | 2 +- tests/log-mode.test.js | 6 ++++++ 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/lib/log-mode.js b/lib/log-mode.js index 20b04ea..2a945ce 100644 --- a/lib/log-mode.js +++ b/lib/log-mode.js @@ -1,7 +1,7 @@ // Log-file mode resolution for fileuploader.log: // - "single" → one file: fileuploader.log // - "daily" → per-day: fileuploader-YYYY-MM-DD.log -// - "session" → per-launch: DD-MM-YYYY-mdu-session-HH-MM.log +// - "session" → per-launch: DD-MM-YYYY-mdu-session-HH-MM-NNNNNN.log // // Pure functions only — no fs, no Date.now() at call time — so they unit-test // cleanly and the main.js call sites pass in `new Date()` + the session stamp. @@ -38,10 +38,11 @@ return `${date.getFullYear()}-${_two(date.getMonth() + 1)}-${_two(date.getDate())}`; } - function formatSessionStamp(date) { + function formatSessionStamp(date, rand) { const d = `${_two(date.getDate())}-${_two(date.getMonth() + 1)}-${date.getFullYear()}`; const t = `${_two(date.getHours())}-${_two(date.getMinutes())}`; - return `${d}-mdu-session-${t}`; + const r = (rand !== undefined && rand !== null && String(rand).trim()) ? `-${String(rand).trim()}` : ''; + return `${d}-mdu-session-${t}${r}`; } /** @@ -83,7 +84,7 @@ */ function stripModeStampFromFileName(fileName) { if (!fileName || typeof fileName !== 'string') return fileName; - const newSessionRe = /^\d{2}-\d{2}-\d{4}-mdu-session-\d{2}-\d{2}(\.[^.]+)?$/; + const newSessionRe = /^\d{2}-\d{2}-\d{4}-mdu-session-\d{2}-\d{2}(?:-\d+)?(\.[^.]+)?$/; const mNew = fileName.match(newSessionRe); if (mNew) return `fileuploader${mNew[1] || ''}`; // Order matters: session first (longer, more specific) before daily. diff --git a/main.js b/main.js index 9d3c33d..afb1580 100644 --- a/main.js +++ b/main.js @@ -563,10 +563,10 @@ function getBaseLogFilePath() { // Log-mode bookkeeping. Three modes (see lib/log-mode.js): single, daily, session. // The session-id is stamped ONCE at main-process startup so every write of a // 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 -// a cheap hedge against same-second restart collisions. +// main process, so a new SESSION_ID, so a new session file. A 6-digit random is +// appended as a cheap hedge against same-minute restart collisions. const { resolveLogFileName, formatSessionStamp, formatDateStamp, stripModeStampFromFileName } = require('./lib/log-mode'); -const SESSION_ID = formatSessionStamp(new Date(), process.pid); +const SESSION_ID = formatSessionStamp(new Date(), String(Math.floor(100000 + Math.random() * 900000))); let _activeLogKey = null; // remembers (mode + date-or-session) so cache rolls correctly let _activeLogPath = null; diff --git a/package.json b/package.json index b025ae5..939b687 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "multi-hoster-uploader", - "version": "3.3.107", + "version": "3.3.108", "description": "Upload files to doodstream, voe, vidmoly, byse simultaneously", "main": "main.js", "scripts": { diff --git a/tests/log-mode.test.js b/tests/log-mode.test.js index 1eb77c1..4becf1e 100644 --- a/tests/log-mode.test.js +++ b/tests/log-mode.test.js @@ -69,6 +69,11 @@ test('formatSessionStamp: DD-MM-YYYY-mdu-session-HH-MM', () => { assert.equal(formatSessionStamp(new Date(2026, 5, 26, 6, 2, 36)), '26-06-2026-mdu-session-06-02'); }); +test('formatSessionStamp: appends a 6-digit suffix when a rand is supplied', () => { + assert.equal(formatSessionStamp(new Date(2026, 5, 26, 6, 2, 36), '847581'), '26-06-2026-mdu-session-06-02-847581'); + assert.equal(formatSessionStamp(new Date(2026, 5, 26, 6, 2, 36), 847581), '26-06-2026-mdu-session-06-02-847581'); +}); + test('resolveLogFileName: session mode with missing sessionId falls back to single (never emits malformed name)', () => { assert.equal( resolveLogFileName({ baseName: 'fileuploader', ext: '.log', mode: 'session' }), @@ -109,6 +114,7 @@ test('stripModeStampFromFileName: strips a session-stamp suffix (with and withou test('stripModeStampFromFileName: new DD-MM-YYYY-mdu-session-HH-MM resets to the default base', () => { assert.equal(stripModeStampFromFileName('26-06-2026-mdu-session-06-02.log'), 'fileuploader.log'); + assert.equal(stripModeStampFromFileName('26-06-2026-mdu-session-06-02-847581.log'), 'fileuploader.log'); }); test('regression: resolveLogFileName(stripModeStampFromFileName(...)) is idempotent — persisting then re-resolving never compounds stamps', () => {