| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| import fs from 'fs'; |
| import path from 'path'; |
| import { createClient, type Client, type InStatement, type ResultSet, type Row } from '@libsql/client'; |
|
|
| export interface TursoDb { |
| client: Client; |
| |
| execute(sql: string, args?: any[]): Promise<ResultSet>; |
| |
| run(sql: string, args?: any[]): Promise<{ changes: number; lastInsertRowid: number }>; |
| |
| get<T = any>(sql: string, args?: any[]): Promise<T | undefined>; |
| |
| all<T = any>(sql: string, args?: any[]): Promise<T[]>; |
| |
| execMulti(sql: string): Promise<void>; |
| |
| batch(statements: InStatement[]): Promise<ResultSet[]>; |
| } |
|
|
| let db: TursoDb | null = null; |
|
|
| function createTursoDb(client: Client): TursoDb { |
| return { |
| client, |
|
|
| async execute(sql: string, args: any[] = []): Promise<ResultSet> { |
| return client.execute({ sql, args }); |
| }, |
|
|
| async run(sql: string, args: any[] = []) { |
| const result = await client.execute({ sql, args }); |
| return { |
| changes: result.rowsAffected, |
| lastInsertRowid: result.lastInsertRowid !== undefined ? Number(result.lastInsertRowid) : 0, |
| }; |
| }, |
|
|
| async get<T = any>(sql: string, args: any[] = []): Promise<T | undefined> { |
| const result = await client.execute({ sql, args }); |
| return (result.rows[0] as T) ?? undefined; |
| }, |
|
|
| async all<T = any>(sql: string, args: any[] = []): Promise<T[]> { |
| const result = await client.execute({ sql, args }); |
| return result.rows as T[]; |
| }, |
|
|
| async execMulti(sql: string): Promise<void> { |
| const statements = sql |
| .split(';') |
| .map(s => s.trim()) |
| .filter(s => s.length > 0); |
| for (const stmt of statements) { |
| await client.execute(stmt); |
| } |
| }, |
|
|
| async batch(statements: InStatement[]): Promise<ResultSet[]> { |
| if (statements.length === 0) return []; |
| return client.batch(statements, 'write'); |
| }, |
| }; |
| } |
|
|
| |
| |
| |
| |
| |
| export function initClient(url?: string): TursoDb { |
| const dbUrl = url ?? process.env.TURSO_DATABASE_URL ?? 'file:./data/freeapi.db'; |
|
|
| if (process.env.NODE_ENV === 'production' && !process.env.TURSO_DATABASE_URL && !url) { |
| console.warn('\n[WARNING] TURSO_DATABASE_URL is not set in production. Falling back to local SQLite database. Any changes will be lost when the container restarts!\n'); |
| } |
|
|
| if (dbUrl.startsWith('file:')) { |
| const filePath = dbUrl.slice(5); |
| const dir = path.dirname(filePath); |
| if (!fs.existsSync(dir)) { |
| fs.mkdirSync(dir, { recursive: true }); |
| } |
| } |
|
|
| const authToken = process.env.TURSO_AUTH_TOKEN; |
|
|
| const client = createClient({ |
| url: dbUrl, |
| authToken: authToken || undefined, |
| }); |
|
|
| db = createTursoDb(client); |
| return db; |
| } |
|
|
| |
| export function getDb(): TursoDb { |
| if (!db) { |
| throw new Error('Database not initialized. Call initClient() first.'); |
| } |
| return db; |
| } |
|
|
| |
| export type { Client, ResultSet, InStatement, Row }; |
|
|