2026-06-26 10:50:24 +02:00
/**
* SQLite Adapter – implements DatabaseInterface with better-sqlite3
*/
import Database from 'better-sqlite3' ;
import { readFileSync } from 'fs' ;
import { join , dirname } from 'path' ;
import { fileURLToPath } from 'url' ;
2026-06-26 14:55:38 +02:00
import { randomUUID } from 'crypto' ;
2026-06-26 10:50:24 +02:00
import type {
DatabaseInterface , DBProject , DBDrawing , DBLayer ,
2026-06-26 14:55:38 +02:00
DBElement , DBBlock , DBSetting , DBUser , DBSession ,
2026-06-28 13:52:54 +02:00
DBNotification , DBProjectShare ,
2026-07-01 21:02:31 +02:00
DBGlobalBlockFolder , DBGlobalBlock ,
2026-07-03 17:21:42 +02:00
DBProjectFolder ,
2026-06-26 10:50:24 +02:00
} from './DatabaseInterface.js' ;
const __dirname = dirname ( fileURLToPath ( import . meta . url ) ) ;
export class SqliteAdapter implements DatabaseInterface {
private db : Database.Database ;
constructor ( dbPath : string = ':memory:' ) {
this . db = new Database ( dbPath ) ;
this . db . pragma ( 'journal_mode = WAL' ) ;
this . db . pragma ( 'foreign_keys = ON' ) ;
}
async init ( ) : Promise < void > {
const schemaPath = join ( __dirname , 'schema.sql' ) ;
const schema = readFileSync ( schemaPath , 'utf-8' ) ;
this . db . exec ( schema ) ;
2026-07-03 17:21:42 +02:00
// Add folder_id column to projects table for existing databases
const columns = this . db . prepare ( "PRAGMA table_info('projects')" ) . all ( ) as { name : string } [ ] ;
if ( ! columns . some ( col = > col . name === 'folder_id' ) ) {
this . db . exec ( 'ALTER TABLE projects ADD COLUMN folder_id TEXT REFERENCES project_folders(id) ON DELETE SET NULL' ) ;
}
2026-06-26 10:50:24 +02:00
}
close ( ) : void {
this . db . close ( ) ;
}
// ─── Users ──────────────────────────────────────────
getUser ( id : string ) : DBUser | null {
return ( this . db . prepare ( 'SELECT * FROM users WHERE id = ?' ) . get ( id ) as DBUser ) ? ? null ;
}
getUserByEmail ( email : string ) : DBUser | null {
return ( this . db . prepare ( 'SELECT * FROM users WHERE email = ?' ) . get ( email ) as DBUser ) ? ? null ;
}
createUser ( data : Partial < DBUser > ) : DBUser {
const id = data . id ? ? ` user- ${ Date . now ( ) } ` ;
this . db . prepare (
'INSERT INTO users (id, email, password_hash, name, role) VALUES (?, ?, ?, ?, ?)' ,
) . run ( id , data . email ! , data . password_hash ! , data . name ! , data . role ? ? 'planer' ) ;
return this . getUser ( id ) ! ;
}
updateUser ( id : string , data : Partial < DBUser > ) : DBUser | null {
const sets : string [ ] = [ ] ;
const vals : any [ ] = [ ] ;
if ( data . email !== undefined ) { sets . push ( 'email = ?' ) ; vals . push ( data . email ) ; }
if ( data . password_hash !== undefined ) { sets . push ( 'password_hash = ?' ) ; vals . push ( data . password_hash ) ; }
if ( data . name !== undefined ) { sets . push ( 'name = ?' ) ; vals . push ( data . name ) ; }
if ( data . role !== undefined ) { sets . push ( 'role = ?' ) ; vals . push ( data . role ) ; }
sets . push ( "updated_at = datetime('now')" ) ;
if ( sets . length > 1 ) {
this . db . prepare ( ` UPDATE users SET ${ sets . join ( ', ' ) } WHERE id = ? ` ) . run ( . . . vals , id ) ;
}
return this . getUser ( id ) ;
}
deleteUser ( id : string ) : boolean {
return this . db . prepare ( 'DELETE FROM users WHERE id = ?' ) . run ( id ) . changes > 0 ;
}
listUsers ( ) : DBUser [ ] {
return this . db . prepare ( 'SELECT * FROM users ORDER BY created_at DESC' ) . all ( ) as DBUser [ ] ;
}
// ─── Projects ──────────────────────────────────────
2026-07-03 17:21:42 +02:00
listProjects ( ownerId? : string , folderId? : string | null ) : DBProject [ ] {
if ( ownerId && folderId !== undefined ) {
if ( folderId === null ) {
return this . db . prepare ( 'SELECT * FROM projects WHERE owner_id = ? AND folder_id IS NULL ORDER BY updated_at DESC' ) . all ( ownerId ) as DBProject [ ] ;
}
return this . db . prepare ( 'SELECT * FROM projects WHERE owner_id = ? AND folder_id = ? ORDER BY updated_at DESC' ) . all ( ownerId , folderId ) as DBProject [ ] ;
}
2026-06-30 12:16:49 +02:00
if ( ownerId ) {
return this . db . prepare ( 'SELECT * FROM projects WHERE owner_id = ? ORDER BY updated_at DESC' ) . all ( ownerId ) as DBProject [ ] ;
}
2026-07-03 17:21:42 +02:00
if ( folderId !== undefined ) {
if ( folderId === null ) {
return this . db . prepare ( 'SELECT * FROM projects WHERE folder_id IS NULL ORDER BY updated_at DESC' ) . all ( ) as DBProject [ ] ;
}
return this . db . prepare ( 'SELECT * FROM projects WHERE folder_id = ? ORDER BY updated_at DESC' ) . all ( folderId ) as DBProject [ ] ;
}
2026-06-26 10:50:24 +02:00
return this . db . prepare ( 'SELECT * FROM projects ORDER BY updated_at DESC' ) . all ( ) as DBProject [ ] ;
}
getProject ( id : string ) : DBProject | null {
return ( this . db . prepare ( 'SELECT * FROM projects WHERE id = ?' ) . get ( id ) as DBProject ) ? ? null ;
}
createProject ( data : Partial < DBProject > ) : DBProject {
const id = data . id ? ? ` proj- ${ Date . now ( ) } ` ;
this . db . prepare (
2026-07-03 17:21:42 +02:00
'INSERT INTO projects (id, name, description, owner_id, folder_id) VALUES (?, ?, ?, ?, ?)' ,
) . run ( id , data . name ? ? 'Unbenannt' , data . description ? ? null , data . owner_id ? ? 'user-default' , data . folder_id ? ? null ) ;
2026-06-26 10:50:24 +02:00
return this . getProject ( id ) ! ;
}
updateProject ( id : string , data : Partial < DBProject > ) : DBProject | null {
2026-06-30 14:37:59 +02:00
const sets : string [ ] = [ ] ;
2026-07-03 17:21:42 +02:00
const vals : unknown [ ] = [ ] ;
2026-06-26 10:50:24 +02:00
if ( data . name !== undefined ) { sets . push ( 'name = ?' ) ; vals . push ( data . name ) ; }
if ( data . description !== undefined ) { sets . push ( 'description = ?' ) ; vals . push ( data . description ) ; }
2026-07-03 17:21:42 +02:00
if ( data . folder_id !== undefined ) { sets . push ( 'folder_id = ?' ) ; vals . push ( data . folder_id ) ; }
2026-06-26 10:50:24 +02:00
sets . push ( "updated_at = datetime('now')" ) ;
if ( sets . length > 1 ) {
this . db . prepare ( ` UPDATE projects SET ${ sets . join ( ', ' ) } WHERE id = ? ` ) . run ( . . . vals , id ) ;
}
return this . getProject ( id ) ;
}
deleteProject ( id : string ) : boolean {
return this . db . prepare ( 'DELETE FROM projects WHERE id = ?' ) . run ( id ) . changes > 0 ;
}
2026-07-03 17:21:42 +02:00
moveProjectToFolder ( projectId : string , folderId : string | null ) : DBProject | null {
this . db . prepare ( 'UPDATE projects SET folder_id = ?, updated_at = datetime(\'now\') WHERE id = ?' ) . run ( folderId , projectId ) ;
return this . getProject ( projectId ) ;
}
// ─── Project Folders ─────────────────────────────────
listProjectFolders ( ownerId : string ) : DBProjectFolder [ ] {
return this . db . prepare ( 'SELECT * FROM project_folders WHERE owner_id = ? ORDER BY name' ) . all ( ownerId ) as DBProjectFolder [ ] ;
}
getProjectFolder ( id : string ) : DBProjectFolder | null {
return ( this . db . prepare ( 'SELECT * FROM project_folders WHERE id = ?' ) . get ( id ) as DBProjectFolder ) ? ? null ;
}
createProjectFolder ( data : Partial < DBProjectFolder > ) : DBProjectFolder {
const id = data . id ? ? ` pfolder- ${ Date . now ( ) } ` ;
this . db . prepare (
'INSERT INTO project_folders (id, name, parent_id, owner_id) VALUES (?, ?, ?, ?)' ,
) . run ( id , data . name ? ? 'Neuer Ordner' , data . parent_id ? ? null , data . owner_id ? ? 'user-default' ) ;
return this . getProjectFolder ( id ) ! ;
}
updateProjectFolder ( id : string , data : Partial < DBProjectFolder > ) : DBProjectFolder | null {
const sets : string [ ] = [ ] ;
const vals : unknown [ ] = [ ] ;
if ( data . name !== undefined ) { sets . push ( 'name = ?' ) ; vals . push ( data . name ) ; }
if ( data . parent_id !== undefined ) { sets . push ( 'parent_id = ?' ) ; vals . push ( data . parent_id ) ; }
if ( sets . length > 0 ) {
this . db . prepare ( ` UPDATE project_folders SET ${ sets . join ( ', ' ) } WHERE id = ? ` ) . run ( . . . vals , id ) ;
}
return this . getProjectFolder ( id ) ;
}
deleteProjectFolder ( id : string ) : boolean {
// Move projects inside this folder to root (folder_id = NULL) before deleting
this . db . prepare ( 'UPDATE projects SET folder_id = NULL WHERE folder_id = ?' ) . run ( id ) ;
return this . db . prepare ( 'DELETE FROM project_folders WHERE id = ?' ) . run ( id ) . changes > 0 ;
}
2026-06-26 10:50:24 +02:00
// ─── Drawings ──────────────────────────────────────
listDrawings ( projectId : string ) : DBDrawing [ ] {
return this . db . prepare ( 'SELECT * FROM drawings WHERE project_id = ? ORDER BY updated_at DESC' ) . all ( projectId ) as DBDrawing [ ] ;
}
getDrawing ( id : string ) : DBDrawing | null {
return ( this . db . prepare ( 'SELECT * FROM drawings WHERE id = ?' ) . get ( id ) as DBDrawing ) ? ? null ;
}
createDrawing ( data : Partial < DBDrawing > ) : DBDrawing {
const id = data . id ? ? ` draw- ${ Date . now ( ) } ` ;
this . db . prepare (
'INSERT INTO drawings (id, project_id, name, data_json) VALUES (?, ?, ?, ?)' ,
) . run ( id , data . project_id ! , data . name ? ? 'Unbenannt' , data . data_json ? ? '{}' ) ;
return this . getDrawing ( id ) ! ;
}
updateDrawing ( id : string , data : Partial < DBDrawing > ) : DBDrawing | null {
const sets : string [ ] = [ ] ;
const vals : any [ ] = [ ] ;
if ( data . name !== undefined ) { sets . push ( 'name = ?' ) ; vals . push ( data . name ) ; }
if ( data . data_json !== undefined ) { sets . push ( 'data_json = ?' ) ; vals . push ( data . data_json ) ; }
sets . push ( "updated_at = datetime('now')" ) ;
if ( sets . length > 1 ) {
this . db . prepare ( ` UPDATE drawings SET ${ sets . join ( ', ' ) } WHERE id = ? ` ) . run ( . . . vals , id ) ;
}
return this . getDrawing ( id ) ;
}
deleteDrawing ( id : string ) : boolean {
return this . db . prepare ( 'DELETE FROM drawings WHERE id = ?' ) . run ( id ) . changes > 0 ;
}
// ─── Layers ────────────────────────────────────────
listLayers ( drawingId : string ) : DBLayer [ ] {
return this . db . prepare ( 'SELECT * FROM layers WHERE drawing_id = ? ORDER BY sort_order' ) . all ( drawingId ) as DBLayer [ ] ;
}
createLayer ( data : Partial < DBLayer > ) : DBLayer {
const id = data . id ? ? ` layer- ${ Date . now ( ) } ` ;
this . db . prepare (
'INSERT INTO layers (id, drawing_id, name, visible, locked, color, line_type, transparency, sort_order, parent_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)' ,
) . run ( id , data . drawing_id ! , data . name ? ? 'Layer' , data . visible ? ? 1 , data . locked ? ? 0 ,
data . color ? ? '#ffffff' , data . line_type ? ? 'solid' , data . transparency ? ? 0 ,
data . sort_order ? ? 0 , data . parent_id ? ? null ) ;
return this . db . prepare ( 'SELECT * FROM layers WHERE id = ?' ) . get ( id ) as DBLayer ;
}
updateLayer ( id : string , data : Partial < DBLayer > ) : DBLayer | null {
const sets : string [ ] = [ ] ;
const vals : any [ ] = [ ] ;
for ( const [ k , v ] of Object . entries ( data ) ) {
if ( [ 'name' , 'visible' , 'locked' , 'color' , 'line_type' , 'transparency' , 'sort_order' , 'parent_id' ] . includes ( k ) ) {
sets . push ( ` ${ k } = ? ` ) ; vals . push ( v ) ;
}
}
if ( sets . length > 0 ) {
this . db . prepare ( ` UPDATE layers SET ${ sets . join ( ', ' ) } WHERE id = ? ` ) . run ( . . . vals , id ) ;
}
return this . db . prepare ( 'SELECT * FROM layers WHERE id = ?' ) . get ( id ) as DBLayer ? ? null ;
}
deleteLayer ( id : string ) : boolean {
return this . db . prepare ( 'DELETE FROM layers WHERE id = ?' ) . run ( id ) . changes > 0 ;
}
// ─── Elements ──────────────────────────────────────
listElements ( drawingId : string ) : DBElement [ ] {
return this . db . prepare ( 'SELECT * FROM elements WHERE drawing_id = ?' ) . all ( drawingId ) as DBElement [ ] ;
}
createElement ( data : Partial < DBElement > ) : DBElement {
const id = data . id ? ? ` elem- ${ Date . now ( ) } ` ;
this . db . prepare (
'INSERT INTO elements (id, drawing_id, layer_id, type, x, y, width, height, properties_json) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)' ,
) . run ( id , data . drawing_id ! , data . layer_id ! , data . type ! , data . x ? ? 0 , data . y ? ? 0 ,
data . width ? ? 0 , data . height ? ? 0 , data . properties_json ? ? '{}' ) ;
return this . db . prepare ( 'SELECT * FROM elements WHERE id = ?' ) . get ( id ) as DBElement ;
}
updateElement ( id : string , data : Partial < DBElement > ) : DBElement | null {
const sets : string [ ] = [ ] ;
const vals : any [ ] = [ ] ;
for ( const [ k , v ] of Object . entries ( data ) ) {
if ( [ 'layer_id' , 'type' , 'x' , 'y' , 'width' , 'height' , 'properties_json' ] . includes ( k ) ) {
sets . push ( ` ${ k } = ? ` ) ; vals . push ( v ) ;
}
}
if ( sets . length > 0 ) {
this . db . prepare ( ` UPDATE elements SET ${ sets . join ( ', ' ) } WHERE id = ? ` ) . run ( . . . vals , id ) ;
}
return this . db . prepare ( 'SELECT * FROM elements WHERE id = ?' ) . get ( id ) as DBElement ? ? null ;
}
deleteElement ( id : string ) : boolean {
return this . db . prepare ( 'DELETE FROM elements WHERE id = ?' ) . run ( id ) . changes > 0 ;
}
// ─── Blocks ────────────────────────────────────────
listBlocks ( drawingId : string ) : DBBlock [ ] {
return this . db . prepare ( 'SELECT * FROM blocks WHERE drawing_id = ?' ) . all ( drawingId ) as DBBlock [ ] ;
}
createBlock ( data : Partial < DBBlock > ) : DBBlock {
const id = data . id ? ? ` block- ${ Date . now ( ) } ` ;
this . db . prepare (
'INSERT INTO blocks (id, drawing_id, name, description, category, elements_json, thumbnail) VALUES (?, ?, ?, ?, ?, ?, ?)' ,
) . run ( id , data . drawing_id ! , data . name ? ? 'Block' , data . description ? ? null ,
data . category ? ? 'Allgemein' , data . elements_json ? ? '[]' , data . thumbnail ? ? null ) ;
return this . db . prepare ( 'SELECT * FROM blocks WHERE id = ?' ) . get ( id ) as DBBlock ;
}
updateBlock ( id : string , data : Partial < DBBlock > ) : DBBlock | null {
const sets : string [ ] = [ ] ;
const vals : any [ ] = [ ] ;
for ( const [ k , v ] of Object . entries ( data ) ) {
if ( [ 'name' , 'description' , 'category' , 'elements_json' , 'thumbnail' ] . includes ( k ) ) {
sets . push ( ` ${ k } = ? ` ) ; vals . push ( v ) ;
}
}
if ( sets . length > 0 ) {
this . db . prepare ( ` UPDATE blocks SET ${ sets . join ( ', ' ) } WHERE id = ? ` ) . run ( . . . vals , id ) ;
}
return this . db . prepare ( 'SELECT * FROM blocks WHERE id = ?' ) . get ( id ) as DBBlock ? ? null ;
}
deleteBlock ( id : string ) : boolean {
return this . db . prepare ( 'DELETE FROM blocks WHERE id = ?' ) . run ( id ) . changes > 0 ;
}
// ─── Settings ──────────────────────────────────────
getSetting ( key : string ) : DBSetting | null {
return ( this . db . prepare ( 'SELECT * FROM settings WHERE key = ?' ) . get ( key ) as DBSetting ) ? ? null ;
}
setSetting ( key : string , value : string ) : void {
this . db . prepare (
"INSERT INTO settings (key, value, updated_at) VALUES (?, ?, datetime('now')) ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = datetime('now')" ,
) . run ( key , value ) ;
}
2026-06-26 14:55:38 +02:00
// ─── Sessions ───────────────────────────────────────
createSession ( data : Partial < DBSession > ) : DBSession {
const token = data . token ? ? randomUUID ( ) ;
this . db . prepare (
'INSERT INTO sessions (token, user_id, expires_at) VALUES (?, ?, ?)' ,
) . run ( token , data . user_id ! , data . expires_at ! ) ;
return this . getSession ( token ) ! ;
}
getSession ( token : string ) : DBSession | null {
return ( this . db . prepare ( 'SELECT * FROM sessions WHERE token = ?' ) . get ( token ) as DBSession ) ? ? null ;
}
deleteSession ( token : string ) : boolean {
return this . db . prepare ( 'DELETE FROM sessions WHERE token = ?' ) . run ( token ) . changes > 0 ;
}
deleteExpiredSessions ( ) : void {
2026-06-30 13:15:54 +02:00
// Use consistent epoch-millisecond integer comparison.
// <= ensures sessions expiring at exactly the current time are also deleted.
const now = Math . floor ( Date . now ( ) ) ;
this . db . prepare ( 'DELETE FROM sessions WHERE expires_at <= ?' ) . run ( now ) ;
2026-06-26 14:55:38 +02:00
}
2026-06-28 13:52:54 +02:00
// ─── Notifications ──────────────────────────────────
listNotifications ( userId : string ) : DBNotification [ ] {
return this . db . prepare ( 'SELECT * FROM notifications WHERE user_id = ? ORDER BY created_at DESC' ) . all ( userId ) as DBNotification [ ] ;
}
2026-06-28 14:16:50 +02:00
getNotification ( id : string ) : DBNotification | null {
return this . db . prepare ( 'SELECT * FROM notifications WHERE id = ?' ) . get ( id ) as DBNotification | null ;
}
2026-06-28 13:52:54 +02:00
createNotification ( data : Partial < DBNotification > ) : DBNotification {
const id = data . id ? ? ` notif- ${ Date . now ( ) } ` ;
this . db . prepare (
'INSERT INTO notifications (id, user_id, type, title, message, read) VALUES (?, ?, ?, ?, ?, ?)' ,
) . run ( id , data . user_id ! , data . type ? ? 'info' , data . title ! , data . message ! , data . read ? ? 0 ) ;
return this . db . prepare ( 'SELECT * FROM notifications WHERE id = ?' ) . get ( id ) as DBNotification ;
}
markNotificationRead ( id : string ) : boolean {
return this . db . prepare ( 'UPDATE notifications SET read = 1 WHERE id = ?' ) . run ( id ) . changes > 0 ;
}
deleteNotification ( id : string ) : boolean {
return this . db . prepare ( 'DELETE FROM notifications WHERE id = ?' ) . run ( id ) . changes > 0 ;
}
// ─── Project Shares ─────────────────────────────────
listProjectShares ( projectId : string ) : DBProjectShare [ ] {
return this . db . prepare ( 'SELECT * FROM project_shares WHERE project_id = ? ORDER BY created_at DESC' ) . all ( projectId ) as DBProjectShare [ ] ;
}
2026-06-28 14:16:50 +02:00
getProjectShare ( id : string ) : DBProjectShare | null {
return this . db . prepare ( 'SELECT * FROM project_shares WHERE id = ?' ) . get ( id ) as DBProjectShare | null ;
}
2026-06-28 13:52:54 +02:00
createProjectShare ( data : Partial < DBProjectShare > ) : DBProjectShare {
const id = data . id ? ? ` share- ${ Date . now ( ) } ` ;
const shareToken = data . share_token ? ? randomUUID ( ) ;
this . db . prepare (
'INSERT INTO project_shares (id, project_id, shared_with_email, shared_by, permission, share_token) VALUES (?, ?, ?, ?, ?, ?)' ,
) . run ( id , data . project_id ! , data . shared_with_email ! , data . shared_by ! , data . permission ? ? 'view' , shareToken ) ;
return this . db . prepare ( 'SELECT * FROM project_shares WHERE id = ?' ) . get ( id ) as DBProjectShare ;
}
deleteProjectShare ( id : string ) : boolean {
return this . db . prepare ( 'DELETE FROM project_shares WHERE id = ?' ) . run ( id ) . changes > 0 ;
}
2026-07-01 21:02:31 +02:00
// ─── Global Block Folders ──────────────────────────────
listGlobalFolders ( parentId? : string | null ) : DBGlobalBlockFolder [ ] {
if ( parentId === undefined ) {
return this . db . prepare ( 'SELECT * FROM global_block_folders ORDER BY name' ) . all ( ) as DBGlobalBlockFolder [ ] ;
}
if ( parentId === null ) {
return this . db . prepare ( 'SELECT * FROM global_block_folders WHERE parent_id IS NULL ORDER BY name' ) . all ( ) as DBGlobalBlockFolder [ ] ;
}
return this . db . prepare ( 'SELECT * FROM global_block_folders WHERE parent_id = ? ORDER BY name' ) . all ( parentId ) as DBGlobalBlockFolder [ ] ;
}
getGlobalFolder ( id : string ) : DBGlobalBlockFolder | null {
return ( this . db . prepare ( 'SELECT * FROM global_block_folders WHERE id = ?' ) . get ( id ) as DBGlobalBlockFolder ) ? ? null ;
}
createGlobalFolder ( data : Partial < DBGlobalBlockFolder > ) : DBGlobalBlockFolder {
const id = data . id ? ? ` gfolder- ${ Date . now ( ) } ` ;
this . db . prepare (
'INSERT INTO global_block_folders (id, name, parent_id) VALUES (?, ?, ?)' ,
) . run ( id , data . name ? ? 'Neuer Ordner' , data . parent_id ? ? null ) ;
return this . getGlobalFolder ( id ) ! ;
}
updateGlobalFolder ( id : string , data : Partial < DBGlobalBlockFolder > ) : DBGlobalBlockFolder | null {
const sets : string [ ] = [ ] ;
const vals : unknown [ ] = [ ] ;
if ( data . name !== undefined ) { sets . push ( 'name = ?' ) ; vals . push ( data . name ) ; }
if ( data . parent_id !== undefined ) { sets . push ( 'parent_id = ?' ) ; vals . push ( data . parent_id ) ; }
if ( sets . length > 0 ) {
this . db . prepare ( ` UPDATE global_block_folders SET ${ sets . join ( ', ' ) } WHERE id = ? ` ) . run ( . . . vals , id ) ;
}
return this . getGlobalFolder ( id ) ;
}
deleteGlobalFolder ( id : string ) : boolean {
return this . db . prepare ( 'DELETE FROM global_block_folders WHERE id = ?' ) . run ( id ) . changes > 0 ;
}
// ─── Global Blocks ─────────────────────────────────────
listGlobalBlocks ( folderId? : string | null ) : DBGlobalBlock [ ] {
if ( folderId === undefined ) {
return this . db . prepare ( 'SELECT * FROM global_blocks ORDER BY updated_at DESC' ) . all ( ) as DBGlobalBlock [ ] ;
}
if ( folderId === null ) {
return this . db . prepare ( 'SELECT * FROM global_blocks WHERE folder_id IS NULL ORDER BY updated_at DESC' ) . all ( ) as DBGlobalBlock [ ] ;
}
return this . db . prepare ( 'SELECT * FROM global_blocks WHERE folder_id = ? ORDER BY updated_at DESC' ) . all ( folderId ) as DBGlobalBlock [ ] ;
}
getGlobalBlock ( id : string ) : DBGlobalBlock | null {
return ( this . db . prepare ( 'SELECT * FROM global_blocks WHERE id = ?' ) . get ( id ) as DBGlobalBlock ) ? ? null ;
}
createGlobalBlock ( data : Partial < DBGlobalBlock > ) : DBGlobalBlock {
const id = data . id ? ? ` gblock- ${ Date . now ( ) } ` ;
this . db . prepare (
'INSERT INTO global_blocks (id, folder_id, name, block_data, svg_data) VALUES (?, ?, ?, ?, ?)' ,
) . run ( id , data . folder_id ? ? null , data . name ? ? 'Global Block' , data . block_data ? ? '{}' , data . svg_data ? ? null ) ;
return this . getGlobalBlock ( id ) ! ;
}
updateGlobalBlock ( id : string , data : Partial < DBGlobalBlock > ) : DBGlobalBlock | null {
const sets : string [ ] = [ ] ;
const vals : unknown [ ] = [ ] ;
if ( data . name !== undefined ) { sets . push ( 'name = ?' ) ; vals . push ( data . name ) ; }
if ( data . folder_id !== undefined ) { sets . push ( 'folder_id = ?' ) ; vals . push ( data . folder_id ) ; }
if ( data . block_data !== undefined ) { sets . push ( 'block_data = ?' ) ; vals . push ( data . block_data ) ; }
if ( data . svg_data !== undefined ) { sets . push ( 'svg_data = ?' ) ; vals . push ( data . svg_data ) ; }
sets . push ( "updated_at = datetime('now')" ) ;
this . db . prepare ( ` UPDATE global_blocks SET ${ sets . join ( ', ' ) } WHERE id = ? ` ) . run ( . . . vals , id ) ;
return this . getGlobalBlock ( id ) ;
}
deleteGlobalBlock ( id : string ) : boolean {
return this . db . prepare ( 'DELETE FROM global_blocks WHERE id = ?' ) . run ( id ) . changes > 0 ;
}
2026-06-26 10:50:24 +02:00
}