@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.
- package/CHANGELOG.md +23 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/templates/next-app/README.en.md +131 -0
- package/dist/templates/next-app/README.md +115 -20
- package/dist/templates/next-app/docs/en/api.md +387 -0
- package/dist/templates/next-app/docs/en/component.md +544 -0
- package/dist/templates/next-app/docs/en/database.md +496 -0
- package/dist/templates/next-app/docs/en/development-guide.md +727 -0
- package/dist/templates/next-app/docs/en/env.md +563 -0
- package/dist/templates/next-app/docs/en/i18n.md +287 -0
- package/dist/templates/next-app/docs/en/index.md +166 -0
- package/dist/templates/next-app/docs/en/page.md +457 -0
- package/dist/templates/next-app/docs/en/project-structure.md +177 -0
- package/dist/templates/next-app/docs/en/router.md +427 -0
- package/dist/templates/next-app/docs/en/theme.md +532 -0
- package/dist/templates/next-app/docs/en/validator.md +478 -0
- package/dist/templates/next-app/docs/zh/api.md +387 -0
- package/dist/templates/next-app/docs/zh/component.md +544 -0
- package/dist/templates/next-app/docs/zh/database.md +496 -0
- package/dist/templates/next-app/docs/zh/development-guide.md +727 -0
- package/dist/templates/next-app/docs/zh/env.md +563 -0
- package/dist/templates/next-app/docs/zh/i18n.md +287 -0
- package/dist/templates/next-app/docs/zh/index.md +166 -0
- package/dist/templates/next-app/docs/zh/page.md +457 -0
- package/dist/templates/next-app/docs/zh/project-structure.md +177 -0
- package/dist/templates/next-app/docs/zh/router.md +427 -0
- package/dist/templates/next-app/docs/zh/theme.md +532 -0
- package/dist/templates/next-app/docs/zh/validator.md +476 -0
- package/package.json +1 -1
- package/dist/templates/next-app/docs/env.md +0 -94
|
@@ -0,0 +1,563 @@
|
|
|
1
|
+
# Next.js Environment Variable Configuration Guide
|
|
2
|
+
|
|
3
|
+
## What are Environment Variables?
|
|
4
|
+
|
|
5
|
+
Environment variables are a way to configure application behavior across different environments (development, testing, production). In Next.js, environment variables have special characteristics, particularly in how they are handled on the client and server sides.
|
|
6
|
+
|
|
7
|
+
**Key Features**:
|
|
8
|
+
|
|
9
|
+
- Client-side environment variables must start with `NEXT_PUBLIC_`
|
|
10
|
+
- Server-side environment variables can be used directly without special prefix
|
|
11
|
+
- Supports multi-environment configuration and local overrides
|
|
12
|
+
|
|
13
|
+
## Environment Variable Loading Priority
|
|
14
|
+
|
|
15
|
+
Next.js loads environment variables in the following priority order:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
.env.local → .env.development/.env.production → .env
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## File Structure
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
project-root/
|
|
25
|
+
├── .env # Default environment variables
|
|
26
|
+
├── .env.local # Local environment variables (git ignored)
|
|
27
|
+
├── .env.development # Development environment variables
|
|
28
|
+
├── .env.production # Production environment variables
|
|
29
|
+
├── .env.test # Test environment variables
|
|
30
|
+
└── src/
|
|
31
|
+
└── base/
|
|
32
|
+
└── cases/
|
|
33
|
+
└── AppConfig.ts # Application configuration class
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Environment Variable Files
|
|
37
|
+
|
|
38
|
+
### 1. Environment Variable File Examples
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# .env (default configuration)
|
|
42
|
+
# Client-accessible environment variables (starting with 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
|
+
# Server-side only environment variables
|
|
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 (development environment)
|
|
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 (production environment)
|
|
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 (local override, not committed to git)
|
|
63
|
+
NEXT_PUBLIC_FEATURE_FLAG=true
|
|
64
|
+
DATABASE_URL=postgres://local:pass@localhost:5432/local_db
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 2. Environment Variable Usage Guidelines
|
|
68
|
+
|
|
69
|
+
#### Client-Side Environment Variables
|
|
70
|
+
|
|
71
|
+
- Must start with `NEXT_PUBLIC_`
|
|
72
|
+
- Will be bundled into client-side code
|
|
73
|
+
- Suitable for public configuration information
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
# ✅ Correct client-side environment variables
|
|
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
|
+
# ❌ Incorrect client-side environment variables (missing NEXT_PUBLIC_ prefix)
|
|
82
|
+
API_URL=https://api.example.com # Client cannot access
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
#### Server-Side Environment Variables
|
|
86
|
+
|
|
87
|
+
- No special prefix needed
|
|
88
|
+
- Only available on server-side
|
|
89
|
+
- Suitable for sensitive information
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# ✅ Correct server-side environment variables
|
|
93
|
+
DATABASE_URL=postgres://user:pass@localhost:5432/db
|
|
94
|
+
JWT_SECRET=your-secret-key
|
|
95
|
+
API_TOKEN=your-api-token
|
|
96
|
+
|
|
97
|
+
# ❌ Server-side sensitive information should not start with NEXT_PUBLIC_
|
|
98
|
+
NEXT_PUBLIC_DATABASE_URL=postgres://user:pass@localhost:5432/db # Security risk!
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Implementation in Project
|
|
102
|
+
|
|
103
|
+
### 1. Application Configuration Class
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
// src/base/cases/AppConfig.ts
|
|
107
|
+
export class AppConfig implements EnvConfigInterface {
|
|
108
|
+
/**
|
|
109
|
+
* Application name
|
|
110
|
+
* @description Retrieved from NEXT_PUBLIC_APP_NAME environment variable
|
|
111
|
+
*/
|
|
112
|
+
readonly appName = process.env.NEXT_PUBLIC_APP_NAME || '';
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* API base URL
|
|
116
|
+
* @description Retrieved from NEXT_PUBLIC_API_BASE_URL environment variable
|
|
117
|
+
*/
|
|
118
|
+
readonly apiBaseUrl = process.env.NEXT_PUBLIC_API_BASE_URL || '';
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Whether in development environment
|
|
122
|
+
*/
|
|
123
|
+
readonly isDevelopment = process.env.NODE_ENV === 'development';
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Whether in production environment
|
|
127
|
+
*/
|
|
128
|
+
readonly isProduction = process.env.NODE_ENV === 'production';
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Database connection URL (server-side only)
|
|
132
|
+
*/
|
|
133
|
+
readonly databaseUrl = process.env.DATABASE_URL;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* JWT secret (server-side only)
|
|
137
|
+
*/
|
|
138
|
+
readonly jwtSecret = process.env.JWT_SECRET;
|
|
139
|
+
|
|
140
|
+
// ... more configuration items
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### 2. Using Configuration in Client
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
// Using in components
|
|
148
|
+
function Analytics() {
|
|
149
|
+
// ✅ Correct: Get configuration object through IOC
|
|
150
|
+
const appConfig = IOC(IOCIdentifier.AppConfig);
|
|
151
|
+
|
|
152
|
+
useEffect(() => {
|
|
153
|
+
if (appConfig.analyticsId) {
|
|
154
|
+
// Initialize analytics
|
|
155
|
+
}
|
|
156
|
+
}, [appConfig.analyticsId]);
|
|
157
|
+
|
|
158
|
+
return appConfig.debug ? <DebugInfo /> : null;
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### 3. Using Configuration in Server
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
// app/api/auth/[...nextauth]/route.ts
|
|
166
|
+
import { NextResponse } from 'next/server';
|
|
167
|
+
|
|
168
|
+
export async function GET() {
|
|
169
|
+
// ✅ Correct: Get configuration object through 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
|
+
// Use configuration...
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### 4. Using Configuration in Services
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
// Define service class
|
|
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
|
+
## Best Practices
|
|
206
|
+
|
|
207
|
+
### 1. Environment Variable Naming Conventions
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
# ✅ Good naming
|
|
211
|
+
NEXT_PUBLIC_APP_NAME=MyApp
|
|
212
|
+
NEXT_PUBLIC_API_URL=https://api.example.com
|
|
213
|
+
NEXT_PUBLIC_FEATURE_FLAGS={"darkMode":true}
|
|
214
|
+
|
|
215
|
+
# ❌ Bad naming
|
|
216
|
+
next_public_app_name=MyApp # Should use uppercase
|
|
217
|
+
NEXT_PUBLIC_SECRET_KEY=xxx # Sensitive info shouldn't use NEXT_PUBLIC_
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### 2. Configuration Class Implementation
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
// Define configuration interface
|
|
224
|
+
interface EnvConfigInterface {
|
|
225
|
+
readonly env: string;
|
|
226
|
+
readonly appName: string;
|
|
227
|
+
readonly appVersion: string;
|
|
228
|
+
// ... other configuration items
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Implement configuration class
|
|
232
|
+
@injectable()
|
|
233
|
+
export class AppConfig implements EnvConfigInterface {
|
|
234
|
+
/**
|
|
235
|
+
* Current environment mode
|
|
236
|
+
* @description Automatically set based on current .env file
|
|
237
|
+
*/
|
|
238
|
+
readonly env: string = process.env.APP_ENV!;
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Application name
|
|
242
|
+
*/
|
|
243
|
+
readonly appName: string = name;
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Application version
|
|
247
|
+
*/
|
|
248
|
+
readonly appVersion: string = version;
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* User token storage key
|
|
252
|
+
*/
|
|
253
|
+
readonly userTokenKey: string = '_user_token';
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Database connection URL (server-side only)
|
|
257
|
+
*/
|
|
258
|
+
readonly supabaseUrl: string = process.env.SUPABASE_URL!;
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Database anonymous key (server-side only)
|
|
262
|
+
*/
|
|
263
|
+
readonly supabaseAnonKey: string = process.env.SUPABASE_ANON_KEY!;
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* JWT secret (server-side only)
|
|
267
|
+
*/
|
|
268
|
+
readonly jwtSecret: string = process.env.JWT_SECRET!;
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* JWT expiration time
|
|
272
|
+
* @example '30 days'
|
|
273
|
+
* @example '1 year'
|
|
274
|
+
*/
|
|
275
|
+
readonly jwtExpiresIn: StringValue = '30 days';
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* OpenAI API configuration (server-side only)
|
|
279
|
+
*/
|
|
280
|
+
readonly openaiBaseUrl: string = process.env.CEREBRAS_BASE_URL!;
|
|
281
|
+
readonly openaiApiKey: string = process.env.CEREBRAS_API_KEY!;
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### 3. Configuration Validation
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
// Define configuration validator
|
|
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
|
+
* Validate all required configuration items
|
|
300
|
+
* @throws {Error} When required configuration items are missing
|
|
301
|
+
*/
|
|
302
|
+
validateRequiredConfig(): void {
|
|
303
|
+
// Validate basic configuration
|
|
304
|
+
this.validateBasicConfig();
|
|
305
|
+
|
|
306
|
+
// Validate different configurations based on runtime environment
|
|
307
|
+
if (typeof window === 'undefined') {
|
|
308
|
+
this.validateServerConfig();
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Validate basic configuration items
|
|
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
|
+
* Validate server-side configuration items
|
|
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
|
+
* Validate configuration value formats
|
|
351
|
+
*/
|
|
352
|
+
validateConfigFormat(): void {
|
|
353
|
+
// Validate URL format
|
|
354
|
+
if (!this.isValidUrl(this.appConfig.supabaseUrl)) {
|
|
355
|
+
throw new Error('Invalid supabaseUrl format');
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Validate JWT expiration time format
|
|
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
|
+
// Implement duration format validation logic
|
|
377
|
+
return /^\d+\s+(days?|weeks?|months?|years?)$/.test(duration);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
### 4. Handling Sensitive Information
|
|
383
|
+
|
|
384
|
+
```bash
|
|
385
|
+
# .env.local (not committed to 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 (committed to git, as template)
|
|
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
|
+
## Debugging and Troubleshooting
|
|
397
|
+
|
|
398
|
+
### 1. Configuration Debug Tool
|
|
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
|
+
* Print configuration information
|
|
412
|
+
*/
|
|
413
|
+
logConfig(): void {
|
|
414
|
+
this.logger.group('Configuration Debug Info');
|
|
415
|
+
|
|
416
|
+
// Basic configuration
|
|
417
|
+
this.logger.info('Basic Config:', {
|
|
418
|
+
env: this.appConfig.env,
|
|
419
|
+
appName: this.appConfig.appName,
|
|
420
|
+
appVersion: this.appConfig.appVersion
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
// If on server-side, print server configuration
|
|
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
|
+
* Validate configuration health status
|
|
436
|
+
*/
|
|
437
|
+
async checkConfigHealth(): Promise<void> {
|
|
438
|
+
try {
|
|
439
|
+
// Validate database connection
|
|
440
|
+
if (typeof window === 'undefined') {
|
|
441
|
+
await this.checkDatabaseConnection();
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// Validate other configuration items
|
|
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
|
+
// Implement database connection check logic
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
private validateConfigValues(): void {
|
|
459
|
+
// Implement configuration value validation logic
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
private maskSensitiveInfo(value: string): string {
|
|
463
|
+
return value.replace(/^(https?:\/\/[^:]+:)([^@]+)(@.*)$/, '$1****$3');
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
### 2. Common Issues Handling
|
|
469
|
+
|
|
470
|
+
**Issue 1: Configuration Not Properly Injected**
|
|
471
|
+
|
|
472
|
+
```typescript
|
|
473
|
+
// ❌ Wrong: Using environment variables directly
|
|
474
|
+
class UserService {
|
|
475
|
+
private apiUrl = process.env.NEXT_PUBLIC_API_URL;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// ✅ Correct: Get through configuration class
|
|
479
|
+
@injectable()
|
|
480
|
+
class UserService {
|
|
481
|
+
constructor(
|
|
482
|
+
@inject(IOCIdentifier.AppConfig)
|
|
483
|
+
private appConfig: AppConfig
|
|
484
|
+
) {}
|
|
485
|
+
}
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
**Issue 2: Configuration Validation Failed**
|
|
489
|
+
|
|
490
|
+
```typescript
|
|
491
|
+
// ❌ Wrong: No configuration validation
|
|
492
|
+
@injectable()
|
|
493
|
+
class ApiService {
|
|
494
|
+
constructor(
|
|
495
|
+
@inject(IOCIdentifier.AppConfig)
|
|
496
|
+
private appConfig: AppConfig
|
|
497
|
+
) {}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// ✅ Correct: Include configuration validation
|
|
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
|
+
**Issue 3: Configuration Type Handling**
|
|
515
|
+
|
|
516
|
+
```typescript
|
|
517
|
+
// ❌ Wrong: Manual type conversion
|
|
518
|
+
class FeatureService {
|
|
519
|
+
private isDebug = process.env.NEXT_PUBLIC_DEBUG === 'true';
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// ✅ Correct: Handle type conversion in configuration class
|
|
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
|
+
## Summary
|
|
534
|
+
|
|
535
|
+
The object-oriented configuration management system provides:
|
|
536
|
+
|
|
537
|
+
1. **Configuration Encapsulation**:
|
|
538
|
+
- Unified management of all configurations through `AppConfig` class
|
|
539
|
+
- Implement `EnvConfigInterface` interface to ensure configuration completeness
|
|
540
|
+
- Use dependency injection to achieve configuration decoupling
|
|
541
|
+
|
|
542
|
+
2. **Type Safety**:
|
|
543
|
+
- Handle type conversion in configuration class
|
|
544
|
+
- TypeScript interface definitions ensure type correctness
|
|
545
|
+
- Compile-time type checking
|
|
546
|
+
|
|
547
|
+
3. **Configuration Validation**:
|
|
548
|
+
- Dedicated `ConfigValidator` class handles configuration validation
|
|
549
|
+
- Runtime configuration completeness check
|
|
550
|
+
- Configuration format validation
|
|
551
|
+
|
|
552
|
+
4. **Best Practices**:
|
|
553
|
+
- Dependency injection for configuration management
|
|
554
|
+
- Configuration validation and debugging tools
|
|
555
|
+
- Sensitive information protection
|
|
556
|
+
- Type-safe handling
|
|
557
|
+
|
|
558
|
+
Through object-oriented configuration management, we can:
|
|
559
|
+
|
|
560
|
+
- Improve code maintainability and testability
|
|
561
|
+
- Ensure configuration type safety and completeness
|
|
562
|
+
- Easily perform configuration validation and debugging
|
|
563
|
+
- Better manage configuration dependencies
|