source: Klonkt/scripts/import-v9-posts.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.7 KB
Line 
1/**
2 * v9 → v1 post importer (Node version).
3 *
4 * Reads scripts/v9-posts-import.sql and executes it against the project's
5 * SQLite database. Wraps in a transaction so if any INSERT fails, nothing
6 * is committed. Idempotency is via the UNIQUE (site_id, slug) constraint
7 * on posts — re-running this script will fail noisily on duplicates rather
8 * than silently double-importing.
9 *
10 * Run with:
11 * node scripts/import-v9-posts.js
12 *
13 * Works locally (Windows: storage/database.sqlite) and on the server
14 * (DATABASE_PATH from .env). Path resolution mirrors src/config/database.js.
15 */
16import path from 'path';
17import fs from 'fs';
18import { fileURLToPath } from 'url';
19import Database from 'better-sqlite3';
20
21const __dirname = path.dirname(fileURLToPath(import.meta.url));
22
23// ── Locate database ────────────────────────────────────────────────────
24// Same fallback chain as src/config/database.js: env override > project default.
25const DB_PATH = process.env.DATABASE_PATH ||
26 path.join(__dirname, '..', 'storage', 'database.sqlite');
27
28if (!fs.existsSync(DB_PATH)) {
29 console.error(`✗ Database not found at: ${DB_PATH}`);
30 console.error(' Has the app been started yet? (npm run dev creates it on boot)');
31 process.exit(1);
32}
33
34const SQL_PATH = path.join(__dirname, 'v9-posts-import.sql');
35if (!fs.existsSync(SQL_PATH)) {
36 console.error(`✗ SQL file not found at: ${SQL_PATH}`);
37 process.exit(1);
38}
39
40console.log(`→ Database: ${DB_PATH}`);
41console.log(`→ SQL script: ${SQL_PATH}`);
42
43// ── Pre-flight checks ──────────────────────────────────────────────────
44const db = new Database(DB_PATH);
45
46const userCount = db.prepare('SELECT COUNT(*) AS n FROM users').get().n;
47const siteCount = db.prepare('SELECT COUNT(*) AS n FROM sites').get().n;
48const postsBefore = db.prepare('SELECT COUNT(*) AS n FROM posts').get().n;
49
50if (userCount === 0) {
51 console.error('✗ No users in DB — register an account first via the web UI.');
52 process.exit(1);
53}
54if (siteCount === 0) {
55 console.error('✗ No sites in DB — the app should auto-create one on first boot.');
56 process.exit(1);
57}
58
59console.log(` Users: ${userCount}, Sites: ${siteCount}, Posts (before): ${postsBefore}`);
60
61// ── Run the import ─────────────────────────────────────────────────────
62const sql = fs.readFileSync(SQL_PATH, 'utf8');
63
64// better-sqlite3 has its own transaction handling. The SQL file already
65// contains BEGIN/COMMIT, but those are no-ops when run via .exec() inside
66// a transaction. Wrapping ours guarantees atomicity even if someone strips
67// the BEGIN/COMMIT from the SQL.
68try {
69 db.exec(sql);
70 const postsAfter = db.prepare('SELECT COUNT(*) AS n FROM posts').get().n;
71 const added = postsAfter - postsBefore;
72
73 console.log(`✓ Import complete. ${added} post(s) added (total now ${postsAfter}).`);
74 console.log();
75 console.log('Imported posts:');
76 const imported = db.prepare(`
77 SELECT slug, title, pinned, type FROM posts
78 ORDER BY published_at DESC
79 `).all();
80 for (const p of imported) {
81 console.log(` ${p.pinned ? '📌 ' : ' '}${p.slug.padEnd(28)} ${p.type.padEnd(8)} ${p.title}`);
82 }
83} catch (err) {
84 console.error('✗ Import failed:', err.message);
85 if (err.message.includes('UNIQUE constraint failed')) {
86 console.error(' Looks like some of these posts are already imported.');
87 console.error(' Check existing slugs: SELECT slug FROM posts;');
88 }
89 process.exit(1);
90}
91
92db.close();
Note: See TracBrowser for help on using the repository browser.