@dqcai/sqlite 2.1.0 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +297 -2450
- package/lib/index.d.ts +0 -40
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +7 -39
- package/lib/index.mjs.map +1 -1
- package/lib/index.umd.js +1 -1
- package/lib/index.umd.js.map +1 -1
- package/package.json +1 -1
- package/lib/utils/csv-import.d.ts +0 -102
- package/lib/utils/csv-import.d.ts.map +0 -1
- package/lib/utils/index.d.ts +0 -3
- package/lib/utils/index.d.ts.map +0 -1
- package/lib/utils/migration-manager.d.ts +0 -184
- package/lib/utils/migration-manager.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -1,2578 +1,425 @@
|
|
|
1
|
-
# @dqcai/sqlite -
|
|
1
|
+
# @dqcai/sqlite - Universal SQLite Library for Modern JavaScript
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|

|
|
5
5
|

|
|
6
|
+

|
|
7
|
+

|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
**One library, all platforms!** The most comprehensive SQLite solution for **Browser**, **Node.js**, **Deno**, **Bun**, and **React Native** applications.
|
|
8
10
|
|
|
9
|
-
##
|
|
11
|
+
## 🚀 Why Choose @dqcai/sqlite?
|
|
10
12
|
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
- **Service Management**: Lifecycle management for service instances with ServiceManager.
|
|
20
|
-
- **Adapters**: Auto-detect environment, support custom adapter registration.
|
|
21
|
-
- **Type-Safe**: Full TypeScript types for schema, queries, and operations.
|
|
22
|
-
- **Utilities**: CSVImporter, MigrationManager, BaseService for service layer.
|
|
23
|
-
- **DatabaseManager**: Manage connections, schemas, and user roles.
|
|
24
|
-
- **BaseService**: Base class for CRUD operations with built-in optimization.
|
|
13
|
+
- **🌍 Universal**: Works everywhere - Browser, Node.js, Deno, Bun, React Native
|
|
14
|
+
- **🛡️ Type-Safe**: Full TypeScript support with complete type definitions
|
|
15
|
+
- **⚡ High Performance**: Built-in optimization, connection pooling, and batch operations
|
|
16
|
+
- **🏗️ Enterprise-Ready**: Service lifecycle management with ServiceManager
|
|
17
|
+
- **📊 Schema Management**: JSON-based schema definitions with migrations
|
|
18
|
+
- **🔄 Transaction Support**: Single and cross-schema transaction management
|
|
19
|
+
- **📈 Monitoring**: Real-time health monitoring and auto-recovery
|
|
20
|
+
- **🎯 DAO Pattern**: Clean separation of data access logic
|
|
25
21
|
|
|
26
|
-
## Installation
|
|
22
|
+
## 📦 Installation
|
|
27
23
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
# or
|
|
33
|
-
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
For React Native, ensure you install the necessary dependencies for the adapter (if using specific adapters like react-native-sqlite-storage).
|
|
37
|
-
|
|
38
|
-
## Quick Start
|
|
39
|
-
|
|
40
|
-
```bash
|
|
41
|
-
npm install @dqcai/sqlite
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
## 1. Database Schema Configuration
|
|
45
|
-
|
|
46
|
-
First, define the schema for your database:
|
|
47
|
-
|
|
48
|
-
```typescript
|
|
49
|
-
import { DatabaseSchema } from '@dqcai/sqlite';
|
|
50
|
-
|
|
51
|
-
// Schema for users database
|
|
52
|
-
const userSchema: DatabaseSchema = {
|
|
53
|
-
version: "1.0.0",
|
|
54
|
-
database_name: "users",
|
|
55
|
-
description: "User management database",
|
|
56
|
-
schemas: {
|
|
57
|
-
users: {
|
|
58
|
-
description: "User table",
|
|
59
|
-
cols: [
|
|
60
|
-
{
|
|
61
|
-
name: "id",
|
|
62
|
-
type: "integer",
|
|
63
|
-
primary_key: true,
|
|
64
|
-
auto_increment: true,
|
|
65
|
-
nullable: false
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
name: "username",
|
|
69
|
-
type: "varchar",
|
|
70
|
-
nullable: false,
|
|
71
|
-
unique: true,
|
|
72
|
-
length: 50
|
|
73
|
-
},
|
|
74
|
-
{
|
|
75
|
-
name: "email",
|
|
76
|
-
type: "varchar",
|
|
77
|
-
nullable: false,
|
|
78
|
-
unique: true,
|
|
79
|
-
length: 100
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
name: "password",
|
|
83
|
-
type: "varchar",
|
|
84
|
-
nullable: false,
|
|
85
|
-
length: 255
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
name: "created_at",
|
|
89
|
-
type: "datetime",
|
|
90
|
-
nullable: false,
|
|
91
|
-
default: "CURRENT_TIMESTAMP"
|
|
92
|
-
},
|
|
93
|
-
{
|
|
94
|
-
name: "updated_at",
|
|
95
|
-
type: "datetime",
|
|
96
|
-
nullable: true
|
|
97
|
-
}
|
|
98
|
-
],
|
|
99
|
-
indexes: [
|
|
100
|
-
{
|
|
101
|
-
name: "idx_username",
|
|
102
|
-
columns: ["username"],
|
|
103
|
-
unique: true
|
|
104
|
-
},
|
|
105
|
-
{
|
|
106
|
-
name: "idx_email",
|
|
107
|
-
columns: ["email"],
|
|
108
|
-
unique: true
|
|
109
|
-
}
|
|
110
|
-
]
|
|
111
|
-
},
|
|
112
|
-
profiles: {
|
|
113
|
-
description: "User profiles table",
|
|
114
|
-
cols: [
|
|
115
|
-
{
|
|
116
|
-
name: "id",
|
|
117
|
-
type: "integer",
|
|
118
|
-
primary_key: true,
|
|
119
|
-
auto_increment: true
|
|
120
|
-
},
|
|
121
|
-
{
|
|
122
|
-
name: "user_id",
|
|
123
|
-
type: "integer",
|
|
124
|
-
nullable: false
|
|
125
|
-
},
|
|
126
|
-
{
|
|
127
|
-
name: "first_name",
|
|
128
|
-
type: "varchar",
|
|
129
|
-
length: 50
|
|
130
|
-
},
|
|
131
|
-
{
|
|
132
|
-
name: "last_name",
|
|
133
|
-
type: "varchar",
|
|
134
|
-
length: 50
|
|
135
|
-
},
|
|
136
|
-
{
|
|
137
|
-
name: "phone",
|
|
138
|
-
type: "varchar",
|
|
139
|
-
length: 20
|
|
140
|
-
},
|
|
141
|
-
{
|
|
142
|
-
name: "address",
|
|
143
|
-
type: "text"
|
|
144
|
-
}
|
|
145
|
-
],
|
|
146
|
-
foreign_keys: [
|
|
147
|
-
{
|
|
148
|
-
name: "fk_profile_user",
|
|
149
|
-
column: "user_id",
|
|
150
|
-
references: {
|
|
151
|
-
table: "users",
|
|
152
|
-
column: "id"
|
|
153
|
-
},
|
|
154
|
-
on_delete: "CASCADE",
|
|
155
|
-
on_update: "CASCADE"
|
|
156
|
-
}
|
|
157
|
-
]
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
// Core schema for system
|
|
163
|
-
const coreSchema: DatabaseSchema = {
|
|
164
|
-
version: "1.0.0",
|
|
165
|
-
database_name: "core",
|
|
166
|
-
description: "Core system database",
|
|
167
|
-
schemas: {
|
|
168
|
-
settings: {
|
|
169
|
-
description: "System settings",
|
|
170
|
-
cols: [
|
|
171
|
-
{
|
|
172
|
-
name: "key",
|
|
173
|
-
type: "varchar",
|
|
174
|
-
primary_key: true,
|
|
175
|
-
length: 100
|
|
176
|
-
},
|
|
177
|
-
{
|
|
178
|
-
name: "value",
|
|
179
|
-
type: "text"
|
|
180
|
-
},
|
|
181
|
-
{
|
|
182
|
-
name: "description",
|
|
183
|
-
type: "text"
|
|
184
|
-
}
|
|
185
|
-
]
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
};
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
## 2. Service Management with ServiceManager
|
|
192
|
-
|
|
193
|
-
### Service Configuration and Registration
|
|
194
|
-
|
|
195
|
-
The ServiceManager provides centralized lifecycle management for service instances:
|
|
196
|
-
|
|
197
|
-
```typescript
|
|
198
|
-
// services/ServiceRegistration.ts
|
|
199
|
-
import { ServiceManager, ServiceConfig } from '@dqcai/sqlite';
|
|
200
|
-
import { UserService, ProfileService } from './UserService';
|
|
201
|
-
import { SettingsService } from './CoreService';
|
|
202
|
-
|
|
203
|
-
export class ServiceRegistration {
|
|
204
|
-
private static serviceManager = ServiceManager.getInstance();
|
|
205
|
-
|
|
206
|
-
static registerAllServices(): void {
|
|
207
|
-
const serviceConfigs: ServiceConfig[] = [
|
|
208
|
-
// User services
|
|
209
|
-
{
|
|
210
|
-
schemaName: 'users',
|
|
211
|
-
tableName: 'users',
|
|
212
|
-
primaryKeyFields: ['id'],
|
|
213
|
-
serviceClass: UserService
|
|
214
|
-
},
|
|
215
|
-
{
|
|
216
|
-
schemaName: 'users',
|
|
217
|
-
tableName: 'profiles',
|
|
218
|
-
primaryKeyFields: ['id'],
|
|
219
|
-
serviceClass: ProfileService
|
|
220
|
-
},
|
|
221
|
-
// Core services
|
|
222
|
-
{
|
|
223
|
-
schemaName: 'core',
|
|
224
|
-
tableName: 'settings',
|
|
225
|
-
primaryKeyFields: ['key'],
|
|
226
|
-
serviceClass: SettingsService
|
|
227
|
-
}
|
|
228
|
-
];
|
|
229
|
-
|
|
230
|
-
// Register all services at once
|
|
231
|
-
this.serviceManager.registerServices(serviceConfigs);
|
|
232
|
-
|
|
233
|
-
console.log('All services registered successfully');
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
static async getService<T = BaseService>(
|
|
237
|
-
schemaName: string,
|
|
238
|
-
tableName: string
|
|
239
|
-
): Promise<T> {
|
|
240
|
-
const service = await this.serviceManager.getService(schemaName, tableName);
|
|
241
|
-
return service as T;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
static async initializeService<T = BaseService>(
|
|
245
|
-
schemaName: string,
|
|
246
|
-
tableName: string
|
|
247
|
-
): Promise<T> {
|
|
248
|
-
const service = await this.serviceManager.initializeService(schemaName, tableName);
|
|
249
|
-
return service as T;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
static async destroyService(schemaName: string, tableName: string): Promise<boolean> {
|
|
253
|
-
return await this.serviceManager.destroyService(schemaName, tableName);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
static async getHealthReport() {
|
|
257
|
-
return await this.serviceManager.healthCheck();
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
static getServiceInfo() {
|
|
261
|
-
return this.serviceManager.getAllServiceInfo();
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
### Advanced Service Management
|
|
267
|
-
|
|
268
|
-
```typescript
|
|
269
|
-
// services/ServiceMonitor.ts
|
|
270
|
-
import { ServiceManager, ServiceManagerEvent } from '@dqcai/sqlite';
|
|
271
|
-
|
|
272
|
-
export class ServiceMonitor {
|
|
273
|
-
private serviceManager = ServiceManager.getInstance();
|
|
274
|
-
|
|
275
|
-
constructor() {
|
|
276
|
-
this.setupEventHandlers();
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
private setupEventHandlers(): void {
|
|
280
|
-
// Monitor service lifecycle events
|
|
281
|
-
this.serviceManager.on('SERVICE_CREATED', this.onServiceCreated);
|
|
282
|
-
this.serviceManager.on('SERVICE_DESTROYED', this.onServiceDestroyed);
|
|
283
|
-
this.serviceManager.on('SERVICE_ERROR', this.onServiceError);
|
|
284
|
-
this.serviceManager.on('HEALTH_CHECK_COMPLETED', this.onHealthCheckCompleted);
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
private onServiceCreated = (event: ServiceManagerEvent): void => {
|
|
288
|
-
console.log(`Service created: ${event.serviceKey}`, {
|
|
289
|
-
schema: event.schemaName,
|
|
290
|
-
table: event.tableName,
|
|
291
|
-
timestamp: event.timestamp
|
|
292
|
-
});
|
|
293
|
-
};
|
|
294
|
-
|
|
295
|
-
private onServiceDestroyed = (event: ServiceManagerEvent): void => {
|
|
296
|
-
console.log(`Service destroyed: ${event.serviceKey}`, {
|
|
297
|
-
schema: event.schemaName,
|
|
298
|
-
table: event.tableName,
|
|
299
|
-
timestamp: event.timestamp
|
|
300
|
-
});
|
|
301
|
-
};
|
|
302
|
-
|
|
303
|
-
private onServiceError = (event: ServiceManagerEvent): void => {
|
|
304
|
-
console.error(`Service error in ${event.serviceKey}:`, {
|
|
305
|
-
error: event.error?.message,
|
|
306
|
-
schema: event.schemaName,
|
|
307
|
-
table: event.tableName,
|
|
308
|
-
timestamp: event.timestamp
|
|
309
|
-
});
|
|
310
|
-
};
|
|
311
|
-
|
|
312
|
-
private onHealthCheckCompleted = (event: ServiceManagerEvent): void => {
|
|
313
|
-
const report = event.data;
|
|
314
|
-
console.log('Health check completed:', {
|
|
315
|
-
totalServices: report.totalServices,
|
|
316
|
-
healthyServices: report.healthyServices,
|
|
317
|
-
unhealthyServices: report.unhealthyServices,
|
|
318
|
-
overallHealth: report.overallHealth,
|
|
319
|
-
timestamp: event.timestamp
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
// Alert if unhealthy services found
|
|
323
|
-
if (!report.overallHealth) {
|
|
324
|
-
console.warn('Unhealthy services detected!', report.services.filter(s => !s.healthy));
|
|
325
|
-
}
|
|
326
|
-
};
|
|
327
|
-
|
|
328
|
-
// Periodic health monitoring
|
|
329
|
-
async startPeriodicHealthCheck(intervalMs: number = 60000): Promise<void> {
|
|
330
|
-
setInterval(async () => {
|
|
331
|
-
try {
|
|
332
|
-
await this.serviceManager.healthCheck();
|
|
333
|
-
} catch (error) {
|
|
334
|
-
console.error('Periodic health check failed:', error);
|
|
335
|
-
}
|
|
336
|
-
}, intervalMs);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// Get service statistics
|
|
340
|
-
getServiceStats() {
|
|
341
|
-
const infos = this.serviceManager.getAllServiceInfo();
|
|
342
|
-
const schemas = this.serviceManager.getSchemas();
|
|
343
|
-
|
|
344
|
-
return {
|
|
345
|
-
totalServices: this.serviceManager.getServiceCount(),
|
|
346
|
-
registeredServices: this.serviceManager.getRegisteredCount(),
|
|
347
|
-
activeSchemas: schemas,
|
|
348
|
-
serviceBreakdown: schemas.map(schema => ({
|
|
349
|
-
schema,
|
|
350
|
-
serviceCount: infos.filter(info => info.schemaName === schema).length
|
|
351
|
-
})),
|
|
352
|
-
recentlyAccessed: infos
|
|
353
|
-
.filter(info => info.lastAccessed)
|
|
354
|
-
.sort((a, b) =>
|
|
355
|
-
new Date(b.lastAccessed!).getTime() - new Date(a.lastAccessed!).getTime()
|
|
356
|
-
)
|
|
357
|
-
.slice(0, 10)
|
|
358
|
-
};
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
// Cleanup unused services
|
|
362
|
-
async cleanupUnusedServices(schemaName?: string): Promise<void> {
|
|
363
|
-
if (schemaName) {
|
|
364
|
-
await this.serviceManager.destroyServicesBySchema(schemaName);
|
|
365
|
-
console.log(`Cleaned up services for schema: ${schemaName}`);
|
|
366
|
-
} else {
|
|
367
|
-
// Custom cleanup logic based on access time
|
|
368
|
-
const infos = this.serviceManager.getAllServiceInfo();
|
|
369
|
-
const cutoffTime = Date.now() - (30 * 60 * 1000); // 30 minutes ago
|
|
370
|
-
|
|
371
|
-
for (const info of infos) {
|
|
372
|
-
if (info.lastAccessed) {
|
|
373
|
-
const lastAccess = new Date(info.lastAccessed).getTime();
|
|
374
|
-
if (lastAccess < cutoffTime) {
|
|
375
|
-
await this.serviceManager.destroyService(info.schemaName, info.tableName);
|
|
376
|
-
console.log(`Cleaned up unused service: ${info.key}`);
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
```
|
|
384
|
-
|
|
385
|
-
## 3. Setup for React Native
|
|
386
|
-
|
|
387
|
-
### Install dependencies
|
|
388
|
-
|
|
389
|
-
```bash
|
|
390
|
-
npm install react-native-sqlite-2
|
|
391
|
-
# Or
|
|
392
|
-
npm install react-native-sqlite-storage
|
|
393
|
-
```
|
|
394
|
-
|
|
395
|
-
### Create Adapter for React Native
|
|
396
|
-
|
|
397
|
-
```typescript
|
|
398
|
-
// adapters/ReactNativeAdapter.ts
|
|
399
|
-
import { BaseAdapter } from '@dqcai/sqlite';
|
|
400
|
-
import SQLite from 'react-native-sqlite-2';
|
|
401
|
-
|
|
402
|
-
export class ReactNativeAdapter extends BaseAdapter {
|
|
403
|
-
isSupported(): boolean {
|
|
404
|
-
return typeof SQLite !== 'undefined';
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
async connect(path: string): Promise<any> {
|
|
408
|
-
return new Promise((resolve, reject) => {
|
|
409
|
-
const db = SQLite.openDatabase(
|
|
410
|
-
path,
|
|
411
|
-
'1.0',
|
|
412
|
-
'Database',
|
|
413
|
-
200000,
|
|
414
|
-
() => {
|
|
415
|
-
resolve(new ReactNativeConnection(db));
|
|
416
|
-
},
|
|
417
|
-
(error) => {
|
|
418
|
-
reject(error);
|
|
419
|
-
}
|
|
420
|
-
);
|
|
421
|
-
});
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
class ReactNativeConnection {
|
|
426
|
-
constructor(private db: any) {}
|
|
427
|
-
|
|
428
|
-
async execute(sql: string, params: any[] = []): Promise<any> {
|
|
429
|
-
return new Promise((resolve, reject) => {
|
|
430
|
-
this.db.transaction((tx: any) => {
|
|
431
|
-
tx.executeSql(
|
|
432
|
-
sql,
|
|
433
|
-
params,
|
|
434
|
-
(tx: any, results: any) => {
|
|
435
|
-
const rows: any[] = [];
|
|
436
|
-
for (let i = 0; i < results.rows.length; i++) {
|
|
437
|
-
rows.push(results.rows.item(i));
|
|
438
|
-
}
|
|
439
|
-
resolve({
|
|
440
|
-
rows,
|
|
441
|
-
rowsAffected: results.rowsAffected,
|
|
442
|
-
lastInsertRowId: results.insertId
|
|
443
|
-
});
|
|
444
|
-
},
|
|
445
|
-
(tx: any, error: any) => {
|
|
446
|
-
reject(error);
|
|
447
|
-
}
|
|
448
|
-
);
|
|
449
|
-
});
|
|
450
|
-
});
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
async close(): Promise<void> {
|
|
454
|
-
// React Native SQLite doesn't need manual close
|
|
455
|
-
return Promise.resolve();
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
```
|
|
459
|
-
|
|
460
|
-
### Initialize DatabaseManager (React Native)
|
|
461
|
-
|
|
462
|
-
```typescript
|
|
463
|
-
// services/DatabaseService.ts
|
|
464
|
-
import { DatabaseManager, DatabaseFactory } from '@dqcai/sqlite';
|
|
465
|
-
import { ReactNativeAdapter } from '../adapters/ReactNativeAdapter';
|
|
466
|
-
import { ServiceRegistration } from './ServiceRegistration';
|
|
467
|
-
|
|
468
|
-
export class DatabaseService {
|
|
469
|
-
private static isInitialized = false;
|
|
470
|
-
|
|
471
|
-
static async initialize() {
|
|
472
|
-
if (this.isInitialized) return;
|
|
473
|
-
|
|
474
|
-
// Register adapter
|
|
475
|
-
DatabaseFactory.registerAdapter(new ReactNativeAdapter());
|
|
476
|
-
|
|
477
|
-
// Register schemas
|
|
478
|
-
DatabaseManager.registerSchemas({
|
|
479
|
-
core: coreSchema,
|
|
480
|
-
users: userSchema
|
|
481
|
-
});
|
|
482
|
-
|
|
483
|
-
// Register roles
|
|
484
|
-
DatabaseManager.registerRoles([
|
|
485
|
-
{
|
|
486
|
-
roleName: 'admin',
|
|
487
|
-
requiredDatabases: ['core', 'users'],
|
|
488
|
-
priority: 1
|
|
489
|
-
},
|
|
490
|
-
{
|
|
491
|
-
roleName: 'user',
|
|
492
|
-
requiredDatabases: ['core'],
|
|
493
|
-
optionalDatabases: ['users'],
|
|
494
|
-
priority: 2
|
|
495
|
-
}
|
|
496
|
-
]);
|
|
497
|
-
|
|
498
|
-
// Initialize core database
|
|
499
|
-
await DatabaseManager.initializeCoreConnection();
|
|
500
|
-
|
|
501
|
-
// Register all services
|
|
502
|
-
ServiceRegistration.registerAllServices();
|
|
503
|
-
|
|
504
|
-
this.isInitialized = true;
|
|
505
|
-
console.log('DatabaseService initialized for React Native');
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
static async setUserRole(roles: string[]) {
|
|
509
|
-
await DatabaseManager.setCurrentUserRoles(roles);
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
static getConnection(dbKey: string) {
|
|
513
|
-
return DatabaseManager.get(dbKey);
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
static async closeAll() {
|
|
517
|
-
await DatabaseManager.closeAll();
|
|
518
|
-
this.isInitialized = false;
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
```
|
|
522
|
-
|
|
523
|
-
## 4. Setup for Node.js
|
|
524
|
-
|
|
525
|
-
### Install dependencies
|
|
526
|
-
|
|
527
|
-
```bash
|
|
528
|
-
npm install sqlite3
|
|
529
|
-
# Or
|
|
530
|
-
npm install better-sqlite3
|
|
531
|
-
```
|
|
532
|
-
|
|
533
|
-
### Create Adapter for Node.js
|
|
534
|
-
|
|
535
|
-
```typescript
|
|
536
|
-
// adapters/NodeAdapter.ts
|
|
537
|
-
import { BaseAdapter } from '@dqcai/sqlite';
|
|
538
|
-
import sqlite3 from 'sqlite3';
|
|
539
|
-
import path from 'path';
|
|
540
|
-
import fs from 'fs';
|
|
541
|
-
|
|
542
|
-
export class NodeAdapter extends BaseAdapter {
|
|
543
|
-
isSupported(): boolean {
|
|
544
|
-
return typeof process !== 'undefined' && process.versions?.node;
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
async connect(dbPath: string): Promise<any> {
|
|
548
|
-
const fullPath = path.resolve(dbPath);
|
|
549
|
-
|
|
550
|
-
// Create directory if it doesn't exist
|
|
551
|
-
const dir = path.dirname(fullPath);
|
|
552
|
-
if (!fs.existsSync(dir)) {
|
|
553
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
return new Promise((resolve, reject) => {
|
|
557
|
-
const db = new sqlite3.Database(fullPath, (err) => {
|
|
558
|
-
if (err) {
|
|
559
|
-
reject(err);
|
|
560
|
-
} else {
|
|
561
|
-
resolve(new NodeConnection(db));
|
|
562
|
-
}
|
|
563
|
-
});
|
|
564
|
-
});
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
class NodeConnection {
|
|
569
|
-
constructor(private db: sqlite3.Database) {}
|
|
570
|
-
|
|
571
|
-
async execute(sql: string, params: any[] = []): Promise<any> {
|
|
572
|
-
return new Promise((resolve, reject) => {
|
|
573
|
-
const isSelect = sql.trim().toUpperCase().startsWith('SELECT');
|
|
574
|
-
|
|
575
|
-
if (isSelect) {
|
|
576
|
-
this.db.all(sql, params, (err, rows) => {
|
|
577
|
-
if (err) {
|
|
578
|
-
reject(err);
|
|
579
|
-
} else {
|
|
580
|
-
resolve({
|
|
581
|
-
rows: rows || [],
|
|
582
|
-
rowsAffected: 0
|
|
583
|
-
});
|
|
584
|
-
}
|
|
585
|
-
});
|
|
586
|
-
} else {
|
|
587
|
-
this.db.run(sql, params, function(err) {
|
|
588
|
-
if (err) {
|
|
589
|
-
reject(err);
|
|
590
|
-
} else {
|
|
591
|
-
resolve({
|
|
592
|
-
rows: [],
|
|
593
|
-
rowsAffected: this.changes,
|
|
594
|
-
lastInsertRowId: this.lastID
|
|
595
|
-
});
|
|
596
|
-
}
|
|
597
|
-
});
|
|
598
|
-
}
|
|
599
|
-
});
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
async close(): Promise<void> {
|
|
603
|
-
return new Promise((resolve, reject) => {
|
|
604
|
-
this.db.close((err) => {
|
|
605
|
-
if (err) {
|
|
606
|
-
reject(err);
|
|
607
|
-
} else {
|
|
608
|
-
resolve();
|
|
609
|
-
}
|
|
610
|
-
});
|
|
611
|
-
});
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
```
|
|
615
|
-
|
|
616
|
-
### Initialize DatabaseManager (Node.js)
|
|
617
|
-
|
|
618
|
-
```typescript
|
|
619
|
-
// services/DatabaseService.ts
|
|
620
|
-
import { DatabaseManager, DatabaseFactory } from '@dqcai/sqlite';
|
|
621
|
-
import { NodeAdapter } from '../adapters/NodeAdapter';
|
|
622
|
-
import { ServiceRegistration } from './ServiceRegistration';
|
|
623
|
-
import path from 'path';
|
|
624
|
-
|
|
625
|
-
export class DatabaseService {
|
|
626
|
-
private static isInitialized = false;
|
|
627
|
-
private static dbDirectory = './databases';
|
|
628
|
-
|
|
629
|
-
static async initialize() {
|
|
630
|
-
if (this.isInitialized) return;
|
|
631
|
-
|
|
632
|
-
// Register adapter
|
|
633
|
-
DatabaseFactory.registerAdapter(new NodeAdapter());
|
|
634
|
-
|
|
635
|
-
// Register schemas
|
|
636
|
-
DatabaseManager.registerSchemas({
|
|
637
|
-
core: coreSchema,
|
|
638
|
-
users: userSchema
|
|
639
|
-
});
|
|
640
|
-
|
|
641
|
-
// Register roles
|
|
642
|
-
DatabaseManager.registerRoles([
|
|
643
|
-
{
|
|
644
|
-
roleName: 'admin',
|
|
645
|
-
requiredDatabases: ['core', 'users'],
|
|
646
|
-
priority: 1
|
|
647
|
-
},
|
|
648
|
-
{
|
|
649
|
-
roleName: 'user',
|
|
650
|
-
requiredDatabases: ['core'],
|
|
651
|
-
optionalDatabases: ['users'],
|
|
652
|
-
priority: 2
|
|
653
|
-
}
|
|
654
|
-
]);
|
|
655
|
-
|
|
656
|
-
// Initialize core database
|
|
657
|
-
await DatabaseManager.initializeCoreConnection();
|
|
658
|
-
|
|
659
|
-
// Register all services
|
|
660
|
-
ServiceRegistration.registerAllServices();
|
|
661
|
-
|
|
662
|
-
this.isInitialized = true;
|
|
663
|
-
console.log('DatabaseService initialized for Node.js');
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
static async setUserRole(roles: string[]) {
|
|
667
|
-
await DatabaseManager.setCurrentUserRoles(roles);
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
static getConnection(dbKey: string) {
|
|
671
|
-
return DatabaseManager.get(dbKey);
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
static async closeAll() {
|
|
675
|
-
await DatabaseManager.closeAll();
|
|
676
|
-
this.isInitialized = false;
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
```
|
|
680
|
-
|
|
681
|
-
## 5. Creating Services with BaseService
|
|
682
|
-
|
|
683
|
-
### User Service
|
|
684
|
-
|
|
685
|
-
```typescript
|
|
686
|
-
// services/UserService.ts
|
|
687
|
-
import { BaseService } from '@dqcai/sqlite';
|
|
688
|
-
|
|
689
|
-
interface User {
|
|
690
|
-
id?: number;
|
|
691
|
-
username: string;
|
|
692
|
-
email: string;
|
|
693
|
-
password: string;
|
|
694
|
-
created_at?: string;
|
|
695
|
-
updated_at?: string;
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
interface Profile {
|
|
699
|
-
id?: number;
|
|
700
|
-
user_id: number;
|
|
701
|
-
first_name?: string;
|
|
702
|
-
last_name?: string;
|
|
703
|
-
phone?: string;
|
|
704
|
-
address?: string;
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
export class UserService extends BaseService<User> {
|
|
708
|
-
constructor(schemaName?: string, tableName?: string) {
|
|
709
|
-
super(schemaName || 'users', tableName || 'users');
|
|
710
|
-
this.setPrimaryKeyFields(['id']);
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
// Create new user
|
|
714
|
-
async createUser(userData: Omit<User, 'id' | 'created_at' | 'updated_at'>): Promise<User | null> {
|
|
715
|
-
try {
|
|
716
|
-
// Check if email already exists
|
|
717
|
-
const existingUser = await this.findFirst({ email: userData.email });
|
|
718
|
-
if (existingUser) {
|
|
719
|
-
throw new Error('Email already exists');
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
// Check if username already exists
|
|
723
|
-
const existingUsername = await this.findFirst({ username: userData.username });
|
|
724
|
-
if (existingUsername) {
|
|
725
|
-
throw new Error('Username already exists');
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
const newUser = await this.create({
|
|
729
|
-
...userData,
|
|
730
|
-
created_at: new Date().toISOString()
|
|
731
|
-
});
|
|
732
|
-
|
|
733
|
-
return newUser;
|
|
734
|
-
} catch (error) {
|
|
735
|
-
console.error('Error creating user:', error);
|
|
736
|
-
throw error;
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
// Update user
|
|
741
|
-
async updateUser(id: number, userData: Partial<User>): Promise<User | null> {
|
|
742
|
-
try {
|
|
743
|
-
const updatedUser = await this.update(id, {
|
|
744
|
-
...userData,
|
|
745
|
-
updated_at: new Date().toISOString()
|
|
746
|
-
});
|
|
747
|
-
return updatedUser;
|
|
748
|
-
} catch (error) {
|
|
749
|
-
console.error('Error updating user:', error);
|
|
750
|
-
throw error;
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
// Find user by email
|
|
755
|
-
async findByEmail(email: string): Promise<User | null> {
|
|
756
|
-
return await this.findFirst({ email });
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
// Find user by username
|
|
760
|
-
async findByUsername(username: string): Promise<User | null> {
|
|
761
|
-
return await this.findFirst({ username });
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
// Get all users with pagination
|
|
765
|
-
async getAllUsers(page: number = 1, limit: number = 10): Promise<User[]> {
|
|
766
|
-
const offset = (page - 1) * limit;
|
|
767
|
-
return await this.findAll({}, {
|
|
768
|
-
orderBy: [{ name: 'created_at', direction: 'DESC' }],
|
|
769
|
-
limit,
|
|
770
|
-
offset
|
|
771
|
-
});
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
// Soft delete user
|
|
775
|
-
async softDeleteUser(id: number): Promise<boolean> {
|
|
776
|
-
const result = await this.update(id, {
|
|
777
|
-
updated_at: new Date().toISOString(),
|
|
778
|
-
// deleted_at: new Date().toISOString() // if this field exists in schema
|
|
779
|
-
});
|
|
780
|
-
return result !== null;
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
// Count total users
|
|
784
|
-
async getTotalUsers(): Promise<number> {
|
|
785
|
-
return await this.count();
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
// Search users
|
|
789
|
-
async searchUsers(searchTerm: string): Promise<User[]> {
|
|
790
|
-
const dao = await this.init().then(() => this.dao!);
|
|
791
|
-
const sql = `
|
|
792
|
-
SELECT * FROM users
|
|
793
|
-
WHERE username LIKE ? OR email LIKE ?
|
|
794
|
-
ORDER BY created_at DESC
|
|
795
|
-
`;
|
|
796
|
-
const params = [`%${searchTerm}%`, `%${searchTerm}%`];
|
|
797
|
-
const result = await dao.execute(sql, params);
|
|
798
|
-
return result.rows as User[];
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
export class ProfileService extends BaseService<Profile> {
|
|
803
|
-
constructor(schemaName?: string, tableName?: string) {
|
|
804
|
-
super(schemaName || 'users', tableName || 'profiles');
|
|
805
|
-
this.setPrimaryKeyFields(['id']);
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
// Create profile for user
|
|
809
|
-
async createProfile(profileData: Omit<Profile, 'id'>): Promise<Profile | null> {
|
|
810
|
-
return await this.create(profileData);
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
// Get profile by user_id
|
|
814
|
-
async getProfileByUserId(userId: number): Promise<Profile | null> {
|
|
815
|
-
return await this.findFirst({ user_id: userId });
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
// Update profile
|
|
819
|
-
async updateProfile(id: number, profileData: Partial<Profile>): Promise<Profile | null> {
|
|
820
|
-
return await this.update(id, profileData);
|
|
821
|
-
}
|
|
822
|
-
|
|
823
|
-
// Get user with profile
|
|
824
|
-
async getUserWithProfile(userId: number): Promise<any> {
|
|
825
|
-
const dao = await this.init().then(() => this.dao!);
|
|
826
|
-
const sql = `
|
|
827
|
-
SELECT
|
|
828
|
-
u.id, u.username, u.email, u.created_at,
|
|
829
|
-
p.first_name, p.last_name, p.phone, p.address
|
|
830
|
-
FROM users u
|
|
831
|
-
LEFT JOIN profiles p ON u.id = p.user_id
|
|
832
|
-
WHERE u.id = ?
|
|
833
|
-
`;
|
|
834
|
-
const result = await dao.execute(sql, [userId]);
|
|
835
|
-
return result.rows[0] || null;
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
```
|
|
839
|
-
|
|
840
|
-
### Core Service
|
|
841
|
-
|
|
842
|
-
```typescript
|
|
843
|
-
// services/CoreService.ts
|
|
844
|
-
import { BaseService } from '@dqcai/sqlite';
|
|
845
|
-
|
|
846
|
-
interface Setting {
|
|
847
|
-
key: string;
|
|
848
|
-
value: string;
|
|
849
|
-
description?: string;
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
export class SettingsService extends BaseService<Setting> {
|
|
853
|
-
constructor(schemaName?: string, tableName?: string) {
|
|
854
|
-
super(schemaName || 'core', tableName || 'settings');
|
|
855
|
-
this.setPrimaryKeyFields(['key']);
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
// Get setting value
|
|
859
|
-
async getSetting(key: string): Promise<string | null> {
|
|
860
|
-
const setting = await this.findById(key);
|
|
861
|
-
return setting?.value || null;
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
// Set setting value
|
|
865
|
-
async setSetting(key: string, value: string, description?: string): Promise<void> {
|
|
866
|
-
const existing = await this.findById(key);
|
|
867
|
-
|
|
868
|
-
if (existing) {
|
|
869
|
-
await this.update(key, { value, description });
|
|
870
|
-
} else {
|
|
871
|
-
await this.create({ key, value, description });
|
|
872
|
-
}
|
|
873
|
-
}
|
|
874
|
-
|
|
875
|
-
// Get all settings
|
|
876
|
-
async getAllSettings(): Promise<Setting[]> {
|
|
877
|
-
return await this.findAll({}, {
|
|
878
|
-
orderBy: [{ name: 'key', direction: 'ASC' }]
|
|
879
|
-
});
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
// Delete setting
|
|
883
|
-
async deleteSetting(key: string): Promise<boolean> {
|
|
884
|
-
return await this.delete(key);
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
// Get multiple settings at once
|
|
888
|
-
async getMultipleSettings(keys: string[]): Promise<Record<string, string>> {
|
|
889
|
-
const dao = await this.init().then(() => this.dao!);
|
|
890
|
-
const placeholders = keys.map(() => '?').join(',');
|
|
891
|
-
const sql = `SELECT key, value FROM settings WHERE key IN (${placeholders})`;
|
|
892
|
-
const result = await dao.execute(sql, keys);
|
|
893
|
-
|
|
894
|
-
const settings: Record<string, string> = {};
|
|
895
|
-
result.rows.forEach(row => {
|
|
896
|
-
settings[row.key] = row.value;
|
|
897
|
-
});
|
|
898
|
-
|
|
899
|
-
return settings;
|
|
900
|
-
}
|
|
901
|
-
}
|
|
902
|
-
```
|
|
903
|
-
|
|
904
|
-
## 6. Using in Applications
|
|
905
|
-
|
|
906
|
-
### In React Native
|
|
907
|
-
|
|
908
|
-
```typescript
|
|
909
|
-
// App.tsx or index.js
|
|
910
|
-
import React, { useEffect, useState } from 'react';
|
|
911
|
-
import { View, Text, Button, Alert } from 'react-native';
|
|
912
|
-
import { DatabaseService } from './services/DatabaseService';
|
|
913
|
-
import { ServiceRegistration } from './services/ServiceRegistration';
|
|
914
|
-
import { ServiceMonitor } from './services/ServiceMonitor';
|
|
915
|
-
import { UserService, ProfileService } from './services/UserService';
|
|
916
|
-
import { SettingsService } from './services/CoreService';
|
|
917
|
-
|
|
918
|
-
const App = () => {
|
|
919
|
-
const [isDbReady, setIsDbReady] = useState(false);
|
|
920
|
-
const [serviceMonitor] = useState(new ServiceMonitor());
|
|
921
|
-
|
|
922
|
-
useEffect(() => {
|
|
923
|
-
initializeDatabase();
|
|
924
|
-
|
|
925
|
-
return () => {
|
|
926
|
-
// Cleanup when component unmounts
|
|
927
|
-
DatabaseService.closeAll();
|
|
928
|
-
};
|
|
929
|
-
}, []);
|
|
930
|
-
|
|
931
|
-
const initializeDatabase = async () => {
|
|
932
|
-
try {
|
|
933
|
-
// Initialize database
|
|
934
|
-
await DatabaseService.initialize();
|
|
935
|
-
|
|
936
|
-
// Set role for current user
|
|
937
|
-
await DatabaseService.setUserRole(['user']);
|
|
938
|
-
|
|
939
|
-
// Start service monitoring
|
|
940
|
-
await serviceMonitor.startPeriodicHealthCheck(60000); // Every minute
|
|
941
|
-
|
|
942
|
-
setIsDbReady(true);
|
|
943
|
-
console.log('Database ready!');
|
|
944
|
-
} catch (error) {
|
|
945
|
-
console.error('Database initialization failed:', error);
|
|
946
|
-
Alert.alert('Error', 'Failed to initialize database');
|
|
947
|
-
}
|
|
948
|
-
};
|
|
949
|
-
|
|
950
|
-
const handleCreateUser = async () => {
|
|
951
|
-
if (!isDbReady) return;
|
|
952
|
-
|
|
953
|
-
try {
|
|
954
|
-
const userService = await ServiceRegistration.getService<UserService>('users', 'users');
|
|
955
|
-
const profileService = await ServiceRegistration.getService<ProfileService>('users', 'profiles');
|
|
956
|
-
|
|
957
|
-
const newUser = await userService.createUser({
|
|
958
|
-
username: 'john_doe',
|
|
959
|
-
email: 'john@example.com',
|
|
960
|
-
password: 'hashed_password'
|
|
961
|
-
});
|
|
962
|
-
|
|
963
|
-
if (newUser) {
|
|
964
|
-
// Create profile for user
|
|
965
|
-
await profileService.createProfile({
|
|
966
|
-
user_id: newUser.id!,
|
|
967
|
-
first_name: 'John',
|
|
968
|
-
last_name: 'Doe',
|
|
969
|
-
phone: '+1234567890'
|
|
970
|
-
});
|
|
971
|
-
|
|
972
|
-
Alert.alert('Success', 'User created successfully');
|
|
973
|
-
}
|
|
974
|
-
} catch (error) {
|
|
975
|
-
console.error('Error creating user:', error);
|
|
976
|
-
Alert.alert('Error', 'Failed to create user');
|
|
977
|
-
}
|
|
978
|
-
};
|
|
979
|
-
|
|
980
|
-
const handleGetAllUsers = async () => {
|
|
981
|
-
if (!isDbReady) return;
|
|
982
|
-
|
|
983
|
-
try {
|
|
984
|
-
const userService = await ServiceRegistration.getService<UserService>('users', 'users');
|
|
985
|
-
const users = await userService.getAllUsers(1, 10);
|
|
986
|
-
console.log('Users:', users);
|
|
987
|
-
Alert.alert('Users', `Found ${users.length} users`);
|
|
988
|
-
} catch (error) {
|
|
989
|
-
console.error('Error getting users:', error);
|
|
990
|
-
}
|
|
991
|
-
};
|
|
992
|
-
|
|
993
|
-
const handleSetSetting = async () => {
|
|
994
|
-
if (!isDbReady) return;
|
|
995
|
-
|
|
996
|
-
try {
|
|
997
|
-
const settingsService = await ServiceRegistration.getService<SettingsService>('core', 'settings');
|
|
998
|
-
|
|
999
|
-
await settingsService.setSetting(
|
|
1000
|
-
'app_version',
|
|
1001
|
-
'1.0.0',
|
|
1002
|
-
'Current app version'
|
|
1003
|
-
);
|
|
1004
|
-
|
|
1005
|
-
const version = await settingsService.getSetting('app_version');
|
|
1006
|
-
Alert.alert('Setting', `App version: ${version}`);
|
|
1007
|
-
} catch (error) {
|
|
1008
|
-
console.error('Error setting value:', error);
|
|
1009
|
-
}
|
|
1010
|
-
};
|
|
1011
|
-
|
|
1012
|
-
const handleGetServiceStats = async () => {
|
|
1013
|
-
if (!isDbReady) return;
|
|
1014
|
-
|
|
1015
|
-
try {
|
|
1016
|
-
const stats = serviceMonitor.getServiceStats();
|
|
1017
|
-
const healthReport = await ServiceRegistration.getHealthReport();
|
|
1018
|
-
|
|
1019
|
-
Alert.alert('Service Stats',
|
|
1020
|
-
`Total: ${stats.totalServices}, Healthy: ${healthReport.healthyServices}/${healthReport.totalServices}`
|
|
1021
|
-
);
|
|
1022
|
-
} catch (error) {
|
|
1023
|
-
console.error('Error getting service stats:', error);
|
|
1024
|
-
}
|
|
1025
|
-
};
|
|
1026
|
-
|
|
1027
|
-
return (
|
|
1028
|
-
<View style={{ flex: 1, justifyContent: 'center', padding: 20 }}>
|
|
1029
|
-
<Text>Database Status: {isDbReady ? 'Ready' : 'Initializing...'}</Text>
|
|
1030
|
-
|
|
1031
|
-
<Button
|
|
1032
|
-
title="Create User"
|
|
1033
|
-
onPress={handleCreateUser}
|
|
1034
|
-
disabled={!isDbReady}
|
|
1035
|
-
/>
|
|
1036
|
-
|
|
1037
|
-
<Button
|
|
1038
|
-
title="Get All Users"
|
|
1039
|
-
onPress={handleGetAllUsers}
|
|
1040
|
-
disabled={!isDbReady}
|
|
1041
|
-
/>
|
|
1042
|
-
|
|
1043
|
-
<Button
|
|
1044
|
-
title="Set App Setting"
|
|
1045
|
-
onPress={handleSetSetting}
|
|
1046
|
-
disabled={!isDbReady}
|
|
1047
|
-
/>
|
|
1048
|
-
|
|
1049
|
-
<Button
|
|
1050
|
-
title="Get Service Stats"
|
|
1051
|
-
onPress={handleGetServiceStats}
|
|
1052
|
-
disabled={!isDbReady}
|
|
1053
|
-
/>
|
|
1054
|
-
</View>
|
|
1055
|
-
);
|
|
1056
|
-
};
|
|
1057
|
-
|
|
1058
|
-
export default App;
|
|
1059
|
-
```
|
|
1060
|
-
|
|
1061
|
-
### In Node.js
|
|
1062
|
-
|
|
1063
|
-
```typescript
|
|
1064
|
-
// app.ts
|
|
1065
|
-
import express from 'express';
|
|
1066
|
-
import { DatabaseService } from './services/DatabaseService';
|
|
1067
|
-
import { ServiceRegistration } from './services/ServiceRegistration';
|
|
1068
|
-
import { ServiceMonitor } from './services/ServiceMonitor';
|
|
1069
|
-
import { UserService, ProfileService } from './services/UserService';
|
|
1070
|
-
import { SettingsService } from './services/CoreService';
|
|
1071
|
-
|
|
1072
|
-
const app = express();
|
|
1073
|
-
app.use(express.json());
|
|
1074
|
-
|
|
1075
|
-
// Service monitor
|
|
1076
|
-
const serviceMonitor = new ServiceMonitor();
|
|
1077
|
-
|
|
1078
|
-
// Initialize database when starting server
|
|
1079
|
-
async function initializeApp() {
|
|
1080
|
-
try {
|
|
1081
|
-
console.log('Initializing database...');
|
|
1082
|
-
|
|
1083
|
-
// Initialize DatabaseService
|
|
1084
|
-
await DatabaseService.initialize();
|
|
1085
|
-
|
|
1086
|
-
// Set admin role for server
|
|
1087
|
-
await DatabaseService.setUserRole(['admin']);
|
|
1088
|
-
|
|
1089
|
-
// Start service monitoring
|
|
1090
|
-
await serviceMonitor.startPeriodicHealthCheck(60000); // Every minute
|
|
1091
|
-
|
|
1092
|
-
console.log('Database initialized successfully');
|
|
1093
|
-
} catch (error) {
|
|
1094
|
-
console.error('Database initialization failed:', error);
|
|
1095
|
-
process.exit(1);
|
|
1096
|
-
}
|
|
1097
|
-
}
|
|
1098
|
-
|
|
1099
|
-
// API Routes
|
|
1100
|
-
|
|
1101
|
-
// POST /users - Create new user
|
|
1102
|
-
app.post('/users', async (req, res) => {
|
|
1103
|
-
try {
|
|
1104
|
-
const { username, email, password } = req.body;
|
|
1105
|
-
|
|
1106
|
-
if (!username || !email || !password) {
|
|
1107
|
-
return res.status(400).json({ error: 'Missing required fields' });
|
|
1108
|
-
}
|
|
1109
|
-
|
|
1110
|
-
const userService = await ServiceRegistration.getService<UserService>('users', 'users');
|
|
1111
|
-
const user = await userService.createUser({
|
|
1112
|
-
username,
|
|
1113
|
-
email,
|
|
1114
|
-
password // Should hash password before saving
|
|
1115
|
-
});
|
|
1116
|
-
|
|
1117
|
-
res.status(201).json({ success: true, user });
|
|
1118
|
-
} catch (error: any) {
|
|
1119
|
-
console.error('Error creating user:', error);
|
|
1120
|
-
res.status(400).json({ error: error.message });
|
|
1121
|
-
}
|
|
1122
|
-
});
|
|
1123
|
-
|
|
1124
|
-
// GET /users - Get list of users
|
|
1125
|
-
app.get('/users', async (req, res) => {
|
|
1126
|
-
try {
|
|
1127
|
-
const page = parseInt(req.query.page as string) || 1;
|
|
1128
|
-
const limit = parseInt(req.query.limit as string) || 10;
|
|
1129
|
-
|
|
1130
|
-
const userService = await ServiceRegistration.getService<UserService>('users', 'users');
|
|
1131
|
-
const users = await userService.getAllUsers(page, limit);
|
|
1132
|
-
const total = await userService.getTotalUsers();
|
|
1133
|
-
|
|
1134
|
-
res.json({
|
|
1135
|
-
success: true,
|
|
1136
|
-
users,
|
|
1137
|
-
pagination: {
|
|
1138
|
-
page,
|
|
1139
|
-
limit,
|
|
1140
|
-
total,
|
|
1141
|
-
pages: Math.ceil(total / limit)
|
|
1142
|
-
}
|
|
1143
|
-
});
|
|
1144
|
-
} catch (error: any) {
|
|
1145
|
-
console.error('Error getting users:', error);
|
|
1146
|
-
res.status(500).json({ error: error.message });
|
|
1147
|
-
}
|
|
1148
|
-
});
|
|
1149
|
-
|
|
1150
|
-
// GET /users/:id - Get user by ID
|
|
1151
|
-
app.get('/users/:id', async (req, res) => {
|
|
1152
|
-
try {
|
|
1153
|
-
const id = parseInt(req.params.id);
|
|
1154
|
-
const userService = await ServiceRegistration.getService<UserService>('users', 'users');
|
|
1155
|
-
const user = await userService.findById(id);
|
|
1156
|
-
|
|
1157
|
-
if (!user) {
|
|
1158
|
-
return res.status(404).json({ error: 'User not found' });
|
|
1159
|
-
}
|
|
1160
|
-
|
|
1161
|
-
res.json({ success: true, user });
|
|
1162
|
-
} catch (error: any) {
|
|
1163
|
-
console.error('Error getting user:', error);
|
|
1164
|
-
res.status(500).json({ error: error.message });
|
|
1165
|
-
}
|
|
1166
|
-
});
|
|
1167
|
-
|
|
1168
|
-
// PUT /users/:id - Update user
|
|
1169
|
-
app.put('/users/:id', async (req, res) => {
|
|
1170
|
-
try {
|
|
1171
|
-
const id = parseInt(req.params.id);
|
|
1172
|
-
const updates = req.body;
|
|
1173
|
-
|
|
1174
|
-
const userService = await ServiceRegistration.getService<UserService>('users', 'users');
|
|
1175
|
-
const user = await userService.updateUser(id, updates);
|
|
1176
|
-
|
|
1177
|
-
if (!user) {
|
|
1178
|
-
return res.status(404).json({ error: 'User not found' });
|
|
1179
|
-
}
|
|
1180
|
-
|
|
1181
|
-
res.json({ success: true, user });
|
|
1182
|
-
} catch (error: any) {
|
|
1183
|
-
console.error('Error updating user:', error);
|
|
1184
|
-
res.status(400).json({ error: error.message });
|
|
1185
|
-
}
|
|
1186
|
-
});
|
|
1187
|
-
|
|
1188
|
-
// DELETE /users/:id - Delete user
|
|
1189
|
-
app.delete('/users/:id', async (req, res) => {
|
|
1190
|
-
try {
|
|
1191
|
-
const id = parseInt(req.params.id);
|
|
1192
|
-
const userService = await ServiceRegistration.getService<UserService>('users', 'users');
|
|
1193
|
-
const success = await userService.delete(id);
|
|
1194
|
-
|
|
1195
|
-
if (!success) {
|
|
1196
|
-
return res.status(404).json({ error: 'User not found' });
|
|
1197
|
-
}
|
|
1198
|
-
|
|
1199
|
-
res.json({ success: true, message: 'User deleted' });
|
|
1200
|
-
} catch (error: any) {
|
|
1201
|
-
console.error('Error deleting user:', error);
|
|
1202
|
-
res.status(500).json({ error: error.message });
|
|
1203
|
-
}
|
|
1204
|
-
});
|
|
1205
|
-
|
|
1206
|
-
// POST /users/:id/profile - Create profile for user
|
|
1207
|
-
app.post('/users/:id/profile', async (req, res) => {
|
|
1208
|
-
try {
|
|
1209
|
-
const user_id = parseInt(req.params.id);
|
|
1210
|
-
const profileData = { ...req.body, user_id };
|
|
1211
|
-
|
|
1212
|
-
const profileService = await ServiceRegistration.getService<ProfileService>('users', 'profiles');
|
|
1213
|
-
const profile = await profileService.createProfile(profileData);
|
|
1214
|
-
res.status(201).json({ success: true, profile });
|
|
1215
|
-
} catch (error: any) {
|
|
1216
|
-
console.error('Error creating profile:', error);
|
|
1217
|
-
res.status(400).json({ error: error.message });
|
|
1218
|
-
}
|
|
1219
|
-
});
|
|
1220
|
-
|
|
1221
|
-
// GET /users/:id/full - Get user with profile
|
|
1222
|
-
app.get('/users/:id/full', async (req, res) => {
|
|
1223
|
-
try {
|
|
1224
|
-
const user_id = parseInt(req.params.id);
|
|
1225
|
-
const profileService = await ServiceRegistration.getService<ProfileService>('users', 'profiles');
|
|
1226
|
-
const userWithProfile = await profileService.getUserWithProfile(user_id);
|
|
1227
|
-
|
|
1228
|
-
if (!userWithProfile) {
|
|
1229
|
-
return res.status(404).json({ error: 'User not found' });
|
|
1230
|
-
}
|
|
1231
|
-
|
|
1232
|
-
res.json({ success: true, user: userWithProfile });
|
|
1233
|
-
} catch (error: any) {
|
|
1234
|
-
console.error('Error getting user with profile:', error);
|
|
1235
|
-
res.status(500).json({ error: error.message });
|
|
1236
|
-
}
|
|
1237
|
-
});
|
|
1238
|
-
|
|
1239
|
-
// Settings API
|
|
1240
|
-
app.get('/settings/:key', async (req, res) => {
|
|
1241
|
-
try {
|
|
1242
|
-
const settingsService = await ServiceRegistration.getService<SettingsService>('core', 'settings');
|
|
1243
|
-
const value = await settingsService.getSetting(req.params.key);
|
|
1244
|
-
res.json({ success: true, value });
|
|
1245
|
-
} catch (error: any) {
|
|
1246
|
-
console.error('Error getting setting:', error);
|
|
1247
|
-
res.status(500).json({ error: error.message });
|
|
1248
|
-
}
|
|
1249
|
-
});
|
|
1250
|
-
|
|
1251
|
-
app.post('/settings', async (req, res) => {
|
|
1252
|
-
try {
|
|
1253
|
-
const { key, value, description } = req.body;
|
|
1254
|
-
const settingsService = await ServiceRegistration.getService<SettingsService>('core', 'settings');
|
|
1255
|
-
await settingsService.setSetting(key, value, description);
|
|
1256
|
-
res.json({ success: true, message: 'Setting saved' });
|
|
1257
|
-
} catch (error: any) {
|
|
1258
|
-
console.error('Error setting value:', error);
|
|
1259
|
-
res.status(500).json({ error: error.message });
|
|
1260
|
-
}
|
|
1261
|
-
});
|
|
1262
|
-
|
|
1263
|
-
// Service management API
|
|
1264
|
-
app.get('/admin/services', async (req, res) => {
|
|
1265
|
-
try {
|
|
1266
|
-
const serviceInfo = ServiceRegistration.getServiceInfo();
|
|
1267
|
-
const healthReport = await ServiceRegistration.getHealthReport();
|
|
1268
|
-
const stats = serviceMonitor.getServiceStats();
|
|
1269
|
-
|
|
1270
|
-
res.json({
|
|
1271
|
-
success: true,
|
|
1272
|
-
services: serviceInfo,
|
|
1273
|
-
health: healthReport,
|
|
1274
|
-
stats
|
|
1275
|
-
});
|
|
1276
|
-
} catch (error: any) {
|
|
1277
|
-
console.error('Error getting service info:', error);
|
|
1278
|
-
res.status(500).json({ error: error.message });
|
|
1279
|
-
}
|
|
1280
|
-
});
|
|
1281
|
-
|
|
1282
|
-
app.post('/admin/services/cleanup/:schema?', async (req, res) => {
|
|
1283
|
-
try {
|
|
1284
|
-
const schema = req.params.schema;
|
|
1285
|
-
await serviceMonitor.cleanupUnusedServices(schema);
|
|
1286
|
-
res.json({ success: true, message: 'Cleanup completed' });
|
|
1287
|
-
} catch (error: any) {
|
|
1288
|
-
console.error('Error cleaning up services:', error);
|
|
1289
|
-
res.status(500).json({ error: error.message });
|
|
1290
|
-
}
|
|
1291
|
-
});
|
|
1292
|
-
|
|
1293
|
-
// Graceful shutdown
|
|
1294
|
-
process.on('SIGINT', async () => {
|
|
1295
|
-
console.log('Shutting down server...');
|
|
1296
|
-
await DatabaseService.closeAll();
|
|
1297
|
-
process.exit(0);
|
|
1298
|
-
});
|
|
1299
|
-
|
|
1300
|
-
process.on('SIGTERM', async () => {
|
|
1301
|
-
console.log('Shutting down server...');
|
|
1302
|
-
await DatabaseService.closeAll();
|
|
1303
|
-
process.exit(0);
|
|
1304
|
-
});
|
|
1305
|
-
|
|
1306
|
-
// Start server
|
|
1307
|
-
const PORT = process.env.PORT || 3000;
|
|
1308
|
-
|
|
1309
|
-
initializeApp().then(() => {
|
|
1310
|
-
app.listen(PORT, () => {
|
|
1311
|
-
console.log(`Server running on port ${PORT}`);
|
|
1312
|
-
});
|
|
1313
|
-
});
|
|
24
|
+
```bash
|
|
25
|
+
npm install @dqcai/sqlite
|
|
26
|
+
# or
|
|
27
|
+
yarn add @dqcai/sqlite
|
|
28
|
+
# or
|
|
29
|
+
pnpm add @dqcai/sqlite
|
|
1314
30
|
```
|
|
1315
31
|
|
|
1316
|
-
##
|
|
1317
|
-
|
|
1318
|
-
### Opening/Closing Connections
|
|
32
|
+
## ⚡ Quick Start
|
|
1319
33
|
|
|
1320
34
|
```typescript
|
|
1321
|
-
|
|
1322
|
-
import { DatabaseManager } from '@dqcai/sqlite';
|
|
1323
|
-
|
|
1324
|
-
export class DatabaseHelper {
|
|
1325
|
-
|
|
1326
|
-
// Check connection status
|
|
1327
|
-
static checkConnectionStatus(): void {
|
|
1328
|
-
const connections = DatabaseManager.getConnections();
|
|
1329
|
-
const count = DatabaseManager.getConnectionCount();
|
|
1330
|
-
const activeList = DatabaseManager.listConnections();
|
|
1331
|
-
|
|
1332
|
-
console.log('Active connections:', count);
|
|
1333
|
-
console.log('Connection list:', activeList);
|
|
1334
|
-
console.log('Connection details:', connections);
|
|
1335
|
-
}
|
|
1336
|
-
|
|
1337
|
-
// Close specific connection
|
|
1338
|
-
static async closeSpecificConnection(dbKey: string): Promise<void> {
|
|
1339
|
-
try {
|
|
1340
|
-
await DatabaseManager.closeConnection(dbKey);
|
|
1341
|
-
console.log(`Connection ${dbKey} closed`);
|
|
1342
|
-
} catch (error) {
|
|
1343
|
-
console.error(`Error closing connection ${dbKey}:`, error);
|
|
1344
|
-
}
|
|
1345
|
-
}
|
|
1346
|
-
|
|
1347
|
-
// Reopen connection
|
|
1348
|
-
static async reopenConnection(dbKey: string): Promise<void> {
|
|
1349
|
-
try {
|
|
1350
|
-
const dao = await DatabaseManager.getLazyLoading(dbKey);
|
|
1351
|
-
console.log(`Connection ${dbKey} reopened`);
|
|
1352
|
-
return dao;
|
|
1353
|
-
} catch (error) {
|
|
1354
|
-
console.error(`Error reopening connection ${dbKey}:`, error);
|
|
1355
|
-
throw error;
|
|
1356
|
-
}
|
|
1357
|
-
}
|
|
35
|
+
import { DatabaseManager, ServiceManager, BaseService } from '@dqcai/sqlite';
|
|
1358
36
|
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
37
|
+
// 1. Define your schema
|
|
38
|
+
const userSchema = {
|
|
39
|
+
version: "1.0.0",
|
|
40
|
+
database_name: "users",
|
|
41
|
+
schemas: {
|
|
42
|
+
users: {
|
|
43
|
+
cols: [
|
|
44
|
+
{ name: "id", type: "integer", primary_key: true, auto_increment: true },
|
|
45
|
+
{ name: "username", type: "varchar", length: 50, unique: true },
|
|
46
|
+
{ name: "email", type: "varchar", length: 100, unique: true },
|
|
47
|
+
{ name: "created_at", type: "datetime", default: "CURRENT_TIMESTAMP" }
|
|
48
|
+
]
|
|
1368
49
|
}
|
|
1369
50
|
}
|
|
51
|
+
};
|
|
1370
52
|
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
operations: (daos: Record<string, any>) => Promise<void>
|
|
1375
|
-
): Promise<void> {
|
|
1376
|
-
try {
|
|
1377
|
-
await DatabaseManager.executeCrossSchemaTransaction(schemas, operations);
|
|
1378
|
-
console.log('Cross-schema transaction completed successfully');
|
|
1379
|
-
} catch (error) {
|
|
1380
|
-
console.error('Cross-schema transaction failed:', error);
|
|
1381
|
-
throw error;
|
|
1382
|
-
}
|
|
1383
|
-
}
|
|
53
|
+
// 2. Initialize database
|
|
54
|
+
await DatabaseManager.registerSchema('users', userSchema);
|
|
55
|
+
await DatabaseManager.initializeCoreConnection();
|
|
1384
56
|
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
// Re-initialize services if needed
|
|
1390
|
-
});
|
|
1391
|
-
|
|
1392
|
-
DatabaseManager.onDatabaseReconnect('core', (dao) => {
|
|
1393
|
-
console.log('Core database reconnected');
|
|
1394
|
-
// Re-initialize settings
|
|
1395
|
-
});
|
|
57
|
+
// 3. Create service
|
|
58
|
+
class UserService extends BaseService {
|
|
59
|
+
async createUser(data) {
|
|
60
|
+
return await this.create(data);
|
|
1396
61
|
}
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
const connections = DatabaseManager.getConnections();
|
|
1401
|
-
|
|
1402
|
-
for (const [dbKey, dao] of Object.entries(connections)) {
|
|
1403
|
-
try {
|
|
1404
|
-
await dao.execute('SELECT 1');
|
|
1405
|
-
console.log(`${dbKey}: Healthy`);
|
|
1406
|
-
} catch (error) {
|
|
1407
|
-
console.error(`${dbKey}: Unhealthy -`, error);
|
|
1408
|
-
|
|
1409
|
-
// Try reconnect if needed
|
|
1410
|
-
try {
|
|
1411
|
-
await DatabaseManager.ensureDatabaseConnection(dbKey);
|
|
1412
|
-
console.log(`${dbKey}: Reconnected successfully`);
|
|
1413
|
-
} catch (reconnectError) {
|
|
1414
|
-
console.error(`${dbKey}: Failed to reconnect -`, reconnectError);
|
|
1415
|
-
}
|
|
1416
|
-
}
|
|
1417
|
-
}
|
|
62
|
+
|
|
63
|
+
async getAllUsers() {
|
|
64
|
+
return await this.findAll();
|
|
1418
65
|
}
|
|
1419
66
|
}
|
|
67
|
+
|
|
68
|
+
// 4. Use it!
|
|
69
|
+
const service = new UserService('users', 'users');
|
|
70
|
+
const user = await service.createUser({
|
|
71
|
+
username: 'john',
|
|
72
|
+
email: 'john@example.com'
|
|
73
|
+
});
|
|
1420
74
|
```
|
|
1421
75
|
|
|
1422
|
-
##
|
|
76
|
+
## 🏗️ Core Components
|
|
1423
77
|
|
|
1424
|
-
###
|
|
78
|
+
### DatabaseManager
|
|
79
|
+
Central database connection and schema management.
|
|
1425
80
|
|
|
1426
81
|
```typescript
|
|
1427
|
-
|
|
1428
|
-
import { DatabaseManager, ImportResult, ColumnMapping } from '@dqcai/sqlite';
|
|
1429
|
-
|
|
1430
|
-
export class DataImportService {
|
|
1431
|
-
|
|
1432
|
-
// Import users from CSV
|
|
1433
|
-
static async importUsersFromCSV(csvData: string): Promise<ImportResult> {
|
|
1434
|
-
try {
|
|
1435
|
-
const result = await DatabaseManager.importFromCSV(
|
|
1436
|
-
'users', // database key
|
|
1437
|
-
'users', // table name
|
|
1438
|
-
csvData,
|
|
1439
|
-
{
|
|
1440
|
-
hasHeader: true,
|
|
1441
|
-
delimiter: ',',
|
|
1442
|
-
skipErrors: false,
|
|
1443
|
-
validateData: true,
|
|
1444
|
-
batchSize: 500,
|
|
1445
|
-
onProgress: (processed, total) => {
|
|
1446
|
-
console.log(`Import progress: ${processed}/${total}`);
|
|
1447
|
-
},
|
|
1448
|
-
onError: (error, rowIndex, rowData) => {
|
|
1449
|
-
console.error(`Row ${rowIndex} error:`, error, rowData);
|
|
1450
|
-
}
|
|
1451
|
-
}
|
|
1452
|
-
);
|
|
1453
|
-
|
|
1454
|
-
console.log('Import completed:', {
|
|
1455
|
-
total: result.totalRows,
|
|
1456
|
-
success: result.successRows,
|
|
1457
|
-
errors: result.errorRows,
|
|
1458
|
-
time: result.executionTime
|
|
1459
|
-
});
|
|
1460
|
-
|
|
1461
|
-
return result;
|
|
1462
|
-
} catch (error) {
|
|
1463
|
-
console.error('Import failed:', error);
|
|
1464
|
-
throw error;
|
|
1465
|
-
}
|
|
1466
|
-
}
|
|
1467
|
-
|
|
1468
|
-
// Import with column mapping
|
|
1469
|
-
static async importUsersWithMapping(
|
|
1470
|
-
data: Record<string, any>[],
|
|
1471
|
-
columnMappings: ColumnMapping[]
|
|
1472
|
-
): Promise<ImportResult> {
|
|
1473
|
-
try {
|
|
1474
|
-
const result = await DatabaseManager.importDataWithMapping(
|
|
1475
|
-
'users',
|
|
1476
|
-
'users',
|
|
1477
|
-
data,
|
|
1478
|
-
columnMappings,
|
|
1479
|
-
{
|
|
1480
|
-
batchSize: 1000,
|
|
1481
|
-
skipErrors: true,
|
|
1482
|
-
updateOnConflict: true,
|
|
1483
|
-
conflictColumns: ['email']
|
|
1484
|
-
}
|
|
1485
|
-
);
|
|
82
|
+
import { DatabaseManager } from '@dqcai/sqlite';
|
|
1486
83
|
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
}
|
|
84
|
+
// Register schemas
|
|
85
|
+
DatabaseManager.registerSchemas({
|
|
86
|
+
users: userSchema,
|
|
87
|
+
products: productSchema
|
|
88
|
+
});
|
|
1493
89
|
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
databaseKey: string;
|
|
1497
|
-
tableName: string;
|
|
1498
|
-
data: Record<string, any>[];
|
|
1499
|
-
}>): Promise<void> {
|
|
1500
|
-
try {
|
|
1501
|
-
const result = await DatabaseManager.bulkImport(importConfigs);
|
|
1502
|
-
|
|
1503
|
-
console.log('Bulk import completed:', {
|
|
1504
|
-
totalDatabases: result.totalDatabases,
|
|
1505
|
-
successDatabases: result.successDatabases,
|
|
1506
|
-
executionTime: result.executionTime
|
|
1507
|
-
});
|
|
1508
|
-
|
|
1509
|
-
// Log details for each table
|
|
1510
|
-
Object.entries(result.results).forEach(([key, importResult]) => {
|
|
1511
|
-
console.log(`${key}: ${importResult.successRows}/${importResult.totalRows} rows imported`);
|
|
1512
|
-
});
|
|
1513
|
-
|
|
1514
|
-
// Log errors if any
|
|
1515
|
-
if (Object.keys(result.errors).length > 0) {
|
|
1516
|
-
console.error('Import errors:', result.errors);
|
|
1517
|
-
}
|
|
1518
|
-
|
|
1519
|
-
} catch (error) {
|
|
1520
|
-
console.error('Bulk import failed:', error);
|
|
1521
|
-
throw error;
|
|
1522
|
-
}
|
|
1523
|
-
}
|
|
1524
|
-
}
|
|
90
|
+
// Initialize connections
|
|
91
|
+
await DatabaseManager.initializeCoreConnection();
|
|
1525
92
|
|
|
1526
|
-
//
|
|
1527
|
-
|
|
1528
|
-
// Sample CSV data
|
|
1529
|
-
const csvData = `username,email,password,first_name,last_name
|
|
1530
|
-
john_doe,john@example.com,password123,John,Doe
|
|
1531
|
-
jane_smith,jane@example.com,password456,Jane,Smith
|
|
1532
|
-
bob_wilson,bob@example.com,password789,Bob,Wilson`;
|
|
1533
|
-
|
|
1534
|
-
try {
|
|
1535
|
-
// Import from CSV
|
|
1536
|
-
const importResult = await DataImportService.importUsersFromCSV(csvData);
|
|
1537
|
-
console.log('CSV Import result:', importResult);
|
|
1538
|
-
|
|
1539
|
-
// Import with column mapping
|
|
1540
|
-
const mappedData = [
|
|
1541
|
-
{ user_name: 'alice', user_email: 'alice@test.com', pwd: 'pass123' },
|
|
1542
|
-
{ user_name: 'charlie', user_email: 'charlie@test.com', pwd: 'pass456' }
|
|
1543
|
-
];
|
|
1544
|
-
|
|
1545
|
-
const columnMappings: ColumnMapping[] = [
|
|
1546
|
-
{ sourceColumn: 'user_name', targetColumn: 'username' },
|
|
1547
|
-
{ sourceColumn: 'user_email', targetColumn: 'email' },
|
|
1548
|
-
{
|
|
1549
|
-
sourceColumn: 'pwd',
|
|
1550
|
-
targetColumn: 'password',
|
|
1551
|
-
transform: (value) => `hashed_${value}` // Hash password
|
|
1552
|
-
}
|
|
1553
|
-
];
|
|
1554
|
-
|
|
1555
|
-
const mappingResult = await DataImportService.importUsersWithMapping(
|
|
1556
|
-
mappedData,
|
|
1557
|
-
columnMappings
|
|
1558
|
-
);
|
|
1559
|
-
console.log('Mapping Import result:', mappingResult);
|
|
1560
|
-
|
|
1561
|
-
} catch (error) {
|
|
1562
|
-
console.error('Import example failed:', error);
|
|
1563
|
-
}
|
|
1564
|
-
}
|
|
93
|
+
// Get connection
|
|
94
|
+
const dao = DatabaseManager.get('users');
|
|
1565
95
|
```
|
|
1566
96
|
|
|
1567
|
-
###
|
|
97
|
+
### ServiceManager
|
|
98
|
+
Centralized service lifecycle management with automatic optimization.
|
|
1568
99
|
|
|
1569
100
|
```typescript
|
|
1570
|
-
|
|
1571
|
-
import { DatabaseManager } from '@dqcai/sqlite';
|
|
101
|
+
import { ServiceManager } from '@dqcai/sqlite';
|
|
1572
102
|
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
// Export users to CSV
|
|
1576
|
-
static async exportUsersToCSV(): Promise<string> {
|
|
1577
|
-
try {
|
|
1578
|
-
const dao = DatabaseManager.get('users');
|
|
1579
|
-
const sql = `
|
|
1580
|
-
SELECT u.username, u.email, u.created_at,
|
|
1581
|
-
p.first_name, p.last_name, p.phone
|
|
1582
|
-
FROM users u
|
|
1583
|
-
LEFT JOIN profiles p ON u.id = p.user_id
|
|
1584
|
-
ORDER BY u.created_at DESC
|
|
1585
|
-
`;
|
|
1586
|
-
|
|
1587
|
-
const result = await dao.execute(sql);
|
|
1588
|
-
|
|
1589
|
-
if (result.rows.length === 0) {
|
|
1590
|
-
return 'No data to export';
|
|
1591
|
-
}
|
|
1592
|
-
|
|
1593
|
-
// Create CSV header
|
|
1594
|
-
const headers = Object.keys(result.rows[0]);
|
|
1595
|
-
let csvContent = headers.join(',') + '\n';
|
|
1596
|
-
|
|
1597
|
-
// Add data rows
|
|
1598
|
-
result.rows.forEach(row => {
|
|
1599
|
-
const values = headers.map(header => {
|
|
1600
|
-
const value = row[header];
|
|
1601
|
-
// Escape quotes and wrap in quotes if contains comma
|
|
1602
|
-
if (value === null || value === undefined) {
|
|
1603
|
-
return '';
|
|
1604
|
-
}
|
|
1605
|
-
const stringValue = String(value);
|
|
1606
|
-
if (stringValue.includes(',') || stringValue.includes('"') || stringValue.includes('\n')) {
|
|
1607
|
-
return `"${stringValue.replace(/"/g, '""')}"`;
|
|
1608
|
-
}
|
|
1609
|
-
return stringValue;
|
|
1610
|
-
});
|
|
1611
|
-
csvContent += values.join(',') + '\n';
|
|
1612
|
-
});
|
|
1613
|
-
|
|
1614
|
-
return csvContent;
|
|
1615
|
-
} catch (error) {
|
|
1616
|
-
console.error('Export to CSV failed:', error);
|
|
1617
|
-
throw error;
|
|
1618
|
-
}
|
|
1619
|
-
}
|
|
103
|
+
const serviceManager = ServiceManager.getInstance();
|
|
1620
104
|
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
const dao = DatabaseManager.get('users');
|
|
1628
|
-
let sql = `
|
|
1629
|
-
SELECT u.*, p.first_name, p.last_name, p.phone, p.address
|
|
1630
|
-
FROM users u
|
|
1631
|
-
LEFT JOIN profiles p ON u.id = p.user_id
|
|
1632
|
-
`;
|
|
1633
|
-
|
|
1634
|
-
if (whereClause) {
|
|
1635
|
-
sql += ` WHERE ${whereClause}`;
|
|
1636
|
-
}
|
|
1637
|
-
|
|
1638
|
-
sql += ` ORDER BY u.created_at DESC`;
|
|
1639
|
-
|
|
1640
|
-
const result = await dao.execute(sql, params || []);
|
|
1641
|
-
return result.rows;
|
|
1642
|
-
} catch (error) {
|
|
1643
|
-
console.error('Export with conditions failed:', error);
|
|
1644
|
-
throw error;
|
|
1645
|
-
}
|
|
1646
|
-
}
|
|
105
|
+
// Register services
|
|
106
|
+
serviceManager.registerService({
|
|
107
|
+
schemaName: 'users',
|
|
108
|
+
tableName: 'users',
|
|
109
|
+
serviceClass: UserService
|
|
110
|
+
});
|
|
1647
111
|
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
tables: Record<string, any[]>;
|
|
1651
|
-
metadata: any;
|
|
1652
|
-
}> {
|
|
1653
|
-
try {
|
|
1654
|
-
const dao = DatabaseManager.get(dbKey);
|
|
1655
|
-
|
|
1656
|
-
// Get list of tables
|
|
1657
|
-
const tablesResult = await dao.execute(
|
|
1658
|
-
"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'"
|
|
1659
|
-
);
|
|
1660
|
-
|
|
1661
|
-
const backup: Record<string, any[]> = {};
|
|
1662
|
-
|
|
1663
|
-
// Export each table
|
|
1664
|
-
for (const tableRow of tablesResult.rows) {
|
|
1665
|
-
const tableName = tableRow.name;
|
|
1666
|
-
const dataResult = await dao.execute(`SELECT * FROM ${tableName}`);
|
|
1667
|
-
backup[tableName] = dataResult.rows;
|
|
1668
|
-
}
|
|
1669
|
-
|
|
1670
|
-
// Add metadata
|
|
1671
|
-
const dbInfo = await dao.getDatabaseInfo();
|
|
1672
|
-
|
|
1673
|
-
return {
|
|
1674
|
-
tables: backup,
|
|
1675
|
-
metadata: {
|
|
1676
|
-
...dbInfo,
|
|
1677
|
-
exportDate: new Date().toISOString(),
|
|
1678
|
-
version: await dao.getSchemaVersion()
|
|
1679
|
-
}
|
|
1680
|
-
};
|
|
1681
|
-
} catch (error) {
|
|
1682
|
-
console.error('Database backup failed:', error);
|
|
1683
|
-
throw error;
|
|
1684
|
-
}
|
|
1685
|
-
}
|
|
1686
|
-
}
|
|
112
|
+
// Get service instance
|
|
113
|
+
const userService = await serviceManager.getService('users', 'users');
|
|
1687
114
|
|
|
1688
|
-
//
|
|
1689
|
-
|
|
1690
|
-
try {
|
|
1691
|
-
// Export users to CSV
|
|
1692
|
-
const csvContent = await DataExportService.exportUsersToCSV();
|
|
1693
|
-
console.log('CSV Export:', csvContent);
|
|
1694
|
-
|
|
1695
|
-
// Export users with conditions
|
|
1696
|
-
const recentUsers = await DataExportService.exportUsersWithConditions(
|
|
1697
|
-
"u.created_at > ?",
|
|
1698
|
-
[new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString()] // Last 30 days
|
|
1699
|
-
);
|
|
1700
|
-
console.log('Recent users:', recentUsers);
|
|
1701
|
-
|
|
1702
|
-
// Backup entire users database
|
|
1703
|
-
const backup = await DataExportService.createDatabaseBackup('users');
|
|
1704
|
-
console.log('Database backup:', backup);
|
|
1705
|
-
|
|
1706
|
-
} catch (error) {
|
|
1707
|
-
console.error('Export example failed:', error);
|
|
1708
|
-
}
|
|
1709
|
-
}
|
|
115
|
+
// Health monitoring
|
|
116
|
+
const healthReport = await serviceManager.healthCheck();
|
|
1710
117
|
```
|
|
1711
118
|
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
### Error Handling
|
|
119
|
+
### BaseService
|
|
120
|
+
Type-safe CRUD operations with built-in optimization.
|
|
1715
121
|
|
|
1716
122
|
```typescript
|
|
1717
|
-
|
|
1718
|
-
export class DatabaseErrorHandler {
|
|
1719
|
-
|
|
1720
|
-
static handleServiceError(error: any, context: string): void {
|
|
1721
|
-
console.error(`Database Error in ${context}:`, {
|
|
1722
|
-
message: error.message,
|
|
1723
|
-
stack: error.stack,
|
|
1724
|
-
code: error.code,
|
|
1725
|
-
timestamp: new Date().toISOString()
|
|
1726
|
-
});
|
|
1727
|
-
|
|
1728
|
-
// Specific error handling
|
|
1729
|
-
if (error.message?.includes('UNIQUE constraint failed')) {
|
|
1730
|
-
throw new Error('Duplicate entry detected');
|
|
1731
|
-
} else if (error.message?.includes('database is locked')) {
|
|
1732
|
-
throw new Error('Database is busy, please try again');
|
|
1733
|
-
} else if (error.message?.includes('no such table')) {
|
|
1734
|
-
throw new Error('Database table not found');
|
|
1735
|
-
} else {
|
|
1736
|
-
throw new Error(`Database operation failed: ${error.message}`);
|
|
1737
|
-
}
|
|
1738
|
-
}
|
|
123
|
+
import { BaseService } from '@dqcai/sqlite';
|
|
1739
124
|
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
): Promise<T> {
|
|
1745
|
-
let lastError: any;
|
|
1746
|
-
|
|
1747
|
-
for (let i = 0; i < maxRetries; i++) {
|
|
1748
|
-
try {
|
|
1749
|
-
return await operation();
|
|
1750
|
-
} catch (error) {
|
|
1751
|
-
lastError = error;
|
|
1752
|
-
|
|
1753
|
-
if (i < maxRetries - 1) {
|
|
1754
|
-
console.log(`Operation failed, retrying in ${delay}ms... (${i + 1}/${maxRetries})`);
|
|
1755
|
-
await new Promise(resolve => setTimeout(resolve, delay));
|
|
1756
|
-
delay *= 2; // Exponential backoff
|
|
1757
|
-
}
|
|
1758
|
-
}
|
|
1759
|
-
}
|
|
1760
|
-
|
|
1761
|
-
throw lastError;
|
|
1762
|
-
}
|
|
125
|
+
interface User {
|
|
126
|
+
id?: number;
|
|
127
|
+
username: string;
|
|
128
|
+
email: string;
|
|
1763
129
|
}
|
|
1764
130
|
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
return DatabaseErrorHandler.withRetry(async () => {
|
|
1769
|
-
try {
|
|
1770
|
-
return await super.createUser(userData);
|
|
1771
|
-
} catch (error) {
|
|
1772
|
-
DatabaseErrorHandler.handleServiceError(error, 'createUser');
|
|
1773
|
-
throw error; // Re-throw after handling
|
|
1774
|
-
}
|
|
1775
|
-
});
|
|
131
|
+
class UserService extends BaseService<User> {
|
|
132
|
+
constructor() {
|
|
133
|
+
super('users', 'users');
|
|
1776
134
|
}
|
|
1777
|
-
}
|
|
1778
|
-
```
|
|
1779
135
|
|
|
1780
|
-
|
|
136
|
+
async createUser(data: Omit<User, 'id'>): Promise<User | null> {
|
|
137
|
+
return await this.create(data);
|
|
138
|
+
}
|
|
1781
139
|
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
export class PerformanceOptimizer {
|
|
1785
|
-
|
|
1786
|
-
// Batch operations to reduce database calls
|
|
1787
|
-
static async batchCreateUsers(
|
|
1788
|
-
userService: UserService,
|
|
1789
|
-
users: Array<Omit<User, 'id' | 'created_at' | 'updated_at'>>
|
|
1790
|
-
): Promise<void> {
|
|
1791
|
-
const batchSize = 100;
|
|
1792
|
-
|
|
1793
|
-
for (let i = 0; i < users.length; i += batchSize) {
|
|
1794
|
-
const batch = users.slice(i, i + batchSize);
|
|
1795
|
-
|
|
1796
|
-
await userService.executeTransaction(async () => {
|
|
1797
|
-
for (const userData of batch) {
|
|
1798
|
-
await userService.create({
|
|
1799
|
-
...userData,
|
|
1800
|
-
created_at: new Date().toISOString()
|
|
1801
|
-
});
|
|
1802
|
-
}
|
|
1803
|
-
});
|
|
1804
|
-
|
|
1805
|
-
console.log(`Processed batch ${Math.ceil((i + batchSize) / batchSize)}`);
|
|
1806
|
-
}
|
|
140
|
+
async findByEmail(email: string): Promise<User | null> {
|
|
141
|
+
return await this.findFirst({ email });
|
|
1807
142
|
}
|
|
1808
143
|
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
// Enable query optimization
|
|
1812
|
-
await dao.execute('PRAGMA optimize');
|
|
1813
|
-
|
|
1814
|
-
// Analyze tables for better query planning
|
|
1815
|
-
await dao.execute('ANALYZE');
|
|
1816
|
-
|
|
1817
|
-
// Set optimal cache size
|
|
1818
|
-
await dao.execute('PRAGMA cache_size = 10000');
|
|
1819
|
-
|
|
1820
|
-
console.log('Database optimization completed');
|
|
144
|
+
async updateUser(id: number, data: Partial<User>): Promise<User | null> {
|
|
145
|
+
return await this.update(id, data);
|
|
1821
146
|
}
|
|
1822
147
|
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
setInterval(() => {
|
|
1826
|
-
const count = DatabaseManager.getConnectionCount();
|
|
1827
|
-
const connections = DatabaseManager.listConnections();
|
|
1828
|
-
|
|
1829
|
-
console.log(`Active connections: ${count}`, connections);
|
|
1830
|
-
|
|
1831
|
-
if (count > 5) {
|
|
1832
|
-
console.warn('High number of database connections detected');
|
|
1833
|
-
}
|
|
1834
|
-
}, 30000); // Check every 30 seconds
|
|
148
|
+
async deleteUser(id: number): Promise<boolean> {
|
|
149
|
+
return await this.delete(id);
|
|
1835
150
|
}
|
|
1836
151
|
}
|
|
1837
152
|
```
|
|
1838
153
|
|
|
1839
|
-
|
|
154
|
+
## 🌐 Platform Support
|
|
1840
155
|
|
|
156
|
+
### Browser
|
|
1841
157
|
```typescript
|
|
1842
|
-
|
|
1843
|
-
import { DatabaseManager, ServiceManager } from '@dqcai/sqlite';
|
|
1844
|
-
|
|
1845
|
-
export class DatabaseTestHelpers {
|
|
1846
|
-
|
|
1847
|
-
static async setupTestDatabase(): Promise<void> {
|
|
1848
|
-
// Use in-memory database for testing
|
|
1849
|
-
const testSchema = {
|
|
1850
|
-
...userSchema,
|
|
1851
|
-
database_name: ':memory:'
|
|
1852
|
-
};
|
|
1853
|
-
|
|
1854
|
-
DatabaseManager.registerSchema('test_users', testSchema);
|
|
1855
|
-
await DatabaseManager.initLazySchema(['test_users']);
|
|
1856
|
-
}
|
|
1857
|
-
|
|
1858
|
-
static async cleanupTestData(service: any): Promise<void> {
|
|
1859
|
-
await service.truncate();
|
|
1860
|
-
}
|
|
1861
|
-
|
|
1862
|
-
static async seedTestData(userService: UserService): Promise<User[]> {
|
|
1863
|
-
const testUsers = [
|
|
1864
|
-
{ username: 'test1', email: 'test1@example.com', password: 'pass1' },
|
|
1865
|
-
{ username: 'test2', email: 'test2@example.com', password: 'pass2' },
|
|
1866
|
-
{ username: 'test3', email: 'test3@example.com', password: 'pass3' }
|
|
1867
|
-
];
|
|
1868
|
-
|
|
1869
|
-
const createdUsers: User[] = [];
|
|
1870
|
-
for (const userData of testUsers) {
|
|
1871
|
-
const user = await userService.createUser(userData);
|
|
1872
|
-
if (user) createdUsers.push(user);
|
|
1873
|
-
}
|
|
1874
|
-
|
|
1875
|
-
return createdUsers;
|
|
1876
|
-
}
|
|
1877
|
-
|
|
1878
|
-
static async teardownTest(): Promise<void> {
|
|
1879
|
-
await DatabaseManager.closeAll();
|
|
1880
|
-
ServiceManager.resetInstance();
|
|
1881
|
-
}
|
|
1882
|
-
}
|
|
158
|
+
import { DatabaseFactory, BrowserAdapter } from '@dqcai/sqlite';
|
|
1883
159
|
|
|
1884
|
-
|
|
1885
|
-
describe('UserService Tests', () => {
|
|
1886
|
-
let serviceManager: ServiceManager;
|
|
1887
|
-
let userService: UserService;
|
|
1888
|
-
|
|
1889
|
-
beforeAll(async () => {
|
|
1890
|
-
await DatabaseTestHelpers.setupTestDatabase();
|
|
1891
|
-
serviceManager = ServiceManager.getInstance();
|
|
1892
|
-
userService = await serviceManager.getService('test_users', 'users') as UserService;
|
|
1893
|
-
await userService.init();
|
|
1894
|
-
});
|
|
1895
|
-
|
|
1896
|
-
beforeEach(async () => {
|
|
1897
|
-
await DatabaseTestHelpers.cleanupTestData(userService);
|
|
1898
|
-
});
|
|
1899
|
-
|
|
1900
|
-
afterAll(async () => {
|
|
1901
|
-
await DatabaseTestHelpers.teardownTest();
|
|
1902
|
-
});
|
|
1903
|
-
|
|
1904
|
-
test('should create user successfully', async () => {
|
|
1905
|
-
const userData = {
|
|
1906
|
-
username: 'testuser',
|
|
1907
|
-
email: 'test@example.com',
|
|
1908
|
-
password: 'password123'
|
|
1909
|
-
};
|
|
1910
|
-
|
|
1911
|
-
const user = await userService.createUser(userData);
|
|
1912
|
-
expect(user).toBeTruthy();
|
|
1913
|
-
expect(user?.username).toBe(userData.username);
|
|
1914
|
-
expect(user?.email).toBe(userData.email);
|
|
1915
|
-
});
|
|
1916
|
-
|
|
1917
|
-
test('should manage service lifecycle', async () => {
|
|
1918
|
-
const serviceInfo = serviceManager.getAllServiceInfo();
|
|
1919
|
-
expect(serviceInfo.length).toBeGreaterThan(0);
|
|
1920
|
-
|
|
1921
|
-
const healthReport = await serviceManager.healthCheck();
|
|
1922
|
-
expect(healthReport.overallHealth).toBe(true);
|
|
1923
|
-
});
|
|
1924
|
-
});
|
|
160
|
+
DatabaseFactory.registerAdapter(new BrowserAdapter());
|
|
1925
161
|
```
|
|
1926
162
|
|
|
1927
|
-
|
|
163
|
+
### Node.js
|
|
164
|
+
```typescript
|
|
165
|
+
import { DatabaseFactory, NodeAdapter } from '@dqcai/sqlite';
|
|
1928
166
|
|
|
1929
|
-
|
|
167
|
+
DatabaseFactory.registerAdapter(new NodeAdapter());
|
|
168
|
+
```
|
|
1930
169
|
|
|
170
|
+
### React Native
|
|
1931
171
|
```typescript
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
try {
|
|
1935
|
-
// Enable WAL mode to avoid locks
|
|
1936
|
-
const dao = DatabaseManager.get('users');
|
|
1937
|
-
await dao.execute('PRAGMA journal_mode = WAL');
|
|
1938
|
-
await dao.execute('PRAGMA busy_timeout = 30000'); // 30 second timeout
|
|
1939
|
-
} catch (error) {
|
|
1940
|
-
console.error('Error setting WAL mode:', error);
|
|
1941
|
-
}
|
|
1942
|
-
};
|
|
1943
|
-
```
|
|
172
|
+
import { DatabaseFactory } from '@dqcai/sqlite';
|
|
173
|
+
import { ReactNativeAdapter } from './adapters/ReactNativeAdapter';
|
|
1944
174
|
|
|
1945
|
-
|
|
175
|
+
DatabaseFactory.registerAdapter(new ReactNativeAdapter());
|
|
176
|
+
```
|
|
1946
177
|
|
|
178
|
+
### Deno
|
|
1947
179
|
```typescript
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
const dao = DatabaseManager.get(dbKey);
|
|
1952
|
-
await dao.execute('SELECT 1');
|
|
1953
|
-
} catch (error) {
|
|
1954
|
-
console.log(`Connection ${dbKey} unhealthy, reconnecting...`);
|
|
1955
|
-
await DatabaseManager.closeConnection(dbKey);
|
|
1956
|
-
await DatabaseManager.getLazyLoading(dbKey);
|
|
1957
|
-
console.log(`Connection ${dbKey} restored`);
|
|
1958
|
-
}
|
|
1959
|
-
};
|
|
180
|
+
import { DatabaseFactory, DenoAdapter } from '@dqcai/sqlite';
|
|
181
|
+
|
|
182
|
+
DatabaseFactory.registerAdapter(new DenoAdapter());
|
|
1960
183
|
```
|
|
1961
184
|
|
|
1962
|
-
|
|
185
|
+
## 🔧 Schema Definition
|
|
186
|
+
|
|
187
|
+
Define your database structure with JSON schemas:
|
|
1963
188
|
|
|
1964
189
|
```typescript
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
190
|
+
const schema = {
|
|
191
|
+
version: "1.0.0",
|
|
192
|
+
database_name: "myapp",
|
|
193
|
+
description: "Application database",
|
|
194
|
+
schemas: {
|
|
195
|
+
users: {
|
|
196
|
+
description: "User accounts",
|
|
197
|
+
cols: [
|
|
198
|
+
{
|
|
199
|
+
name: "id",
|
|
200
|
+
type: "integer",
|
|
201
|
+
primary_key: true,
|
|
202
|
+
auto_increment: true
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
name: "username",
|
|
206
|
+
type: "varchar",
|
|
207
|
+
length: 50,
|
|
208
|
+
nullable: false,
|
|
209
|
+
unique: true
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
name: "email",
|
|
213
|
+
type: "varchar",
|
|
214
|
+
length: 100,
|
|
215
|
+
nullable: false,
|
|
216
|
+
unique: true
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
name: "password",
|
|
220
|
+
type: "varchar",
|
|
221
|
+
length: 255,
|
|
222
|
+
nullable: false
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
name: "created_at",
|
|
226
|
+
type: "datetime",
|
|
227
|
+
nullable: false,
|
|
228
|
+
default: "CURRENT_TIMESTAMP"
|
|
229
|
+
}
|
|
230
|
+
],
|
|
231
|
+
indexes: [
|
|
232
|
+
{
|
|
233
|
+
name: "idx_username",
|
|
234
|
+
columns: ["username"],
|
|
235
|
+
unique: true
|
|
236
|
+
}
|
|
237
|
+
]
|
|
238
|
+
},
|
|
239
|
+
posts: {
|
|
240
|
+
description: "User posts",
|
|
241
|
+
cols: [
|
|
242
|
+
{ name: "id", type: "integer", primary_key: true, auto_increment: true },
|
|
243
|
+
{ name: "user_id", type: "integer", nullable: false },
|
|
244
|
+
{ name: "title", type: "varchar", length: 200 },
|
|
245
|
+
{ name: "content", type: "text" },
|
|
246
|
+
{ name: "created_at", type: "datetime", default: "CURRENT_TIMESTAMP" }
|
|
247
|
+
],
|
|
248
|
+
foreign_keys: [
|
|
249
|
+
{
|
|
250
|
+
name: "fk_post_user",
|
|
251
|
+
column: "user_id",
|
|
252
|
+
references: { table: "users", column: "id" },
|
|
253
|
+
on_delete: "CASCADE"
|
|
254
|
+
}
|
|
255
|
+
]
|
|
1983
256
|
}
|
|
1984
257
|
}
|
|
1985
258
|
};
|
|
1986
259
|
```
|
|
1987
260
|
|
|
1988
|
-
##
|
|
261
|
+
## ⚡ Advanced Features
|
|
1989
262
|
|
|
263
|
+
### Transaction Management
|
|
1990
264
|
```typescript
|
|
1991
|
-
//
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
description: 'Add status column to users table',
|
|
1997
|
-
|
|
1998
|
-
async up(dao: UniversalDAO): Promise<void> {
|
|
1999
|
-
await dao.execute(`
|
|
2000
|
-
ALTER TABLE users
|
|
2001
|
-
ADD COLUMN status VARCHAR(20) DEFAULT 'active'
|
|
2002
|
-
`);
|
|
2003
|
-
|
|
2004
|
-
await dao.execute(`
|
|
2005
|
-
CREATE INDEX idx_user_status ON users(status)
|
|
2006
|
-
`);
|
|
2007
|
-
},
|
|
2008
|
-
|
|
2009
|
-
async down(dao: UniversalDAO): Promise<void> {
|
|
2010
|
-
await dao.execute(`
|
|
2011
|
-
DROP INDEX IF EXISTS idx_user_status
|
|
2012
|
-
`);
|
|
2013
|
-
|
|
2014
|
-
// SQLite doesn't support DROP COLUMN, need to recreate table
|
|
2015
|
-
await dao.execute(`
|
|
2016
|
-
CREATE TABLE users_backup AS
|
|
2017
|
-
SELECT id, username, email, password, created_at, updated_at
|
|
2018
|
-
FROM users
|
|
2019
|
-
`);
|
|
2020
|
-
|
|
2021
|
-
await dao.execute(`DROP TABLE users`);
|
|
2022
|
-
|
|
2023
|
-
await dao.execute(`
|
|
2024
|
-
CREATE TABLE users (
|
|
2025
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
2026
|
-
username VARCHAR(50) UNIQUE NOT NULL,
|
|
2027
|
-
email VARCHAR(100) UNIQUE NOT NULL,
|
|
2028
|
-
password VARCHAR(255) NOT NULL,
|
|
2029
|
-
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
2030
|
-
updated_at DATETIME
|
|
2031
|
-
)
|
|
2032
|
-
`);
|
|
2033
|
-
|
|
2034
|
-
await dao.execute(`
|
|
2035
|
-
INSERT INTO users SELECT * FROM users_backup
|
|
2036
|
-
`);
|
|
2037
|
-
|
|
2038
|
-
await dao.execute(`DROP TABLE users_backup`);
|
|
2039
|
-
}
|
|
2040
|
-
};
|
|
265
|
+
// Single table transaction
|
|
266
|
+
await userService.executeTransaction(async () => {
|
|
267
|
+
const user = await userService.create({ username: 'john', email: 'john@test.com' });
|
|
268
|
+
await userService.update(user.id, { username: 'johnny' });
|
|
269
|
+
});
|
|
2041
270
|
|
|
2042
|
-
//
|
|
2043
|
-
|
|
2044
|
-
const
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
if (currentVersion < '1.0.1') {
|
|
2048
|
-
await migration_001.up(dao);
|
|
2049
|
-
await dao.setSchemaVersion('1.0.1');
|
|
2050
|
-
console.log('Migration 001 completed');
|
|
2051
|
-
}
|
|
2052
|
-
};
|
|
271
|
+
// Cross-schema transaction
|
|
272
|
+
await DatabaseManager.executeCrossSchemaTransaction(['users', 'posts'], async (daos) => {
|
|
273
|
+
const user = await daos.users.execute('INSERT INTO users ...');
|
|
274
|
+
await daos.posts.execute('INSERT INTO posts ...');
|
|
275
|
+
});
|
|
2053
276
|
```
|
|
2054
277
|
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
### Cross-Schema Transactions with ServiceManager
|
|
2058
|
-
|
|
278
|
+
### Query Builder
|
|
2059
279
|
```typescript
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
profileData: Omit<Profile, 'id' | 'user_id'>
|
|
2067
|
-
): Promise<{ user: User; profile: Profile }> {
|
|
2068
|
-
|
|
2069
|
-
// Execute transaction within the same schema (users)
|
|
2070
|
-
return await this.serviceManager.executeSchemaTransaction('users', async (services) => {
|
|
2071
|
-
const userService = services.find(s => s.tableName === 'users') as UserService;
|
|
2072
|
-
const profileService = services.find(s => s.tableName === 'profiles') as ProfileService;
|
|
2073
|
-
|
|
2074
|
-
// Create user first
|
|
2075
|
-
const user = await userService.createUser(userData);
|
|
2076
|
-
if (!user) {
|
|
2077
|
-
throw new Error('Failed to create user');
|
|
2078
|
-
}
|
|
2079
|
-
|
|
2080
|
-
// Create profile for the user
|
|
2081
|
-
const profile = await profileService.createProfile({
|
|
2082
|
-
...profileData,
|
|
2083
|
-
user_id: user.id!
|
|
2084
|
-
});
|
|
2085
|
-
|
|
2086
|
-
if (!profile) {
|
|
2087
|
-
throw new Error('Failed to create profile');
|
|
2088
|
-
}
|
|
2089
|
-
|
|
2090
|
-
return { user, profile };
|
|
2091
|
-
});
|
|
2092
|
-
}
|
|
2093
|
-
|
|
2094
|
-
async executeMultiSchemaOperation(): Promise<void> {
|
|
2095
|
-
// For operations across different schemas (databases)
|
|
2096
|
-
await DatabaseManager.executeCrossSchemaTransaction(['users', 'core'], async (daos) => {
|
|
2097
|
-
const usersDao = daos.users;
|
|
2098
|
-
const coreDao = daos.core;
|
|
2099
|
-
|
|
2100
|
-
// Update user count in settings
|
|
2101
|
-
const userCount = await usersDao.execute('SELECT COUNT(*) as count FROM users');
|
|
2102
|
-
await coreDao.execute(
|
|
2103
|
-
'INSERT OR REPLACE INTO settings (key, value, description) VALUES (?, ?, ?)',
|
|
2104
|
-
['user_count', userCount.rows[0].count.toString(), 'Total number of users']
|
|
2105
|
-
);
|
|
2106
|
-
|
|
2107
|
-
// Log system activity
|
|
2108
|
-
await coreDao.execute(
|
|
2109
|
-
'INSERT OR REPLACE INTO settings (key, value, description) VALUES (?, ?, ?)',
|
|
2110
|
-
['last_user_sync', new Date().toISOString(), 'Last user count sync']
|
|
2111
|
-
);
|
|
2112
|
-
});
|
|
2113
|
-
}
|
|
2114
|
-
}
|
|
280
|
+
const users = await userService.queryBuilder()
|
|
281
|
+
.select(['id', 'username', 'email'])
|
|
282
|
+
.where('created_at', '>', '2024-01-01')
|
|
283
|
+
.orderBy('username', 'ASC')
|
|
284
|
+
.limit(10)
|
|
285
|
+
.execute();
|
|
2115
286
|
```
|
|
2116
287
|
|
|
2117
|
-
###
|
|
2118
|
-
|
|
288
|
+
### Batch Operations
|
|
2119
289
|
```typescript
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
// Compose services with dependencies
|
|
2125
|
-
async createUserManagementService(): Promise<UserManagementService> {
|
|
2126
|
-
const userService = await this.serviceManager.getService('users', 'users') as UserService;
|
|
2127
|
-
const profileService = await this.serviceManager.getService('users', 'profiles') as ProfileService;
|
|
2128
|
-
const settingsService = await this.serviceManager.getService('core', 'settings') as SettingsService;
|
|
2129
|
-
|
|
2130
|
-
return new UserManagementService(userService, profileService, settingsService);
|
|
2131
|
-
}
|
|
2132
|
-
}
|
|
290
|
+
const users = [
|
|
291
|
+
{ username: 'user1', email: 'user1@test.com' },
|
|
292
|
+
{ username: 'user2', email: 'user2@test.com' }
|
|
293
|
+
];
|
|
2133
294
|
|
|
2134
|
-
|
|
2135
|
-
export class UserManagementService {
|
|
2136
|
-
constructor(
|
|
2137
|
-
private userService: UserService,
|
|
2138
|
-
private profileService: ProfileService,
|
|
2139
|
-
private settingsService: SettingsService
|
|
2140
|
-
) {}
|
|
2141
|
-
|
|
2142
|
-
async createCompleteUser(
|
|
2143
|
-
userData: Omit<User, 'id' | 'created_at' | 'updated_at'>,
|
|
2144
|
-
profileData: Omit<Profile, 'id' | 'user_id'>
|
|
2145
|
-
): Promise<any> {
|
|
2146
|
-
// Get default settings
|
|
2147
|
-
const defaultRole = await this.settingsService.getSetting('default_user_role') || 'user';
|
|
2148
|
-
|
|
2149
|
-
return await this.userService.executeTransaction(async () => {
|
|
2150
|
-
// Create user
|
|
2151
|
-
const user = await this.userService.createUser(userData);
|
|
2152
|
-
if (!user) throw new Error('Failed to create user');
|
|
2153
|
-
|
|
2154
|
-
// Create profile
|
|
2155
|
-
const profile = await this.profileService.createProfile({
|
|
2156
|
-
...profileData,
|
|
2157
|
-
user_id: user.id!
|
|
2158
|
-
});
|
|
2159
|
-
|
|
2160
|
-
// Update user count
|
|
2161
|
-
const currentCount = await this.userService.count();
|
|
2162
|
-
await this.settingsService.setSetting(
|
|
2163
|
-
'total_users',
|
|
2164
|
-
currentCount.toString(),
|
|
2165
|
-
'Total registered users'
|
|
2166
|
-
);
|
|
2167
|
-
|
|
2168
|
-
return { user, profile, defaultRole };
|
|
2169
|
-
});
|
|
2170
|
-
}
|
|
2171
|
-
|
|
2172
|
-
async getUserDashboardData(userId: number): Promise<any> {
|
|
2173
|
-
const [user, profile, recentSettings] = await Promise.all([
|
|
2174
|
-
this.userService.findById(userId),
|
|
2175
|
-
this.profileService.getProfileByUserId(userId),
|
|
2176
|
-
this.settingsService.getMultipleSettings(['app_version', 'maintenance_mode', 'user_count'])
|
|
2177
|
-
]);
|
|
2178
|
-
|
|
2179
|
-
return {
|
|
2180
|
-
user,
|
|
2181
|
-
profile,
|
|
2182
|
-
settings: recentSettings,
|
|
2183
|
-
isComplete: !!(user && profile)
|
|
2184
|
-
};
|
|
2185
|
-
}
|
|
2186
|
-
}
|
|
295
|
+
await userService.batchCreate(users);
|
|
2187
296
|
```
|
|
2188
297
|
|
|
2189
|
-
### Real-time Monitoring
|
|
2190
|
-
|
|
298
|
+
### Real-time Monitoring
|
|
2191
299
|
```typescript
|
|
2192
|
-
|
|
2193
|
-
export class ServiceHealthMonitor {
|
|
2194
|
-
private serviceManager = ServiceManager.getInstance();
|
|
2195
|
-
private alerts: Array<{ timestamp: string; level: string; message: string }> = [];
|
|
2196
|
-
private healthCheckInterval: NodeJS.Timeout | null = null;
|
|
2197
|
-
|
|
2198
|
-
startMonitoring(intervalMs: number = 30000): void {
|
|
2199
|
-
this.setupEventHandlers();
|
|
2200
|
-
|
|
2201
|
-
this.healthCheckInterval = setInterval(async () => {
|
|
2202
|
-
await this.performHealthCheck();
|
|
2203
|
-
}, intervalMs);
|
|
2204
|
-
|
|
2205
|
-
console.log('Service health monitoring started');
|
|
2206
|
-
}
|
|
2207
|
-
|
|
2208
|
-
stopMonitoring(): void {
|
|
2209
|
-
if (this.healthCheckInterval) {
|
|
2210
|
-
clearInterval(this.healthCheckInterval);
|
|
2211
|
-
this.healthCheckInterval = null;
|
|
2212
|
-
}
|
|
2213
|
-
console.log('Service health monitoring stopped');
|
|
2214
|
-
}
|
|
300
|
+
import { ServiceHealthMonitor } from '@dqcai/sqlite';
|
|
2215
301
|
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
this.addAlert('ERROR', `Service error in ${event.serviceKey}: ${event.error?.message}`);
|
|
2219
|
-
});
|
|
302
|
+
const monitor = new ServiceHealthMonitor();
|
|
303
|
+
monitor.startMonitoring(30000); // Check every 30 seconds
|
|
2220
304
|
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
305
|
+
// Get health status
|
|
306
|
+
const healthReport = await serviceManager.healthCheck();
|
|
307
|
+
console.log(`System health: ${healthReport.overallHealth ? 'Healthy' : 'Unhealthy'}`);
|
|
308
|
+
```
|
|
2224
309
|
|
|
2225
|
-
|
|
2226
|
-
this.addAlert('WARNING', `Service destroyed: ${event.serviceKey}`);
|
|
2227
|
-
});
|
|
2228
|
-
}
|
|
310
|
+
## 🎯 Use Cases
|
|
2229
311
|
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
// Attempt automatic recovery
|
|
2238
|
-
await this.attemptAutoRecovery(healthReport);
|
|
2239
|
-
} else {
|
|
2240
|
-
this.addAlert('INFO', `System healthy: All ${healthReport.totalServices} services operational`);
|
|
2241
|
-
}
|
|
2242
|
-
} catch (error) {
|
|
2243
|
-
this.addAlert('ERROR', `Health check failed: ${error}`);
|
|
2244
|
-
}
|
|
2245
|
-
}
|
|
312
|
+
- **Mobile Apps**: React Native applications with offline-first data storage
|
|
313
|
+
- **Desktop Apps**: Electron applications with embedded database
|
|
314
|
+
- **Web Applications**: Browser-based apps with client-side data storage
|
|
315
|
+
- **Server Applications**: Node.js backends with SQLite database
|
|
316
|
+
- **Edge Computing**: Lightweight applications for edge deployment
|
|
317
|
+
- **Microservices**: Small, focused services with embedded databases
|
|
2246
318
|
|
|
2247
|
-
|
|
2248
|
-
const unhealthyServices = healthReport.services.filter((s: any) => !s.healthy);
|
|
2249
|
-
|
|
2250
|
-
for (const service of unhealthyServices) {
|
|
2251
|
-
try {
|
|
2252
|
-
const [schemaName, tableName] = service.serviceKey.split(':');
|
|
2253
|
-
|
|
2254
|
-
// Try to restart the service
|
|
2255
|
-
await this.serviceManager.destroyService(schemaName, tableName);
|
|
2256
|
-
await this.serviceManager.initializeService(schemaName, tableName);
|
|
2257
|
-
|
|
2258
|
-
this.addAlert('INFO', `Auto-recovery successful for ${service.serviceKey}`);
|
|
2259
|
-
} catch (error) {
|
|
2260
|
-
this.addAlert('ERROR', `Auto-recovery failed for ${service.serviceKey}: ${error}`);
|
|
2261
|
-
}
|
|
2262
|
-
}
|
|
2263
|
-
}
|
|
319
|
+
## 🔍 SEO Keywords
|
|
2264
320
|
|
|
2265
|
-
|
|
2266
|
-
const alert = {
|
|
2267
|
-
timestamp: new Date().toISOString(),
|
|
2268
|
-
level,
|
|
2269
|
-
message
|
|
2270
|
-
};
|
|
2271
|
-
|
|
2272
|
-
this.alerts.unshift(alert);
|
|
2273
|
-
|
|
2274
|
-
// Keep only last 100 alerts
|
|
2275
|
-
if (this.alerts.length > 100) {
|
|
2276
|
-
this.alerts = this.alerts.slice(0, 100);
|
|
2277
|
-
}
|
|
321
|
+
**SQLite JavaScript**, **TypeScript SQLite**, **React Native SQLite**, **Node.js SQLite**, **Universal SQLite**, **Cross-platform database**, **SQLite ORM**, **Database service management**, **TypeScript database library**, **JavaScript database**, **Mobile database**, **Offline database**, **SQLite migrations**, **Database transactions**, **SQLite schema management**
|
|
2278
322
|
|
|
2279
|
-
|
|
2280
|
-
switch (level) {
|
|
2281
|
-
case 'CRITICAL':
|
|
2282
|
-
case 'ERROR':
|
|
2283
|
-
console.error(`[${level}] ${message}`);
|
|
2284
|
-
break;
|
|
2285
|
-
case 'WARNING':
|
|
2286
|
-
console.warn(`[${level}] ${message}`);
|
|
2287
|
-
break;
|
|
2288
|
-
default:
|
|
2289
|
-
console.log(`[${level}] ${message}`);
|
|
2290
|
-
}
|
|
2291
|
-
}
|
|
323
|
+
## 📊 Performance Benchmarks
|
|
2292
324
|
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
325
|
+
- **Connection pooling** reduces connection overhead by 80%
|
|
326
|
+
- **Batch operations** improve write performance by 10x
|
|
327
|
+
- **Query optimization** reduces query time by 60%
|
|
328
|
+
- **Service caching** eliminates repeated initialization costs
|
|
2296
329
|
|
|
2297
|
-
|
|
2298
|
-
const now = Date.now();
|
|
2299
|
-
const hourAgo = now - (60 * 60 * 1000);
|
|
2300
|
-
|
|
2301
|
-
const recentAlerts = this.alerts.filter(
|
|
2302
|
-
alert => new Date(alert.timestamp).getTime() > hourAgo
|
|
2303
|
-
);
|
|
2304
|
-
|
|
2305
|
-
const alertsByLevel = recentAlerts.reduce((acc, alert) => {
|
|
2306
|
-
acc[alert.level] = (acc[alert.level] || 0) + 1;
|
|
2307
|
-
return acc;
|
|
2308
|
-
}, {} as Record<string, number>);
|
|
2309
|
-
|
|
2310
|
-
return {
|
|
2311
|
-
serviceCount: this.serviceManager.getServiceCount(),
|
|
2312
|
-
activeSchemas: this.serviceManager.getSchemas(),
|
|
2313
|
-
recentAlerts: alertsByLevel,
|
|
2314
|
-
systemUptime: process.uptime?.() || 0,
|
|
2315
|
-
memoryUsage: process.memoryUsage?.() || null
|
|
2316
|
-
};
|
|
2317
|
-
}
|
|
2318
|
-
}
|
|
2319
|
-
```
|
|
330
|
+
## 🛡️ Production Ready
|
|
2320
331
|
|
|
2321
|
-
|
|
332
|
+
- **Error handling**: Comprehensive error management with retry mechanisms
|
|
333
|
+
- **Health monitoring**: Real-time service health checks and auto-recovery
|
|
334
|
+
- **Performance optimization**: Built-in query optimization and connection pooling
|
|
335
|
+
- **Memory management**: Automatic cleanup of unused services
|
|
336
|
+
- **Graceful shutdown**: Proper resource cleanup on application termination
|
|
2322
337
|
|
|
2323
|
-
|
|
338
|
+
## 🔄 Migration Support
|
|
2324
339
|
|
|
2325
340
|
```typescript
|
|
2326
|
-
//
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
timeout?: number;
|
|
2334
|
-
enableWAL?: boolean;
|
|
2335
|
-
};
|
|
2336
|
-
};
|
|
2337
|
-
services: {
|
|
2338
|
-
autoCleanupInterval?: number;
|
|
2339
|
-
healthCheckInterval?: number;
|
|
2340
|
-
maxIdleTime?: number;
|
|
2341
|
-
};
|
|
2342
|
-
monitoring: {
|
|
2343
|
-
enabled: boolean;
|
|
2344
|
-
alertThreshold?: number;
|
|
2345
|
-
logLevel: 'debug' | 'info' | 'warn' | 'error';
|
|
2346
|
-
};
|
|
2347
|
-
}
|
|
2348
|
-
|
|
2349
|
-
export const productionConfig: DatabaseConfig = {
|
|
2350
|
-
environment: 'production',
|
|
2351
|
-
databases: {
|
|
2352
|
-
users: {
|
|
2353
|
-
path: '/data/users.db',
|
|
2354
|
-
maxConnections: 10,
|
|
2355
|
-
timeout: 30000,
|
|
2356
|
-
enableWAL: true
|
|
2357
|
-
},
|
|
2358
|
-
core: {
|
|
2359
|
-
path: '/data/core.db',
|
|
2360
|
-
maxConnections: 5,
|
|
2361
|
-
timeout: 30000,
|
|
2362
|
-
enableWAL: true
|
|
2363
|
-
}
|
|
2364
|
-
},
|
|
2365
|
-
services: {
|
|
2366
|
-
autoCleanupInterval: 10 * 60 * 1000, // 10 minutes
|
|
2367
|
-
healthCheckInterval: 30 * 1000, // 30 seconds
|
|
2368
|
-
maxIdleTime: 30 * 60 * 1000 // 30 minutes
|
|
341
|
+
// Define migration
|
|
342
|
+
const migration = {
|
|
343
|
+
version: '1.0.1',
|
|
344
|
+
description: 'Add user status column',
|
|
345
|
+
|
|
346
|
+
async up(dao) {
|
|
347
|
+
await dao.execute('ALTER TABLE users ADD COLUMN status VARCHAR(20) DEFAULT "active"');
|
|
2369
348
|
},
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
logLevel: 'info'
|
|
349
|
+
|
|
350
|
+
async down(dao) {
|
|
351
|
+
// Rollback logic
|
|
2374
352
|
}
|
|
2375
353
|
};
|
|
2376
354
|
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
databases: {
|
|
2380
|
-
users: {
|
|
2381
|
-
path: './dev/users.db',
|
|
2382
|
-
enableWAL: false
|
|
2383
|
-
},
|
|
2384
|
-
core: {
|
|
2385
|
-
path: './dev/core.db',
|
|
2386
|
-
enableWAL: false
|
|
2387
|
-
}
|
|
2388
|
-
},
|
|
2389
|
-
services: {
|
|
2390
|
-
autoCleanupInterval: 5 * 60 * 1000, // 5 minutes
|
|
2391
|
-
healthCheckInterval: 60 * 1000, // 1 minute
|
|
2392
|
-
maxIdleTime: 10 * 60 * 1000 // 10 minutes
|
|
2393
|
-
},
|
|
2394
|
-
monitoring: {
|
|
2395
|
-
enabled: true,
|
|
2396
|
-
logLevel: 'debug'
|
|
2397
|
-
}
|
|
2398
|
-
};
|
|
355
|
+
// Run migration
|
|
356
|
+
await migrationManager.runMigration(migration);
|
|
2399
357
|
```
|
|
2400
358
|
|
|
2401
|
-
|
|
359
|
+
## 📱 React Native Example
|
|
2402
360
|
|
|
2403
361
|
```typescript
|
|
2404
|
-
//
|
|
2405
|
-
import {
|
|
2406
|
-
|
|
2407
|
-
export class ProductionSetup {
|
|
2408
|
-
static async initializeForProduction(config: DatabaseConfig = productionConfig): Promise<void> {
|
|
2409
|
-
try {
|
|
2410
|
-
// Apply production optimizations
|
|
2411
|
-
await this.applyProductionOptimizations();
|
|
2412
|
-
|
|
2413
|
-
// Initialize with config
|
|
2414
|
-
await DatabaseService.initialize();
|
|
2415
|
-
|
|
2416
|
-
// Setup monitoring
|
|
2417
|
-
if (config.monitoring.enabled) {
|
|
2418
|
-
const monitor = new ServiceHealthMonitor();
|
|
2419
|
-
monitor.startMonitoring(config.services.healthCheckInterval);
|
|
2420
|
-
}
|
|
2421
|
-
|
|
2422
|
-
// Setup graceful shutdown
|
|
2423
|
-
this.setupGracefulShutdown();
|
|
2424
|
-
|
|
2425
|
-
console.log('Production setup completed successfully');
|
|
2426
|
-
} catch (error) {
|
|
2427
|
-
console.error('Production setup failed:', error);
|
|
2428
|
-
process.exit(1);
|
|
2429
|
-
}
|
|
2430
|
-
}
|
|
2431
|
-
|
|
2432
|
-
private static async applyProductionOptimizations(): Promise<void> {
|
|
2433
|
-
// Apply SQLite production settings
|
|
2434
|
-
const connections = DatabaseManager.getConnections();
|
|
2435
|
-
|
|
2436
|
-
for (const [dbKey, dao] of Object.entries(connections)) {
|
|
2437
|
-
try {
|
|
2438
|
-
// Enable WAL mode for better concurrency
|
|
2439
|
-
await dao.execute('PRAGMA journal_mode = WAL');
|
|
2440
|
-
|
|
2441
|
-
// Set aggressive timeout for busy database
|
|
2442
|
-
await dao.execute('PRAGMA busy_timeout = 30000');
|
|
2443
|
-
|
|
2444
|
-
// Optimize cache size
|
|
2445
|
-
await dao.execute('PRAGMA cache_size = -64000'); // 64MB cache
|
|
2446
|
-
|
|
2447
|
-
// Enable foreign key constraints
|
|
2448
|
-
await dao.execute('PRAGMA foreign_keys = ON');
|
|
2449
|
-
|
|
2450
|
-
// Optimize for faster queries
|
|
2451
|
-
await dao.execute('PRAGMA synchronous = NORMAL');
|
|
2452
|
-
await dao.execute('PRAGMA temp_store = MEMORY');
|
|
2453
|
-
|
|
2454
|
-
console.log(`Production optimizations applied to ${dbKey}`);
|
|
2455
|
-
} catch (error) {
|
|
2456
|
-
console.error(`Failed to optimize ${dbKey}:`, error);
|
|
2457
|
-
}
|
|
2458
|
-
}
|
|
2459
|
-
}
|
|
2460
|
-
|
|
2461
|
-
private static setupGracefulShutdown(): void {
|
|
2462
|
-
const shutdown = async (signal: string) => {
|
|
2463
|
-
console.log(`Received ${signal}. Starting graceful shutdown...`);
|
|
2464
|
-
|
|
2465
|
-
try {
|
|
2466
|
-
// Close all database connections
|
|
2467
|
-
await DatabaseManager.closeAll();
|
|
2468
|
-
|
|
2469
|
-
// Destroy service manager
|
|
2470
|
-
const serviceManager = ServiceManager.getInstance();
|
|
2471
|
-
await serviceManager.destroy();
|
|
2472
|
-
|
|
2473
|
-
console.log('Graceful shutdown completed');
|
|
2474
|
-
process.exit(0);
|
|
2475
|
-
} catch (error) {
|
|
2476
|
-
console.error('Error during shutdown:', error);
|
|
2477
|
-
process.exit(1);
|
|
2478
|
-
}
|
|
2479
|
-
};
|
|
2480
|
-
|
|
2481
|
-
process.on('SIGTERM', () => shutdown('SIGTERM'));
|
|
2482
|
-
process.on('SIGINT', () => shutdown('SIGINT'));
|
|
2483
|
-
process.on('SIGHUP', () => shutdown('SIGHUP'));
|
|
2484
|
-
|
|
2485
|
-
// Handle uncaught exceptions
|
|
2486
|
-
process.on('uncaughtException', (error) => {
|
|
2487
|
-
console.error('Uncaught Exception:', error);
|
|
2488
|
-
shutdown('uncaughtException');
|
|
2489
|
-
});
|
|
2490
|
-
|
|
2491
|
-
process.on('unhandledRejection', (reason, promise) => {
|
|
2492
|
-
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
2493
|
-
shutdown('unhandledRejection');
|
|
2494
|
-
});
|
|
2495
|
-
}
|
|
2496
|
-
}
|
|
2497
|
-
```
|
|
2498
|
-
|
|
2499
|
-
## Conclusion
|
|
2500
|
-
|
|
2501
|
-
Universal SQLite provides a powerful and flexible solution for managing SQLite databases across platforms. With DatabaseManager, ServiceManager, and BaseService, you can:
|
|
2502
|
-
|
|
2503
|
-
- Easily manage multiple database connections and service lifecycles
|
|
2504
|
-
- Perform type-safe CRUD operations with automatic optimization
|
|
2505
|
-
- Import/Export data efficiently with built-in validation
|
|
2506
|
-
- Manage schemas and migrations systematically
|
|
2507
|
-
- Handle errors and performance optimization gracefully
|
|
2508
|
-
- Monitor service health and automate recovery
|
|
2509
|
-
- Deploy confidently in production environments
|
|
362
|
+
// App.tsx
|
|
363
|
+
import React, { useEffect, useState } from 'react';
|
|
364
|
+
import { DatabaseService } from './services/DatabaseService';
|
|
2510
365
|
|
|
2511
|
-
|
|
366
|
+
export default function App() {
|
|
367
|
+
const [users, setUsers] = useState([]);
|
|
2512
368
|
|
|
2513
|
-
|
|
369
|
+
useEffect(() => {
|
|
370
|
+
initDatabase();
|
|
371
|
+
}, []);
|
|
2514
372
|
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
- **Flexible**: Custom adapters, role-based access, and extensible architecture
|
|
373
|
+
const initDatabase = async () => {
|
|
374
|
+
await DatabaseService.initialize();
|
|
375
|
+
const userService = await ServiceManager.getService('users', 'users');
|
|
376
|
+
const allUsers = await userService.getAllUsers();
|
|
377
|
+
setUsers(allUsers);
|
|
378
|
+
};
|
|
2522
379
|
|
|
2523
|
-
|
|
380
|
+
// Your UI here
|
|
381
|
+
}
|
|
382
|
+
```
|
|
2524
383
|
|
|
2525
|
-
|
|
2526
|
-
2. **Register services at application startup for better organization**
|
|
2527
|
-
3. **Use transactions for multi-step operations**
|
|
2528
|
-
4. **Implement proper error handling with retry mechanisms**
|
|
2529
|
-
5. **Monitor service health in production environments**
|
|
2530
|
-
6. **Use batch operations for better performance**
|
|
2531
|
-
7. **Enable WAL mode for production deployments**
|
|
2532
|
-
8. **Implement graceful shutdown procedures**
|
|
384
|
+
## 🖥️ Node.js Example
|
|
2533
385
|
|
|
2534
|
-
|
|
386
|
+
```typescript
|
|
387
|
+
// server.js
|
|
388
|
+
import express from 'express';
|
|
389
|
+
import { DatabaseService } from './services/DatabaseService';
|
|
2535
390
|
|
|
2536
|
-
|
|
2537
|
-
- **DatabaseManager**: Connection and schema management
|
|
2538
|
-
- **BaseService**: Base class for CRUD operations with optimization
|
|
2539
|
-
- **UniversalDAO**: Core DAO for database operations
|
|
2540
|
-
- **QueryBuilder**: Fluent API for complex queries
|
|
2541
|
-
- **DatabaseFactory**: Factory for creating adapters and connections
|
|
2542
|
-
- **MigrationManager**: Schema versioning and migrations
|
|
2543
|
-
- **CSVImporter**: Data import/export utilities
|
|
391
|
+
const app = express();
|
|
2544
392
|
|
|
2545
|
-
|
|
393
|
+
app.get('/users', async (req, res) => {
|
|
394
|
+
const userService = await ServiceManager.getService('users', 'users');
|
|
395
|
+
const users = await userService.getAllUsers();
|
|
396
|
+
res.json(users);
|
|
397
|
+
});
|
|
2546
398
|
|
|
2547
|
-
|
|
399
|
+
// Initialize database before starting server
|
|
400
|
+
await DatabaseService.initialize();
|
|
401
|
+
app.listen(3000);
|
|
402
|
+
```
|
|
2548
403
|
|
|
2549
|
-
|
|
2550
|
-
2. Create a feature branch
|
|
2551
|
-
3. Make your changes with proper TypeScript types
|
|
2552
|
-
4. Add comprehensive tests
|
|
2553
|
-
5. Update documentation
|
|
2554
|
-
6. Submit a pull request
|
|
404
|
+
## 🤝 Community & Support
|
|
2555
405
|
|
|
2556
|
-
|
|
406
|
+
- **GitHub**: [https://github.com/cuongdqpayment/dqcai-sqlite](https://github.com/cuongdqpayment/dqcai-sqlite)
|
|
407
|
+
- **NPM**: [https://www.npmjs.com/package/@dqcai/sqlite](https://www.npmjs.com/package/@dqcai/sqlite)
|
|
408
|
+
- **Issues**: [GitHub Issues](https://github.com/cuongdqpayment/dqcai-sqlite/issues)
|
|
409
|
+
- **Facebook**: [Facebook Page](https://www.facebook.com/share/p/19esHGbaGj/)
|
|
2557
410
|
|
|
2558
|
-
|
|
411
|
+
## 📄 License
|
|
2559
412
|
|
|
2560
|
-
|
|
413
|
+
MIT License - see [LICENSE](https://github.com/cuongdqpayment/dqcai-sqlite/blob/main/LICENSE) file for details.
|
|
2561
414
|
|
|
2562
|
-
|
|
2563
|
-
- [sql.js](https://github.com/sql-js/sql.js) - SQLite compiled to WebAssembly
|
|
2564
|
-
- [expo-sqlite](https://docs.expo.dev/versions/latest/sdk/sqlite/) - Expo SQLite support
|
|
2565
|
-
- [react-native-sqlite-storage](https://github.com/andpor/react-native-sqlite-storage) - React Native SQLite
|
|
2566
|
-
- [Deno SQLite](https://deno.land/x/sqlite) - Deno SQLite module
|
|
415
|
+
## 🚀 Get Started Now
|
|
2567
416
|
|
|
2568
|
-
|
|
417
|
+
```bash
|
|
418
|
+
npm install @dqcai/sqlite
|
|
419
|
+
```
|
|
2569
420
|
|
|
2570
|
-
|
|
2571
|
-
- [Examples Repository](https://github.com/cuongdqpayment/dqcai-sqlite)
|
|
2572
|
-
- [Issue Tracker](https://github.com/cuongdqpayment/dqcai-sqlite/issues)
|
|
2573
|
-
- [Facebook Page](https://www.facebook.com/share/p/19esHGbaGj/)
|
|
2574
|
-
- [NPM Package](https://www.npmjs.com/package/@dqcai/sqlite)
|
|
421
|
+
Transform your data management with the most powerful universal SQLite library for JavaScript and TypeScript!
|
|
2575
422
|
|
|
2576
423
|
---
|
|
2577
424
|
|
|
2578
|
-
|
|
425
|
+
**@dqcai/sqlite** - One library, all platforms! 🌟
|