@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,563 @@
1
+ # Next.js 环境变量配置指南
2
+
3
+ ## 什么是环境变量?
4
+
5
+ 环境变量是一种在不同环境(开发、测试、生产)中配置应用行为的方式。在 Next.js 中,环境变量的使用有其特殊性,尤其是在客户端和服务端的处理上。
6
+
7
+ **关键特点**:
8
+
9
+ - 客户端环境变量必须以 `NEXT_PUBLIC_` 开头
10
+ - 服务端环境变量可以直接使用,无需特殊前缀
11
+ - 支持多环境配置和本地覆盖
12
+
13
+ ## 环境变量加载优先级
14
+
15
+ Next.js 按照以下优先级加载环境变量:
16
+
17
+ ```
18
+ .env.local → .env.development/.env.production → .env
19
+ ```
20
+
21
+ ## 文件结构
22
+
23
+ ```
24
+ 项目根目录/
25
+ ├── .env # 默认环境变量
26
+ ├── .env.local # 本地环境变量(git ignored)
27
+ ├── .env.development # 开发环境变量
28
+ ├── .env.production # 生产环境变量
29
+ ├── .env.test # 测试环境变量
30
+ └── src/
31
+ └── base/
32
+ └── cases/
33
+ └── AppConfig.ts # 应用配置类
34
+ ```
35
+
36
+ ## 环境变量文件
37
+
38
+ ### 1. 环境变量文件示例
39
+
40
+ ```bash
41
+ # .env (默认配置)
42
+ # 客户端可访问的环境变量(以 NEXT_PUBLIC_ 开头)
43
+ NEXT_PUBLIC_APP_NAME=MyApp
44
+ NEXT_PUBLIC_API_BASE_URL=http://api.example.com
45
+ NEXT_PUBLIC_ENABLE_ANALYTICS=false
46
+
47
+ # 仅服务端可访问的环境变量
48
+ DATABASE_URL=postgres://user:pass@localhost:5432/db
49
+ JWT_SECRET=your-secret-key
50
+ API_TOKEN=server-side-token
51
+
52
+ # .env.development (开发环境)
53
+ NEXT_PUBLIC_API_BASE_URL=http://localhost:3000/api
54
+ NEXT_PUBLIC_DEBUG=true
55
+ DATABASE_URL=postgres://dev:pass@localhost:5432/dev_db
56
+
57
+ # .env.production (生产环境)
58
+ NEXT_PUBLIC_API_BASE_URL=https://api.production.com
59
+ NEXT_PUBLIC_DEBUG=false
60
+ DATABASE_URL=postgres://prod:pass@production:5432/prod_db
61
+
62
+ # .env.local (本地覆盖,不提交到 git)
63
+ NEXT_PUBLIC_FEATURE_FLAG=true
64
+ DATABASE_URL=postgres://local:pass@localhost:5432/local_db
65
+ ```
66
+
67
+ ### 2. 环境变量使用规范
68
+
69
+ #### 客户端环境变量
70
+
71
+ - 必须以 `NEXT_PUBLIC_` 开头
72
+ - 会被打包到客户端代码中
73
+ - 适用于公开的配置信息
74
+
75
+ ```bash
76
+ # ✅ 正确的客户端环境变量
77
+ NEXT_PUBLIC_API_URL=https://api.example.com
78
+ NEXT_PUBLIC_GOOGLE_ANALYTICS_ID=UA-XXXXX
79
+ NEXT_PUBLIC_FEATURE_FLAGS={"newUI":true}
80
+
81
+ # ❌ 错误的客户端环境变量(没有 NEXT_PUBLIC_ 前缀)
82
+ API_URL=https://api.example.com # 客户端无法访问
83
+ ```
84
+
85
+ #### 服务端环境变量
86
+
87
+ - 无需特殊前缀
88
+ - 只在服务端可用
89
+ - 适用于敏感信息
90
+
91
+ ```bash
92
+ # ✅ 正确的服务端环境变量
93
+ DATABASE_URL=postgres://user:pass@localhost:5432/db
94
+ JWT_SECRET=your-secret-key
95
+ API_TOKEN=your-api-token
96
+
97
+ # ❌ 不应该以 NEXT_PUBLIC_ 开头的服务端敏感信息
98
+ NEXT_PUBLIC_DATABASE_URL=postgres://user:pass@localhost:5432/db # 安全风险!
99
+ ```
100
+
101
+ ## 项目中的实现
102
+
103
+ ### 1. 应用配置类
104
+
105
+ ```typescript
106
+ // src/base/cases/AppConfig.ts
107
+ export class AppConfig implements EnvConfigInterface {
108
+ /**
109
+ * 应用名称
110
+ * @description 从 NEXT_PUBLIC_APP_NAME 环境变量获取
111
+ */
112
+ readonly appName = process.env.NEXT_PUBLIC_APP_NAME || '';
113
+
114
+ /**
115
+ * API 基础 URL
116
+ * @description 从 NEXT_PUBLIC_API_BASE_URL 环境变量获取
117
+ */
118
+ readonly apiBaseUrl = process.env.NEXT_PUBLIC_API_BASE_URL || '';
119
+
120
+ /**
121
+ * 是否为开发环境
122
+ */
123
+ readonly isDevelopment = process.env.NODE_ENV === 'development';
124
+
125
+ /**
126
+ * 是否为生产环境
127
+ */
128
+ readonly isProduction = process.env.NODE_ENV === 'production';
129
+
130
+ /**
131
+ * 数据库连接 URL(仅服务端可用)
132
+ */
133
+ readonly databaseUrl = process.env.DATABASE_URL;
134
+
135
+ /**
136
+ * JWT 密钥(仅服务端可用)
137
+ */
138
+ readonly jwtSecret = process.env.JWT_SECRET;
139
+
140
+ // ... 更多配置项
141
+ }
142
+ ```
143
+
144
+ ### 2. 在客户端使用配置
145
+
146
+ ```typescript
147
+ // 在组件中使用
148
+ function Analytics() {
149
+ // ✅ 正确:通过 IOC 获取配置对象
150
+ const appConfig = IOC(IOCIdentifier.AppConfig);
151
+
152
+ useEffect(() => {
153
+ if (appConfig.analyticsId) {
154
+ // 初始化 analytics
155
+ }
156
+ }, [appConfig.analyticsId]);
157
+
158
+ return appConfig.debug ? <DebugInfo /> : null;
159
+ }
160
+ ```
161
+
162
+ ### 3. 在服务端使用配置
163
+
164
+ ```typescript
165
+ // app/api/auth/[...nextauth]/route.ts
166
+ import { NextResponse } from 'next/server';
167
+
168
+ export async function GET() {
169
+ // ✅ 正确:通过 IOC 获取配置对象
170
+ const appConfig = IOC(IOCIdentifier.AppConfig);
171
+
172
+ if (!appConfig.databaseUrl || !appConfig.jwtSecret) {
173
+ return NextResponse.json(
174
+ { error: 'Server configuration error' },
175
+ { status: 500 }
176
+ );
177
+ }
178
+
179
+ // 使用配置...
180
+ }
181
+ ```
182
+
183
+ ### 4. 在服务中使用配置
184
+
185
+ ```typescript
186
+ // 定义服务类
187
+ @injectable()
188
+ export class AdminService {
189
+ constructor(
190
+ @inject(IOCIdentifier.AppConfig)
191
+ private appConfig: AppConfig
192
+ ) {}
193
+
194
+ async fetchAdminData() {
195
+ const response = await fetch('https://api.example.com/admin', {
196
+ headers: {
197
+ Authorization: `Bearer ${this.appConfig.apiToken}`
198
+ }
199
+ });
200
+ return response.json();
201
+ }
202
+ }
203
+ ```
204
+
205
+ ## 最佳实践
206
+
207
+ ### 1. 环境变量命名规范
208
+
209
+ ```bash
210
+ # ✅ 好的命名
211
+ NEXT_PUBLIC_APP_NAME=MyApp
212
+ NEXT_PUBLIC_API_URL=https://api.example.com
213
+ NEXT_PUBLIC_FEATURE_FLAGS={"darkMode":true}
214
+
215
+ # ❌ 不好的命名
216
+ next_public_app_name=MyApp # 应该使用大写
217
+ NEXT_PUBLIC_SECRET_KEY=xxx # 敏感信息不应该用 NEXT_PUBLIC_
218
+ ```
219
+
220
+ ### 2. 配置类实现
221
+
222
+ ```typescript
223
+ // 定义配置接口
224
+ interface EnvConfigInterface {
225
+ readonly env: string;
226
+ readonly appName: string;
227
+ readonly appVersion: string;
228
+ // ... 其他配置项
229
+ }
230
+
231
+ // 实现配置类
232
+ @injectable()
233
+ export class AppConfig implements EnvConfigInterface {
234
+ /**
235
+ * 当前环境模式
236
+ * @description 基于当前使用的 .env 文件自动设置
237
+ */
238
+ readonly env: string = process.env.APP_ENV!;
239
+
240
+ /**
241
+ * 应用名称
242
+ */
243
+ readonly appName: string = name;
244
+
245
+ /**
246
+ * 应用版本
247
+ */
248
+ readonly appVersion: string = version;
249
+
250
+ /**
251
+ * 用户令牌存储键
252
+ */
253
+ readonly userTokenKey: string = '_user_token';
254
+
255
+ /**
256
+ * 数据库连接 URL(仅服务端)
257
+ */
258
+ readonly supabaseUrl: string = process.env.SUPABASE_URL!;
259
+
260
+ /**
261
+ * 数据库匿名密钥(仅服务端)
262
+ */
263
+ readonly supabaseAnonKey: string = process.env.SUPABASE_ANON_KEY!;
264
+
265
+ /**
266
+ * JWT 密钥(仅服务端)
267
+ */
268
+ readonly jwtSecret: string = process.env.JWT_SECRET!;
269
+
270
+ /**
271
+ * JWT 过期时间
272
+ * @example '30 days'
273
+ * @example '1 year'
274
+ */
275
+ readonly jwtExpiresIn: StringValue = '30 days';
276
+
277
+ /**
278
+ * OpenAI API 配置(仅服务端)
279
+ */
280
+ readonly openaiBaseUrl: string = process.env.CEREBRAS_BASE_URL!;
281
+ readonly openaiApiKey: string = process.env.CEREBRAS_API_KEY!;
282
+ }
283
+ ```
284
+
285
+ ### 3. 配置验证
286
+
287
+ ```typescript
288
+ // 定义配置验证器
289
+ @injectable()
290
+ export class ConfigValidator {
291
+ constructor(
292
+ @inject(IOCIdentifier.AppConfig)
293
+ private appConfig: AppConfig,
294
+ @inject(IOCIdentifier.Logger)
295
+ private logger: LoggerInterface
296
+ ) {}
297
+
298
+ /**
299
+ * 验证所有必需的配置项
300
+ * @throws {Error} 当必需的配置项缺失时抛出错误
301
+ */
302
+ validateRequiredConfig(): void {
303
+ // 验证基础配置
304
+ this.validateBasicConfig();
305
+
306
+ // 根据运行环境验证不同的配置
307
+ if (typeof window === 'undefined') {
308
+ this.validateServerConfig();
309
+ }
310
+ }
311
+
312
+ /**
313
+ * 验证基础配置项
314
+ */
315
+ private validateBasicConfig(): void {
316
+ const requiredConfigs: Array<keyof AppConfig> = [
317
+ 'env',
318
+ 'appName',
319
+ 'appVersion',
320
+ 'userTokenKey'
321
+ ];
322
+
323
+ for (const key of requiredConfigs) {
324
+ if (!this.appConfig[key]) {
325
+ throw new Error(`Missing required config: ${key}`);
326
+ }
327
+ }
328
+ }
329
+
330
+ /**
331
+ * 验证服务端配置项
332
+ */
333
+ private validateServerConfig(): void {
334
+ const requiredServerConfigs: Array<keyof AppConfig> = [
335
+ 'supabaseUrl',
336
+ 'supabaseAnonKey',
337
+ 'jwtSecret',
338
+ 'openaiBaseUrl',
339
+ 'openaiApiKey'
340
+ ];
341
+
342
+ for (const key of requiredServerConfigs) {
343
+ if (!this.appConfig[key]) {
344
+ throw new Error(`Missing required server config: ${key}`);
345
+ }
346
+ }
347
+ }
348
+
349
+ /**
350
+ * 验证配置值的格式
351
+ */
352
+ validateConfigFormat(): void {
353
+ // 验证 URL 格式
354
+ if (!this.isValidUrl(this.appConfig.supabaseUrl)) {
355
+ throw new Error('Invalid supabaseUrl format');
356
+ }
357
+
358
+ // 验证 JWT 过期时间格式
359
+ if (!this.isValidDuration(this.appConfig.jwtExpiresIn)) {
360
+ throw new Error('Invalid jwtExpiresIn format');
361
+ }
362
+
363
+ this.logger.info('All config formats validated successfully');
364
+ }
365
+
366
+ private isValidUrl(url: string): boolean {
367
+ try {
368
+ new URL(url);
369
+ return true;
370
+ } catch {
371
+ return false;
372
+ }
373
+ }
374
+
375
+ private isValidDuration(duration: string): boolean {
376
+ // 实现持续时间格式验证逻辑
377
+ return /^\d+\s+(days?|weeks?|months?|years?)$/.test(duration);
378
+ }
379
+ }
380
+ ```
381
+
382
+ ### 4. 敏感信息处理
383
+
384
+ ```bash
385
+ # .env.local (不提交到 git)
386
+ DATABASE_URL=postgres://user:pass@localhost:5432/db
387
+ JWT_SECRET=your-secret-key
388
+ API_TOKEN=your-api-token
389
+
390
+ # .env.template (提交到 git,作为模板)
391
+ DATABASE_URL=postgres://user:password@localhost:5432/dbname
392
+ JWT_SECRET=your-jwt-secret-here
393
+ API_TOKEN=your-api-token-here
394
+ ```
395
+
396
+ ## 调试和故障排除
397
+
398
+ ### 1. 配置调试工具
399
+
400
+ ```typescript
401
+ @injectable()
402
+ export class ConfigDebugger {
403
+ constructor(
404
+ @inject(IOCIdentifier.AppConfig)
405
+ private appConfig: AppConfig,
406
+ @inject(IOCIdentifier.Logger)
407
+ private logger: LoggerInterface
408
+ ) {}
409
+
410
+ /**
411
+ * 打印配置信息
412
+ */
413
+ logConfig(): void {
414
+ this.logger.group('Configuration Debug Info');
415
+
416
+ // 基础配置
417
+ this.logger.info('Basic Config:', {
418
+ env: this.appConfig.env,
419
+ appName: this.appConfig.appName,
420
+ appVersion: this.appConfig.appVersion
421
+ });
422
+
423
+ // 如果在服务端,打印服务端配置
424
+ if (typeof window === 'undefined') {
425
+ this.logger.info('Server Config:', {
426
+ supabaseUrl: this.maskSensitiveInfo(this.appConfig.supabaseUrl),
427
+ jwtExpiresIn: this.appConfig.jwtExpiresIn
428
+ });
429
+ }
430
+
431
+ this.logger.groupEnd();
432
+ }
433
+
434
+ /**
435
+ * 验证配置健康状态
436
+ */
437
+ async checkConfigHealth(): Promise<void> {
438
+ try {
439
+ // 验证数据库连接
440
+ if (typeof window === 'undefined') {
441
+ await this.checkDatabaseConnection();
442
+ }
443
+
444
+ // 验证其他配置项
445
+ this.validateConfigValues();
446
+
447
+ this.logger.info('Configuration health check passed');
448
+ } catch (error) {
449
+ this.logger.error('Configuration health check failed:', error);
450
+ throw error;
451
+ }
452
+ }
453
+
454
+ private async checkDatabaseConnection(): Promise<void> {
455
+ // 实现数据库连接检查逻辑
456
+ }
457
+
458
+ private validateConfigValues(): void {
459
+ // 实现配置值验证逻辑
460
+ }
461
+
462
+ private maskSensitiveInfo(value: string): string {
463
+ return value.replace(/^(https?:\/\/[^:]+:)([^@]+)(@.*)$/, '$1****$3');
464
+ }
465
+ }
466
+ ```
467
+
468
+ ### 2. 常见问题处理
469
+
470
+ **问题 1:配置未正确注入**
471
+
472
+ ```typescript
473
+ // ❌ 错误:直接使用环境变量
474
+ class UserService {
475
+ private apiUrl = process.env.NEXT_PUBLIC_API_URL;
476
+ }
477
+
478
+ // ✅ 正确:通过配置类获取
479
+ @injectable()
480
+ class UserService {
481
+ constructor(
482
+ @inject(IOCIdentifier.AppConfig)
483
+ private appConfig: AppConfig
484
+ ) {}
485
+ }
486
+ ```
487
+
488
+ **问题 2:配置验证失败**
489
+
490
+ ```typescript
491
+ // ❌ 错误:没有配置验证
492
+ @injectable()
493
+ class ApiService {
494
+ constructor(
495
+ @inject(IOCIdentifier.AppConfig)
496
+ private appConfig: AppConfig
497
+ ) {}
498
+ }
499
+
500
+ // ✅ 正确:包含配置验证
501
+ @injectable()
502
+ class ApiService {
503
+ constructor(
504
+ @inject(IOCIdentifier.AppConfig)
505
+ private appConfig: AppConfig,
506
+ @inject(IOCIdentifier.ConfigValidator)
507
+ private configValidator: ConfigValidator
508
+ ) {
509
+ this.configValidator.validateRequiredConfig();
510
+ }
511
+ }
512
+ ```
513
+
514
+ **问题 3:配置类型处理**
515
+
516
+ ```typescript
517
+ // ❌ 错误:手动类型转换
518
+ class FeatureService {
519
+ private isDebug = process.env.NEXT_PUBLIC_DEBUG === 'true';
520
+ }
521
+
522
+ // ✅ 正确:在配置类中处理类型转换
523
+ @injectable()
524
+ export class AppConfig implements EnvConfigInterface {
525
+ readonly debug: boolean = this.parseBoolean(process.env.NEXT_PUBLIC_DEBUG);
526
+
527
+ private parseBoolean(value: string | undefined): boolean {
528
+ return value?.toLowerCase() === 'true';
529
+ }
530
+ }
531
+ ```
532
+
533
+ ## 总结
534
+
535
+ 面向对象的配置管理系统提供了:
536
+
537
+ 1. **配置封装**:
538
+ - 通过 `AppConfig` 类统一管理所有配置
539
+ - 实现 `EnvConfigInterface` 接口确保配置完整性
540
+ - 使用依赖注入实现配置的解耦
541
+
542
+ 2. **类型安全**:
543
+ - 配置类中处理类型转换
544
+ - TypeScript 接口定义确保类型正确
545
+ - 编译时类型检查
546
+
547
+ 3. **配置验证**:
548
+ - 专门的 `ConfigValidator` 类处理配置验证
549
+ - 运行时配置完整性检查
550
+ - 配置格式验证
551
+
552
+ 4. **最佳实践**:
553
+ - 依赖注入管理配置依赖
554
+ - 配置验证和调试工具
555
+ - 敏感信息保护
556
+ - 类型安全处理
557
+
558
+ 通过面向对象的方式管理配置,我们可以:
559
+
560
+ - 提高代码的可维护性和可测试性
561
+ - 确保配置的类型安全和完整性
562
+ - 方便地进行配置验证和调试
563
+ - 更好地管理配置依赖关系