source: Klonkt/scripts/migrate-posts-to-html.js@ b5bae24

main
Last change on this file since b5bae24 was b5bae24, checked in by Robin Genis <roboburr@…>, 3 months ago

reconcile: commit uncommitted live /srv state (playback fix + prefetch, SF favicon, smooth scroll, scripts)

The live working tree /srv/prutfolio was ahead of the bare repo with direct
server edits that had never been committed back. The deploy hook does
checkout -f main, so the next deploy would have reverted the 3 modified
tracked files to f33db01 and lost this work:

  • audio-player.js: robust playback — retry+backoff on network hiccups (no longer skipping immediately) + next-track prefetch (downloads the next track while the current one plays -> ended->next swaps instantly, covering the autoplay lapse). Fix for the sometimes-next-doesn't-play bug.
  • server.js: favicon mark p -> SF (SoundFabrics rebrand). shell.ejs: audio-player.js cache buster ?v=5 -> ?v=6 + favicon ?v=sf.
  • Plus previously untracked project files committed: lenis.min.js + smooth-scroll.js (not yet wired), scripts/ (v9 import/migration), deploy/ docs (DEPLOY.md/backup.sh/nginx/verify.ps1), .well-known/assetlinks.json. audio-player.js.bak.20260614 deliberately NOT committed.

Co-Authored-By: Claude <noreply@…>

  • Property mode set to 100644
File size: 3.1 KB
RevLine 
[b5bae24]1/**
2 * P58 — One-shot migration: convert markdown post.content to sanitized HTML.
3 *
4 * Run on the server AFTER deploying P58:
5 * node scripts/migrate-posts-to-html.js
6 *
7 * Idempotent: posts whose content already starts with '<' are detected as
8 * HTML and skipped. Markdown posts are rendered via MarkdownService, then
9 * sanitized via HtmlSanitizerService, then written back. Original markdown
10 * is preserved in posts.content_legacy_md so we can roll back if needed.
11 *
12 * Re-running the script after the column is added is safe — the second run
13 * will see HTML content and skip everything.
14 */
15
16import db from '../src/config/database.js';
17import MarkdownService from '../src/services/MarkdownService.js';
18import HtmlSanitizerService from '../src/services/HtmlSanitizerService.js';
19
20function ensureBackupColumn() {
21 // Try to add the legacy_md backup column; SQLite throws if it exists.
22 try {
23 db.exec(`ALTER TABLE posts ADD COLUMN content_legacy_md TEXT`);
24 console.log(' + added posts.content_legacy_md column');
25 } catch (e) {
26 if (!/duplicate column/i.test(e.message)) throw e;
27 }
28}
29
30function looksLikeHtml(s) {
31 if (!s) return false;
32 // Heuristic: starts with a tag, OR contains common block-level tags. Doesn't
33 // need to be airtight — false positives just mean we skip a post that's
34 // already mostly HTML, which is fine.
35 const t = s.trimStart();
36 return t.startsWith('<') || /<\/?(p|h[1-6]|ul|ol|li|blockquote|figure|img|div)\b/i.test(s);
37}
38
39function migrateOne(post) {
40 if (!post.content || !post.content.trim()) {
41 return { id: post.id, action: 'empty' };
42 }
43 if (looksLikeHtml(post.content)) {
44 return { id: post.id, action: 'skipped-already-html' };
45 }
46 if (post.content_legacy_md) {
47 // Already migrated (legacy backup exists) but content somehow markdown again?
48 return { id: post.id, action: 'skipped-already-backed-up' };
49 }
50
51 const renderedHtml = MarkdownService.render(post.content);
52 const cleanHtml = HtmlSanitizerService.sanitize(renderedHtml);
53
54 db.prepare(`
55 UPDATE posts SET content = ?, content_legacy_md = ?, updated_at = updated_at
56 WHERE id = ?
57 `).run(cleanHtml, post.content, post.id);
58
59 return { id: post.id, action: 'migrated', from: post.content.length, to: cleanHtml.length };
60}
61
62function main() {
63 console.log('PrutFolio P58 — markdown → HTML post migration\n');
64 ensureBackupColumn();
65
66 const posts = db.prepare(`SELECT id, slug, content, content_legacy_md FROM posts`).all();
67 console.log(` found ${posts.length} posts\n`);
68
69 const stats = { migrated: 0, skipped: 0, empty: 0 };
70 for (const p of posts) {
71 const r = migrateOne(p);
72 const tag = r.action.startsWith('skipped') ? 'skipped' :
73 r.action === 'empty' ? 'empty' : 'migrated';
74 stats[tag]++;
75 const detail = r.action === 'migrated' ? ` (${r.from} md → ${r.to} html chars)` : ` [${r.action}]`;
76 console.log(` - ${p.slug}${detail}`);
77 }
78
79 console.log(`\nDone. migrated=${stats.migrated} skipped=${stats.skipped} empty=${stats.empty}`);
80 console.log('Backup column posts.content_legacy_md retains original markdown.');
81}
82
83main();
Note: See TracBrowser for help on using the repository browser.