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:
+22
-157
@@ -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';
|
||||
|
||||
Reference in New Issue
Block a user