Multi-Hoster-Upload/lib
Administrator 7a40afbe7e fix(config): fsync config writes + guard against account-wipe on a corrupt read (v3.3.105)
A user's server crashed hard during an upload and lost all configured accounts. It
was NOT the v3.3.104 update (a second server updated fine and kept its accounts) —
it was a data-durability hole exposed by the crash:

- Config writes were atomic (tmp + rename) but never fsync'd, so a hard crash could
  leave electron-config.json truncated/unflushed on disk.
- On restart, load() reads the truncated file, falls back to .bak, and if that is
  also bad returns empty DEFAULTS. The next settings/queue save then persists EMPTY
  hosters — permanently wiping the accounts. Worse, the async _atomicWrite blindly
  copied the (now truncated) live file over .bak, so an empty live could clobber a
  good backup.

Hardening (lib/config-store.js + main.js; no behavior change in the happy path):
- fsync before rename in both write paths — _atomicWrite (openSync/writeSync/
  fsyncSync/closeSync) and the synchronous save-global-settings-sync on window close.
  A hard crash can no longer leave a truncated config.
- _atomicWrite only refreshes .bak when the current live file is non-trivial
  (trim length > 2), so an empty/truncated live can never overwrite a good backup
  (the sync-save path already did this).
- Wipe-guard (_guardHosters): save(), saveRotationCursors() and the sync close-save
  never intend to change hosters; if after a load() the hosters are all empty and
  the write did not explicitly provide hosters, recover them from disk
  (_recoverHostersFromDisk: live -> .bak -> .pre-history-split.bak) instead of
  persisting the wipe. An explicit save({hosters: {}}) (user deleted all accounts)
  is still allowed. Restored hosters are already-encrypted on disk and
  encryptCredentials skips already-encrypted fields, so re-serializing is safe.
- load() gained a third fallback tier — the permanent pre-history-split.bak snapshot
  (which still holds the accounts) — so load() itself recovers after corruption.

Recovery for the already-affected server: copy
%APPDATA%/multi-hoster-uploader/electron-config.json.pre-history-split.bak (or .bak)
over electron-config.json with the app closed.

2 new regression tests (post-wipe valid-empty live + .bak → guard restores accounts;
an explicit empty-hosters save is not blocked). 409 tests pass; clean boot.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 03:53:01 +02:00
..
account-auth.js fix(doodstream): upload via the doodapi API when an API key exists 2026-05-28 21:42:19 +02:00
account-rotation.js fix(rotation): persist the round-robin cursor so drip-fed uploads keep rotating 2026-06-17 03:33:20 +02:00
backup-crypto.js feat(backup): import legacy password-encrypted backups 2026-04-17 11:22:33 +02:00
clouddrop-upload.js perf(uploads): 1MB read-ahead to absorb read-bursts + instrument the config-persist/load path (v3.3.98) 2026-06-21 17:55:34 +02:00
coalesced-set.js test(coalesce): extract done-removal coalescer + 11 unit tests 2026-04-28 11:59:32 +02:00
config-store.js fix(config): fsync config writes + guard against account-wipe on a corrupt read (v3.3.105) 2026-06-22 03:53:01 +02:00
diagnostics-agent.js fix(diagnostics): harden read-only agent — grep ReDoS, prototype-chain whitelist bypass, redaction gaps 2026-06-19 18:40:57 +02:00
diagnostics-collectors.js perf(history): gate the unconditional History-tab reload + fix the diagnostics history regression (v3.3.101) 2026-06-21 19:45:50 +02:00
doodstream-upload.js perf(uploads): 1MB read-ahead to absorb read-bursts + instrument the config-persist/load path (v3.3.98) 2026-06-21 17:55:34 +02:00
file-probe.js fix(rotation): suspect byse rejections now reach every account instead of dying on the first 2026-06-15 01:11:37 +02:00
folder-monitor.js 🐛 fix: folder monitor re-detect deleted files, atomic sync save 2026-03-21 13:31:54 +01:00
hosters.js perf(uploads): 1MB read-ahead to absorb read-bursts + instrument the config-persist/load path (v3.3.98) 2026-06-21 17:55:34 +02:00
ip-allowlist.js feat(diagnostics): network bind + fail-closed IP allowlist + host-in-code (Tailscale, like rd-diagnostics-mcp) 2026-06-19 19:32:29 +02:00
log-mode.js fix(log): persist BARE log path (no compounded daily/session stamps) 2026-06-04 22:08:15 +02:00
log-policy.js feat(log): per-hoster toggle for writing links to fileuploader.log 2026-05-23 15:29:25 +02:00
log-rotation.js test(log): extract log-rotation into testable module + 10 unit tests 2026-04-28 05:10:53 +02:00
orphan-tmp.js fix(queue): close two lost-work / sticky-ghost edge cases found by intensive testing 2026-06-19 06:51:48 +02:00
queue-dedup.js fix(queue): close two lost-work / sticky-ghost edge cases found by intensive testing 2026-06-19 06:51:48 +02:00
queue-prune.js test(queue): extract terminal-job prune into testable module + 10 tests 2026-04-28 06:41:47 +02:00
remote-capture-preload.js debug: add IPC logging from capture window to main process 2026-03-12 08:46:18 +01:00
remote-capture.html fix: native resolution capture + correct click offset for title bar 2026-03-12 08:55:46 +01:00
remote-server.js fix(remote-server): cap WS maxPayload (256 KiB) + guard sendToClient — close a pre-auth parse freeze-sink and a send-throw crash 2026-06-21 03:06:08 +02:00
secret-store.js fix: encrypt hoster credentials at rest; history CSV Link column urls-only 2026-04-19 11:53:59 +02:00
semaphore.js test: add unit tests (41) and UI smoke tests (21), fix semaphore listener leak 2026-03-10 10:55:50 +01:00
stats.js feat(ui): per-hoster success rate, session-paused badge, post-batch retry, link export formats 2026-06-07 20:32:35 +02:00
support-bundle.js fix(diagnostics): harden read-only agent — grep ReDoS, prototype-chain whitelist bypass, redaction gaps 2026-06-19 18:40:57 +02:00
throttle-timer.js fix(queue): completed files no longer reappear in the queue after restart 2026-06-19 04:35:27 +02:00
throttle.js fix: multiple bugs found in deep code analysis 2026-03-11 04:16:50 +01:00
throttled-cache.js test(sort): extract throttled-cache utility + 12 unit tests 2026-04-28 07:12:52 +02:00
updater.js fix(update): cancel active uploads before downloading + add download stall-timeout 2026-06-21 16:33:20 +02:00
upload-log.js fix(queue): stop finished uploads from re-appearing as pending ghosts across restart 2026-06-19 14:53:04 +02:00
upload-manager.js perf(uploads): make the batch-start file-stat non-blocking — kill the 336ms main stall (v3.3.103) 2026-06-21 20:53:48 +02:00
vidmoly-upload.js perf(uploads): 1MB read-ahead to absorb read-bursts + instrument the config-persist/load path (v3.3.98) 2026-06-21 17:55:34 +02:00
voe-upload.js perf(uploads): 1MB read-ahead to absorb read-bursts + instrument the config-persist/load path (v3.3.98) 2026-06-21 17:55:34 +02:00
webhook-notify.js fix(webhook): retry+429+status handling, await before shutdown, error-path notify, abort/auto-retry suppress, Discord limits 2026-06-10 00:08:02 +02:00