| import { writeFileSync, existsSync } from "fs"; |
| import { join } from "path"; |
| import * as Y from "yjs"; |
| import { getDataDir, docPath, sanitizeName } from "./utils.js"; |
| import { |
| isHfStorageEnabled, |
| getDatasetId, |
| setUserToken, |
| pullPublishedAssets, |
| schedulePush, |
| recordLocalSave, |
| recordLocalSaveError, |
| } from "./hf-storage.js"; |
|
|
| const DEFAULT_DOC_NAME = "default"; |
| const SAVE_DEBOUNCE_MS = 2000; |
| const saveTimers = new Map<string, ReturnType<typeof setTimeout>>(); |
| const lastSaveTimestamp = new Map<string, number>(); |
|
|
| |
| export function debouncedSave(documentName: string, ydoc: Y.Doc) { |
| const existing = saveTimers.get(documentName); |
| if (existing) clearTimeout(existing); |
|
|
| saveTimers.set(documentName, setTimeout(() => { |
| saveTimers.delete(documentName); |
| try { |
| const state = Y.encodeStateAsUpdate(ydoc); |
| const buf = Buffer.from(state); |
| writeFileSync(docPath(documentName), buf); |
| lastSaveTimestamp.set(documentName, Date.now()); |
| recordLocalSave(documentName); |
| console.log(`[persist] saved "${documentName}": ${buf.length} bytes`); |
|
|
| if (isHfStorageEnabled()) { |
| schedulePush(documentName, buf); |
| } |
| } catch (err) { |
| |
| |
| |
| |
| |
| recordLocalSaveError(documentName, err); |
| console.error(`[persist] failed to save "${documentName}":`, (err as Error).message); |
| } |
| }, SAVE_DEBOUNCE_MS)); |
| } |
|
|
| |
| export function resetSaveTimers() { |
| for (const t of saveTimers.values()) clearTimeout(t); |
| saveTimers.clear(); |
| lastSaveTimestamp.clear(); |
| } |
|
|
| let _publishedRestored = false; |
| let _restoreInProgress: Promise<void> | null = null; |
|
|
| export async function ensurePublishedRestored(token?: string): Promise<void> { |
| const DATA_DIR = getDataDir(); |
| if (_publishedRestored) return; |
| if (!getDatasetId()) return; |
|
|
| const publishedPath = join(DATA_DIR, "published", sanitizeName(DEFAULT_DOC_NAME), "index.html"); |
| if (existsSync(publishedPath)) { |
| _publishedRestored = true; |
| return; |
| } |
|
|
| if (_restoreInProgress) { |
| await _restoreInProgress; |
| return; |
| } |
|
|
| if (token) setUserToken(token); |
|
|
| _restoreInProgress = (async () => { |
| try { |
| const found = await pullPublishedAssets(DEFAULT_DOC_NAME, DATA_DIR); |
| if (found) { |
| _publishedRestored = true; |
| console.log("[server] restored published article from HF dataset"); |
| } |
| } catch (err) { |
| console.warn("[server] failed to restore published:", (err as Error).message); |
| } finally { |
| _restoreInProgress = null; |
| } |
| })(); |
|
|
| await _restoreInProgress; |
| } |
|
|
| |
| export function resetPublishedRestored() { |
| _publishedRestored = false; |
| _restoreInProgress = null; |
| } |
|
|