forked from trent_larson/crowd-funder-for-time-pwa
add more to the inital migration, and refactor the locations of types
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
import migrationService from '../services/migrationService';
|
||||
import type { QueryExecResult } from '../services/migrationService';
|
||||
|
||||
// Each migration can include multiple SQL statements (with semicolons)
|
||||
const MIGRATIONS = [
|
||||
{
|
||||
name: '001_create_accounts_table',
|
||||
name: '001_initial',
|
||||
// see ../db/tables files for explanations
|
||||
sql: `
|
||||
CREATE TABLE IF NOT EXISTS accounts (
|
||||
@@ -18,8 +19,72 @@ const MIGRATIONS = [
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_accounts_did ON accounts(did);
|
||||
CREATE INDEX IF NOT EXISTS idx_accounts_publicKeyHex ON accounts(publicKeyHex);
|
||||
`
|
||||
|
||||
CREATE TABLE IF NOT EXISTS secret (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
secret TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS settings (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
accountDid TEXT,
|
||||
activeDid TEXT,
|
||||
apiServer TEXT,
|
||||
filterFeedByNearby BOOLEAN,
|
||||
filterFeedByVisible BOOLEAN,
|
||||
finishedOnboarding BOOLEAN,
|
||||
firstName TEXT,
|
||||
hideRegisterPromptOnNewContact BOOLEAN,
|
||||
isRegistered BOOLEAN,
|
||||
lastName TEXT,
|
||||
lastAckedOfferToUserJwtId TEXT,
|
||||
lastAckedOfferToUserProjectsJwtId TEXT,
|
||||
lastNotifiedClaimId TEXT,
|
||||
lastViewedClaimId TEXT,
|
||||
notifyingNewActivityTime TEXT,
|
||||
notifyingReminderMessage TEXT,
|
||||
notifyingReminderTime TEXT,
|
||||
partnerApiServer TEXT,
|
||||
passkeyExpirationMinutes INTEGER,
|
||||
profileImageUrl TEXT,
|
||||
searchBoxes TEXT, -- Stored as JSON string
|
||||
showContactGivesInline BOOLEAN,
|
||||
showGeneralAdvanced BOOLEAN,
|
||||
showShortcutBvc BOOLEAN,
|
||||
vapid TEXT,
|
||||
warnIfProdServer BOOLEAN,
|
||||
warnIfTestServer BOOLEAN,
|
||||
webPushServer TEXT
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_settings_accountDid ON settings(accountDid);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS contacts (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
did TEXT NOT NULL,
|
||||
name TEXT,
|
||||
contactMethods TEXT, -- Stored as JSON string
|
||||
nextPubKeyHashB64 TEXT,
|
||||
notes TEXT,
|
||||
profileImageUrl TEXT,
|
||||
publicKeyBase64 TEXT,
|
||||
seesMe BOOLEAN,
|
||||
registered BOOLEAN
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_contacts_did ON contacts(did);
|
||||
CREATE INDEX IF NOT EXISTS idx_contacts_name ON contacts(name);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS logs (
|
||||
date TEXT PRIMARY KEY,
|
||||
message TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS temp (
|
||||
id TEXT PRIMARY KEY,
|
||||
blobB64 TEXT
|
||||
);
|
||||
`
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
14
src/interfaces/database.ts
Normal file
14
src/interfaces/database.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
export type SqlValue = string | number | null | Uint8Array;
|
||||
|
||||
export interface QueryExecResult {
|
||||
columns: Array<string>;
|
||||
values: Array<Array<SqlValue>>;
|
||||
}
|
||||
|
||||
export interface DatabaseService {
|
||||
initialize(): Promise<void>;
|
||||
query(sql: string, params?: any[]): Promise<QueryExecResult[]>;
|
||||
run(sql: string, params?: any[]): Promise<{ changes: number; lastId?: number }>;
|
||||
get(sql: string, params?: any[]): Promise<any[] | undefined>;
|
||||
all(sql: string, params?: any[]): Promise<any[][]>;
|
||||
}
|
||||
@@ -549,7 +549,7 @@ export const generateSaveAndActivateIdentity = async (): Promise<string> => {
|
||||
mnemonic: mnemonic,
|
||||
publicKeyHex: newId.keys[0].publicKeyHex,
|
||||
});
|
||||
|
||||
|
||||
await updateDefaultSettings({ activeDid: newId.did });
|
||||
} catch (error) {
|
||||
console.error("Failed to update default settings:", error);
|
||||
|
||||
27
src/services/database.d.ts
vendored
Normal file
27
src/services/database.d.ts
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
import { DatabaseService } from '../interfaces/database';
|
||||
|
||||
declare module '@jlongster/sql.js' {
|
||||
interface SQL {
|
||||
Database: any;
|
||||
FS: any;
|
||||
register_for_idb: (fs: any) => void;
|
||||
}
|
||||
|
||||
function initSqlJs(config: { locateFile: (file: string) => string }): Promise<SQL>;
|
||||
export default initSqlJs;
|
||||
}
|
||||
|
||||
declare module 'absurd-sql' {
|
||||
export class SQLiteFS {
|
||||
constructor(fs: any, backend: any);
|
||||
}
|
||||
}
|
||||
|
||||
declare module 'absurd-sql/dist/indexeddb-backend' {
|
||||
export default class IndexedDBBackend {
|
||||
constructor();
|
||||
}
|
||||
}
|
||||
|
||||
declare const databaseService: DatabaseService;
|
||||
export default databaseService;
|
||||
@@ -1,81 +0,0 @@
|
||||
import initSqlJs from '@jlongster/sql.js';
|
||||
import { SQLiteFS } from 'absurd-sql';
|
||||
import IndexedDBBackend from 'absurd-sql/dist/indexeddb-backend';
|
||||
import { runMigrations } from '../db-sql/migration';
|
||||
|
||||
class DatabaseService {
|
||||
constructor() {
|
||||
this.db = null;
|
||||
this.SQL = null;
|
||||
this.initialized = false;
|
||||
}
|
||||
|
||||
async initialize() {
|
||||
if (this.initialized) return;
|
||||
|
||||
this.SQL = await initSqlJs({
|
||||
locateFile: file => {
|
||||
return new URL(`/node_modules/@jlongster/sql.js/dist/${file}`, import.meta.url).href;
|
||||
}
|
||||
});
|
||||
|
||||
let sqlFS = new SQLiteFS(this.SQL.FS, new IndexedDBBackend());
|
||||
this.SQL.register_for_idb(sqlFS);
|
||||
|
||||
this.SQL.FS.mkdir('/sql');
|
||||
this.SQL.FS.mount(sqlFS, {}, '/sql');
|
||||
|
||||
const path = '/sql/db.sqlite';
|
||||
if (typeof SharedArrayBuffer === 'undefined') {
|
||||
let stream = this.SQL.FS.open(path, 'a+');
|
||||
await stream.node.contents.readIfFallback();
|
||||
this.SQL.FS.close(stream);
|
||||
}
|
||||
|
||||
this.db = new this.SQL.Database(path, { filename: true });
|
||||
this.db.exec(`
|
||||
PRAGMA journal_mode=MEMORY;
|
||||
`);
|
||||
const sqlExec = this.db.exec.bind(this.db);
|
||||
|
||||
// Run migrations
|
||||
await runMigrations(sqlExec);
|
||||
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
async query(sql, params = []) {
|
||||
if (!this.initialized) {
|
||||
await this.initialize();
|
||||
}
|
||||
return this.db.exec(sql, params);
|
||||
}
|
||||
|
||||
async run(sql, params = []) {
|
||||
if (!this.initialized) {
|
||||
await this.initialize();
|
||||
}
|
||||
return this.db.run(sql, params);
|
||||
}
|
||||
|
||||
async get(sql, params = []) {
|
||||
if (!this.initialized) {
|
||||
await this.initialize();
|
||||
}
|
||||
const result = await this.db.exec(sql, params);
|
||||
return result[0]?.values[0];
|
||||
}
|
||||
|
||||
async all(sql, params = []) {
|
||||
if (!this.initialized) {
|
||||
await this.initialize();
|
||||
}
|
||||
const result = await this.db.exec(sql, params);
|
||||
return result[0]?.values || [];
|
||||
}
|
||||
}
|
||||
|
||||
// Create a singleton instance
|
||||
const databaseService = new DatabaseService();
|
||||
|
||||
export default databaseService;
|
||||
97
src/services/database.ts
Normal file
97
src/services/database.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
// Add type declarations for external modules
|
||||
declare module '@jlongster/sql.js';
|
||||
declare module 'absurd-sql';
|
||||
declare module 'absurd-sql/dist/indexeddb-backend';
|
||||
|
||||
import initSqlJs from '@jlongster/sql.js';
|
||||
import { SQLiteFS } from 'absurd-sql';
|
||||
import IndexedDBBackend from 'absurd-sql/dist/indexeddb-backend';
|
||||
import { runMigrations } from '../db-sql/migration';
|
||||
import { QueryExecResult } from './migrationService';
|
||||
|
||||
interface SQLDatabase {
|
||||
exec: (sql: string, params?: any[]) => Promise<QueryExecResult[]>;
|
||||
run: (sql: string, params?: any[]) => Promise<{ changes: number; lastId?: number }>;
|
||||
}
|
||||
|
||||
class DatabaseService {
|
||||
private db: SQLDatabase | null;
|
||||
private initialized: boolean;
|
||||
|
||||
constructor() {
|
||||
this.db = null;
|
||||
this.initialized = false;
|
||||
}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
if (this.initialized) return;
|
||||
|
||||
const SQL = await initSqlJs({
|
||||
locateFile: (file: string) => {
|
||||
return new URL(`/node_modules/@jlongster/sql.js/dist/${file}`, import.meta.url).href;
|
||||
}
|
||||
});
|
||||
|
||||
let sqlFS = new SQLiteFS(SQL.FS, new IndexedDBBackend());
|
||||
SQL.register_for_idb(sqlFS);
|
||||
|
||||
SQL.FS.mkdir('/sql');
|
||||
SQL.FS.mount(sqlFS, {}, '/sql');
|
||||
|
||||
const path = '/sql/db.sqlite';
|
||||
if (typeof SharedArrayBuffer === 'undefined') {
|
||||
let stream = SQL.FS.open(path, 'a+');
|
||||
await stream.node.contents.readIfFallback();
|
||||
SQL.FS.close(stream);
|
||||
}
|
||||
|
||||
this.db = new SQL.Database(path, { filename: true });
|
||||
if (!this.db) {
|
||||
throw new Error('Failed to initialize database');
|
||||
}
|
||||
|
||||
await this.db.exec(`
|
||||
PRAGMA journal_mode=MEMORY;
|
||||
`);
|
||||
const sqlExec = this.db.exec.bind(this.db);
|
||||
|
||||
// Run migrations
|
||||
await runMigrations(sqlExec);
|
||||
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
private ensureInitialized(): void {
|
||||
if (!this.initialized || !this.db) {
|
||||
throw new Error('Database not initialized');
|
||||
}
|
||||
}
|
||||
|
||||
// Used for inserts, updates, and deletes
|
||||
async run(sql: string, params: any[] = []): Promise<{ changes: number; lastId?: number }> {
|
||||
this.ensureInitialized();
|
||||
return this.db!.run(sql, params);
|
||||
}
|
||||
|
||||
async query(sql: string, params: any[] = []): Promise<QueryExecResult[]> {
|
||||
this.ensureInitialized();
|
||||
return this.db!.exec(sql, params);
|
||||
}
|
||||
|
||||
async get(sql: string, params: any[] = []): Promise<any[] | undefined> {
|
||||
this.ensureInitialized();
|
||||
const result = await this.db!.exec(sql, params);
|
||||
return result[0]?.values[0];
|
||||
}
|
||||
|
||||
async all(sql: string, params: any[] = []): Promise<any[][]> {
|
||||
this.ensureInitialized();
|
||||
const result = await this.db!.exec(sql, params);
|
||||
return result[0]?.values || [];
|
||||
}
|
||||
}
|
||||
|
||||
// Create a singleton instance
|
||||
const databaseService = new DatabaseService();
|
||||
|
||||
export default databaseService;
|
||||
Reference in New Issue
Block a user