Add SQLite support

This adds SQLite support, but a few queries are broken because they're PostgreSQL-exclusive/specific.

I don't see enough value (at least for now) in making the queries harder to read (or the code around them) "just" to enable SQLite support, so I'll abandon this for now, but push the code up in case it helps anyone else add this kind of support (and then change the queries for themselves).

Related to #49
This commit is contained in:
Bruno Bernardino 2025-12-01 08:50:14 +00:00
parent 3fdda5b34e
commit cb5160c359
No known key found for this signature in database
GPG key ID: D1B0A69ADD114ECE
6 changed files with 85 additions and 12 deletions

View file

@ -1,5 +1,6 @@
PORT=8000
# These POSTGRESQL_* below are only used if the config.core.databaseEngine is 'postgresql'
POSTGRESQL_HOST="postgresql" # docker container name or external hostname/IP
POSTGRESQL_USER="postgres"
POSTGRESQL_PASSWORD="fake"

View file

@ -22,6 +22,8 @@ const config: PartialDeep<Config> = {
// },
// core: {
// enabledApps: ['news', 'notes', 'photos', 'expenses', 'contacts', 'calendar'], // dashboard and files cannot be disabled
// databaseEngine: 'postgresql', // The database engine to use. Currently only 'postgresql' and 'sqlite' are supported.
// sqliteFilePath: '', // The path to the SQLite database file. Only used if databaseEngine is 'sqlite'.
// },
// visuals: {
// title: 'My own cloud',

View file

@ -26,6 +26,8 @@ export class AppConfig {
},
core: {
enabledApps: ['news', 'notes', 'photos', 'expenses', 'contacts', 'calendar'],
databaseEngine: 'postgresql',
sqliteFilePath: '',
},
visuals: {
title: '',

View file

@ -1,6 +1,11 @@
import { Client } from 'postgres';
import { DatabaseSync } from 'node:sqlite';
import '@std/dotenv/load';
import { Config } from '/lib/types.ts';
import { AppConfig } from '/lib/config.ts';
const POSTGRESQL_HOST = Deno.env.get('POSTGRESQL_HOST') || '';
const POSTGRESQL_USER = Deno.env.get('POSTGRESQL_USER') || '';
const POSTGRESQL_PASSWORD = Deno.env.get('POSTGRESQL_PASSWORD') || '';
@ -20,8 +25,9 @@ const tls = POSTGRESQL_CAFILE
};
export default class Database {
protected db?: Client;
protected throwOnConnectionError?: boolean;
protected db?: Client | DatabaseSync;
private databaseEngine: Config['core']['databaseEngine'] = 'postgresql';
private throwOnConnectionError?: boolean;
constructor(
{ connectNow = false, throwOnConnectionError = false }: { connectNow?: boolean; throwOnConnectionError?: boolean } =
@ -30,11 +36,43 @@ export default class Database {
this.throwOnConnectionError = throwOnConnectionError;
if (connectNow) {
this.connectToPostgres();
this.connectToDatabase();
}
}
protected async connectToPostgres() {
private async connectToDatabase() {
if (this.db) {
return this.db;
}
const config = await AppConfig.getConfig();
this.databaseEngine = config.core.databaseEngine;
if (this.databaseEngine === 'postgresql') {
await this.connectToPostgres();
} else {
await this.connectToSQLite();
}
}
private async disconnectFromDatabase() {
if (!this.db) {
return;
}
const config = await AppConfig.getConfig();
this.databaseEngine = config.core.databaseEngine;
if (this.databaseEngine === 'postgresql') {
await this.disconnectFromPostgres();
} else {
this.disconnectFromSQLite();
}
}
private async connectToPostgres() {
if (this.db) {
return this.db;
}
@ -88,28 +126,54 @@ export default class Database {
}
}
protected async disconnectFromPostgres() {
private async connectToSQLite() {
if (this.db) {
return this.db;
}
const config = await AppConfig.getConfig();
const sqliteDatabase = new DatabaseSync(config.core.sqliteFilePath);
this.db = sqliteDatabase;
}
private disconnectFromSQLite() {
if (!this.db) {
return;
}
await this.db.end();
(this.db as DatabaseSync).close();
}
private async disconnectFromPostgres() {
if (!this.db) {
return;
}
await (this.db as Client).end();
this.db = undefined;
}
public close() {
this.disconnectFromPostgres();
this.disconnectFromDatabase();
}
public async query<T>(sql: string, args?: any[]) {
public async query<T>(sql: string, args?: any[]): Promise<T[]> {
if (!this.db) {
await this.connectToPostgres();
await this.connectToDatabase();
}
const result = await this.db!.queryObject<T>(sql, args);
if (this.databaseEngine === 'postgresql') {
const result = await (this.db as Client).queryObject<T>(sql, args);
return result.rows;
return result.rows;
}
const result = (this.db as DatabaseSync).prepare(sql).all(...(args || [])) as T[];
return result;
}
}

View file

@ -190,6 +190,10 @@ export interface Config {
core: {
/** dashboard and files cannot be disabled */
enabledApps: OptionalApp[];
/** The database engine to use. Currently only 'postgresql' and 'sqlite' are supported. */
databaseEngine: 'postgresql' | 'sqlite';
/** The path to the SQLite database file. Only used if databaseEngine is 'sqlite'. */
sqliteFilePath: string;
};
visuals: {
/** An override title of the application. Empty shows the default title. */

View file

@ -72,7 +72,7 @@ async function runMigrations(missingMigrations: string[]): Promise<void> {
await db.query(migrationSql);
await db.query(sql`INSERT INTO "public"."bewcloud_migrations" ("name", "executed_at") VALUES ($1, NOW())`, [
await db.query(sql`INSERT INTO "bewcloud_migrations" ("name", "executed_at") VALUES ($1, NOW())`, [
missingMigration,
]);