@dqcai/sqlite 2.0.5 → 2.1.0
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 +954 -192
- package/lib/core/base-service.d.ts.map +1 -1
- package/lib/core/index.d.ts +1 -0
- package/lib/core/index.d.ts.map +1 -1
- package/lib/core/service-manager.d.ts +151 -0
- package/lib/core/service-manager.d.ts.map +1 -0
- package/lib/index.d.ts +1 -0
- 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 +15 -15
- package/lib/index.mjs.map +1 -1
- package/lib/index.umd.js +1 -1
- package/lib/index.umd.js.map +1 -1
- package/lib/types.d.ts +1 -0
- package/lib/types.d.ts.map +1 -1
- package/lib/utils/migration-manager.d.ts +3 -3
- package/lib/utils/migration-manager.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,50 +4,51 @@
|
|
|
4
4
|

|
|
5
5
|

|
|
6
6
|
|
|
7
|
-
UniversalSQLite
|
|
7
|
+
UniversalSQLite is a comprehensive, cross-platform SQLite library designed to work seamlessly across environments like Browser, Node.js, Deno, Bun, and React Native. The library provides a unified interface for managing SQLite databases, including schema creation, CRUD operations, advanced queries, migrations, data import/export, transaction management, and service lifecycle management. It uses the DAO (Data Access Object) pattern to separate data access logic, supports role-based access control, and integrates easily with various frameworks.
|
|
8
8
|
|
|
9
9
|
## Features
|
|
10
10
|
|
|
11
|
-
- **Cross-Platform Support**:
|
|
12
|
-
- **Schema-Based Management**:
|
|
13
|
-
- **DAO Pattern**: UniversalDAO
|
|
14
|
-
- **Query Builder**:
|
|
15
|
-
- **Migration System**:
|
|
16
|
-
- **Data Import/Export**:
|
|
17
|
-
- **Role-Based Access**:
|
|
18
|
-
- **Transaction Management**:
|
|
19
|
-
- **
|
|
20
|
-
- **
|
|
21
|
-
- **
|
|
22
|
-
- **
|
|
23
|
-
- **
|
|
11
|
+
- **Cross-Platform Support**: Works on Browser, Node.js, Deno, Bun, React Native (iOS/Android/Windows).
|
|
12
|
+
- **Schema-Based Management**: Create and manage databases from JSON schemas.
|
|
13
|
+
- **DAO Pattern**: UniversalDAO for CRUD operations, queries, and transactions.
|
|
14
|
+
- **Query Builder**: Build complex queries with join, where, group by, having, union, CTE.
|
|
15
|
+
- **Migration System**: Manage migrations with up/down scripts.
|
|
16
|
+
- **Data Import/Export**: Support import from CSV/JSON with mapping, validation, and export to CSV.
|
|
17
|
+
- **Role-Based Access**: Manage connections based on user roles.
|
|
18
|
+
- **Transaction Management**: Support single and cross-schema transactions.
|
|
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.
|
|
24
25
|
|
|
25
26
|
## Installation
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
Install via npm or yarn:
|
|
28
29
|
|
|
29
30
|
```bash
|
|
30
|
-
npm install @dqcai/sqlite@2.
|
|
31
|
-
#
|
|
32
|
-
yarn add @dqcai/sqlite@2.
|
|
31
|
+
npm install @dqcai/sqlite@2.1.0
|
|
32
|
+
# or
|
|
33
|
+
yarn add @dqcai/sqlite@2.1.0
|
|
33
34
|
```
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
For React Native, ensure you install the necessary dependencies for the adapter (if using specific adapters like react-native-sqlite-storage).
|
|
36
37
|
|
|
37
|
-
##
|
|
38
|
+
## Quick Start
|
|
38
39
|
|
|
39
40
|
```bash
|
|
40
41
|
npm install @dqcai/sqlite
|
|
41
42
|
```
|
|
42
43
|
|
|
43
|
-
## 1.
|
|
44
|
+
## 1. Database Schema Configuration
|
|
44
45
|
|
|
45
|
-
|
|
46
|
+
First, define the schema for your database:
|
|
46
47
|
|
|
47
48
|
```typescript
|
|
48
49
|
import { DatabaseSchema } from '@dqcai/sqlite';
|
|
49
50
|
|
|
50
|
-
// Schema
|
|
51
|
+
// Schema for users database
|
|
51
52
|
const userSchema: DatabaseSchema = {
|
|
52
53
|
version: "1.0.0",
|
|
53
54
|
database_name: "users",
|
|
@@ -158,7 +159,7 @@ const userSchema: DatabaseSchema = {
|
|
|
158
159
|
}
|
|
159
160
|
};
|
|
160
161
|
|
|
161
|
-
//
|
|
162
|
+
// Core schema for system
|
|
162
163
|
const coreSchema: DatabaseSchema = {
|
|
163
164
|
version: "1.0.0",
|
|
164
165
|
database_name: "core",
|
|
@@ -187,17 +188,211 @@ const coreSchema: DatabaseSchema = {
|
|
|
187
188
|
};
|
|
188
189
|
```
|
|
189
190
|
|
|
190
|
-
## 2.
|
|
191
|
+
## 2. Service Management with ServiceManager
|
|
191
192
|
|
|
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
|
|
193
388
|
|
|
194
389
|
```bash
|
|
195
390
|
npm install react-native-sqlite-2
|
|
196
|
-
#
|
|
391
|
+
# Or
|
|
197
392
|
npm install react-native-sqlite-storage
|
|
198
393
|
```
|
|
199
394
|
|
|
200
|
-
###
|
|
395
|
+
### Create Adapter for React Native
|
|
201
396
|
|
|
202
397
|
```typescript
|
|
203
398
|
// adapters/ReactNativeAdapter.ts
|
|
@@ -256,18 +451,19 @@ class ReactNativeConnection {
|
|
|
256
451
|
}
|
|
257
452
|
|
|
258
453
|
async close(): Promise<void> {
|
|
259
|
-
// React Native SQLite
|
|
454
|
+
// React Native SQLite doesn't need manual close
|
|
260
455
|
return Promise.resolve();
|
|
261
456
|
}
|
|
262
457
|
}
|
|
263
458
|
```
|
|
264
459
|
|
|
265
|
-
###
|
|
460
|
+
### Initialize DatabaseManager (React Native)
|
|
266
461
|
|
|
267
462
|
```typescript
|
|
268
463
|
// services/DatabaseService.ts
|
|
269
464
|
import { DatabaseManager, DatabaseFactory } from '@dqcai/sqlite';
|
|
270
465
|
import { ReactNativeAdapter } from '../adapters/ReactNativeAdapter';
|
|
466
|
+
import { ServiceRegistration } from './ServiceRegistration';
|
|
271
467
|
|
|
272
468
|
export class DatabaseService {
|
|
273
469
|
private static isInitialized = false;
|
|
@@ -275,16 +471,16 @@ export class DatabaseService {
|
|
|
275
471
|
static async initialize() {
|
|
276
472
|
if (this.isInitialized) return;
|
|
277
473
|
|
|
278
|
-
//
|
|
474
|
+
// Register adapter
|
|
279
475
|
DatabaseFactory.registerAdapter(new ReactNativeAdapter());
|
|
280
476
|
|
|
281
|
-
//
|
|
477
|
+
// Register schemas
|
|
282
478
|
DatabaseManager.registerSchemas({
|
|
283
479
|
core: coreSchema,
|
|
284
480
|
users: userSchema
|
|
285
481
|
});
|
|
286
482
|
|
|
287
|
-
//
|
|
483
|
+
// Register roles
|
|
288
484
|
DatabaseManager.registerRoles([
|
|
289
485
|
{
|
|
290
486
|
roleName: 'admin',
|
|
@@ -299,9 +495,12 @@ export class DatabaseService {
|
|
|
299
495
|
}
|
|
300
496
|
]);
|
|
301
497
|
|
|
302
|
-
//
|
|
498
|
+
// Initialize core database
|
|
303
499
|
await DatabaseManager.initializeCoreConnection();
|
|
304
500
|
|
|
501
|
+
// Register all services
|
|
502
|
+
ServiceRegistration.registerAllServices();
|
|
503
|
+
|
|
305
504
|
this.isInitialized = true;
|
|
306
505
|
console.log('DatabaseService initialized for React Native');
|
|
307
506
|
}
|
|
@@ -321,17 +520,17 @@ export class DatabaseService {
|
|
|
321
520
|
}
|
|
322
521
|
```
|
|
323
522
|
|
|
324
|
-
##
|
|
523
|
+
## 4. Setup for Node.js
|
|
325
524
|
|
|
326
|
-
###
|
|
525
|
+
### Install dependencies
|
|
327
526
|
|
|
328
527
|
```bash
|
|
329
528
|
npm install sqlite3
|
|
330
|
-
#
|
|
529
|
+
# Or
|
|
331
530
|
npm install better-sqlite3
|
|
332
531
|
```
|
|
333
532
|
|
|
334
|
-
###
|
|
533
|
+
### Create Adapter for Node.js
|
|
335
534
|
|
|
336
535
|
```typescript
|
|
337
536
|
// adapters/NodeAdapter.ts
|
|
@@ -348,7 +547,7 @@ export class NodeAdapter extends BaseAdapter {
|
|
|
348
547
|
async connect(dbPath: string): Promise<any> {
|
|
349
548
|
const fullPath = path.resolve(dbPath);
|
|
350
549
|
|
|
351
|
-
//
|
|
550
|
+
// Create directory if it doesn't exist
|
|
352
551
|
const dir = path.dirname(fullPath);
|
|
353
552
|
if (!fs.existsSync(dir)) {
|
|
354
553
|
fs.mkdirSync(dir, { recursive: true });
|
|
@@ -414,12 +613,13 @@ class NodeConnection {
|
|
|
414
613
|
}
|
|
415
614
|
```
|
|
416
615
|
|
|
417
|
-
###
|
|
616
|
+
### Initialize DatabaseManager (Node.js)
|
|
418
617
|
|
|
419
618
|
```typescript
|
|
420
619
|
// services/DatabaseService.ts
|
|
421
620
|
import { DatabaseManager, DatabaseFactory } from '@dqcai/sqlite';
|
|
422
621
|
import { NodeAdapter } from '../adapters/NodeAdapter';
|
|
622
|
+
import { ServiceRegistration } from './ServiceRegistration';
|
|
423
623
|
import path from 'path';
|
|
424
624
|
|
|
425
625
|
export class DatabaseService {
|
|
@@ -429,16 +629,16 @@ export class DatabaseService {
|
|
|
429
629
|
static async initialize() {
|
|
430
630
|
if (this.isInitialized) return;
|
|
431
631
|
|
|
432
|
-
//
|
|
632
|
+
// Register adapter
|
|
433
633
|
DatabaseFactory.registerAdapter(new NodeAdapter());
|
|
434
634
|
|
|
435
|
-
//
|
|
635
|
+
// Register schemas
|
|
436
636
|
DatabaseManager.registerSchemas({
|
|
437
637
|
core: coreSchema,
|
|
438
638
|
users: userSchema
|
|
439
639
|
});
|
|
440
640
|
|
|
441
|
-
//
|
|
641
|
+
// Register roles
|
|
442
642
|
DatabaseManager.registerRoles([
|
|
443
643
|
{
|
|
444
644
|
roleName: 'admin',
|
|
@@ -453,9 +653,12 @@ export class DatabaseService {
|
|
|
453
653
|
}
|
|
454
654
|
]);
|
|
455
655
|
|
|
456
|
-
//
|
|
656
|
+
// Initialize core database
|
|
457
657
|
await DatabaseManager.initializeCoreConnection();
|
|
458
658
|
|
|
659
|
+
// Register all services
|
|
660
|
+
ServiceRegistration.registerAllServices();
|
|
661
|
+
|
|
459
662
|
this.isInitialized = true;
|
|
460
663
|
console.log('DatabaseService initialized for Node.js');
|
|
461
664
|
}
|
|
@@ -475,7 +678,7 @@ export class DatabaseService {
|
|
|
475
678
|
}
|
|
476
679
|
```
|
|
477
680
|
|
|
478
|
-
##
|
|
681
|
+
## 5. Creating Services with BaseService
|
|
479
682
|
|
|
480
683
|
### User Service
|
|
481
684
|
|
|
@@ -502,21 +705,21 @@ interface Profile {
|
|
|
502
705
|
}
|
|
503
706
|
|
|
504
707
|
export class UserService extends BaseService<User> {
|
|
505
|
-
constructor() {
|
|
506
|
-
super('users', 'users');
|
|
708
|
+
constructor(schemaName?: string, tableName?: string) {
|
|
709
|
+
super(schemaName || 'users', tableName || 'users');
|
|
507
710
|
this.setPrimaryKeyFields(['id']);
|
|
508
711
|
}
|
|
509
712
|
|
|
510
|
-
//
|
|
713
|
+
// Create new user
|
|
511
714
|
async createUser(userData: Omit<User, 'id' | 'created_at' | 'updated_at'>): Promise<User | null> {
|
|
512
715
|
try {
|
|
513
|
-
//
|
|
716
|
+
// Check if email already exists
|
|
514
717
|
const existingUser = await this.findFirst({ email: userData.email });
|
|
515
718
|
if (existingUser) {
|
|
516
719
|
throw new Error('Email already exists');
|
|
517
720
|
}
|
|
518
721
|
|
|
519
|
-
//
|
|
722
|
+
// Check if username already exists
|
|
520
723
|
const existingUsername = await this.findFirst({ username: userData.username });
|
|
521
724
|
if (existingUsername) {
|
|
522
725
|
throw new Error('Username already exists');
|
|
@@ -534,7 +737,7 @@ export class UserService extends BaseService<User> {
|
|
|
534
737
|
}
|
|
535
738
|
}
|
|
536
739
|
|
|
537
|
-
//
|
|
740
|
+
// Update user
|
|
538
741
|
async updateUser(id: number, userData: Partial<User>): Promise<User | null> {
|
|
539
742
|
try {
|
|
540
743
|
const updatedUser = await this.update(id, {
|
|
@@ -548,17 +751,17 @@ export class UserService extends BaseService<User> {
|
|
|
548
751
|
}
|
|
549
752
|
}
|
|
550
753
|
|
|
551
|
-
//
|
|
754
|
+
// Find user by email
|
|
552
755
|
async findByEmail(email: string): Promise<User | null> {
|
|
553
756
|
return await this.findFirst({ email });
|
|
554
757
|
}
|
|
555
758
|
|
|
556
|
-
//
|
|
759
|
+
// Find user by username
|
|
557
760
|
async findByUsername(username: string): Promise<User | null> {
|
|
558
761
|
return await this.findFirst({ username });
|
|
559
762
|
}
|
|
560
763
|
|
|
561
|
-
//
|
|
764
|
+
// Get all users with pagination
|
|
562
765
|
async getAllUsers(page: number = 1, limit: number = 10): Promise<User[]> {
|
|
563
766
|
const offset = (page - 1) * limit;
|
|
564
767
|
return await this.findAll({}, {
|
|
@@ -568,21 +771,21 @@ export class UserService extends BaseService<User> {
|
|
|
568
771
|
});
|
|
569
772
|
}
|
|
570
773
|
|
|
571
|
-
//
|
|
774
|
+
// Soft delete user
|
|
572
775
|
async softDeleteUser(id: number): Promise<boolean> {
|
|
573
776
|
const result = await this.update(id, {
|
|
574
777
|
updated_at: new Date().toISOString(),
|
|
575
|
-
// deleted_at: new Date().toISOString() //
|
|
778
|
+
// deleted_at: new Date().toISOString() // if this field exists in schema
|
|
576
779
|
});
|
|
577
780
|
return result !== null;
|
|
578
781
|
}
|
|
579
782
|
|
|
580
|
-
//
|
|
783
|
+
// Count total users
|
|
581
784
|
async getTotalUsers(): Promise<number> {
|
|
582
785
|
return await this.count();
|
|
583
786
|
}
|
|
584
787
|
|
|
585
|
-
//
|
|
788
|
+
// Search users
|
|
586
789
|
async searchUsers(searchTerm: string): Promise<User[]> {
|
|
587
790
|
const dao = await this.init().then(() => this.dao!);
|
|
588
791
|
const sql = `
|
|
@@ -597,27 +800,27 @@ export class UserService extends BaseService<User> {
|
|
|
597
800
|
}
|
|
598
801
|
|
|
599
802
|
export class ProfileService extends BaseService<Profile> {
|
|
600
|
-
constructor() {
|
|
601
|
-
super('users', 'profiles');
|
|
803
|
+
constructor(schemaName?: string, tableName?: string) {
|
|
804
|
+
super(schemaName || 'users', tableName || 'profiles');
|
|
602
805
|
this.setPrimaryKeyFields(['id']);
|
|
603
806
|
}
|
|
604
807
|
|
|
605
|
-
//
|
|
808
|
+
// Create profile for user
|
|
606
809
|
async createProfile(profileData: Omit<Profile, 'id'>): Promise<Profile | null> {
|
|
607
810
|
return await this.create(profileData);
|
|
608
811
|
}
|
|
609
812
|
|
|
610
|
-
//
|
|
813
|
+
// Get profile by user_id
|
|
611
814
|
async getProfileByUserId(userId: number): Promise<Profile | null> {
|
|
612
815
|
return await this.findFirst({ user_id: userId });
|
|
613
816
|
}
|
|
614
817
|
|
|
615
|
-
//
|
|
818
|
+
// Update profile
|
|
616
819
|
async updateProfile(id: number, profileData: Partial<Profile>): Promise<Profile | null> {
|
|
617
820
|
return await this.update(id, profileData);
|
|
618
821
|
}
|
|
619
822
|
|
|
620
|
-
//
|
|
823
|
+
// Get user with profile
|
|
621
824
|
async getUserWithProfile(userId: number): Promise<any> {
|
|
622
825
|
const dao = await this.init().then(() => this.dao!);
|
|
623
826
|
const sql = `
|
|
@@ -647,18 +850,18 @@ interface Setting {
|
|
|
647
850
|
}
|
|
648
851
|
|
|
649
852
|
export class SettingsService extends BaseService<Setting> {
|
|
650
|
-
constructor() {
|
|
651
|
-
super('core', 'settings');
|
|
853
|
+
constructor(schemaName?: string, tableName?: string) {
|
|
854
|
+
super(schemaName || 'core', tableName || 'settings');
|
|
652
855
|
this.setPrimaryKeyFields(['key']);
|
|
653
856
|
}
|
|
654
857
|
|
|
655
|
-
//
|
|
858
|
+
// Get setting value
|
|
656
859
|
async getSetting(key: string): Promise<string | null> {
|
|
657
860
|
const setting = await this.findById(key);
|
|
658
861
|
return setting?.value || null;
|
|
659
862
|
}
|
|
660
863
|
|
|
661
|
-
//
|
|
864
|
+
// Set setting value
|
|
662
865
|
async setSetting(key: string, value: string, description?: string): Promise<void> {
|
|
663
866
|
const existing = await this.findById(key);
|
|
664
867
|
|
|
@@ -669,19 +872,19 @@ export class SettingsService extends BaseService<Setting> {
|
|
|
669
872
|
}
|
|
670
873
|
}
|
|
671
874
|
|
|
672
|
-
//
|
|
875
|
+
// Get all settings
|
|
673
876
|
async getAllSettings(): Promise<Setting[]> {
|
|
674
877
|
return await this.findAll({}, {
|
|
675
878
|
orderBy: [{ name: 'key', direction: 'ASC' }]
|
|
676
879
|
});
|
|
677
880
|
}
|
|
678
881
|
|
|
679
|
-
//
|
|
882
|
+
// Delete setting
|
|
680
883
|
async deleteSetting(key: string): Promise<boolean> {
|
|
681
884
|
return await this.delete(key);
|
|
682
885
|
}
|
|
683
886
|
|
|
684
|
-
//
|
|
887
|
+
// Get multiple settings at once
|
|
685
888
|
async getMultipleSettings(keys: string[]): Promise<Record<string, string>> {
|
|
686
889
|
const dao = await this.init().then(() => this.dao!);
|
|
687
890
|
const placeholders = keys.map(() => '?').join(',');
|
|
@@ -698,45 +901,43 @@ export class SettingsService extends BaseService<Setting> {
|
|
|
698
901
|
}
|
|
699
902
|
```
|
|
700
903
|
|
|
701
|
-
##
|
|
904
|
+
## 6. Using in Applications
|
|
702
905
|
|
|
703
|
-
###
|
|
906
|
+
### In React Native
|
|
704
907
|
|
|
705
908
|
```typescript
|
|
706
|
-
// App.tsx
|
|
909
|
+
// App.tsx or index.js
|
|
707
910
|
import React, { useEffect, useState } from 'react';
|
|
708
911
|
import { View, Text, Button, Alert } from 'react-native';
|
|
709
912
|
import { DatabaseService } from './services/DatabaseService';
|
|
913
|
+
import { ServiceRegistration } from './services/ServiceRegistration';
|
|
914
|
+
import { ServiceMonitor } from './services/ServiceMonitor';
|
|
710
915
|
import { UserService, ProfileService } from './services/UserService';
|
|
711
916
|
import { SettingsService } from './services/CoreService';
|
|
712
917
|
|
|
713
918
|
const App = () => {
|
|
714
919
|
const [isDbReady, setIsDbReady] = useState(false);
|
|
715
|
-
const [
|
|
716
|
-
const [profileService] = useState(new ProfileService());
|
|
717
|
-
const [settingsService] = useState(new SettingsService());
|
|
920
|
+
const [serviceMonitor] = useState(new ServiceMonitor());
|
|
718
921
|
|
|
719
922
|
useEffect(() => {
|
|
720
923
|
initializeDatabase();
|
|
721
924
|
|
|
722
925
|
return () => {
|
|
723
|
-
// Cleanup
|
|
926
|
+
// Cleanup when component unmounts
|
|
724
927
|
DatabaseService.closeAll();
|
|
725
928
|
};
|
|
726
929
|
}, []);
|
|
727
930
|
|
|
728
931
|
const initializeDatabase = async () => {
|
|
729
932
|
try {
|
|
730
|
-
//
|
|
933
|
+
// Initialize database
|
|
731
934
|
await DatabaseService.initialize();
|
|
732
935
|
|
|
733
|
-
//
|
|
936
|
+
// Set role for current user
|
|
734
937
|
await DatabaseService.setUserRole(['user']);
|
|
735
938
|
|
|
736
|
-
//
|
|
737
|
-
await
|
|
738
|
-
await profileService.init();
|
|
739
|
-
await settingsService.init();
|
|
939
|
+
// Start service monitoring
|
|
940
|
+
await serviceMonitor.startPeriodicHealthCheck(60000); // Every minute
|
|
740
941
|
|
|
741
942
|
setIsDbReady(true);
|
|
742
943
|
console.log('Database ready!');
|
|
@@ -750,6 +951,9 @@ const App = () => {
|
|
|
750
951
|
if (!isDbReady) return;
|
|
751
952
|
|
|
752
953
|
try {
|
|
954
|
+
const userService = await ServiceRegistration.getService<UserService>('users', 'users');
|
|
955
|
+
const profileService = await ServiceRegistration.getService<ProfileService>('users', 'profiles');
|
|
956
|
+
|
|
753
957
|
const newUser = await userService.createUser({
|
|
754
958
|
username: 'john_doe',
|
|
755
959
|
email: 'john@example.com',
|
|
@@ -757,7 +961,7 @@ const App = () => {
|
|
|
757
961
|
});
|
|
758
962
|
|
|
759
963
|
if (newUser) {
|
|
760
|
-
//
|
|
964
|
+
// Create profile for user
|
|
761
965
|
await profileService.createProfile({
|
|
762
966
|
user_id: newUser.id!,
|
|
763
967
|
first_name: 'John',
|
|
@@ -777,6 +981,7 @@ const App = () => {
|
|
|
777
981
|
if (!isDbReady) return;
|
|
778
982
|
|
|
779
983
|
try {
|
|
984
|
+
const userService = await ServiceRegistration.getService<UserService>('users', 'users');
|
|
780
985
|
const users = await userService.getAllUsers(1, 10);
|
|
781
986
|
console.log('Users:', users);
|
|
782
987
|
Alert.alert('Users', `Found ${users.length} users`);
|
|
@@ -789,6 +994,8 @@ const App = () => {
|
|
|
789
994
|
if (!isDbReady) return;
|
|
790
995
|
|
|
791
996
|
try {
|
|
997
|
+
const settingsService = await ServiceRegistration.getService<SettingsService>('core', 'settings');
|
|
998
|
+
|
|
792
999
|
await settingsService.setSetting(
|
|
793
1000
|
'app_version',
|
|
794
1001
|
'1.0.0',
|
|
@@ -802,6 +1009,21 @@ const App = () => {
|
|
|
802
1009
|
}
|
|
803
1010
|
};
|
|
804
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
|
+
|
|
805
1027
|
return (
|
|
806
1028
|
<View style={{ flex: 1, justifyContent: 'center', padding: 20 }}>
|
|
807
1029
|
<Text>Database Status: {isDbReady ? 'Ready' : 'Initializing...'}</Text>
|
|
@@ -823,6 +1045,12 @@ const App = () => {
|
|
|
823
1045
|
onPress={handleSetSetting}
|
|
824
1046
|
disabled={!isDbReady}
|
|
825
1047
|
/>
|
|
1048
|
+
|
|
1049
|
+
<Button
|
|
1050
|
+
title="Get Service Stats"
|
|
1051
|
+
onPress={handleGetServiceStats}
|
|
1052
|
+
disabled={!isDbReady}
|
|
1053
|
+
/>
|
|
826
1054
|
</View>
|
|
827
1055
|
);
|
|
828
1056
|
};
|
|
@@ -830,38 +1058,36 @@ const App = () => {
|
|
|
830
1058
|
export default App;
|
|
831
1059
|
```
|
|
832
1060
|
|
|
833
|
-
###
|
|
1061
|
+
### In Node.js
|
|
834
1062
|
|
|
835
1063
|
```typescript
|
|
836
1064
|
// app.ts
|
|
837
1065
|
import express from 'express';
|
|
838
1066
|
import { DatabaseService } from './services/DatabaseService';
|
|
1067
|
+
import { ServiceRegistration } from './services/ServiceRegistration';
|
|
1068
|
+
import { ServiceMonitor } from './services/ServiceMonitor';
|
|
839
1069
|
import { UserService, ProfileService } from './services/UserService';
|
|
840
1070
|
import { SettingsService } from './services/CoreService';
|
|
841
1071
|
|
|
842
1072
|
const app = express();
|
|
843
1073
|
app.use(express.json());
|
|
844
1074
|
|
|
845
|
-
//
|
|
846
|
-
const
|
|
847
|
-
const profileService = new ProfileService();
|
|
848
|
-
const settingsService = new SettingsService();
|
|
1075
|
+
// Service monitor
|
|
1076
|
+
const serviceMonitor = new ServiceMonitor();
|
|
849
1077
|
|
|
850
|
-
//
|
|
1078
|
+
// Initialize database when starting server
|
|
851
1079
|
async function initializeApp() {
|
|
852
1080
|
try {
|
|
853
1081
|
console.log('Initializing database...');
|
|
854
1082
|
|
|
855
|
-
//
|
|
1083
|
+
// Initialize DatabaseService
|
|
856
1084
|
await DatabaseService.initialize();
|
|
857
1085
|
|
|
858
|
-
// Set role
|
|
1086
|
+
// Set admin role for server
|
|
859
1087
|
await DatabaseService.setUserRole(['admin']);
|
|
860
1088
|
|
|
861
|
-
//
|
|
862
|
-
await
|
|
863
|
-
await profileService.init();
|
|
864
|
-
await settingsService.init();
|
|
1089
|
+
// Start service monitoring
|
|
1090
|
+
await serviceMonitor.startPeriodicHealthCheck(60000); // Every minute
|
|
865
1091
|
|
|
866
1092
|
console.log('Database initialized successfully');
|
|
867
1093
|
} catch (error) {
|
|
@@ -872,7 +1098,7 @@ async function initializeApp() {
|
|
|
872
1098
|
|
|
873
1099
|
// API Routes
|
|
874
1100
|
|
|
875
|
-
// POST /users -
|
|
1101
|
+
// POST /users - Create new user
|
|
876
1102
|
app.post('/users', async (req, res) => {
|
|
877
1103
|
try {
|
|
878
1104
|
const { username, email, password } = req.body;
|
|
@@ -881,10 +1107,11 @@ app.post('/users', async (req, res) => {
|
|
|
881
1107
|
return res.status(400).json({ error: 'Missing required fields' });
|
|
882
1108
|
}
|
|
883
1109
|
|
|
1110
|
+
const userService = await ServiceRegistration.getService<UserService>('users', 'users');
|
|
884
1111
|
const user = await userService.createUser({
|
|
885
1112
|
username,
|
|
886
1113
|
email,
|
|
887
|
-
password //
|
|
1114
|
+
password // Should hash password before saving
|
|
888
1115
|
});
|
|
889
1116
|
|
|
890
1117
|
res.status(201).json({ success: true, user });
|
|
@@ -894,12 +1121,13 @@ app.post('/users', async (req, res) => {
|
|
|
894
1121
|
}
|
|
895
1122
|
});
|
|
896
1123
|
|
|
897
|
-
// GET /users -
|
|
1124
|
+
// GET /users - Get list of users
|
|
898
1125
|
app.get('/users', async (req, res) => {
|
|
899
1126
|
try {
|
|
900
1127
|
const page = parseInt(req.query.page as string) || 1;
|
|
901
1128
|
const limit = parseInt(req.query.limit as string) || 10;
|
|
902
1129
|
|
|
1130
|
+
const userService = await ServiceRegistration.getService<UserService>('users', 'users');
|
|
903
1131
|
const users = await userService.getAllUsers(page, limit);
|
|
904
1132
|
const total = await userService.getTotalUsers();
|
|
905
1133
|
|
|
@@ -919,10 +1147,11 @@ app.get('/users', async (req, res) => {
|
|
|
919
1147
|
}
|
|
920
1148
|
});
|
|
921
1149
|
|
|
922
|
-
// GET /users/:id -
|
|
1150
|
+
// GET /users/:id - Get user by ID
|
|
923
1151
|
app.get('/users/:id', async (req, res) => {
|
|
924
1152
|
try {
|
|
925
1153
|
const id = parseInt(req.params.id);
|
|
1154
|
+
const userService = await ServiceRegistration.getService<UserService>('users', 'users');
|
|
926
1155
|
const user = await userService.findById(id);
|
|
927
1156
|
|
|
928
1157
|
if (!user) {
|
|
@@ -936,12 +1165,13 @@ app.get('/users/:id', async (req, res) => {
|
|
|
936
1165
|
}
|
|
937
1166
|
});
|
|
938
1167
|
|
|
939
|
-
// PUT /users/:id -
|
|
1168
|
+
// PUT /users/:id - Update user
|
|
940
1169
|
app.put('/users/:id', async (req, res) => {
|
|
941
1170
|
try {
|
|
942
1171
|
const id = parseInt(req.params.id);
|
|
943
1172
|
const updates = req.body;
|
|
944
1173
|
|
|
1174
|
+
const userService = await ServiceRegistration.getService<UserService>('users', 'users');
|
|
945
1175
|
const user = await userService.updateUser(id, updates);
|
|
946
1176
|
|
|
947
1177
|
if (!user) {
|
|
@@ -955,10 +1185,11 @@ app.put('/users/:id', async (req, res) => {
|
|
|
955
1185
|
}
|
|
956
1186
|
});
|
|
957
1187
|
|
|
958
|
-
// DELETE /users/:id -
|
|
1188
|
+
// DELETE /users/:id - Delete user
|
|
959
1189
|
app.delete('/users/:id', async (req, res) => {
|
|
960
1190
|
try {
|
|
961
1191
|
const id = parseInt(req.params.id);
|
|
1192
|
+
const userService = await ServiceRegistration.getService<UserService>('users', 'users');
|
|
962
1193
|
const success = await userService.delete(id);
|
|
963
1194
|
|
|
964
1195
|
if (!success) {
|
|
@@ -972,12 +1203,13 @@ app.delete('/users/:id', async (req, res) => {
|
|
|
972
1203
|
}
|
|
973
1204
|
});
|
|
974
1205
|
|
|
975
|
-
// POST /users/:id/profile -
|
|
1206
|
+
// POST /users/:id/profile - Create profile for user
|
|
976
1207
|
app.post('/users/:id/profile', async (req, res) => {
|
|
977
1208
|
try {
|
|
978
1209
|
const user_id = parseInt(req.params.id);
|
|
979
1210
|
const profileData = { ...req.body, user_id };
|
|
980
1211
|
|
|
1212
|
+
const profileService = await ServiceRegistration.getService<ProfileService>('users', 'profiles');
|
|
981
1213
|
const profile = await profileService.createProfile(profileData);
|
|
982
1214
|
res.status(201).json({ success: true, profile });
|
|
983
1215
|
} catch (error: any) {
|
|
@@ -986,10 +1218,11 @@ app.post('/users/:id/profile', async (req, res) => {
|
|
|
986
1218
|
}
|
|
987
1219
|
});
|
|
988
1220
|
|
|
989
|
-
// GET /users/:id/full -
|
|
1221
|
+
// GET /users/:id/full - Get user with profile
|
|
990
1222
|
app.get('/users/:id/full', async (req, res) => {
|
|
991
1223
|
try {
|
|
992
1224
|
const user_id = parseInt(req.params.id);
|
|
1225
|
+
const profileService = await ServiceRegistration.getService<ProfileService>('users', 'profiles');
|
|
993
1226
|
const userWithProfile = await profileService.getUserWithProfile(user_id);
|
|
994
1227
|
|
|
995
1228
|
if (!userWithProfile) {
|
|
@@ -1006,6 +1239,7 @@ app.get('/users/:id/full', async (req, res) => {
|
|
|
1006
1239
|
// Settings API
|
|
1007
1240
|
app.get('/settings/:key', async (req, res) => {
|
|
1008
1241
|
try {
|
|
1242
|
+
const settingsService = await ServiceRegistration.getService<SettingsService>('core', 'settings');
|
|
1009
1243
|
const value = await settingsService.getSetting(req.params.key);
|
|
1010
1244
|
res.json({ success: true, value });
|
|
1011
1245
|
} catch (error: any) {
|
|
@@ -1017,6 +1251,7 @@ app.get('/settings/:key', async (req, res) => {
|
|
|
1017
1251
|
app.post('/settings', async (req, res) => {
|
|
1018
1252
|
try {
|
|
1019
1253
|
const { key, value, description } = req.body;
|
|
1254
|
+
const settingsService = await ServiceRegistration.getService<SettingsService>('core', 'settings');
|
|
1020
1255
|
await settingsService.setSetting(key, value, description);
|
|
1021
1256
|
res.json({ success: true, message: 'Setting saved' });
|
|
1022
1257
|
} catch (error: any) {
|
|
@@ -1025,6 +1260,36 @@ app.post('/settings', async (req, res) => {
|
|
|
1025
1260
|
}
|
|
1026
1261
|
});
|
|
1027
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
|
+
|
|
1028
1293
|
// Graceful shutdown
|
|
1029
1294
|
process.on('SIGINT', async () => {
|
|
1030
1295
|
console.log('Shutting down server...');
|
|
@@ -1048,9 +1313,9 @@ initializeApp().then(() => {
|
|
|
1048
1313
|
});
|
|
1049
1314
|
```
|
|
1050
1315
|
|
|
1051
|
-
##
|
|
1316
|
+
## 7. Database Management with DatabaseManager
|
|
1052
1317
|
|
|
1053
|
-
###
|
|
1318
|
+
### Opening/Closing Connections
|
|
1054
1319
|
|
|
1055
1320
|
```typescript
|
|
1056
1321
|
// DatabaseHelper.ts
|
|
@@ -1058,7 +1323,7 @@ import { DatabaseManager } from '@dqcai/sqlite';
|
|
|
1058
1323
|
|
|
1059
1324
|
export class DatabaseHelper {
|
|
1060
1325
|
|
|
1061
|
-
//
|
|
1326
|
+
// Check connection status
|
|
1062
1327
|
static checkConnectionStatus(): void {
|
|
1063
1328
|
const connections = DatabaseManager.getConnections();
|
|
1064
1329
|
const count = DatabaseManager.getConnectionCount();
|
|
@@ -1069,7 +1334,7 @@ export class DatabaseHelper {
|
|
|
1069
1334
|
console.log('Connection details:', connections);
|
|
1070
1335
|
}
|
|
1071
1336
|
|
|
1072
|
-
//
|
|
1337
|
+
// Close specific connection
|
|
1073
1338
|
static async closeSpecificConnection(dbKey: string): Promise<void> {
|
|
1074
1339
|
try {
|
|
1075
1340
|
await DatabaseManager.closeConnection(dbKey);
|
|
@@ -1079,7 +1344,7 @@ export class DatabaseHelper {
|
|
|
1079
1344
|
}
|
|
1080
1345
|
}
|
|
1081
1346
|
|
|
1082
|
-
//
|
|
1347
|
+
// Reopen connection
|
|
1083
1348
|
static async reopenConnection(dbKey: string): Promise<void> {
|
|
1084
1349
|
try {
|
|
1085
1350
|
const dao = await DatabaseManager.getLazyLoading(dbKey);
|
|
@@ -1091,7 +1356,7 @@ export class DatabaseHelper {
|
|
|
1091
1356
|
}
|
|
1092
1357
|
}
|
|
1093
1358
|
|
|
1094
|
-
//
|
|
1359
|
+
// Ensure connection exists
|
|
1095
1360
|
static async ensureConnection(dbKey: string): Promise<void> {
|
|
1096
1361
|
try {
|
|
1097
1362
|
const dao = await DatabaseManager.ensureDatabaseConnection(dbKey);
|
|
@@ -1103,7 +1368,7 @@ export class DatabaseHelper {
|
|
|
1103
1368
|
}
|
|
1104
1369
|
}
|
|
1105
1370
|
|
|
1106
|
-
//
|
|
1371
|
+
// Execute cross-schema transaction
|
|
1107
1372
|
static async executeTransactionAcrossSchemas(
|
|
1108
1373
|
schemas: string[],
|
|
1109
1374
|
operations: (daos: Record<string, any>) => Promise<void>
|
|
@@ -1117,11 +1382,11 @@ export class DatabaseHelper {
|
|
|
1117
1382
|
}
|
|
1118
1383
|
}
|
|
1119
1384
|
|
|
1120
|
-
// Event listeners
|
|
1385
|
+
// Event listeners for reconnection
|
|
1121
1386
|
static setupReconnectionHandlers(): void {
|
|
1122
1387
|
DatabaseManager.onDatabaseReconnect('users', (dao) => {
|
|
1123
1388
|
console.log('Users database reconnected');
|
|
1124
|
-
// Re-initialize services
|
|
1389
|
+
// Re-initialize services if needed
|
|
1125
1390
|
});
|
|
1126
1391
|
|
|
1127
1392
|
DatabaseManager.onDatabaseReconnect('core', (dao) => {
|
|
@@ -1130,7 +1395,7 @@ export class DatabaseHelper {
|
|
|
1130
1395
|
});
|
|
1131
1396
|
}
|
|
1132
1397
|
|
|
1133
|
-
// Health check
|
|
1398
|
+
// Health check all connections
|
|
1134
1399
|
static async performHealthCheck(): Promise<void> {
|
|
1135
1400
|
const connections = DatabaseManager.getConnections();
|
|
1136
1401
|
|
|
@@ -1141,7 +1406,7 @@ export class DatabaseHelper {
|
|
|
1141
1406
|
} catch (error) {
|
|
1142
1407
|
console.error(`${dbKey}: Unhealthy -`, error);
|
|
1143
1408
|
|
|
1144
|
-
//
|
|
1409
|
+
// Try reconnect if needed
|
|
1145
1410
|
try {
|
|
1146
1411
|
await DatabaseManager.ensureDatabaseConnection(dbKey);
|
|
1147
1412
|
console.log(`${dbKey}: Reconnected successfully`);
|
|
@@ -1154,9 +1419,9 @@ export class DatabaseHelper {
|
|
|
1154
1419
|
}
|
|
1155
1420
|
```
|
|
1156
1421
|
|
|
1157
|
-
##
|
|
1422
|
+
## 8. Data Import/Export
|
|
1158
1423
|
|
|
1159
|
-
### Import
|
|
1424
|
+
### Import from CSV
|
|
1160
1425
|
|
|
1161
1426
|
```typescript
|
|
1162
1427
|
// services/DataImportService.ts
|
|
@@ -1164,7 +1429,7 @@ import { DatabaseManager, ImportResult, ColumnMapping } from '@dqcai/sqlite';
|
|
|
1164
1429
|
|
|
1165
1430
|
export class DataImportService {
|
|
1166
1431
|
|
|
1167
|
-
// Import users
|
|
1432
|
+
// Import users from CSV
|
|
1168
1433
|
static async importUsersFromCSV(csvData: string): Promise<ImportResult> {
|
|
1169
1434
|
try {
|
|
1170
1435
|
const result = await DatabaseManager.importFromCSV(
|
|
@@ -1200,7 +1465,7 @@ export class DataImportService {
|
|
|
1200
1465
|
}
|
|
1201
1466
|
}
|
|
1202
1467
|
|
|
1203
|
-
// Import
|
|
1468
|
+
// Import with column mapping
|
|
1204
1469
|
static async importUsersWithMapping(
|
|
1205
1470
|
data: Record<string, any>[],
|
|
1206
1471
|
columnMappings: ColumnMapping[]
|
|
@@ -1226,7 +1491,7 @@ export class DataImportService {
|
|
|
1226
1491
|
}
|
|
1227
1492
|
}
|
|
1228
1493
|
|
|
1229
|
-
// Bulk import
|
|
1494
|
+
// Bulk import multiple tables at once
|
|
1230
1495
|
static async bulkImportData(importConfigs: Array<{
|
|
1231
1496
|
databaseKey: string;
|
|
1232
1497
|
tableName: string;
|
|
@@ -1241,12 +1506,12 @@ export class DataImportService {
|
|
|
1241
1506
|
executionTime: result.executionTime
|
|
1242
1507
|
});
|
|
1243
1508
|
|
|
1244
|
-
// Log
|
|
1509
|
+
// Log details for each table
|
|
1245
1510
|
Object.entries(result.results).forEach(([key, importResult]) => {
|
|
1246
1511
|
console.log(`${key}: ${importResult.successRows}/${importResult.totalRows} rows imported`);
|
|
1247
1512
|
});
|
|
1248
1513
|
|
|
1249
|
-
// Log
|
|
1514
|
+
// Log errors if any
|
|
1250
1515
|
if (Object.keys(result.errors).length > 0) {
|
|
1251
1516
|
console.error('Import errors:', result.errors);
|
|
1252
1517
|
}
|
|
@@ -1258,20 +1523,20 @@ export class DataImportService {
|
|
|
1258
1523
|
}
|
|
1259
1524
|
}
|
|
1260
1525
|
|
|
1261
|
-
//
|
|
1526
|
+
// Example import usage
|
|
1262
1527
|
async function exampleImportUsage() {
|
|
1263
|
-
// CSV data
|
|
1528
|
+
// Sample CSV data
|
|
1264
1529
|
const csvData = `username,email,password,first_name,last_name
|
|
1265
1530
|
john_doe,john@example.com,password123,John,Doe
|
|
1266
1531
|
jane_smith,jane@example.com,password456,Jane,Smith
|
|
1267
1532
|
bob_wilson,bob@example.com,password789,Bob,Wilson`;
|
|
1268
1533
|
|
|
1269
1534
|
try {
|
|
1270
|
-
// Import
|
|
1535
|
+
// Import from CSV
|
|
1271
1536
|
const importResult = await DataImportService.importUsersFromCSV(csvData);
|
|
1272
1537
|
console.log('CSV Import result:', importResult);
|
|
1273
1538
|
|
|
1274
|
-
// Import
|
|
1539
|
+
// Import with column mapping
|
|
1275
1540
|
const mappedData = [
|
|
1276
1541
|
{ user_name: 'alice', user_email: 'alice@test.com', pwd: 'pass123' },
|
|
1277
1542
|
{ user_name: 'charlie', user_email: 'charlie@test.com', pwd: 'pass456' }
|
|
@@ -1299,7 +1564,7 @@ bob_wilson,bob@example.com,password789,Bob,Wilson`;
|
|
|
1299
1564
|
}
|
|
1300
1565
|
```
|
|
1301
1566
|
|
|
1302
|
-
### Export
|
|
1567
|
+
### Export Data
|
|
1303
1568
|
|
|
1304
1569
|
```typescript
|
|
1305
1570
|
// services/DataExportService.ts
|
|
@@ -1307,7 +1572,7 @@ import { DatabaseManager } from '@dqcai/sqlite';
|
|
|
1307
1572
|
|
|
1308
1573
|
export class DataExportService {
|
|
1309
1574
|
|
|
1310
|
-
// Export users
|
|
1575
|
+
// Export users to CSV
|
|
1311
1576
|
static async exportUsersToCSV(): Promise<string> {
|
|
1312
1577
|
try {
|
|
1313
1578
|
const dao = DatabaseManager.get('users');
|
|
@@ -1325,15 +1590,15 @@ export class DataExportService {
|
|
|
1325
1590
|
return 'No data to export';
|
|
1326
1591
|
}
|
|
1327
1592
|
|
|
1328
|
-
//
|
|
1593
|
+
// Create CSV header
|
|
1329
1594
|
const headers = Object.keys(result.rows[0]);
|
|
1330
1595
|
let csvContent = headers.join(',') + '\n';
|
|
1331
1596
|
|
|
1332
|
-
//
|
|
1597
|
+
// Add data rows
|
|
1333
1598
|
result.rows.forEach(row => {
|
|
1334
1599
|
const values = headers.map(header => {
|
|
1335
1600
|
const value = row[header];
|
|
1336
|
-
// Escape quotes
|
|
1601
|
+
// Escape quotes and wrap in quotes if contains comma
|
|
1337
1602
|
if (value === null || value === undefined) {
|
|
1338
1603
|
return '';
|
|
1339
1604
|
}
|
|
@@ -1353,7 +1618,7 @@ export class DataExportService {
|
|
|
1353
1618
|
}
|
|
1354
1619
|
}
|
|
1355
1620
|
|
|
1356
|
-
// Export
|
|
1621
|
+
// Export with custom conditions
|
|
1357
1622
|
static async exportUsersWithConditions(
|
|
1358
1623
|
whereClause?: string,
|
|
1359
1624
|
params?: any[]
|
|
@@ -1380,7 +1645,7 @@ export class DataExportService {
|
|
|
1380
1645
|
}
|
|
1381
1646
|
}
|
|
1382
1647
|
|
|
1383
|
-
//
|
|
1648
|
+
// Create full database backup
|
|
1384
1649
|
static async createDatabaseBackup(dbKey: string): Promise<{
|
|
1385
1650
|
tables: Record<string, any[]>;
|
|
1386
1651
|
metadata: any;
|
|
@@ -1388,21 +1653,21 @@ export class DataExportService {
|
|
|
1388
1653
|
try {
|
|
1389
1654
|
const dao = DatabaseManager.get(dbKey);
|
|
1390
1655
|
|
|
1391
|
-
//
|
|
1656
|
+
// Get list of tables
|
|
1392
1657
|
const tablesResult = await dao.execute(
|
|
1393
1658
|
"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'"
|
|
1394
1659
|
);
|
|
1395
1660
|
|
|
1396
1661
|
const backup: Record<string, any[]> = {};
|
|
1397
1662
|
|
|
1398
|
-
// Export
|
|
1663
|
+
// Export each table
|
|
1399
1664
|
for (const tableRow of tablesResult.rows) {
|
|
1400
1665
|
const tableName = tableRow.name;
|
|
1401
1666
|
const dataResult = await dao.execute(`SELECT * FROM ${tableName}`);
|
|
1402
1667
|
backup[tableName] = dataResult.rows;
|
|
1403
1668
|
}
|
|
1404
1669
|
|
|
1405
|
-
//
|
|
1670
|
+
// Add metadata
|
|
1406
1671
|
const dbInfo = await dao.getDatabaseInfo();
|
|
1407
1672
|
|
|
1408
1673
|
return {
|
|
@@ -1420,21 +1685,21 @@ export class DataExportService {
|
|
|
1420
1685
|
}
|
|
1421
1686
|
}
|
|
1422
1687
|
|
|
1423
|
-
//
|
|
1688
|
+
// Example export usage
|
|
1424
1689
|
async function exampleExportUsage() {
|
|
1425
1690
|
try {
|
|
1426
|
-
// Export users
|
|
1691
|
+
// Export users to CSV
|
|
1427
1692
|
const csvContent = await DataExportService.exportUsersToCSV();
|
|
1428
1693
|
console.log('CSV Export:', csvContent);
|
|
1429
1694
|
|
|
1430
|
-
// Export users
|
|
1695
|
+
// Export users with conditions
|
|
1431
1696
|
const recentUsers = await DataExportService.exportUsersWithConditions(
|
|
1432
1697
|
"u.created_at > ?",
|
|
1433
|
-
[new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString()] // 30
|
|
1698
|
+
[new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString()] // Last 30 days
|
|
1434
1699
|
);
|
|
1435
1700
|
console.log('Recent users:', recentUsers);
|
|
1436
1701
|
|
|
1437
|
-
// Backup
|
|
1702
|
+
// Backup entire users database
|
|
1438
1703
|
const backup = await DataExportService.createDatabaseBackup('users');
|
|
1439
1704
|
console.log('Database backup:', backup);
|
|
1440
1705
|
|
|
@@ -1444,7 +1709,7 @@ async function exampleExportUsage() {
|
|
|
1444
1709
|
}
|
|
1445
1710
|
```
|
|
1446
1711
|
|
|
1447
|
-
##
|
|
1712
|
+
## 9. Best Practices & Tips
|
|
1448
1713
|
|
|
1449
1714
|
### Error Handling
|
|
1450
1715
|
|
|
@@ -1497,7 +1762,7 @@ export class DatabaseErrorHandler {
|
|
|
1497
1762
|
}
|
|
1498
1763
|
}
|
|
1499
1764
|
|
|
1500
|
-
//
|
|
1765
|
+
// Using error handler
|
|
1501
1766
|
class SafeUserService extends UserService {
|
|
1502
1767
|
async createUser(userData: Omit<User, 'id' | 'created_at' | 'updated_at'>): Promise<User | null> {
|
|
1503
1768
|
return DatabaseErrorHandler.withRetry(async () => {
|
|
@@ -1505,7 +1770,7 @@ class SafeUserService extends UserService {
|
|
|
1505
1770
|
return await super.createUser(userData);
|
|
1506
1771
|
} catch (error) {
|
|
1507
1772
|
DatabaseErrorHandler.handleServiceError(error, 'createUser');
|
|
1508
|
-
throw error; // Re-throw
|
|
1773
|
+
throw error; // Re-throw after handling
|
|
1509
1774
|
}
|
|
1510
1775
|
});
|
|
1511
1776
|
}
|
|
@@ -1518,7 +1783,7 @@ class SafeUserService extends UserService {
|
|
|
1518
1783
|
// utils/PerformanceOptimizer.ts
|
|
1519
1784
|
export class PerformanceOptimizer {
|
|
1520
1785
|
|
|
1521
|
-
// Batch operations
|
|
1786
|
+
// Batch operations to reduce database calls
|
|
1522
1787
|
static async batchCreateUsers(
|
|
1523
1788
|
userService: UserService,
|
|
1524
1789
|
users: Array<Omit<User, 'id' | 'created_at' | 'updated_at'>>
|
|
@@ -1575,7 +1840,7 @@ export class PerformanceOptimizer {
|
|
|
1575
1840
|
|
|
1576
1841
|
```typescript
|
|
1577
1842
|
// utils/TestHelpers.ts
|
|
1578
|
-
import { DatabaseManager } from '@dqcai/sqlite';
|
|
1843
|
+
import { DatabaseManager, ServiceManager } from '@dqcai/sqlite';
|
|
1579
1844
|
|
|
1580
1845
|
export class DatabaseTestHelpers {
|
|
1581
1846
|
|
|
@@ -1609,15 +1874,22 @@ export class DatabaseTestHelpers {
|
|
|
1609
1874
|
|
|
1610
1875
|
return createdUsers;
|
|
1611
1876
|
}
|
|
1877
|
+
|
|
1878
|
+
static async teardownTest(): Promise<void> {
|
|
1879
|
+
await DatabaseManager.closeAll();
|
|
1880
|
+
ServiceManager.resetInstance();
|
|
1881
|
+
}
|
|
1612
1882
|
}
|
|
1613
1883
|
|
|
1614
1884
|
// Example test
|
|
1615
1885
|
describe('UserService Tests', () => {
|
|
1886
|
+
let serviceManager: ServiceManager;
|
|
1616
1887
|
let userService: UserService;
|
|
1617
1888
|
|
|
1618
1889
|
beforeAll(async () => {
|
|
1619
1890
|
await DatabaseTestHelpers.setupTestDatabase();
|
|
1620
|
-
|
|
1891
|
+
serviceManager = ServiceManager.getInstance();
|
|
1892
|
+
userService = await serviceManager.getService('test_users', 'users') as UserService;
|
|
1621
1893
|
await userService.init();
|
|
1622
1894
|
});
|
|
1623
1895
|
|
|
@@ -1626,7 +1898,7 @@ describe('UserService Tests', () => {
|
|
|
1626
1898
|
});
|
|
1627
1899
|
|
|
1628
1900
|
afterAll(async () => {
|
|
1629
|
-
await
|
|
1901
|
+
await DatabaseTestHelpers.teardownTest();
|
|
1630
1902
|
});
|
|
1631
1903
|
|
|
1632
1904
|
test('should create user successfully', async () => {
|
|
@@ -1641,21 +1913,29 @@ describe('UserService Tests', () => {
|
|
|
1641
1913
|
expect(user?.username).toBe(userData.username);
|
|
1642
1914
|
expect(user?.email).toBe(userData.email);
|
|
1643
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
|
+
});
|
|
1644
1924
|
});
|
|
1645
1925
|
```
|
|
1646
1926
|
|
|
1647
|
-
##
|
|
1927
|
+
## 10. Troubleshooting Common Issues
|
|
1648
1928
|
|
|
1649
1929
|
### Database Locked
|
|
1650
1930
|
|
|
1651
1931
|
```typescript
|
|
1652
|
-
//
|
|
1932
|
+
// Resolve database locked issues
|
|
1653
1933
|
const handleDatabaseLocked = async () => {
|
|
1654
1934
|
try {
|
|
1655
|
-
// Enable WAL mode
|
|
1935
|
+
// Enable WAL mode to avoid locks
|
|
1656
1936
|
const dao = DatabaseManager.get('users');
|
|
1657
1937
|
await dao.execute('PRAGMA journal_mode = WAL');
|
|
1658
|
-
await dao.execute('PRAGMA busy_timeout = 30000'); // 30
|
|
1938
|
+
await dao.execute('PRAGMA busy_timeout = 30000'); // 30 second timeout
|
|
1659
1939
|
} catch (error) {
|
|
1660
1940
|
console.error('Error setting WAL mode:', error);
|
|
1661
1941
|
}
|
|
@@ -1665,7 +1945,7 @@ const handleDatabaseLocked = async () => {
|
|
|
1665
1945
|
### Connection Issues
|
|
1666
1946
|
|
|
1667
1947
|
```typescript
|
|
1668
|
-
//
|
|
1948
|
+
// Check and restore connections
|
|
1669
1949
|
const ensureConnectionHealth = async (dbKey: string) => {
|
|
1670
1950
|
try {
|
|
1671
1951
|
const dao = DatabaseManager.get(dbKey);
|
|
@@ -1679,7 +1959,33 @@ const ensureConnectionHealth = async (dbKey: string) => {
|
|
|
1679
1959
|
};
|
|
1680
1960
|
```
|
|
1681
1961
|
|
|
1682
|
-
|
|
1962
|
+
### Service Management Issues
|
|
1963
|
+
|
|
1964
|
+
```typescript
|
|
1965
|
+
// Service troubleshooting
|
|
1966
|
+
const troubleshootServices = async () => {
|
|
1967
|
+
const serviceManager = ServiceManager.getInstance();
|
|
1968
|
+
|
|
1969
|
+
// Get service health report
|
|
1970
|
+
const healthReport = await serviceManager.healthCheck();
|
|
1971
|
+
console.log('Service Health:', healthReport);
|
|
1972
|
+
|
|
1973
|
+
// Clean up problematic services
|
|
1974
|
+
if (!healthReport.overallHealth) {
|
|
1975
|
+
const unhealthyServices = healthReport.services.filter(s => !s.healthy);
|
|
1976
|
+
|
|
1977
|
+
for (const service of unhealthyServices) {
|
|
1978
|
+
const [schemaName, tableName] = service.serviceKey.split(':');
|
|
1979
|
+
console.log(`Attempting to restart service: ${service.serviceKey}`);
|
|
1980
|
+
|
|
1981
|
+
await serviceManager.destroyService(schemaName, tableName);
|
|
1982
|
+
await serviceManager.getService(schemaName, tableName);
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1985
|
+
};
|
|
1986
|
+
```
|
|
1987
|
+
|
|
1988
|
+
## 11. Migration & Schema Updates
|
|
1683
1989
|
|
|
1684
1990
|
```typescript
|
|
1685
1991
|
// migrations/001_add_user_status.ts
|
|
@@ -1705,7 +2011,7 @@ export const migration_001 = {
|
|
|
1705
2011
|
DROP INDEX IF EXISTS idx_user_status
|
|
1706
2012
|
`);
|
|
1707
2013
|
|
|
1708
|
-
// SQLite
|
|
2014
|
+
// SQLite doesn't support DROP COLUMN, need to recreate table
|
|
1709
2015
|
await dao.execute(`
|
|
1710
2016
|
CREATE TABLE users_backup AS
|
|
1711
2017
|
SELECT id, username, email, password, created_at, updated_at
|
|
@@ -1733,7 +2039,7 @@ export const migration_001 = {
|
|
|
1733
2039
|
}
|
|
1734
2040
|
};
|
|
1735
2041
|
|
|
1736
|
-
//
|
|
2042
|
+
// Run migration
|
|
1737
2043
|
const runMigration = async () => {
|
|
1738
2044
|
const dao = DatabaseManager.get('users');
|
|
1739
2045
|
const currentVersion = await dao.getSchemaVersion();
|
|
@@ -1746,56 +2052,512 @@ const runMigration = async () => {
|
|
|
1746
2052
|
};
|
|
1747
2053
|
```
|
|
1748
2054
|
|
|
1749
|
-
##
|
|
2055
|
+
## 12. Advanced Features
|
|
1750
2056
|
|
|
1751
|
-
|
|
2057
|
+
### Cross-Schema Transactions with ServiceManager
|
|
1752
2058
|
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
2059
|
+
```typescript
|
|
2060
|
+
// Advanced transaction management across multiple services
|
|
2061
|
+
export class TransactionManager {
|
|
2062
|
+
private serviceManager = ServiceManager.getInstance();
|
|
2063
|
+
|
|
2064
|
+
async executeUserProfileTransaction(
|
|
2065
|
+
userData: Omit<User, 'id' | 'created_at' | 'updated_at'>,
|
|
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;
|
|
1758
2073
|
|
|
1759
|
-
|
|
2074
|
+
// Create user first
|
|
2075
|
+
const user = await userService.createUser(userData);
|
|
2076
|
+
if (!user) {
|
|
2077
|
+
throw new Error('Failed to create user');
|
|
2078
|
+
}
|
|
1760
2079
|
|
|
1761
|
-
|
|
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
|
+
);
|
|
1762
2106
|
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
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
|
+
}
|
|
2115
|
+
```
|
|
1771
2116
|
|
|
1772
|
-
|
|
2117
|
+
### Service Composition and Dependency Injection
|
|
1773
2118
|
|
|
2119
|
+
```typescript
|
|
2120
|
+
// Advanced service composition
|
|
2121
|
+
export class ServiceComposer {
|
|
2122
|
+
private serviceManager = ServiceManager.getInstance();
|
|
1774
2123
|
|
|
1775
|
-
|
|
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;
|
|
1776
2129
|
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
2130
|
+
return new UserManagementService(userService, profileService, settingsService);
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
|
|
2134
|
+
// Composite service example
|
|
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
|
+
}
|
|
2187
|
+
```
|
|
2188
|
+
|
|
2189
|
+
### Real-time Monitoring and Alerting
|
|
2190
|
+
|
|
2191
|
+
```typescript
|
|
2192
|
+
// Real-time service monitoring
|
|
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
|
+
}
|
|
2215
|
+
|
|
2216
|
+
private setupEventHandlers(): void {
|
|
2217
|
+
this.serviceManager.on('SERVICE_ERROR', (event) => {
|
|
2218
|
+
this.addAlert('ERROR', `Service error in ${event.serviceKey}: ${event.error?.message}`);
|
|
2219
|
+
});
|
|
2220
|
+
|
|
2221
|
+
this.serviceManager.on('SERVICE_CREATED', (event) => {
|
|
2222
|
+
this.addAlert('INFO', `Service created: ${event.serviceKey}`);
|
|
2223
|
+
});
|
|
2224
|
+
|
|
2225
|
+
this.serviceManager.on('SERVICE_DESTROYED', (event) => {
|
|
2226
|
+
this.addAlert('WARNING', `Service destroyed: ${event.serviceKey}`);
|
|
2227
|
+
});
|
|
2228
|
+
}
|
|
2229
|
+
|
|
2230
|
+
private async performHealthCheck(): Promise<void> {
|
|
2231
|
+
try {
|
|
2232
|
+
const healthReport = await this.serviceManager.healthCheck();
|
|
2233
|
+
|
|
2234
|
+
if (!healthReport.overallHealth) {
|
|
2235
|
+
this.addAlert('CRITICAL', `System unhealthy: ${healthReport.unhealthyServices}/${healthReport.totalServices} services down`);
|
|
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
|
+
}
|
|
2246
|
+
|
|
2247
|
+
private async attemptAutoRecovery(healthReport: any): Promise<void> {
|
|
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
|
+
}
|
|
2264
|
+
|
|
2265
|
+
private addAlert(level: string, message: string): void {
|
|
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
|
+
}
|
|
2278
|
+
|
|
2279
|
+
// Log based on severity
|
|
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
|
+
}
|
|
2292
|
+
|
|
2293
|
+
getRecentAlerts(limit: number = 20): typeof this.alerts {
|
|
2294
|
+
return this.alerts.slice(0, limit);
|
|
2295
|
+
}
|
|
2296
|
+
|
|
2297
|
+
getSystemMetrics(): any {
|
|
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
|
+
```
|
|
2320
|
+
|
|
2321
|
+
## 13. Production Deployment Considerations
|
|
2322
|
+
|
|
2323
|
+
### Configuration Management
|
|
2324
|
+
|
|
2325
|
+
```typescript
|
|
2326
|
+
// config/DatabaseConfig.ts
|
|
2327
|
+
export interface DatabaseConfig {
|
|
2328
|
+
environment: 'development' | 'production' | 'test';
|
|
2329
|
+
databases: {
|
|
2330
|
+
[key: string]: {
|
|
2331
|
+
path: string;
|
|
2332
|
+
maxConnections?: number;
|
|
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
|
|
2369
|
+
},
|
|
2370
|
+
monitoring: {
|
|
2371
|
+
enabled: true,
|
|
2372
|
+
alertThreshold: 80, // Alert when 80% of services are unhealthy
|
|
2373
|
+
logLevel: 'info'
|
|
2374
|
+
}
|
|
2375
|
+
};
|
|
2376
|
+
|
|
2377
|
+
export const developmentConfig: DatabaseConfig = {
|
|
2378
|
+
environment: 'development',
|
|
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
|
+
};
|
|
2399
|
+
```
|
|
2400
|
+
|
|
2401
|
+
### Production Service Setup
|
|
2402
|
+
|
|
2403
|
+
```typescript
|
|
2404
|
+
// production/ProductionSetup.ts
|
|
2405
|
+
import { DatabaseConfig, productionConfig } from '../config/DatabaseConfig';
|
|
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
|
|
2510
|
+
|
|
2511
|
+
The library supports both React Native and Node.js well, helping you build database-driven applications consistently and maintainably across different platforms.
|
|
2512
|
+
|
|
2513
|
+
## Key Features Summary
|
|
2514
|
+
|
|
2515
|
+
- **Cross-Platform**: Browser, Node.js, Deno, Bun, React Native (iOS/Android/Windows)
|
|
2516
|
+
- **Service Management**: Centralized lifecycle management with ServiceManager
|
|
2517
|
+
- **Type Safety**: Full TypeScript support for schemas, queries, and operations
|
|
2518
|
+
- **Performance**: Built-in optimization, connection pooling, and batch operations
|
|
2519
|
+
- **Monitoring**: Real-time health monitoring and automatic recovery
|
|
2520
|
+
- **Production Ready**: Comprehensive error handling and graceful shutdown
|
|
2521
|
+
- **Flexible**: Custom adapters, role-based access, and extensible architecture
|
|
2522
|
+
|
|
2523
|
+
## Best Practices Summary
|
|
2524
|
+
|
|
2525
|
+
1. **Always use ServiceManager for service lifecycle management**
|
|
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**
|
|
2533
|
+
|
|
2534
|
+
## API Reference
|
|
2535
|
+
|
|
2536
|
+
- **ServiceManager**: Centralized service lifecycle management
|
|
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
|
|
2544
|
+
|
|
2545
|
+
See source code for detailed types and method signatures.
|
|
1784
2546
|
|
|
1785
2547
|
## Contributing
|
|
1786
2548
|
|
|
1787
2549
|
1. Fork the repository
|
|
1788
2550
|
2. Create a feature branch
|
|
1789
|
-
3. Make your changes
|
|
1790
|
-
4. Add tests
|
|
1791
|
-
5.
|
|
1792
|
-
|
|
2551
|
+
3. Make your changes with proper TypeScript types
|
|
2552
|
+
4. Add comprehensive tests
|
|
2553
|
+
5. Update documentation
|
|
2554
|
+
6. Submit a pull request
|
|
1793
2555
|
|
|
1794
|
-
##
|
|
2556
|
+
## License
|
|
1795
2557
|
|
|
1796
2558
|
MIT © [Cuong Doan](https://github.com/cuongdqpayment)
|
|
1797
2559
|
|
|
1798
|
-
##
|
|
2560
|
+
## Acknowledgments
|
|
1799
2561
|
|
|
1800
2562
|
- [sqlite3](https://www.npmjs.com/package/sqlite3) - Node.js SQLite bindings
|
|
1801
2563
|
- [sql.js](https://github.com/sql-js/sql.js) - SQLite compiled to WebAssembly
|
|
@@ -1803,14 +2565,14 @@ MIT © [Cuong Doan](https://github.com/cuongdqpayment)
|
|
|
1803
2565
|
- [react-native-sqlite-storage](https://github.com/andpor/react-native-sqlite-storage) - React Native SQLite
|
|
1804
2566
|
- [Deno SQLite](https://deno.land/x/sqlite) - Deno SQLite module
|
|
1805
2567
|
|
|
1806
|
-
##
|
|
2568
|
+
## Links
|
|
1807
2569
|
|
|
1808
2570
|
- [Documentation](https://github.com/cuongdqpayment/dqcai-sqlite/docs)
|
|
1809
2571
|
- [Examples Repository](https://github.com/cuongdqpayment/dqcai-sqlite)
|
|
1810
2572
|
- [Issue Tracker](https://github.com/cuongdqpayment/dqcai-sqlite/issues)
|
|
1811
|
-
- [
|
|
2573
|
+
- [Facebook Page](https://www.facebook.com/share/p/19esHGbaGj/)
|
|
1812
2574
|
- [NPM Package](https://www.npmjs.com/package/@dqcai/sqlite)
|
|
1813
2575
|
|
|
1814
2576
|
---
|
|
1815
2577
|
|
|
1816
|
-
|
|
2578
|
+
🚀 **@dqcai/sqlite** — One library, all platforms! 🎯
|