@lenne.tech/nest-server 11.8.0 → 11.9.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/dist/config.env.js +3 -0
- package/dist/config.env.js.map +1 -1
- package/dist/core/common/interfaces/server-options.interface.d.ts +13 -0
- package/dist/core/modules/auth/guards/roles.guard.js +4 -3
- package/dist/core/modules/auth/guards/roles.guard.js.map +1 -1
- package/dist/core/modules/auth/services/core-auth.service.js +5 -4
- package/dist/core/modules/auth/services/core-auth.service.js.map +1 -1
- package/dist/core/modules/error-code/core-error-code.controller.d.ts +7 -0
- package/dist/core/modules/error-code/core-error-code.controller.js +45 -0
- package/dist/core/modules/error-code/core-error-code.controller.js.map +1 -0
- package/dist/core/modules/error-code/core-error-code.service.d.ts +16 -0
- package/dist/core/modules/error-code/core-error-code.service.js +65 -0
- package/dist/core/modules/error-code/core-error-code.service.js.map +1 -0
- package/dist/core/modules/error-code/error-code.module.d.ts +7 -0
- package/dist/core/modules/error-code/error-code.module.js +64 -0
- package/dist/core/modules/error-code/error-code.module.js.map +1 -0
- package/dist/core/modules/error-code/error-codes.d.ts +219 -0
- package/dist/core/modules/error-code/error-codes.js +204 -0
- package/dist/core/modules/error-code/error-codes.js.map +1 -0
- package/dist/core/modules/error-code/index.d.ts +5 -0
- package/dist/core/modules/error-code/index.js +22 -0
- package/dist/core/modules/error-code/index.js.map +1 -0
- package/dist/core/modules/error-code/interfaces/error-code.interfaces.d.ts +12 -0
- package/dist/core/modules/error-code/interfaces/error-code.interfaces.js +3 -0
- package/dist/core/modules/error-code/interfaces/error-code.interfaces.js.map +1 -0
- package/dist/core.module.js +8 -0
- package/dist/core.module.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/server/modules/error-code/error-code.controller.d.ts +8 -0
- package/dist/server/modules/error-code/error-code.controller.js +55 -0
- package/dist/server/modules/error-code/error-code.controller.js.map +1 -0
- package/dist/server/modules/error-code/error-code.service.d.ts +4 -0
- package/dist/server/modules/error-code/error-code.service.js +27 -0
- package/dist/server/modules/error-code/error-code.service.js.map +1 -0
- package/dist/server/modules/error-code/error-codes.d.ts +45 -0
- package/dist/server/modules/error-code/error-codes.js +24 -0
- package/dist/server/modules/error-code/error-codes.js.map +1 -0
- package/dist/server/modules/error-code/index.d.ts +3 -0
- package/dist/server/modules/error-code/index.js +20 -0
- package/dist/server/modules/error-code/index.js.map +1 -0
- package/dist/server/server.module.js +7 -0
- package/dist/server/server.module.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/config.env.ts +4 -0
- package/src/core/common/interfaces/server-options.interface.ts +89 -0
- package/src/core/modules/auth/guards/roles.guard.ts +5 -4
- package/src/core/modules/auth/services/core-auth.service.ts +5 -4
- package/src/core/modules/error-code/INTEGRATION-CHECKLIST.md +288 -0
- package/src/core/modules/error-code/core-error-code.controller.ts +54 -0
- package/src/core/modules/error-code/core-error-code.service.ts +135 -0
- package/src/core/modules/error-code/error-code.module.ts +119 -0
- package/src/core/modules/error-code/error-codes.ts +405 -0
- package/src/core/modules/error-code/index.ts +14 -0
- package/src/core/modules/error-code/interfaces/error-code.interfaces.ts +99 -0
- package/src/core.module.ts +16 -0
- package/src/index.ts +6 -0
- package/src/server/modules/error-code/README.md +131 -0
- package/src/server/modules/error-code/error-code.controller.ts +91 -0
- package/src/server/modules/error-code/error-code.service.ts +42 -0
- package/src/server/modules/error-code/error-codes.ts +65 -0
- package/src/server/modules/error-code/index.ts +8 -0
- package/src/server/server.module.ts +10 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { DynamicModule, Global, Module, Type } from '@nestjs/common';
|
|
2
|
+
|
|
3
|
+
import { CoreErrorCodeController } from './core-error-code.controller';
|
|
4
|
+
import { CoreErrorCodeService } from './core-error-code.service';
|
|
5
|
+
import { IErrorCodeModuleConfig } from './interfaces/error-code.interfaces';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Error Code Module
|
|
9
|
+
*
|
|
10
|
+
* Provides error code translations via REST endpoint.
|
|
11
|
+
* Translations are defined in the error registry (Single Source of Truth).
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* // Basic usage (auto-register in CoreModule)
|
|
16
|
+
* // No explicit import needed - included in CoreModule
|
|
17
|
+
*
|
|
18
|
+
* // Extended usage (with custom error registry - RECOMMENDED)
|
|
19
|
+
* const ProjectErrors = {
|
|
20
|
+
* ORDER_NOT_FOUND: {
|
|
21
|
+
* code: 'PROJ_0001',
|
|
22
|
+
* message: 'Order not found',
|
|
23
|
+
* translations: { de: 'Bestellung nicht gefunden.', en: 'Order not found.' }
|
|
24
|
+
* }
|
|
25
|
+
* } as const satisfies IErrorRegistry;
|
|
26
|
+
*
|
|
27
|
+
* ErrorCodeModule.forRoot({ additionalErrorRegistry: ProjectErrors })
|
|
28
|
+
*
|
|
29
|
+
* // Extended usage (with custom controller and service)
|
|
30
|
+
* ErrorCodeModule.forRoot({
|
|
31
|
+
* additionalErrorRegistry: ProjectErrors,
|
|
32
|
+
* controller: ErrorCodeController,
|
|
33
|
+
* service: ErrorCodeService,
|
|
34
|
+
* })
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
@Global()
|
|
38
|
+
@Module({
|
|
39
|
+
// IMPORTANT: Controllers are NOT registered here - they are registered via forRoot()
|
|
40
|
+
// This prevents duplicate controller registration when using custom controllers,
|
|
41
|
+
// which would cause route conflicts (e.g., :locale intercepting /codes)
|
|
42
|
+
exports: [CoreErrorCodeService],
|
|
43
|
+
providers: [CoreErrorCodeService],
|
|
44
|
+
})
|
|
45
|
+
export class ErrorCodeModule {
|
|
46
|
+
/**
|
|
47
|
+
* Gets the controller class to use (custom or default)
|
|
48
|
+
*/
|
|
49
|
+
private static getControllerClass(config?: IErrorCodeModuleConfig): Type<any> {
|
|
50
|
+
return config?.controller || CoreErrorCodeController;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Gets the service class to use (custom or default)
|
|
55
|
+
*/
|
|
56
|
+
private static getServiceClass(config?: IErrorCodeModuleConfig): Type<CoreErrorCodeService> {
|
|
57
|
+
return config?.service || CoreErrorCodeService;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Register the module with configuration
|
|
62
|
+
*
|
|
63
|
+
* Supports the following patterns:
|
|
64
|
+
* 1. **No config**: Uses CoreErrorCodeService and CoreErrorCodeController with core errors only
|
|
65
|
+
* 2. **additionalErrorRegistry only**: Adds project errors to core errors
|
|
66
|
+
* 3. **service only**: Uses custom service class (should register its own errors in constructor)
|
|
67
|
+
* 4. **controller only**: Uses custom controller class with CoreErrorCodeService
|
|
68
|
+
* 5. **Full config**: Uses custom service and controller
|
|
69
|
+
*
|
|
70
|
+
* @param config - Module configuration
|
|
71
|
+
* @returns Dynamic module
|
|
72
|
+
*/
|
|
73
|
+
static forRoot(config?: IErrorCodeModuleConfig): DynamicModule {
|
|
74
|
+
const ControllerClass = this.getControllerClass(config);
|
|
75
|
+
const ServiceClass = this.getServiceClass(config);
|
|
76
|
+
|
|
77
|
+
// Build providers array
|
|
78
|
+
const providers: any[] = [];
|
|
79
|
+
|
|
80
|
+
if (config?.service) {
|
|
81
|
+
// If a custom service is provided, register it under both tokens:
|
|
82
|
+
// 1. Its own class (for direct injection in custom controllers)
|
|
83
|
+
// 2. CoreErrorCodeService (for backward compatibility)
|
|
84
|
+
providers.push(ServiceClass);
|
|
85
|
+
providers.push({
|
|
86
|
+
provide: CoreErrorCodeService,
|
|
87
|
+
useExisting: ServiceClass,
|
|
88
|
+
});
|
|
89
|
+
} else {
|
|
90
|
+
// Use factory to register additional errors
|
|
91
|
+
providers.push({
|
|
92
|
+
provide: CoreErrorCodeService,
|
|
93
|
+
useFactory: () => {
|
|
94
|
+
const service = new CoreErrorCodeService();
|
|
95
|
+
|
|
96
|
+
// Add additional error registry
|
|
97
|
+
if (config?.additionalErrorRegistry) {
|
|
98
|
+
service.registerErrorRegistry(config.additionalErrorRegistry);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return service;
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Export both the base class and custom service class (if provided)
|
|
107
|
+
const exports: any[] = [CoreErrorCodeService];
|
|
108
|
+
if (config?.service && ServiceClass !== CoreErrorCodeService) {
|
|
109
|
+
exports.push(ServiceClass);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
controllers: [ControllerClass],
|
|
114
|
+
exports,
|
|
115
|
+
module: ErrorCodeModule,
|
|
116
|
+
providers,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lenne Tech Nest Server Error Codes - Structured Registry
|
|
3
|
+
*
|
|
4
|
+
* Single Source of Truth for all error codes, messages, and translations.
|
|
5
|
+
* This file serves as the central registry that:
|
|
6
|
+
* - Defines error codes with human-readable messages
|
|
7
|
+
* - Provides translations for multiple languages
|
|
8
|
+
* - Generates the i18n JSON endpoint automatically
|
|
9
|
+
*
|
|
10
|
+
* API Response Format: #PREFIX_XXXX: Short technical description
|
|
11
|
+
* - # = Marker for machine parsing
|
|
12
|
+
* - PREFIX = LTNS (Lenne Tech Nest Server) or project-specific
|
|
13
|
+
* - XXXX = Unique number within range
|
|
14
|
+
* - : = Separator
|
|
15
|
+
* - Description = English developer-friendly message
|
|
16
|
+
*
|
|
17
|
+
* Error code ranges:
|
|
18
|
+
* - LTNS_0001-LTNS_0099: Authentication errors
|
|
19
|
+
* - LTNS_0100-LTNS_0199: Authorization errors
|
|
20
|
+
* - LTNS_0200-LTNS_0299: User errors
|
|
21
|
+
* - LTNS_0300-LTNS_0399: Validation errors
|
|
22
|
+
* - LTNS_0400-LTNS_0499: Resource errors
|
|
23
|
+
* - LTNS_0500-LTNS_0599: File errors
|
|
24
|
+
* - LTNS_0900-LTNS_0999: Internal errors
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* import { UnauthorizedException } from '@nestjs/common';
|
|
29
|
+
* import { ErrorCode } from '@lenne.tech/nest-server';
|
|
30
|
+
*
|
|
31
|
+
* throw new UnauthorizedException(ErrorCode.UNAUTHORIZED);
|
|
32
|
+
* // Response: { statusCode: 401, message: "#LTNS_0100: Unauthorized - User is not logged in" }
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
// =====================================================
|
|
37
|
+
// Type Definitions
|
|
38
|
+
// =====================================================
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Structure for a single error definition
|
|
42
|
+
*/
|
|
43
|
+
export interface IErrorDefinition {
|
|
44
|
+
/** Unique error code (e.g., LTNS_0100) */
|
|
45
|
+
code: string;
|
|
46
|
+
/** English developer message */
|
|
47
|
+
message: string;
|
|
48
|
+
/** Translations for end users */
|
|
49
|
+
translations: {
|
|
50
|
+
[locale: string]: string;
|
|
51
|
+
de: string;
|
|
52
|
+
en: string;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Registry type for error definitions
|
|
58
|
+
*/
|
|
59
|
+
export type IErrorRegistry = Record<string, IErrorDefinition>;
|
|
60
|
+
|
|
61
|
+
// =====================================================
|
|
62
|
+
// Core Error Registry (Single Source of Truth)
|
|
63
|
+
// =====================================================
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* LTNS Error Registry - Contains all core error definitions
|
|
67
|
+
*
|
|
68
|
+
* Each entry includes:
|
|
69
|
+
* - code: The unique identifier
|
|
70
|
+
* - message: English technical description for developers
|
|
71
|
+
* - translations: Localized messages for end users
|
|
72
|
+
*
|
|
73
|
+
* NOTE: Entries are grouped by error code range for maintainability.
|
|
74
|
+
* Do not sort alphabetically - the numeric grouping is intentional.
|
|
75
|
+
*/
|
|
76
|
+
/* eslint-disable perfectionist/sort-objects */
|
|
77
|
+
export const LtnsErrors = {
|
|
78
|
+
// =====================================================
|
|
79
|
+
// Authentication Errors (LTNS_0001-LTNS_0099)
|
|
80
|
+
// =====================================================
|
|
81
|
+
|
|
82
|
+
USER_NOT_FOUND: {
|
|
83
|
+
code: 'LTNS_0001',
|
|
84
|
+
message: 'User not found',
|
|
85
|
+
translations: {
|
|
86
|
+
de: 'Benutzer wurde nicht gefunden.',
|
|
87
|
+
en: 'User not found.',
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
INVALID_PASSWORD: {
|
|
92
|
+
code: 'LTNS_0002',
|
|
93
|
+
message: 'Invalid password',
|
|
94
|
+
translations: {
|
|
95
|
+
de: 'Das eingegebene Passwort ist ungültig.',
|
|
96
|
+
en: 'The provided password is invalid.',
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
INVALID_TOKEN: {
|
|
101
|
+
code: 'LTNS_0003',
|
|
102
|
+
message: 'Invalid or malformed token',
|
|
103
|
+
translations: {
|
|
104
|
+
de: 'Der Token ist ungültig oder fehlerhaft.',
|
|
105
|
+
en: 'The token is invalid or malformed.',
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
TOKEN_EXPIRED: {
|
|
110
|
+
code: 'LTNS_0004',
|
|
111
|
+
message: 'Token has expired',
|
|
112
|
+
translations: {
|
|
113
|
+
de: 'Der Token ist abgelaufen. Bitte melden Sie sich erneut an.',
|
|
114
|
+
en: 'The token has expired. Please sign in again.',
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
REFRESH_TOKEN_REQUIRED: {
|
|
119
|
+
code: 'LTNS_0005',
|
|
120
|
+
message: 'Refresh token required',
|
|
121
|
+
translations: {
|
|
122
|
+
de: 'Ein Refresh-Token wird benötigt.',
|
|
123
|
+
en: 'A refresh token is required.',
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
USER_NOT_VERIFIED: {
|
|
128
|
+
code: 'LTNS_0006',
|
|
129
|
+
message: 'User email not verified',
|
|
130
|
+
translations: {
|
|
131
|
+
de: 'Ihre E-Mail-Adresse wurde noch nicht verifiziert.',
|
|
132
|
+
en: 'Your email address has not been verified yet.',
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
// =====================================================
|
|
137
|
+
// Authorization Errors (LTNS_0100-LTNS_0199)
|
|
138
|
+
// =====================================================
|
|
139
|
+
|
|
140
|
+
UNAUTHORIZED: {
|
|
141
|
+
code: 'LTNS_0100',
|
|
142
|
+
message: 'Unauthorized - User is not logged in',
|
|
143
|
+
translations: {
|
|
144
|
+
de: 'Sie sind nicht angemeldet.',
|
|
145
|
+
en: 'You are not logged in.',
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
ACCESS_DENIED: {
|
|
150
|
+
code: 'LTNS_0101',
|
|
151
|
+
message: 'Access denied - Insufficient permissions',
|
|
152
|
+
translations: {
|
|
153
|
+
de: 'Zugriff verweigert. Sie haben nicht die erforderlichen Berechtigungen.',
|
|
154
|
+
en: 'Access denied. You do not have the required permissions.',
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
RESOURCE_FORBIDDEN: {
|
|
159
|
+
code: 'LTNS_0102',
|
|
160
|
+
message: 'Resource access forbidden',
|
|
161
|
+
translations: {
|
|
162
|
+
de: 'Der Zugriff auf diese Ressource ist nicht erlaubt.',
|
|
163
|
+
en: 'Access to this resource is forbidden.',
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
|
|
167
|
+
// =====================================================
|
|
168
|
+
// User Errors (LTNS_0200-LTNS_0299)
|
|
169
|
+
// =====================================================
|
|
170
|
+
|
|
171
|
+
EMAIL_ALREADY_EXISTS: {
|
|
172
|
+
code: 'LTNS_0200',
|
|
173
|
+
message: 'Email already registered',
|
|
174
|
+
translations: {
|
|
175
|
+
de: 'Diese E-Mail-Adresse ist bereits registriert.',
|
|
176
|
+
en: 'This email address is already registered.',
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
USERNAME_ALREADY_EXISTS: {
|
|
181
|
+
code: 'LTNS_0201',
|
|
182
|
+
message: 'Username already taken',
|
|
183
|
+
translations: {
|
|
184
|
+
de: 'Dieser Benutzername ist bereits vergeben.',
|
|
185
|
+
en: 'This username is already taken.',
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
|
|
189
|
+
// =====================================================
|
|
190
|
+
// Validation Errors (LTNS_0300-LTNS_0399)
|
|
191
|
+
// =====================================================
|
|
192
|
+
|
|
193
|
+
VALIDATION_FAILED: {
|
|
194
|
+
code: 'LTNS_0300',
|
|
195
|
+
message: 'Validation failed',
|
|
196
|
+
translations: {
|
|
197
|
+
de: 'Die Validierung ist fehlgeschlagen.',
|
|
198
|
+
en: 'Validation failed.',
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
|
|
202
|
+
REQUIRED_FIELD_MISSING: {
|
|
203
|
+
code: 'LTNS_0301',
|
|
204
|
+
message: 'Required field missing',
|
|
205
|
+
translations: {
|
|
206
|
+
de: 'Ein erforderliches Feld fehlt.',
|
|
207
|
+
en: 'A required field is missing.',
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
|
|
211
|
+
INVALID_FIELD_FORMAT: {
|
|
212
|
+
code: 'LTNS_0302',
|
|
213
|
+
message: 'Invalid field format',
|
|
214
|
+
translations: {
|
|
215
|
+
de: 'Das Feldformat ist ungültig.',
|
|
216
|
+
en: 'The field format is invalid.',
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
|
|
220
|
+
// =====================================================
|
|
221
|
+
// Resource Errors (LTNS_0400-LTNS_0499)
|
|
222
|
+
// =====================================================
|
|
223
|
+
|
|
224
|
+
RESOURCE_NOT_FOUND: {
|
|
225
|
+
code: 'LTNS_0400',
|
|
226
|
+
message: 'Resource not found',
|
|
227
|
+
translations: {
|
|
228
|
+
de: 'Die angeforderte Ressource wurde nicht gefunden.',
|
|
229
|
+
en: 'The requested resource was not found.',
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
|
|
233
|
+
RESOURCE_ALREADY_EXISTS: {
|
|
234
|
+
code: 'LTNS_0401',
|
|
235
|
+
message: 'Resource already exists',
|
|
236
|
+
translations: {
|
|
237
|
+
de: 'Diese Ressource existiert bereits.',
|
|
238
|
+
en: 'This resource already exists.',
|
|
239
|
+
},
|
|
240
|
+
},
|
|
241
|
+
|
|
242
|
+
// =====================================================
|
|
243
|
+
// File Errors (LTNS_0500-LTNS_0599)
|
|
244
|
+
// =====================================================
|
|
245
|
+
|
|
246
|
+
FILE_NOT_FOUND: {
|
|
247
|
+
code: 'LTNS_0500',
|
|
248
|
+
message: 'File not found',
|
|
249
|
+
translations: {
|
|
250
|
+
de: 'Die Datei wurde nicht gefunden.',
|
|
251
|
+
en: 'The file was not found.',
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
|
|
255
|
+
FILE_UPLOAD_FAILED: {
|
|
256
|
+
code: 'LTNS_0501',
|
|
257
|
+
message: 'File upload failed',
|
|
258
|
+
translations: {
|
|
259
|
+
de: 'Der Datei-Upload ist fehlgeschlagen.',
|
|
260
|
+
en: 'The file upload failed.',
|
|
261
|
+
},
|
|
262
|
+
},
|
|
263
|
+
|
|
264
|
+
FILE_TYPE_NOT_ALLOWED: {
|
|
265
|
+
code: 'LTNS_0502',
|
|
266
|
+
message: 'File type not allowed',
|
|
267
|
+
translations: {
|
|
268
|
+
de: 'Dieser Dateityp ist nicht erlaubt.',
|
|
269
|
+
en: 'This file type is not allowed.',
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
|
|
273
|
+
// =====================================================
|
|
274
|
+
// Internal Errors (LTNS_0900-LTNS_0999)
|
|
275
|
+
// =====================================================
|
|
276
|
+
|
|
277
|
+
INTERNAL_ERROR: {
|
|
278
|
+
code: 'LTNS_0900',
|
|
279
|
+
message: 'Internal server error',
|
|
280
|
+
translations: {
|
|
281
|
+
de: 'Ein interner Serverfehler ist aufgetreten.',
|
|
282
|
+
en: 'An internal server error occurred.',
|
|
283
|
+
},
|
|
284
|
+
},
|
|
285
|
+
|
|
286
|
+
SERVICE_UNAVAILABLE: {
|
|
287
|
+
code: 'LTNS_0901',
|
|
288
|
+
message: 'Service temporarily unavailable',
|
|
289
|
+
translations: {
|
|
290
|
+
de: 'Der Dienst ist vorübergehend nicht verfügbar.',
|
|
291
|
+
en: 'The service is temporarily unavailable.',
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
|
|
295
|
+
LEGACY_AUTH_DISABLED: {
|
|
296
|
+
code: 'LTNS_0902',
|
|
297
|
+
message: 'Legacy authentication is disabled',
|
|
298
|
+
translations: {
|
|
299
|
+
de: 'Die Legacy-Authentifizierung ist deaktiviert. Bitte nutzen Sie die neue Authentifizierung.',
|
|
300
|
+
en: 'Legacy authentication is disabled. Please use the new authentication.',
|
|
301
|
+
},
|
|
302
|
+
},
|
|
303
|
+
} as const satisfies IErrorRegistry;
|
|
304
|
+
/* eslint-enable perfectionist/sort-objects */
|
|
305
|
+
|
|
306
|
+
// =====================================================
|
|
307
|
+
// Generated ErrorCode Object
|
|
308
|
+
// =====================================================
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Generate formatted error message from definition
|
|
312
|
+
* Format: #CODE: Message
|
|
313
|
+
*/
|
|
314
|
+
function formatErrorMessage(def: IErrorDefinition): string {
|
|
315
|
+
return `#${def.code}: ${def.message}`;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Generate ErrorCode object from registry
|
|
320
|
+
* Maps each key to its formatted error string
|
|
321
|
+
*/
|
|
322
|
+
function generateErrorCodes<T extends IErrorRegistry>(registry: T): { [K in keyof T]: string } {
|
|
323
|
+
const result = {} as { [K in keyof T]: string };
|
|
324
|
+
for (const key of Object.keys(registry) as Array<keyof T>) {
|
|
325
|
+
result[key] = formatErrorMessage(registry[key]);
|
|
326
|
+
}
|
|
327
|
+
return result;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* ErrorCode - Use this in your code with NestJS exceptions
|
|
332
|
+
*
|
|
333
|
+
* @example
|
|
334
|
+
* ```typescript
|
|
335
|
+
* throw new UnauthorizedException(ErrorCode.UNAUTHORIZED);
|
|
336
|
+
* // Response: { statusCode: 401, message: "#LTNS_0100: Unauthorized - User is not logged in" }
|
|
337
|
+
* ```
|
|
338
|
+
*/
|
|
339
|
+
export const ErrorCode = generateErrorCodes(LtnsErrors);
|
|
340
|
+
|
|
341
|
+
// =====================================================
|
|
342
|
+
// Type Exports
|
|
343
|
+
// =====================================================
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Type for error code keys (readable names like USER_NOT_FOUND)
|
|
347
|
+
*/
|
|
348
|
+
export type ErrorCodeKey = keyof typeof ErrorCode;
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Type for all error code formatted strings
|
|
352
|
+
*/
|
|
353
|
+
export type ErrorCodeType = (typeof ErrorCode)[keyof typeof ErrorCode];
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Type for raw error codes (like LTNS_0100)
|
|
357
|
+
*/
|
|
358
|
+
export type RawErrorCode = (typeof LtnsErrors)[keyof typeof LtnsErrors]['code'];
|
|
359
|
+
|
|
360
|
+
// =====================================================
|
|
361
|
+
// Helper Functions
|
|
362
|
+
// =====================================================
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Get all error definitions (for i18n endpoint)
|
|
366
|
+
*
|
|
367
|
+
* @returns All error definitions from the registry
|
|
368
|
+
*/
|
|
369
|
+
export function getAllErrorDefinitions(): typeof LtnsErrors {
|
|
370
|
+
return LtnsErrors;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// =====================================================
|
|
374
|
+
// Extension Support
|
|
375
|
+
// =====================================================
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Merge project-specific errors with core errors
|
|
379
|
+
*
|
|
380
|
+
* @param projectErrors - Project-specific error registry
|
|
381
|
+
* @returns Merged error codes object
|
|
382
|
+
*
|
|
383
|
+
* @example
|
|
384
|
+
* ```typescript
|
|
385
|
+
* // In your project
|
|
386
|
+
* const ProjectErrors = {
|
|
387
|
+
* ORDER_NOT_FOUND: {
|
|
388
|
+
* code: 'PROJ_0001',
|
|
389
|
+
* message: 'Order not found',
|
|
390
|
+
* translations: { de: 'Bestellung nicht gefunden.', en: 'Order not found.' }
|
|
391
|
+
* }
|
|
392
|
+
* } as const satisfies IErrorRegistry;
|
|
393
|
+
*
|
|
394
|
+
* export const ErrorCode = mergeErrorCodes(ProjectErrors);
|
|
395
|
+
* // Contains both LTNS_* and PROJ_* errors
|
|
396
|
+
* ```
|
|
397
|
+
*/
|
|
398
|
+
export function mergeErrorCodes<T extends IErrorRegistry>(
|
|
399
|
+
projectErrors: T,
|
|
400
|
+
): { [K in keyof T | keyof typeof LtnsErrors]: string } {
|
|
401
|
+
return {
|
|
402
|
+
...ErrorCode,
|
|
403
|
+
...generateErrorCodes(projectErrors),
|
|
404
|
+
} as { [K in keyof T | keyof typeof LtnsErrors]: string };
|
|
405
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Controller
|
|
2
|
+
export * from './core-error-code.controller';
|
|
3
|
+
|
|
4
|
+
// Service
|
|
5
|
+
export * from './core-error-code.service';
|
|
6
|
+
|
|
7
|
+
// Module
|
|
8
|
+
export * from './error-code.module';
|
|
9
|
+
|
|
10
|
+
// Error Codes
|
|
11
|
+
export * from './error-codes';
|
|
12
|
+
|
|
13
|
+
// Interfaces
|
|
14
|
+
export * from './interfaces/error-code.interfaces';
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { Type } from '@nestjs/common';
|
|
2
|
+
|
|
3
|
+
import { CoreErrorCodeService } from '../core-error-code.service';
|
|
4
|
+
import { IErrorRegistry } from '../error-codes';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Configuration for the error code module
|
|
8
|
+
*/
|
|
9
|
+
export interface IErrorCodeModuleConfig {
|
|
10
|
+
/**
|
|
11
|
+
* Additional error registry to merge with core errors
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* const ProjectErrors = {
|
|
16
|
+
* ORDER_NOT_FOUND: {
|
|
17
|
+
* code: 'PROJ_0001',
|
|
18
|
+
* message: 'Order not found',
|
|
19
|
+
* translations: { de: 'Bestellung nicht gefunden.', en: 'Order not found.' }
|
|
20
|
+
* }
|
|
21
|
+
* } as const satisfies IErrorRegistry;
|
|
22
|
+
*
|
|
23
|
+
* ErrorCodeModule.forRoot({ additionalErrorRegistry: ProjectErrors })
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
additionalErrorRegistry?: IErrorRegistry;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Custom controller class to use instead of CoreErrorCodeController.
|
|
30
|
+
*
|
|
31
|
+
* **Note:** Use a standalone controller (not extending CoreErrorCodeController)
|
|
32
|
+
* to ensure correct route registration order when adding new routes.
|
|
33
|
+
* NestJS registers parent routes first, which can cause parameterized routes
|
|
34
|
+
* to intercept static routes.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```typescript
|
|
38
|
+
* // Standalone controller (RECOMMENDED)
|
|
39
|
+
* @Controller('api/i18n/errors')
|
|
40
|
+
* export class ErrorCodeController {
|
|
41
|
+
* constructor(protected readonly errorCodeService: ErrorCodeService) {}
|
|
42
|
+
*
|
|
43
|
+
* @Get('codes') // Must be defined BEFORE :locale
|
|
44
|
+
* @Roles(RoleEnum.S_EVERYONE)
|
|
45
|
+
* getAllCodes(): string[] {
|
|
46
|
+
* return this.errorCodeService.getErrorCodes();
|
|
47
|
+
* }
|
|
48
|
+
*
|
|
49
|
+
* @Get(':locale')
|
|
50
|
+
* @Roles(RoleEnum.S_EVERYONE)
|
|
51
|
+
* getTranslations(@Param('locale') locale: string) { ... }
|
|
52
|
+
* }
|
|
53
|
+
*
|
|
54
|
+
* // In your module
|
|
55
|
+
* ErrorCodeModule.forRoot({
|
|
56
|
+
* controller: ErrorCodeController,
|
|
57
|
+
* service: ErrorCodeService,
|
|
58
|
+
* })
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
controller?: Type<any>;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Custom service class to use instead of CoreErrorCodeService.
|
|
65
|
+
* The class must extend CoreErrorCodeService.
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```typescript
|
|
69
|
+
* // Your custom service with additional locales
|
|
70
|
+
* @Injectable()
|
|
71
|
+
* export class ErrorCodeService extends CoreErrorCodeService {
|
|
72
|
+
* protected override supportedLocales: SupportedLocale[] = ['de', 'en', 'fr', 'es'];
|
|
73
|
+
*
|
|
74
|
+
* constructor() {
|
|
75
|
+
* super();
|
|
76
|
+
* this.registerErrorRegistry(ProjectErrors);
|
|
77
|
+
* }
|
|
78
|
+
* }
|
|
79
|
+
*
|
|
80
|
+
* // In your module
|
|
81
|
+
* ErrorCodeModule.forRoot({
|
|
82
|
+
* service: ErrorCodeService,
|
|
83
|
+
* })
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
service?: Type<CoreErrorCodeService>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Response format for the translation endpoint
|
|
91
|
+
*/
|
|
92
|
+
export interface IErrorTranslationResponse {
|
|
93
|
+
errors: Record<string, string>;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Supported locales for error translations
|
|
98
|
+
*/
|
|
99
|
+
export type SupportedLocale = 'de' | 'en';
|
package/src/core.module.ts
CHANGED
|
@@ -22,6 +22,7 @@ import { TemplateService } from './core/common/services/template.service';
|
|
|
22
22
|
import { BetterAuthUserMapper } from './core/modules/better-auth/better-auth-user.mapper';
|
|
23
23
|
import { BetterAuthModule } from './core/modules/better-auth/better-auth.module';
|
|
24
24
|
import { BetterAuthService } from './core/modules/better-auth/better-auth.service';
|
|
25
|
+
import { ErrorCodeModule } from './core/modules/error-code/error-code.module';
|
|
25
26
|
import { CoreHealthCheckModule } from './core/modules/health-check/core-health-check.module';
|
|
26
27
|
|
|
27
28
|
/**
|
|
@@ -226,6 +227,21 @@ export class CoreModule implements NestModule {
|
|
|
226
227
|
Object.assign({ driver: ApolloDriver }, config.graphQl.driver, config.graphQl.options),
|
|
227
228
|
),
|
|
228
229
|
];
|
|
230
|
+
|
|
231
|
+
// Add ErrorCodeModule based on configuration
|
|
232
|
+
// autoRegister defaults to true (backward compatible)
|
|
233
|
+
const errorCodeConfig = config.errorCode;
|
|
234
|
+
const isErrorCodeAutoRegister = errorCodeConfig?.autoRegister !== false;
|
|
235
|
+
|
|
236
|
+
if (isErrorCodeAutoRegister) {
|
|
237
|
+
// Always use forRoot() - it registers the controller and handles configuration
|
|
238
|
+
imports.push(
|
|
239
|
+
ErrorCodeModule.forRoot({
|
|
240
|
+
additionalErrorRegistry: errorCodeConfig?.additionalErrorRegistry,
|
|
241
|
+
}),
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
|
|
229
245
|
if (config.healthCheck) {
|
|
230
246
|
imports.push(CoreHealthCheckModule);
|
|
231
247
|
}
|
package/src/index.ts
CHANGED
|
@@ -131,6 +131,12 @@ export * from './core/modules/auth/tokens.decorator';
|
|
|
131
131
|
|
|
132
132
|
export * from './core/modules/better-auth';
|
|
133
133
|
|
|
134
|
+
// =====================================================================================================================
|
|
135
|
+
// Core - Modules - ErrorCode
|
|
136
|
+
// =====================================================================================================================
|
|
137
|
+
|
|
138
|
+
export * from './core/modules/error-code';
|
|
139
|
+
|
|
134
140
|
// =====================================================================================================================
|
|
135
141
|
// Core - Modules - File
|
|
136
142
|
// =====================================================================================================================
|