@qlover/create-app 0.7.14 → 0.7.15

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.
Files changed (31) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.js +1 -1
  4. package/dist/templates/next-app/README.en.md +131 -0
  5. package/dist/templates/next-app/README.md +115 -20
  6. package/dist/templates/next-app/docs/en/api.md +387 -0
  7. package/dist/templates/next-app/docs/en/component.md +544 -0
  8. package/dist/templates/next-app/docs/en/database.md +496 -0
  9. package/dist/templates/next-app/docs/en/development-guide.md +727 -0
  10. package/dist/templates/next-app/docs/en/env.md +563 -0
  11. package/dist/templates/next-app/docs/en/i18n.md +287 -0
  12. package/dist/templates/next-app/docs/en/index.md +166 -0
  13. package/dist/templates/next-app/docs/en/page.md +457 -0
  14. package/dist/templates/next-app/docs/en/project-structure.md +177 -0
  15. package/dist/templates/next-app/docs/en/router.md +427 -0
  16. package/dist/templates/next-app/docs/en/theme.md +532 -0
  17. package/dist/templates/next-app/docs/en/validator.md +478 -0
  18. package/dist/templates/next-app/docs/zh/api.md +387 -0
  19. package/dist/templates/next-app/docs/zh/component.md +544 -0
  20. package/dist/templates/next-app/docs/zh/database.md +496 -0
  21. package/dist/templates/next-app/docs/zh/development-guide.md +727 -0
  22. package/dist/templates/next-app/docs/zh/env.md +563 -0
  23. package/dist/templates/next-app/docs/zh/i18n.md +287 -0
  24. package/dist/templates/next-app/docs/zh/index.md +166 -0
  25. package/dist/templates/next-app/docs/zh/page.md +457 -0
  26. package/dist/templates/next-app/docs/zh/project-structure.md +177 -0
  27. package/dist/templates/next-app/docs/zh/router.md +427 -0
  28. package/dist/templates/next-app/docs/zh/theme.md +532 -0
  29. package/dist/templates/next-app/docs/zh/validator.md +476 -0
  30. package/package.json +1 -1
  31. package/dist/templates/next-app/docs/env.md +0 -94
@@ -0,0 +1,496 @@
1
+ # Database Development Guide
2
+
3
+ ## Table of Contents
4
+
5
+ 1. [Database Architecture Overview](#database-architecture-overview)
6
+ 2. [Supabase Implementation](#supabase-implementation)
7
+ 3. [Database Interfaces and Abstraction Layer](#database-interfaces-and-abstraction-layer)
8
+ 4. [Repository Pattern Implementation](#repository-pattern-implementation)
9
+ 5. [MongoDB Implementation Example](#mongodb-implementation-example)
10
+ 6. [Best Practices and Examples](#best-practices-and-examples)
11
+
12
+ ## Database Architecture Overview
13
+
14
+ ### 1. Overall Architecture
15
+
16
+ The project adopts a layered database architecture design:
17
+
18
+ ```
19
+ Application Layer Data Access Layer
20
+ ┌──────────────┐ ┌──────────────┐
21
+ │Business Service│ │ DB Interface│
22
+ ├──────────────┤ ├──────────────┤
23
+ │Repo Interface │ │ DB Bridge │
24
+ ├──────────────┤ ◄─────┤ │
25
+ │Repo Implement │ │Implementation │
26
+ └──────────────┘ └──────────────┘
27
+ ```
28
+
29
+ ### 2. Core Components
30
+
31
+ - **Database Interface**: `DBBridgeInterface`
32
+ - **Database Implementation**: `SupabaseBridge`, `MongoDBBridge`, etc.
33
+ - **Repository Interface**: `UserRepositoryInterface`, etc.
34
+ - **Repository Implementation**: `UserRepository`, etc.
35
+
36
+ ### 3. Data Models
37
+
38
+ ```typescript
39
+ // User model example
40
+ interface UserSchema {
41
+ id: number;
42
+ email: string;
43
+ password: string;
44
+ role: string;
45
+ created_at: string;
46
+ updated_at: string;
47
+ email_confirmed_at?: string;
48
+ }
49
+ ```
50
+
51
+ ## Supabase Implementation
52
+
53
+ ### 1. Database Bridge
54
+
55
+ ```typescript
56
+ @injectable()
57
+ export class SupabaseBridge implements DBBridgeInterface {
58
+ protected supabase: SupabaseClient;
59
+
60
+ constructor(
61
+ @inject(I.AppConfig) appConfig: AppConfig,
62
+ @inject(I.Logger) protected logger: LoggerInterface
63
+ ) {
64
+ // Initialize Supabase client
65
+ this.supabase = createClient(
66
+ appConfig.supabaseUrl,
67
+ appConfig.supabaseAnonKey
68
+ );
69
+ }
70
+
71
+ // Query implementation
72
+ async get(event: BridgeEvent): Promise<SupabaseBridgeResponse<unknown>> {
73
+ const { table, fields = '*', where } = event;
74
+ const selectFields = Array.isArray(fields) ? fields.join(',') : fields;
75
+ const handler = this.supabase.from(table).select(selectFields);
76
+
77
+ this.handleWhere(handler, where ?? []);
78
+
79
+ return this.catch(await handler);
80
+ }
81
+
82
+ // Pagination query implementation
83
+ async pagination(event: BridgeEvent): Promise<DBBridgeResponse<unknown[]>> {
84
+ const { table, fields = '*', where, page = 1, pageSize = 10 } = event;
85
+ const selectFields = Array.isArray(fields) ? fields.join(',') : fields;
86
+
87
+ const handler = this.supabase
88
+ .from(table)
89
+ .select(selectFields, { count: 'exact' })
90
+ .range((page - 1) * pageSize, page * pageSize - 1);
91
+
92
+ this.handleWhere(handler, where ?? []);
93
+
94
+ return this.catch(await handler);
95
+ }
96
+
97
+ // WHERE condition handling
98
+ protected handleWhere(
99
+ handler: PostgrestFilterBuilder<any, any, any>,
100
+ where: Where[]
101
+ ): void {
102
+ where.forEach(([field, operation, value]) => {
103
+ const method = whereHandlerMaps[operation];
104
+ if (method) {
105
+ handler[method](field, value);
106
+ }
107
+ });
108
+ }
109
+ }
110
+ ```
111
+
112
+ ### 2. Query Condition Mapping
113
+
114
+ ```typescript
115
+ // Supabase operator mapping
116
+ const whereHandlerMaps = {
117
+ '=': 'eq', // Equal
118
+ '!=': 'neq', // Not equal
119
+ '>': 'gt', // Greater than
120
+ '<': 'lt', // Less than
121
+ '>=': 'gte', // Greater than or equal
122
+ '<=': 'lte' // Less than or equal
123
+ };
124
+ ```
125
+
126
+ ## Database Interfaces and Abstraction Layer
127
+
128
+ ### 1. Database Bridge Interface
129
+
130
+ ```typescript
131
+ export interface DBBridgeInterface {
132
+ // Basic CRUD operations
133
+ add(event: BridgeEvent): Promise<DBBridgeResponse<unknown>>;
134
+ update(event: BridgeEvent): Promise<DBBridgeResponse<unknown>>;
135
+ delete(event: BridgeEvent): Promise<DBBridgeResponse<unknown>>;
136
+ get(event: BridgeEvent): Promise<DBBridgeResponse<unknown>>;
137
+
138
+ // Pagination query
139
+ pagination(event: BridgeEvent): Promise<DBBridgeResponse<unknown[]>>;
140
+ }
141
+
142
+ // Query event definition
143
+ export interface BridgeEvent {
144
+ table: string;
145
+ fields?: string | string[];
146
+ where?: Where[];
147
+ data?: unknown;
148
+ page?: number;
149
+ pageSize?: number;
150
+ }
151
+
152
+ // Unified response format
153
+ export interface DBBridgeResponse<T> {
154
+ error?: unknown;
155
+ data: T;
156
+ pagination?: PaginationInfo;
157
+ }
158
+ ```
159
+
160
+ ### 2. Query Condition Types
161
+
162
+ ```typescript
163
+ // Operator types
164
+ export type WhereOperation = '=' | '!=' | '>' | '<' | '>=' | '<=';
165
+
166
+ // Query condition type
167
+ export type Where = [string, WhereOperation, string | number];
168
+
169
+ // Pagination information
170
+ export interface PaginationInfo {
171
+ total: number;
172
+ page: number;
173
+ pageSize: number;
174
+ }
175
+ ```
176
+
177
+ ## Repository Pattern Implementation
178
+
179
+ ### 1. User Repository Interface
180
+
181
+ ```typescript
182
+ export interface UserRepositoryInterface {
183
+ // Get all users
184
+ getAll(): Promise<unknown>;
185
+
186
+ // Query user by email
187
+ getUserByEmail(email: string): Promise<UserSchema | null>;
188
+
189
+ // Add user
190
+ add(params: {
191
+ email: string;
192
+ password: string;
193
+ }): Promise<UserSchema[] | null>;
194
+
195
+ // Update user
196
+ updateById(
197
+ id: number,
198
+ params: Partial<Omit<UserSchema, 'id' | 'created_at'>>
199
+ ): Promise<void>;
200
+
201
+ // Pagination query
202
+ pagination<UserSchema>(params: {
203
+ page: number;
204
+ pageSize: number;
205
+ }): Promise<PaginationInterface<UserSchema>>;
206
+ }
207
+ ```
208
+
209
+ ### 2. User Repository Implementation
210
+
211
+ ```typescript
212
+ @injectable()
213
+ export class UserRepository implements UserRepositoryInterface {
214
+ readonly name = 'fe_users';
215
+
216
+ // Safe field list (excluding sensitive information)
217
+ protected safeFields = [
218
+ 'created_at',
219
+ 'email',
220
+ 'email_confirmed_at',
221
+ 'id',
222
+ 'role',
223
+ 'updated_at'
224
+ ];
225
+
226
+ constructor(
227
+ @inject(I.DBBridgeInterface) protected dbBridge: DBBridgeInterface
228
+ ) {}
229
+
230
+ async getUserByEmail(email: string): Promise<UserSchema | null> {
231
+ const result = await this.dbBridge.get({
232
+ table: this.name,
233
+ where: [['email', '=', email]]
234
+ });
235
+
236
+ return isEmpty(result.data) ? null : last(result.data as UserSchema[]);
237
+ }
238
+
239
+ async pagination<UserSchema>(
240
+ params: PaginationParams
241
+ ): Promise<PaginationResult> {
242
+ const result = await this.dbBridge.pagination({
243
+ table: this.name,
244
+ page: params.page,
245
+ pageSize: params.pageSize,
246
+ fields: this.safeFields // Only return safe fields
247
+ });
248
+
249
+ return {
250
+ list: result.data as UserSchema[],
251
+ total: result.count ?? 0,
252
+ page: params.page,
253
+ pageSize: params.pageSize
254
+ };
255
+ }
256
+ }
257
+ ```
258
+
259
+ ## MongoDB Implementation Example
260
+
261
+ ### 1. MongoDB Bridge
262
+
263
+ ```typescript
264
+ @injectable()
265
+ export class MongoDBBridge implements DBBridgeInterface {
266
+ protected client: MongoClient;
267
+ protected db: Db;
268
+
269
+ constructor(
270
+ @inject(I.AppConfig) appConfig: AppConfig,
271
+ @inject(I.Logger) protected logger: LoggerInterface
272
+ ) {
273
+ this.client = new MongoClient(appConfig.mongodbUrl);
274
+ this.db = this.client.db(appConfig.mongodbDatabase);
275
+ }
276
+
277
+ async get(event: BridgeEvent): Promise<DBBridgeResponse<unknown>> {
278
+ const { table, where } = event;
279
+ const collection = this.db.collection(table);
280
+
281
+ const query = this.buildQuery(where ?? []);
282
+ const data = await collection.find(query).toArray();
283
+
284
+ return { data };
285
+ }
286
+
287
+ async pagination(event: BridgeEvent): Promise<DBBridgeResponse<unknown[]>> {
288
+ const { table, where, page = 1, pageSize = 10 } = event;
289
+ const collection = this.db.collection(table);
290
+
291
+ const query = this.buildQuery(where ?? []);
292
+ const total = await collection.countDocuments(query);
293
+ const data = await collection
294
+ .find(query)
295
+ .skip((page - 1) * pageSize)
296
+ .limit(pageSize)
297
+ .toArray();
298
+
299
+ return {
300
+ data,
301
+ pagination: { total, page, pageSize }
302
+ };
303
+ }
304
+
305
+ protected buildQuery(where: Where[]): Document {
306
+ return where.reduce((query, [field, operation, value]) => {
307
+ switch (operation) {
308
+ case '=':
309
+ return { ...query, [field]: value };
310
+ case '!=':
311
+ return { ...query, [field]: { $ne: value } };
312
+ case '>':
313
+ return { ...query, [field]: { $gt: value } };
314
+ case '<':
315
+ return { ...query, [field]: { $lt: value } };
316
+ case '>=':
317
+ return { ...query, [field]: { $gte: value } };
318
+ case '<=':
319
+ return { ...query, [field]: { $lte: value } };
320
+ default:
321
+ return query;
322
+ }
323
+ }, {});
324
+ }
325
+ }
326
+ ```
327
+
328
+ ### 2. MongoDB Configuration
329
+
330
+ ```typescript
331
+ // Add MongoDB configuration in AppConfig
332
+ export class AppConfig implements EnvConfigInterface {
333
+ readonly mongodbUrl: string = process.env.MONGODB_URL!;
334
+ readonly mongodbDatabase: string = process.env.MONGODB_DATABASE!;
335
+ }
336
+
337
+ // Register MongoDB bridge
338
+ export class ServerIOCRegister {
339
+ protected registerImplement(ioc: IOCContainerInterface): void {
340
+ // Use MongoDB implementation
341
+ ioc.bind(I.DBBridgeInterface, ioc.get(MongoDBBridge));
342
+ // Or use Supabase implementation
343
+ // ioc.bind(I.DBBridgeInterface, ioc.get(SupabaseBridge));
344
+ }
345
+ }
346
+ ```
347
+
348
+ ## Best Practices and Examples
349
+
350
+ ### 1. Repository Pattern Usage
351
+
352
+ ```typescript
353
+ @injectable()
354
+ export class UserService {
355
+ constructor(
356
+ @inject(I.UserRepository)
357
+ private userRepository: UserRepositoryInterface
358
+ ) {}
359
+
360
+ async getUserProfile(email: string): Promise<UserProfile | null> {
361
+ const user = await this.userRepository.getUserByEmail(email);
362
+ if (!user) return null;
363
+
364
+ // Convert to business model
365
+ return this.mapToUserProfile(user);
366
+ }
367
+
368
+ async listUsers(
369
+ params: PaginationParams
370
+ ): Promise<PaginationResult<UserProfile>> {
371
+ const result = await this.userRepository.pagination(params);
372
+
373
+ return {
374
+ ...result,
375
+ list: result.list.map(this.mapToUserProfile)
376
+ };
377
+ }
378
+ }
379
+ ```
380
+
381
+ ### 2. Transaction Handling
382
+
383
+ ```typescript
384
+ @injectable()
385
+ export class TransactionManager {
386
+ async withTransaction<T>(
387
+ callback: (transaction: Transaction) => Promise<T>
388
+ ): Promise<T> {
389
+ const transaction = await this.dbBridge.startTransaction();
390
+
391
+ try {
392
+ const result = await callback(transaction);
393
+ await transaction.commit();
394
+ return result;
395
+ } catch (error) {
396
+ await transaction.rollback();
397
+ throw error;
398
+ }
399
+ }
400
+ }
401
+
402
+ // Using transactions
403
+ async createUserWithProfile(data: UserData): Promise<User> {
404
+ return this.transactionManager.withTransaction(async (transaction) => {
405
+ const user = await this.userRepository.add(data, transaction);
406
+ await this.profileRepository.add(data.profile, transaction);
407
+ return user;
408
+ });
409
+ }
410
+ ```
411
+
412
+ ### 3. Query Optimization
413
+
414
+ ```typescript
415
+ @injectable()
416
+ export class OptimizedUserRepository extends UserRepository {
417
+ // Use field selection to optimize queries
418
+ async getUserProfile(id: number): Promise<UserProfile> {
419
+ const result = await this.dbBridge.get({
420
+ table: this.name,
421
+ fields: this.safeFields, // Only select needed fields
422
+ where: [['id', '=', id]]
423
+ });
424
+ return this.mapToProfile(result.data);
425
+ }
426
+
427
+ // Use index to optimize queries
428
+ async searchUsers(query: string): Promise<User[]> {
429
+ const result = await this.dbBridge.get({
430
+ table: this.name,
431
+ where: [
432
+ ['email', '=', query], // Assuming email field is indexed
433
+ ['role', '=', 'user']
434
+ ]
435
+ });
436
+ return result.data as User[];
437
+ }
438
+ }
439
+ ```
440
+
441
+ ### 4. Caching Strategy
442
+
443
+ ```typescript
444
+ @injectable()
445
+ export class CachedUserRepository extends UserRepository {
446
+ constructor(
447
+ @inject(I.DBBridgeInterface) dbBridge: DBBridgeInterface,
448
+ @inject(I.CacheManager) private cache: CacheManager
449
+ ) {
450
+ super(dbBridge);
451
+ }
452
+
453
+ async getUserByEmail(email: string): Promise<UserSchema | null> {
454
+ // Check cache first
455
+ const cached = await this.cache.get(`user:${email}`);
456
+ if (cached) return cached;
457
+
458
+ // Cache miss, query database
459
+ const user = await super.getUserByEmail(email);
460
+ if (user) {
461
+ await this.cache.set(`user:${email}`, user, '1h');
462
+ }
463
+
464
+ return user;
465
+ }
466
+ }
467
+ ```
468
+
469
+ ## Summary
470
+
471
+ The project's database design follows these principles:
472
+
473
+ 1. **Abstraction Layers**:
474
+ - Clear interface definitions
475
+ - Replaceable database implementations
476
+ - Repository pattern encapsulating business logic
477
+
478
+ 2. **Type Safety**:
479
+ - Complete TypeScript type definitions
480
+ - Query condition type checking
481
+ - Response data type validation
482
+
483
+ 3. **Security**:
484
+ - Field-level access control
485
+ - Parameterized queries preventing injection
486
+ - Sensitive data filtering
487
+
488
+ 4. **Extensibility**:
489
+ - Support for multiple database implementations
490
+ - Plugin-based feature extension
491
+ - Unified query interface
492
+
493
+ 5. **Performance Optimization**:
494
+ - Field selection optimization
495
+ - Query condition optimization
496
+ - Cache strategy support