tfrere HF Staff commited on
Commit
d2072ce
·
1 Parent(s): 727352b

feat: restore published article from HF dataset on startup

Browse files

After a container rebuild, the local data directory is empty.
On boot, the server now pulls published assets (HTML, PDF, thumbnail,
meta) from the HF dataset back to local storage so the published
article is served immediately without needing to re-publish.

Made-with: Cursor

Files changed (2) hide show
  1. backend/src/hf-storage.ts +46 -0
  2. backend/src/server.ts +13 -1
backend/src/hf-storage.ts CHANGED
@@ -140,6 +140,52 @@ export async function flushAll(): Promise<void> {
140
  await Promise.allSettled(names.map((n) => pushDocument(n)));
141
  }
142
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143
  // ---------- Published Assets ----------
144
 
145
  interface PublishedPayload {
 
140
  await Promise.allSettled(names.map((n) => pushDocument(n)));
141
  }
142
 
143
+ /**
144
+ * Pull published assets (HTML, PDF, thumbnail, meta) from the HF dataset
145
+ * and write them to the local data directory so the server can serve them.
146
+ * Called on startup to restore published state after a container rebuild.
147
+ */
148
+ export async function pullPublishedAssets(
149
+ docName: string,
150
+ localDir: string,
151
+ ): Promise<boolean> {
152
+ if (!HF_DATASET_ID) return false;
153
+
154
+ const accessToken = getToken();
155
+ if (!accessToken) return false;
156
+
157
+ const safeName = sanitizeName(docName);
158
+ const base = `published/${safeName}`;
159
+ const files = ["index.html", "article.pdf", "thumb.jpg", "meta.json"];
160
+
161
+ let foundAny = false;
162
+
163
+ for (const file of files) {
164
+ try {
165
+ const res = await downloadFile({
166
+ repo,
167
+ path: `${base}/${file}`,
168
+ accessToken,
169
+ });
170
+ if (!res) continue;
171
+
172
+ const { mkdirSync, writeFileSync } = await import("fs");
173
+ const { join } = await import("path");
174
+ const outDir = join(localDir, "published", safeName);
175
+ mkdirSync(outDir, { recursive: true });
176
+
177
+ const buf = Buffer.from(await res.arrayBuffer());
178
+ writeFileSync(join(outDir, file), buf);
179
+ console.log(`[hf-storage] pulled ${base}/${file} (${buf.length} bytes)`);
180
+ foundAny = true;
181
+ } catch {
182
+ // File may not exist (e.g. PDF not generated); skip silently
183
+ }
184
+ }
185
+
186
+ return foundAny;
187
+ }
188
+
189
  // ---------- Published Assets ----------
190
 
191
  interface PublishedPayload {
backend/src/server.ts CHANGED
@@ -19,6 +19,7 @@ import {
19
  setUserToken,
20
  uploadImageToHf,
21
  pullDocument,
 
22
  schedulePush,
23
  flushAll,
24
  } from "./hf-storage.js";
@@ -406,9 +407,20 @@ if (existsSync(staticDir)) {
406
 
407
  // ---------- Start ----------
408
 
409
- const server = httpServer.listen(PORT, () => {
410
  console.log(`[server] running on http://localhost:${PORT}`);
411
  console.log(`[server] collab websocket at ws://localhost:${PORT}/collab/:doc`);
 
 
 
 
 
 
 
 
 
 
 
412
  });
413
 
414
  // Graceful shutdown: flush pending docs to HF
 
19
  setUserToken,
20
  uploadImageToHf,
21
  pullDocument,
22
+ pullPublishedAssets,
23
  schedulePush,
24
  flushAll,
25
  } from "./hf-storage.js";
 
407
 
408
  // ---------- Start ----------
409
 
410
+ const server = httpServer.listen(PORT, async () => {
411
  console.log(`[server] running on http://localhost:${PORT}`);
412
  console.log(`[server] collab websocket at ws://localhost:${PORT}/collab/:doc`);
413
+
414
+ // Restore published articles from HF dataset after a container rebuild
415
+ if (getDatasetId()) {
416
+ try {
417
+ const found = await pullPublishedAssets(DEFAULT_DOC_NAME, DATA_DIR);
418
+ if (found) console.log("[server] restored published article from HF dataset");
419
+ else console.log("[server] no published article found in HF dataset");
420
+ } catch (err) {
421
+ console.warn("[server] failed to restore published article:", (err as Error).message);
422
+ }
423
+ }
424
  });
425
 
426
  // Graceful shutdown: flush pending docs to HF