resave-hosted-empty-frontmatter.mjs
111 lines 4.0 KB
Raw
sha256:8915fe406161f95c1681f9469375e7bae5b28c884f00bedbdef65e4b0cd0738d docs(flow): commit FLOW-V0-SPEC.md hygiene for 7A-INT merge Human 11 hours ago
1 #!/usr/bin/env node
2 /**
3 * Phase D remediation: re-POST notes whose stored frontmatter parses to {} so the gateway
4 * merges provenance (knowtation_edited_at, etc.) and optional title from the path.
5 *
6 * Use only after verify-hosted-hub-api investigation shows write_path_ok_legacy_data_likely.
7 *
8 * KNOWTATION_HUB_TOKEN='...' node scripts/resave-hosted-empty-frontmatter.mjs --dry-run
9 * KNOWTATION_HUB_TOKEN='...' node scripts/resave-hosted-empty-frontmatter.mjs --execute
10 *
11 * Same env as verify: KNOWTATION_HUB_API, KNOWTATION_HUB_VAULT_ID, KNOWTATION_HUB_TOKEN_FILE
12 */
13
14 import fs from 'fs';
15 import path from 'path';
16 import { fileURLToPath } from 'url';
17 import dotenv from 'dotenv';
18 import { materializeListFrontmatter } from '../hub/gateway/note-facets.mjs';
19
20 const __dirname = path.dirname(fileURLToPath(import.meta.url));
21 const repoRoot = path.resolve(__dirname, '..');
22 dotenv.config({ path: path.join(repoRoot, '.env') });
23
24 function resolveToken() {
25 let t = process.env.KNOWTATION_HUB_TOKEN || process.env.HUB_JWT || '';
26 const fp = (process.env.KNOWTATION_HUB_TOKEN_FILE || '').trim();
27 if (!t && fp) {
28 const expanded = fp.startsWith('~') ? path.join(process.env.HOME || '', fp.slice(1)) : fp;
29 t = fs.readFileSync(expanded, 'utf8').trim();
30 }
31 return t;
32 }
33
34 const token = resolveToken();
35 const apiBase = (process.env.KNOWTATION_HUB_API || 'https://knowtation-gateway.netlify.app').replace(/\/$/, '');
36 const vaultId = process.env.KNOWTATION_HUB_VAULT_ID || 'default';
37 const execute = process.argv.includes('--execute');
38 const dryRun = process.argv.includes('--dry-run') || !execute;
39
40 function headers() {
41 return {
42 Accept: 'application/json',
43 'Content-Type': 'application/json',
44 'X-Vault-Id': vaultId,
45 Authorization: 'Bearer ' + token,
46 };
47 }
48
49 function titleFromPath(p) {
50 const base = path.posix.basename(String(p || ''), '.md');
51 return base || 'note';
52 }
53
54 async function main() {
55 if (!token) {
56 console.error('Set KNOWTATION_HUB_TOKEN (or HUB_JWT / KNOWTATION_HUB_TOKEN_FILE).');
57 process.exit(1);
58 }
59 if (!execute && !process.argv.includes('--dry-run')) {
60 console.error('Pass --dry-run (default) or --execute.');
61 process.exit(1);
62 }
63
64 const listRes = await fetch(`${apiBase}/api/v1/notes?limit=500&offset=0`, { headers: headers() });
65 const listText = await listRes.text();
66 if (!listRes.ok) {
67 console.error('List failed', listRes.status, listText.slice(0, 400));
68 process.exit(1);
69 }
70 const data = JSON.parse(listText);
71 const notes = Array.isArray(data.notes) ? data.notes : [];
72 const targets = [];
73 for (const n of notes) {
74 const fm = materializeListFrontmatter(n.frontmatter);
75 if (Object.keys(fm).length === 0 && n.path) targets.push(n.path);
76 }
77 console.log('empty_frontmatter_paths', targets.length);
78 if (dryRun) {
79 targets.forEach((p) => console.log('would_resave', p));
80 console.log('Dry run only. Re-run with --execute to POST each note (same body, new minimal frontmatter).');
81 return;
82 }
83
84 for (const p of targets) {
85 const enc = encodeURIComponent(p);
86 const getRes = await fetch(`${apiBase}/api/v1/notes/${enc}`, { headers: headers() });
87 const getText = await getRes.text();
88 if (!getRes.ok) {
89 console.error('GET failed', p, getRes.status, getText.slice(0, 120));
90 continue;
91 }
92 const note = JSON.parse(getText);
93 const body = typeof note.body === 'string' ? note.body : '';
94 const fmStr = JSON.stringify({ title: titleFromPath(p) });
95 const postBody = JSON.stringify({ path: p, body, frontmatter: fmStr });
96 const postRes = await fetch(`${apiBase}/api/v1/notes`, {
97 method: 'POST',
98 headers: headers(),
99 body: postBody,
100 });
101 const postText = await postRes.text();
102 console.log('POST', p, postRes.status, postRes.ok ? 'ok' : postText.slice(0, 80));
103 await new Promise((r) => setTimeout(r, 150));
104 }
105 console.log('Done. Re-run npm run verify:hosted-api to confirm empty_frontmatter_count dropped.');
106 }
107
108 main().catch((e) => {
109 console.error(e);
110 process.exit(1);
111 });
File History 1 commit
sha256:8915fe406161f95c1681f9469375e7bae5b28c884f00bedbdef65e4b0cd0738d docs(flow): commit FLOW-V0-SPEC.md hygiene for 7A-INT merge Human 11 hours ago