import Database from 'better-sqlite3'; import { readFileSync, readdirSync, existsSync } from 'fs'; import { join } from 'path'; const BUNDLE = 'C:/Users/d-chrka/Downloads/bundle-restore'; const DB_PATH = 'C:/work/chrka/myNote/ka-note/server/ka-note.db'; const db = new Database(DB_PATH); db.pragma('journal_mode = WAL'); db.pragma('foreign_keys = OFF'); const manifest = JSON.parse(readFileSync(join(BUNDLE, 'manifest.json'), 'utf8')); const userId = manifest.userId; console.log(`userId: ${userId}`); // Helper: upsert with all fields function upsertContexts() { const rows = JSON.parse(readFileSync(join(BUNDLE, 'contexts.json'), 'utf8')); const stmt = db.prepare(` INSERT INTO contexts (id, user_id, name, type, sort_order, meta, archived_at, is_favorite, updated_at, deleted_at, purged_at, version) VALUES (@id, @userId, @name, @type, @sortOrder, @meta, @archivedAt, @isFavorite, @updatedAt, @deletedAt, @purgedAt, @version) ON CONFLICT(id, user_id) DO UPDATE SET name=excluded.name, type=excluded.type, sort_order=excluded.sort_order, meta=excluded.meta, archived_at=excluded.archived_at, is_favorite=excluded.is_favorite, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, purged_at=excluded.purged_at, version=excluded.version `); const run = db.transaction(() => { for (const r of rows) { stmt.run({ ...r, userId, meta: r.meta ? JSON.stringify(r.meta) : null, isFavorite: b(r.isFavorite) }); } }); run(); console.log(`contexts: ${rows.length}`); } function upsertTopics() { const rows = JSON.parse(readFileSync(join(BUNDLE, 'topics.json'), 'utf8')); const stmt = db.prepare(` INSERT INTO topics (id, user_id, context_id, title, status, snooze_until, sort_order, is_new, updated_at, deleted_at, purged_at, version) VALUES (@id, @userId, @contextId, @title, @status, @snoozeUntil, @sortOrder, @isNew, @updatedAt, @deletedAt, @purgedAt, @version) ON CONFLICT(id, user_id) DO UPDATE SET context_id=excluded.context_id, title=excluded.title, status=excluded.status, snooze_until=excluded.snooze_until, sort_order=excluded.sort_order, is_new=excluded.is_new, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, purged_at=excluded.purged_at, version=excluded.version `); const run = db.transaction(() => { for (const r of rows) { stmt.run({ ...r, userId }); } }); run(); console.log(`topics: ${rows.length}`); } function upsertHistory() { const histDir = join(BUNDLE, 'history'); if (!existsSync(histDir)) { console.log('history: 0'); return; } const files = readdirSync(histDir).filter(f => f.endsWith('.meta.json')); const stmt = db.prepare(` INSERT INTO history_entries (id, user_id, topic_id, date, text, sort_order, linked_context_id, done_at, wiedervorlage_date, wiedervorlage_resolved_at, is_private, updated_at, deleted_at, purged_at, version) VALUES (@id, @userId, @topicId, @date, @text, @sortOrder, @linkedContextId, @doneAt, @wiedervorlageDate, @wiedervorlageResolvedAt, @isPrivate, @updatedAt, @deletedAt, @purgedAt, @version) ON CONFLICT(id, user_id) DO UPDATE SET topic_id=excluded.topic_id, date=excluded.date, text=excluded.text, sort_order=excluded.sort_order, linked_context_id=excluded.linked_context_id, done_at=excluded.done_at, wiedervorlage_date=excluded.wiedervorlage_date, wiedervorlage_resolved_at=excluded.wiedervorlage_resolved_at, is_private=excluded.is_private, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, purged_at=excluded.purged_at, version=excluded.version `); const run = db.transaction(() => { for (const f of files) { const meta = JSON.parse(readFileSync(join(histDir, f), 'utf8')); const mdFile = join(histDir, f.replace('.meta.json', '.md')); const text = existsSync(mdFile) ? readFileSync(mdFile, 'utf8') : ''; stmt.run({ ...meta, userId, text, isPrivate: b(meta.isPrivate) }); } }); run(); console.log(`history: ${files.length}`); } function upsertRatings() { const rows = JSON.parse(readFileSync(join(BUNDLE, 'ratings.json'), 'utf8')); if (!rows.length) { console.log('ratings: 0'); return; } const stmt = db.prepare(` INSERT INTO ratings (id, user_id, topic_id, history_entry_id, person_name, value, comment, updated_at, deleted_at, purged_at, version) VALUES (@id, @userId, @topicId, @historyEntryId, @personName, @value, @comment, @updatedAt, @deletedAt, @purgedAt, @version) ON CONFLICT(id, user_id) DO UPDATE SET value=excluded.value, comment=excluded.comment, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, purged_at=excluded.purged_at, version=excluded.version `); const run = db.transaction(() => { for (const r of rows) stmt.run({ ...r, userId, isPrivate: b(r.isPrivate), isFavorite: b(r.isFavorite) }); }); run(); console.log(`ratings: ${rows.length}`); } function upsertPages() { const wikiDir = join(BUNDLE, 'wiki'); if (!existsSync(wikiDir)) { console.log('pages: 0'); return; } const files = readdirSync(wikiDir).filter(f => f.endsWith('.meta.json')); const stmt = db.prepare(` INSERT INTO pages (id, user_id, title, body, is_private, is_favorite, sort_order, updated_at, deleted_at, purged_at, version) VALUES (@id, @userId, @title, @body, @isPrivate, @isFavorite, @sortOrder, @updatedAt, @deletedAt, @purgedAt, @version) ON CONFLICT(id, user_id) DO UPDATE SET title=excluded.title, body=excluded.body, is_private=excluded.is_private, is_favorite=excluded.is_favorite, sort_order=excluded.sort_order, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, purged_at=excluded.purged_at, version=excluded.version `); const run = db.transaction(() => { for (const f of files) { const meta = JSON.parse(readFileSync(join(wikiDir, f), 'utf8')); const mdFile = join(wikiDir, f.replace('.meta.json', '.md')); const body = existsSync(mdFile) ? readFileSync(mdFile, 'utf8') : ''; stmt.run({ ...meta, userId, body, isPrivate: b(meta.isPrivate), isFavorite: b(meta.isFavorite) }); } }); run(); console.log(`pages: ${files.length}`); } function upsertNotebooks() { if (!existsSync(join(BUNDLE, 'notebooks.json'))) { console.log('notebooks: 0'); return; } const rows = JSON.parse(readFileSync(join(BUNDLE, 'notebooks.json'), 'utf8')); if (!rows.length) { console.log('notebooks: 0'); return; } const stmt = db.prepare(` INSERT INTO notebooks (id, user_id, name, context_id, is_private, is_favorite, sort_order, updated_at, deleted_at, purged_at, version) VALUES (@id, @userId, @name, @contextId, @isPrivate, @isFavorite, @sortOrder, @updatedAt, @deletedAt, @purgedAt, @version) ON CONFLICT(id, user_id) DO UPDATE SET name=excluded.name, context_id=excluded.context_id, is_private=excluded.is_private, is_favorite=excluded.is_favorite, sort_order=excluded.sort_order, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, purged_at=excluded.purged_at, version=excluded.version `); const run = db.transaction(() => { for (const r of rows) stmt.run({ ...r, userId }); }); run(); console.log(`notebooks: ${rows.length}`); } function upsertPageNotebooks() { if (!existsSync(join(BUNDLE, 'page_notebooks.json'))) { console.log('page_notebooks: 0'); return; } const rows = JSON.parse(readFileSync(join(BUNDLE, 'page_notebooks.json'), 'utf8')); if (!rows.length) { console.log('page_notebooks: 0'); return; } const stmt = db.prepare(` INSERT INTO page_notebooks (id, user_id, page_id, notebook_id, sort_order, updated_at, deleted_at, purged_at, version) VALUES (@id, @userId, @pageId, @notebookId, @sortOrder, @updatedAt, @deletedAt, @purgedAt, @version) ON CONFLICT(id, user_id) DO UPDATE SET sort_order=excluded.sort_order, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, purged_at=excluded.purged_at, version=excluded.version `); const run = db.transaction(() => { for (const r of rows) stmt.run({ ...r, userId }); }); run(); console.log(`page_notebooks: ${rows.length}`); } upsertContexts(); upsertTopics(); upsertHistory(); upsertRatings(); upsertPages(); upsertNotebooks(); upsertPageNotebooks(); // Verify const counts = db.prepare(` SELECT (SELECT COUNT(*) FROM contexts WHERE user_id=?) as contexts, (SELECT COUNT(*) FROM topics WHERE user_id=?) as topics, (SELECT COUNT(*) FROM history_entries WHERE user_id=?) as history, (SELECT COUNT(*) FROM pages WHERE user_id=?) as pages `).get(userId, userId, userId, userId); console.log('Final counts:', counts); db.pragma('integrity_check'); db.close(); console.log('Done.');