Add files via upload
This commit is contained in:
@@ -20,7 +20,7 @@ class StorageFile {
|
|||||||
this._retryTimer = null;
|
this._retryTimer = null;
|
||||||
this._maxRetries = Number.isFinite(opts.maxRetries) ? opts.maxRetries : 5;
|
this._maxRetries = Number.isFinite(opts.maxRetries) ? opts.maxRetries : 5;
|
||||||
const debounceMs = Number.isFinite(opts.debounceMs) ? opts.debounceMs : 2000;
|
const debounceMs = Number.isFinite(opts.debounceMs) ? opts.debounceMs : 2000;
|
||||||
this._saveDebounced = debounce(() => this.saveNow(), debounceMs);
|
this._saveDebounced = debounce(() => this.saveNow({ silent: true }), debounceMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
@@ -71,12 +71,31 @@ class StorageFile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async saveNow() {
|
/**
|
||||||
|
* 立即保存
|
||||||
|
* @param {Object} options
|
||||||
|
* @param {boolean} options.silent - 静默模式:失败时不抛异常,返回 false
|
||||||
|
* @returns {Promise<boolean>} 是否保存成功
|
||||||
|
*/
|
||||||
|
async saveNow({ silent = true } = {}) {
|
||||||
|
// 🔧 核心修复:非静默模式等待当前保存完成
|
||||||
if (this._saving) {
|
if (this._saving) {
|
||||||
this._pendingSave = true;
|
this._pendingSave = true;
|
||||||
return;
|
|
||||||
|
if (!silent) {
|
||||||
|
await this._waitForSaveComplete();
|
||||||
|
if (this._dirtyVersion > this._savedVersion) {
|
||||||
|
return this.saveNow({ silent });
|
||||||
|
}
|
||||||
|
return this._dirtyVersion === this._savedVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.cache || this._dirtyVersion === this._savedVersion) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
if (!this.cache || this._dirtyVersion === this._savedVersion) return;
|
|
||||||
|
|
||||||
this._saving = true;
|
this._saving = true;
|
||||||
this._pendingSave = false;
|
this._pendingSave = false;
|
||||||
@@ -90,31 +109,55 @@ class StorageFile {
|
|||||||
headers: getRequestHeaders(),
|
headers: getRequestHeaders(),
|
||||||
body: JSON.stringify({ name: this.filename, data: base64 }),
|
body: JSON.stringify({ name: this.filename, data: base64 }),
|
||||||
});
|
});
|
||||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
if (!res.ok) {
|
||||||
|
throw new Error(`服务器返回 ${res.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
this._savedVersion = Math.max(this._savedVersion, versionToSave);
|
this._savedVersion = Math.max(this._savedVersion, versionToSave);
|
||||||
this._retryCount = 0;
|
this._retryCount = 0;
|
||||||
if (this._retryTimer) {
|
if (this._retryTimer) {
|
||||||
clearTimeout(this._retryTimer);
|
clearTimeout(this._retryTimer);
|
||||||
this._retryTimer = null;
|
this._retryTimer = null;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[ServerStorage] 保存失败:', err);
|
console.error('[ServerStorage] 保存失败:', err);
|
||||||
this._retryCount++;
|
this._retryCount++;
|
||||||
|
|
||||||
const delay = Math.min(30000, 2000 * (2 ** Math.max(0, this._retryCount - 1)));
|
const delay = Math.min(30000, 2000 * (2 ** Math.max(0, this._retryCount - 1)));
|
||||||
if (!this._retryTimer && this._retryCount <= this._maxRetries) {
|
if (!this._retryTimer && this._retryCount <= this._maxRetries) {
|
||||||
this._retryTimer = setTimeout(() => {
|
this._retryTimer = setTimeout(() => {
|
||||||
this._retryTimer = null;
|
this._retryTimer = null;
|
||||||
this.saveNow();
|
this.saveNow({ silent: true });
|
||||||
}, delay);
|
}, delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!silent) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
this._saving = false;
|
this._saving = false;
|
||||||
|
|
||||||
if (this._pendingSave || this._dirtyVersion > this._savedVersion) {
|
if (this._pendingSave || this._dirtyVersion > this._savedVersion) {
|
||||||
this._saveDebounced();
|
this._saveDebounced();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 等待保存完成 */
|
||||||
|
_waitForSaveComplete() {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
const check = () => {
|
||||||
|
if (!this._saving) resolve();
|
||||||
|
else setTimeout(check, 50);
|
||||||
|
};
|
||||||
|
check();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
clearCache() {
|
clearCache() {
|
||||||
this.cache = null;
|
this.cache = null;
|
||||||
this._loading = null;
|
this._loading = null;
|
||||||
@@ -137,4 +180,4 @@ class StorageFile {
|
|||||||
|
|
||||||
export const TasksStorage = new StorageFile('LittleWhiteBox_Tasks.json');
|
export const TasksStorage = new StorageFile('LittleWhiteBox_Tasks.json');
|
||||||
export const StoryOutlineStorage = new StorageFile('LittleWhiteBox_StoryOutline.json');
|
export const StoryOutlineStorage = new StorageFile('LittleWhiteBox_StoryOutline.json');
|
||||||
export const NovelDrawStorage = new StorageFile('LittleWhiteBox_NovelDraw.json', { debounceMs: 800 });
|
export const NovelDrawStorage = new StorageFile('LittleWhiteBox_NovelDraw.json', { debounceMs: 800 });
|
||||||
|
|||||||
Reference in New Issue
Block a user