diff --git a/src/lib/components/SettingsModal.svelte b/src/lib/components/SettingsModal.svelte index b0a04b8..cad9d27 100644 --- a/src/lib/components/SettingsModal.svelte +++ b/src/lib/components/SettingsModal.svelte @@ -8,15 +8,15 @@ getOpenAi, chatModels, } from "$lib/stores/stores"; - import { DB_NAME } from "$lib/constants"; import AutosizeTextarea from "./AutosizeTextarea.svelte"; import { getSystem } from "$lib/gui"; import { onMount } from "svelte"; - import { ChatMessage, Thread } from "$lib/db"; + import { ChatMessage, Thread, getLatestDbName } from "$lib/db"; import { mapKeys, toCamelCase } from "$lib/utils"; import CloseButton from "./CloseButton.svelte"; import type OpenAI from "openai"; + let dbName = ""; let schema; let migrationVersion; onMount(async () => { @@ -32,7 +32,10 @@ .filter((x) => x.id.startsWith("gpt")) .sort((a, b) => a.id.localeCompare(b.id)); } + + dbName = getLatestDbName() || ""; }); + let showAdvanced = false; @@ -285,7 +288,7 @@
-           {DB_NAME}/{$openAiConfig.siteId} 
+           {dbName}/{$openAiConfig.siteId} 
           

Database identifier used locally for persistent storage. diff --git a/src/lib/constants.ts b/src/lib/constants.ts index a9e4f9b..e69de29 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -1,26 +0,0 @@ -/** - * A record of the databases we expect to be accessible to the app. Although - * clearly not enforced, this list should be treated as append-only. This does - * not mean the db version can't be moved backwards, just do so by appending. - * - * Appending a new db name effectively resets the db, without data loss since - * you can change it back. This is good for testing as if on a new system. - */ -export const DB_NAMES = [ - "chat_db-v1", - "chat_db-v2", - "chat_db-v3", - "chat_db-v4", - "chat_db-v5", - "chat_db-v8.1", - "chat_db-v8.2", // Testing out migration fixes - "chat_db-v8.1", // Moving back to 8.1 now that migrations are fixed. No data changed, just migration code - "chat_db-v9.1", // Starting fresh in dev - "chat_db-v9.2", // QA importing - "chat_db-v10", // Corrupted db - "chat_db-v10.1", // Testing syncing - "chat_db-v11", // fts - "chat_db-v11.11", // fts -]; - -export const DB_NAME = DB_NAMES.at(-1); diff --git a/src/lib/db.ts b/src/lib/db.ts index 53be2c0..490d377 100644 --- a/src/lib/db.ts +++ b/src/lib/db.ts @@ -1,7 +1,6 @@ import initWasm, { SQLite3, DB } from "@vlcn.io/crsqlite-wasm"; import type { TXAsync } from "@vlcn.io/xplat-api"; import wasmUrl from "@vlcn.io/crsqlite-wasm/crsqlite.wasm?url"; -import { DB_NAME } from "../lib/constants"; import { db, sqlite, @@ -20,6 +19,67 @@ import { basename, debounce, groupBy, sha1sum, toCamelCase, toSnakeCase } from " import { extractFragments } from "./markdown"; import tblrx, { TblRx } from "@vlcn.io/rx-tbl"; +const legacyDbNames = [ + "chat_db-v1", + "chat_db-v2", + "chat_db-v3", + "chat_db-v4", + "chat_db-v5", + "chat_db-v8.1", + "chat_db-v8.2", // Testing out migration fixes + "chat_db-v8.1", // Moving back to 8.1 now that migrations are fixed. No data changed, just migration code + "chat_db-v9.1", // Starting fresh in dev + "chat_db-v9.2", // QA importing + "chat_db-v10", // Corrupted db + "chat_db-v10.1", // Testing syncing + "chat_db-v11", // fts + "chat_db-v11.11", // fts +]; + +const lsKey = "prompta--dbNames"; + +/** + * Get a record of the databases we expect to be accessible to the app. Although + * clearly not enforced, this list should be treated as append-only. This does + * not mean the db version can't be moved backwards, just do so by appending. + * + * Appending a new db name effectively resets the db, without data loss since + * you can change it back. This is good for testing as if on a new system. + */ +const getDbNames = () => { + if (typeof localStorage === "undefined") { + throw new Error("localStorage is not available"); + } + + let _dbNames = localStorage.getItem(lsKey); + + if (!_dbNames) { + _dbNames = JSON.stringify(legacyDbNames); + localStorage.setItem(lsKey, _dbNames); + } + + let dbNames: string[] = []; + + if (_dbNames) { + dbNames = JSON.parse(_dbNames); + } + + return dbNames; +}; + +export const getLatestDbName = () => { + return getDbNames().at(-1); +}; + +export const incrementDbName = () => { + const v = (getDbNames().length + 1).toString().padStart(3, "0"); + const next = `chat_db-v${v}`; + + localStorage.setItem(lsKey, JSON.stringify([...getDbNames(), next])); + + return next; +}; + // ======================================================================================== // Rudimentary Migration System // ======================================================================================== @@ -112,17 +172,21 @@ const migrateDb = async (db: DB) => { } }; -export const initDb = async () => { +export const initDb = async (dbName: string) => { if (_db) { console.debug("DB already initialized"); return; } + if (!dbName) { + throw new Error("No database name provided"); + } + // @note This only works in the browser. Don't use SSR anywhere where you need this _sqlite = await initWasm(() => wasmUrl); sqlite.set(_sqlite); - _db = await _sqlite.open(DB_NAME); + _db = await _sqlite.open(dbName); db.set(_db); await migrateDb(_db); diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 031b17c..55e122b 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -115,3 +115,9 @@ export const sha1sum = async (s: string) => { export const isPWAInstalled = () => { return globalThis.matchMedia("(display-mode: standalone)").matches; }; + +export function wrapError(innerError: Error, newMessage: string): Error { + const wrappedError = new Error(newMessage + "\n" + innerError.message); + wrappedError.stack = `${newMessage}\nCaused by: ${innerError.stack}`; + return wrappedError; +} diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index a253364..3c59e00 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -2,7 +2,7 @@ import "../app.postcss"; import { openAiConfig, syncStore, showInitScreen } from "../lib/stores/stores"; import { onMount } from "svelte"; - import { DatabaseMeta, initDb } from "$lib/db"; + import { DatabaseMeta, getLatestDbName, incrementDbName, initDb } from "$lib/db"; import SettingsModal from "$lib/components/SettingsModal.svelte"; import { getSystem } from "$lib/gui"; import classNames from "classnames"; @@ -10,10 +10,10 @@ import Toaster from "$lib/toast/Toaster.svelte"; import { assets } from "$app/paths"; import FullScreenError from "$lib/components/FullScreenError.svelte"; + import { wrapError } from "$lib/utils"; const sys = getSystem(); let startupError: Error | null = null; - startupError = new Error("Test error"); let appReady = false; const handleHardReset = async () => { @@ -23,9 +23,14 @@ if (!confirmed) return; - await sys.alert("TODO: Reset the database"); + appReady = false; - // await DatabaseMeta.hardReset(); + incrementDbName(); + + // Reloading the window should cause handleStartup to be called again on app + // mount, using the new db name. Using a new db name is equivalent to using + // a new db. The old db will not be removed, but the app will have no + // existing data. location.reload(); }; @@ -39,18 +44,14 @@ try { const start = performance.now(); console.debug("Initializing database"); - await initDb(); + await initDb(getLatestDbName() || ""); console.debug(`Database initialized in ${performance.now() - start}ms`); } catch (err: any) { - await sys.alert( - `There was an error initializing the database. Please try again. If the problem persists, please report it on GitHub.` + - err.message - ); - throw err; + throw wrapError(err, `There was an error initializing the database.`); + } finally { + clearTimeout(_timeout); } - clearTimeout(_timeout); - if (!$openAiConfig.apiKey) { $showInitScreen = true; console.warn(`No API key found. Please enter one in the settings.`); @@ -171,12 +172,11 @@ })} > {#if startupError} - +

-

The app could not be initialized

-

+

What can you do? -

+