1
1
import fs from 'fs' ;
2
2
import path from 'path' ;
3
-
4
3
import Database from 'better-sqlite3' ;
5
4
import { app , ipcMain } from 'electron' ;
6
-
7
5
import logger from './log' ;
8
6
9
7
const isDevelopment = process . env . NODE_ENV === 'development' ;
10
- // const sqlite = isDevelopment ? sqlite3.verbose() : sqlite3;
11
8
const registry = new Map ( ) ;
12
9
13
10
/**
14
- * The app.getPath() method is only available once the app is 'ready'.
15
- * Use app.on('ready' () => { ... }); to detect the 'ready' event.
16
- * See Electron's Event: 'ready' event for more information.
17
- *
18
- * https://github.com/electron/electron/blob/main/docs/api/app.md#appgetpathname
11
+ * Open or retrieve an existing SQLite database.
19
12
*/
20
13
const openDatabase = ( name : string ) => {
21
- /**
22
- * Check registry
23
- */
24
- if ( registry . has ( name ) ) {
25
- return { name } ;
26
- }
14
+ // Check registry first
15
+ if ( registry . has ( name ) ) {
16
+ return { name } ;
17
+ }
27
18
28
- try {
29
- /**
30
- * Determine database folder
31
- *
32
- * @NOTE - there seems to be a weird conflict with electron-store if I use the name 'databases'
33
- * in the app's userData folder - it was deleting the directory every time the app started ??!!
34
- */
35
- const dbFolder = isDevelopment
36
- ? path . resolve ( 'databases' )
37
- : path . resolve ( app . getPath ( 'userData' ) , 'wcpos_dbs' ) ;
19
+ try {
20
+ const dbFolder = isDevelopment
21
+ ? path . resolve ( 'databases' )
22
+ : path . resolve ( app . getPath ( 'userData' ) , 'wcpos_dbs' ) ;
38
23
39
- /**
40
- * Create folder if it doesn't exist
41
- */
42
- if ( ! fs . existsSync ( dbFolder ) ) {
43
- try {
44
- fs . mkdirSync ( dbFolder , { recursive : true } ) ;
45
- logger . info ( `Created database folder: ${ dbFolder } ` ) ;
46
- } catch ( err ) {
47
- logger . error ( `Failed to create database folder: ${ dbFolder } ` , err ) ;
48
- }
49
- }
24
+ // Create folder if it doesn't exist
25
+ if ( ! fs . existsSync ( dbFolder ) ) {
26
+ try {
27
+ fs . mkdirSync ( dbFolder , { recursive : true } ) ;
28
+ logger . info ( `Created database folder: ${ dbFolder } ` ) ;
29
+ } catch ( err ) {
30
+ logger . error ( `Failed to create database folder: ${ dbFolder } ` , err ) ;
31
+ }
32
+ }
50
33
51
- /**
52
- * Open database
53
- */
54
- logger . info ( 'Opening SQLite database' , name ) ;
55
- const db = new Database ( path . resolve ( dbFolder , `${ name } .sqlite3` ) , { verbose : logger . debug } ) ;
56
- logger . info ( 'Opened SQLite database' , db ) ;
34
+ logger . info ( 'Opening SQLite database' , name ) ;
35
+ const db = new Database ( path . resolve ( dbFolder , `${ name } .sqlite3` ) , { verbose : logger . debug } ) ;
36
+ logger . info ( 'Opened SQLite database' , db ) ;
57
37
58
- registry . set ( name , db ) ;
59
- return { name } ;
60
- } catch ( error ) {
61
- logger . error ( 'Failed to open database' , error ) ;
62
- throw error ; // Re- throw the error after logging it to allow further handling upstream
63
- }
38
+ registry . set ( name , db ) ;
39
+ return { name } ;
40
+ } catch ( error ) {
41
+ logger . error ( 'Failed to open database' , error ) ;
42
+ throw error ;
43
+ }
64
44
} ;
65
45
66
46
export const closeAll = ( ) => {
67
- registry . forEach ( ( db , name ) => {
68
- db . close ( ) ;
69
- registry . delete ( name ) ;
70
- logger . info ( `Closed and removed ${ name } from registry.` ) ;
71
- } ) ;
47
+ registry . forEach ( ( db , name ) => {
48
+ db . close ( ) ;
49
+ registry . delete ( name ) ;
50
+ logger . info ( `Closed and removed ${ name } from registry.` ) ;
51
+ } ) ;
72
52
} ;
73
53
74
54
/**
75
- * RxDB sends query params as booleans, but SQLite doesn't support booleans.
55
+ * Convert boolean values in parameters to numbers ( SQLite doesn't support booleans) .
76
56
*/
77
57
function convertBooleansToNumbers ( params : ( string | number | boolean ) [ ] ) : ( string | number ) [ ] {
78
- return params . map ( ( param ) => ( typeof param === 'boolean' ? ( param ? 1 : 0 ) : param ) ) ;
58
+ return params . map ( param => ( typeof param === 'boolean' ? ( param ? 1 : 0 ) : param ) ) ;
79
59
}
80
60
81
61
/**
62
+ * Execute an SQL statement.
82
63
*
64
+ * - SELECT queries: use .all()
65
+ * - PRAGMA assignments (e.g. PRAGMA synchronous = normal): use .run()
66
+ * - Other PRAGMA queries: use .all()
67
+ * - All others: use .run()
83
68
*/
84
69
function executeSql ( db , sql , params ) {
85
- if ( / ^ \s * ( S E L E C T | P R A G M A ) / i. test ( sql ) ) {
86
- return db . prepare ( sql ) . all ( params ) ; // For SELECT or PRAGMA queries
87
- } else {
88
- return db . prepare ( sql ) . run ( params ) ; // For INSERT, UPDATE, DELETE
89
- }
70
+ if ( / ^ \s * S E L E C T / i. test ( sql ) ) {
71
+ return db . prepare ( sql ) . all ( params ) ;
72
+ }
73
+ // PRAGMA assignments like "PRAGMA synchronous = normal" do not return data.
74
+ if ( / ^ \s * P R A G M A \s + \w + \s * = \s * / i. test ( sql ) ) {
75
+ return db . prepare ( sql ) . run ( params ) ;
76
+ }
77
+ if ( / ^ \s * P R A G M A / i. test ( sql ) ) {
78
+ return db . prepare ( sql ) . all ( params ) ;
79
+ }
80
+ return db . prepare ( sql ) . run ( params ) ;
90
81
}
91
82
92
83
/**
93
- *
84
+ * Handle SQLite IPC requests.
94
85
*/
95
86
ipcMain . handle ( 'sqlite' , ( event , obj ) => {
96
- logger . silly ( 'SQL request' , JSON . stringify ( obj , null , 2 ) ) ;
97
- try {
98
- let db ;
99
- switch ( obj . type ) {
100
- case 'open' :
101
- return openDatabase ( obj . name ) ;
102
- case 'close' :
103
- db = registry . get ( obj . name ) ;
104
- if ( ! db ) throw new Error ( `Database connection "${ obj . name } " not found` ) ;
105
- db . close ( ) ;
106
- registry . delete ( obj . name ) ;
107
- logger . info ( `Closed and removed ${ obj . name } from registry.` ) ;
108
- return ;
109
- case 'quit' :
110
- closeAll ( ) ;
111
- return ;
112
- case 'all' :
113
- case 'run' : // These cases are now dynamically handled together
114
- db = registry . get ( obj . name ) ;
115
- if ( ! db ) throw new Error ( `Database connection "${ obj . name } " not found` ) ;
116
- const params = convertBooleansToNumbers ( obj . sql . params ) ;
117
- return executeSql ( db , obj . sql . query , params ) ;
118
- default :
119
- throw new Error ( 'Unknown type' ) ;
120
- }
121
- } catch ( err ) {
122
- logger . error ( 'SQLite error' , err , obj ) ;
123
- throw err ;
124
- }
125
- } ) ;
87
+ logger . silly ( 'SQL request' , JSON . stringify ( obj , null , 2 ) ) ;
88
+ try {
89
+ let db ;
90
+ switch ( obj . type ) {
91
+ case 'open' :
92
+ return openDatabase ( obj . name ) ;
93
+ case 'close' :
94
+ db = registry . get ( obj . name ) ;
95
+ if ( ! db ) throw new Error ( `Database connection "${ obj . name } " not found` ) ;
96
+ db . close ( ) ;
97
+ registry . delete ( obj . name ) ;
98
+ logger . info ( `Closed and removed ${ obj . name } from registry.` ) ;
99
+ return ;
100
+ case 'quit' :
101
+ closeAll ( ) ;
102
+ return ;
103
+ case 'all' :
104
+ case 'run' : {
105
+ db = registry . get ( obj . name ) ;
106
+ if ( ! db ) throw new Error ( `Database connection "${ obj . name } " not found` ) ;
107
+ const convertedParams = convertBooleansToNumbers ( obj . sql . params ) ;
108
+ return executeSql ( db , obj . sql . query , convertedParams ) ;
109
+ }
110
+ default :
111
+ throw new Error ( 'Unknown type' ) ;
112
+ }
113
+ } catch ( err ) {
114
+ logger . error ( 'SQLite error' , err , obj ) ;
115
+ throw err ;
116
+ }
117
+ } ) ;
0 commit comments