release: restore v2.1.19 baseline for v2.1.24
CI / verify (push) Has been cancelled

Restore the v2.1.19 application baseline and retain only the focused import preflight summary with duplicate, unavailable, destination, job, and size-limit visibility.
This commit is contained in:
Sucukdeluxe
2026-08-17 04:25:22 +02:00
parent 9a213a7395
commit d7c9f287e4
87 changed files with 2185 additions and 16125 deletions
+22 -157
View File
@@ -2,7 +2,6 @@ const fs = require('fs');
const path = require('path');
const secretStore = require('./secret-store');
const { normalizeLogMode } = require('./log-mode');
const { normalizeUploadSchedule } = require('./upload-schedule');
const HOSTER_SETTINGS_DEFAULTS = {
retries: 3,
@@ -79,18 +78,6 @@ const DEFAULTS = {
lastBrowseDirectory: '',
removeFromQueueOnDone: false,
deleteSourceAfterSuccessfulUpload: false,
filenameFilter: {
enabled: false,
action: 'include',
matchMode: 'all',
conditions: []
},
uploadSchedule: {
enabled: false,
weekdays: [1, 2, 3, 4, 5, 6, 0],
start: '00:00',
end: '23:59'
},
showDropTarget: false,
globalMaxSpeedKbs: 0, // 0 = unlimited global speed
pendingQueue: null,
@@ -145,20 +132,6 @@ const HISTORY_RETENTION_OPTIONS = [
{ value: '100', label: 'Letzte 100 Uploads' }
];
const DIAGNOSTIC_ERROR_MESSAGES = Object.freeze({
DIAGNOSTIC_CONFIG_READ_FAILED: 'Die Diagnosekonfiguration konnte nicht gelesen werden',
DIAGNOSTIC_CONFIG_INVALID: 'Die Diagnosekonfiguration ist ungültig',
DIAGNOSTIC_HISTORY_NOT_FOUND: 'Die Diagnoseverlaufsdatei wurde nicht gefunden',
DIAGNOSTIC_HISTORY_READ_FAILED: 'Die Diagnoseverlaufsdatei konnte nicht gelesen werden',
DIAGNOSTIC_HISTORY_INVALID: 'Die Diagnoseverlaufsdatei ist ungültig'
});
function diagnosticStoreError(code) {
const error = new Error(DIAGNOSTIC_ERROR_MESSAGES[code]);
error.code = code;
return error;
}
function batchTimestampMs(batch) {
const raw = batch && batch.timestamp;
if (raw === null || raw === undefined || raw === '') return null;
@@ -251,30 +224,6 @@ class ConfigStore {
}
}
_readHistoryFileStrict() {
let raw;
try {
raw = fs.readFileSync(this.historyPath, 'utf-8');
} catch (error) {
if (error && error.code === 'ENOENT') {
throw diagnosticStoreError('DIAGNOSTIC_HISTORY_NOT_FOUND');
}
throw diagnosticStoreError('DIAGNOSTIC_HISTORY_READ_FAILED');
}
if (!raw || raw.trim().length < 2) {
throw diagnosticStoreError('DIAGNOSTIC_HISTORY_INVALID');
}
let parsed;
try {
parsed = JSON.parse(raw);
} catch {
throw diagnosticStoreError('DIAGNOSTIC_HISTORY_INVALID');
}
if (Array.isArray(parsed)) return parsed;
if (parsed && Array.isArray(parsed.history)) return parsed.history;
throw diagnosticStoreError('DIAGNOSTIC_HISTORY_INVALID');
}
_writeHistoryFileDurable(arr) {
const tmp = this.historyPath + '.tmp';
const fd = fs.openSync(tmp, 'w');
@@ -287,35 +236,15 @@ class ConfigStore {
fs.renameSync(tmp, this.historyPath);
}
async _writeHistoryFileAtomic(arr) {
const tmp = this.historyPath + '.tmp';
let handle;
let operationError;
try {
handle = await fs.promises.open(tmp, 'w');
await handle.writeFile(JSON.stringify(arr), 'utf-8');
await handle.sync();
} catch (error) {
operationError = error;
}
if (handle) {
try {
await handle.close();
} catch (error) {
if (!operationError) operationError = error;
}
}
if (operationError) throw operationError;
await fs.promises.rename(tmp, this.historyPath);
let directoryHandle;
try {
directoryHandle = await fs.promises.open(path.dirname(this.historyPath), 'r');
await directoryHandle.sync();
} catch (error) {
if (!['EINVAL', 'EISDIR', 'EPERM', 'ENOTSUP'].includes(error.code)) throw error;
} finally {
if (directoryHandle) await directoryHandle.close();
}
_writeHistoryFileAtomic(arr) {
return new Promise((resolve, reject) => {
const tmp = this.historyPath + '.tmp';
fs.writeFile(tmp, JSON.stringify(arr), 'utf-8', (err) => {
if (err) return reject(err);
try { fs.renameSync(tmp, this.historyPath); } catch (e) { return reject(e); }
resolve();
});
});
}
_quiescedWriteError() {
@@ -393,31 +322,6 @@ class ConfigStore {
return JSON.parse(raw);
}
_readConfigFileStrict() {
let raw;
try {
raw = fs.readFileSync(this.filePath, 'utf-8');
} catch {
throw diagnosticStoreError('DIAGNOSTIC_CONFIG_READ_FAILED');
}
if (!raw || raw.trim().length < 2) {
throw diagnosticStoreError('DIAGNOSTIC_CONFIG_INVALID');
}
let data;
try {
data = JSON.parse(raw);
} catch {
throw diagnosticStoreError('DIAGNOSTIC_CONFIG_INVALID');
}
if (!data || typeof data !== 'object' || Array.isArray(data) ||
!data.hosters || typeof data.hosters !== 'object' || Array.isArray(data.hosters) ||
!data.globalSettings || typeof data.globalSettings !== 'object' || Array.isArray(data.globalSettings) ||
(data.history !== undefined && !Array.isArray(data.history))) {
throw diagnosticStoreError('DIAGNOSTIC_CONFIG_INVALID');
}
return data;
}
_clone(obj) {
try { return structuredClone(obj); }
catch { return JSON.parse(JSON.stringify(obj)); }
@@ -456,11 +360,7 @@ class ConfigStore {
return r;
}
loadDiagnosticsConfig() {
return this._loadImpl(true);
}
_loadImpl(strict = false) {
_loadImpl() {
try {
// In-memory cache keyed on the file's mtime+size. The processed config
// (merged + credential-decrypted) is reparsed/re-decrypted from disk ONLY
@@ -472,27 +372,21 @@ class ConfigStore {
// long-running main-thread drag. load() always returns a CLONE so callers
// can mutate the result without corrupting the cache.
let stat = null;
if (!strict) {
try { stat = fs.statSync(this.filePath); } catch {}
}
try { stat = fs.statSync(this.filePath); } catch {}
const statKey = stat ? `${stat.mtimeMs}:${stat.size}` : '';
if (!strict && stat && this._cache && this._cacheKey === statKey) {
if (stat && this._cache && this._cacheKey === statKey) {
return this._clone(this._cache);
}
let data = null;
if (strict) {
data = this._readConfigFileStrict();
} else {
// Try main config
try { data = this._readAndParse(this.filePath); } catch {}
// Fallback to backup if main is empty/corrupt
if (!data) {
try { data = this._readAndParse(this.filePath + '.bak'); } catch {}
}
if (!data) {
try { data = this._readAndParse(this.filePath + '.pre-history-split.bak'); } catch {}
}
// Try main config
try { data = this._readAndParse(this.filePath); } catch {}
// Fallback to backup if main is empty/corrupt
if (!data) {
try { data = this._readAndParse(this.filePath + '.bak'); } catch {}
}
if (!data) {
try { data = this._readAndParse(this.filePath + '.pre-history-split.bak'); } catch {}
}
if (!data) {
const fresh = JSON.parse(JSON.stringify(DEFAULTS));
@@ -566,7 +460,6 @@ class ConfigStore {
// Downstream readers consume logMode only and must NOT derive from
// sessionLog at call sites.
globalSettings.logMode = normalizeLogMode(globalSettings);
globalSettings.uploadSchedule = normalizeUploadSchedule(globalSettings.uploadSchedule);
const rotationCursors = (data.rotationCursors && typeof data.rotationCursors === 'object' && !Array.isArray(data.rotationCursors))
? data.rotationCursors
: {};
@@ -574,13 +467,13 @@ class ConfigStore {
// Decrypt credentials stored with safeStorage so the rest of the app
// keeps working with plaintext in memory.
secretStore.decryptCredentials(result);
if (!strict && stat) {
if (stat) {
this._cache = result;
this._cacheKey = statKey;
}
return this._clone(result);
} catch (error) {
if (strict || error instanceof secretStore.SecretStoreError) throw error;
if (error instanceof secretStore.SecretStoreError) throw error;
const fresh = JSON.parse(JSON.stringify(DEFAULTS));
fresh.globalSettings.logMode = normalizeLogMode(fresh.globalSettings);
return fresh;
@@ -596,7 +489,6 @@ class ConfigStore {
const hosters = this._clone(config.hosters || {});
const globalSettings = this._clone(config.globalSettings || {});
delete globalSettings.allowPlaintextCredentialStorage;
globalSettings.uploadSchedule = normalizeUploadSchedule(globalSettings.uploadSchedule);
secretStore.encryptCredentials({ hosters });
return JSON.stringify({ ...config, globalSettings, hosters }, null, 2);
}
@@ -719,19 +611,6 @@ class ConfigStore {
});
}
saveFallbackLogPath(logFilePath) {
const snapshot = String(logFilePath || '').trim();
return this._enqueueWrite(() => {
const current = this.load();
current.globalSettings = {
...(current.globalSettings || {}),
logFilePath: snapshot
};
this._guardHosters(current, false);
return this._commit(current);
});
}
saveRendererGlobalSettings(globalSettings) {
const snapshot = this._clone(globalSettings || {});
return this._enqueueWrite(() => {
@@ -795,20 +674,6 @@ class ConfigStore {
return config.history || [];
}
loadDiagnosticsHistory() {
if (this._historyMigrated) return this._readHistoryFileStrict();
try {
return this._readHistoryFileStrict();
} catch (error) {
if (!error || error.code !== 'DIAGNOSTIC_HISTORY_NOT_FOUND') throw error;
}
const config = this.loadDiagnosticsConfig();
if (!Array.isArray(config.history)) {
throw diagnosticStoreError('DIAGNOSTIC_HISTORY_INVALID');
}
return config.history;
}
_atomicWrite(data) {
return new Promise((resolve, reject) => {
const tmpPath = this.filePath + '.tmp';