@djangocfg/api 2.1.55 → 2.1.56
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/auth.cjs +28 -15
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.d.cts +6 -6
- package/dist/auth.d.ts +6 -6
- package/dist/auth.mjs +28 -15
- package/dist/auth.mjs.map +1 -1
- package/dist/clients.cjs +56 -17
- package/dist/clients.cjs.map +1 -1
- package/dist/clients.d.cts +17 -17
- package/dist/clients.d.ts +17 -17
- package/dist/clients.mjs +56 -17
- package/dist/clients.mjs.map +1 -1
- package/dist/hooks.cjs +763 -12
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.d.cts +11 -11
- package/dist/hooks.d.ts +11 -11
- package/dist/hooks.mjs +763 -12
- package/dist/hooks.mjs.map +1 -1
- package/dist/index.cjs +893 -69
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +59 -41
- package/dist/index.d.ts +59 -41
- package/dist/index.mjs +893 -69
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/generated/cfg_accounts/_utils/schemas/CentrifugoToken.schema.ts +1 -1
- package/src/generated/cfg_accounts/_utils/schemas/OAuthAuthorizeRequestRequest.schema.ts +2 -2
- package/src/generated/cfg_accounts/_utils/schemas/OAuthAuthorizeResponse.schema.ts +1 -1
- package/src/generated/cfg_accounts/_utils/schemas/OAuthCallbackRequestRequest.schema.ts +1 -1
- package/src/generated/cfg_accounts/_utils/schemas/OAuthConnection.schema.ts +1 -1
- package/src/generated/cfg_accounts/_utils/schemas/OAuthProvidersResponse.schema.ts +1 -1
- package/src/generated/cfg_accounts/_utils/schemas/OAuthTokenResponse.schema.ts +1 -1
- package/src/generated/cfg_accounts/_utils/schemas/OTPRequestRequest.schema.ts +1 -1
- package/src/generated/cfg_accounts/_utils/schemas/OTPVerifyRequest.schema.ts +1 -1
- package/src/generated/cfg_accounts/_utils/schemas/User.schema.ts +1 -1
- package/src/generated/cfg_accounts/api-instance.ts +61 -13
- package/src/generated/cfg_centrifugo/api-instance.ts +61 -13
- package/src/generated/cfg_totp/CLAUDE.md +90 -0
- package/src/generated/cfg_totp/_utils/fetchers/index.ts +33 -0
- package/src/generated/cfg_totp/_utils/fetchers/totp.ts +49 -0
- package/src/generated/cfg_totp/_utils/fetchers/totp__2fa_management.ts +108 -0
- package/src/generated/cfg_totp/_utils/fetchers/totp__2fa_setup.ts +153 -0
- package/src/generated/cfg_totp/_utils/fetchers/totp__2fa_verification.ts +152 -0
- package/src/generated/cfg_totp/_utils/fetchers/totp__backup_codes.ts +152 -0
- package/src/generated/cfg_totp/_utils/hooks/index.ts +33 -0
- package/src/generated/cfg_totp/_utils/hooks/totp.ts +42 -0
- package/src/generated/cfg_totp/_utils/hooks/totp__2fa_management.ts +58 -0
- package/src/generated/cfg_totp/_utils/hooks/totp__2fa_setup.ts +63 -0
- package/src/generated/cfg_totp/_utils/hooks/totp__2fa_verification.ts +62 -0
- package/src/generated/cfg_totp/_utils/hooks/totp__backup_codes.ts +59 -0
- package/src/generated/cfg_totp/_utils/schemas/BackupCodesRegenerateRequest.schema.ts +19 -0
- package/src/generated/cfg_totp/_utils/schemas/BackupCodesRegenerateResponse.schema.ts +20 -0
- package/src/generated/cfg_totp/_utils/schemas/BackupCodesStatus.schema.ts +21 -0
- package/src/generated/cfg_totp/_utils/schemas/ConfirmSetupRequest.schema.ts +20 -0
- package/src/generated/cfg_totp/_utils/schemas/ConfirmSetupResponse.schema.ts +21 -0
- package/src/generated/cfg_totp/_utils/schemas/DeviceList.schema.ts +26 -0
- package/src/generated/cfg_totp/_utils/schemas/DisableRequest.schema.ts +19 -0
- package/src/generated/cfg_totp/_utils/schemas/PaginatedDeviceListList.schema.ts +24 -0
- package/src/generated/cfg_totp/_utils/schemas/SetupRequest.schema.ts +19 -0
- package/src/generated/cfg_totp/_utils/schemas/SetupResponse.schema.ts +23 -0
- package/src/generated/cfg_totp/_utils/schemas/VerifyBackupRequest.schema.ts +20 -0
- package/src/generated/cfg_totp/_utils/schemas/VerifyRequest.schema.ts +20 -0
- package/src/generated/cfg_totp/_utils/schemas/VerifyResponse.schema.ts +24 -0
- package/src/generated/cfg_totp/_utils/schemas/index.ts +32 -0
- package/src/generated/cfg_totp/api-instance.ts +180 -0
- package/src/generated/cfg_totp/client.ts +313 -0
- package/src/generated/cfg_totp/enums.ts +12 -0
- package/src/generated/cfg_totp/errors.ts +117 -0
- package/src/generated/cfg_totp/http.ts +104 -0
- package/src/generated/cfg_totp/index.ts +302 -0
- package/src/generated/cfg_totp/logger.ts +260 -0
- package/src/generated/cfg_totp/retry.ts +176 -0
- package/src/generated/cfg_totp/schema.json +859 -0
- package/src/generated/cfg_totp/storage.ts +162 -0
- package/src/generated/cfg_totp/totp/client.ts +23 -0
- package/src/generated/cfg_totp/totp/index.ts +3 -0
- package/src/generated/cfg_totp/totp/models.ts +1 -0
- package/src/generated/cfg_totp/totp__2fa_management/client.ts +41 -0
- package/src/generated/cfg_totp/totp__2fa_management/index.ts +3 -0
- package/src/generated/cfg_totp/totp__2fa_management/models.ts +60 -0
- package/src/generated/cfg_totp/totp__2fa_setup/client.ts +32 -0
- package/src/generated/cfg_totp/totp__2fa_setup/index.ts +3 -0
- package/src/generated/cfg_totp/totp__2fa_setup/models.ts +54 -0
- package/src/generated/cfg_totp/totp__2fa_verification/client.ts +32 -0
- package/src/generated/cfg_totp/totp__2fa_verification/index.ts +3 -0
- package/src/generated/cfg_totp/totp__2fa_verification/models.ts +44 -0
- package/src/generated/cfg_totp/totp__backup_codes/client.ts +31 -0
- package/src/generated/cfg_totp/totp__backup_codes/index.ts +3 -0
- package/src/generated/cfg_totp/totp__backup_codes/models.ts +37 -0
- package/src/generated/cfg_totp/validation-events.ts +134 -0
- package/src/generated/cfg_webpush/_utils/schemas/SendPushRequestRequest.schema.ts +2 -2
- package/src/generated/cfg_webpush/_utils/schemas/SubscribeRequestRequest.schema.ts +1 -1
- package/src/generated/cfg_webpush/api-instance.ts +61 -13
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
// Auto-generated by DjangoCFG - see CLAUDE.md
|
|
2
|
+
/**
|
|
3
|
+
* Django CFG API - API Client with JWT Management
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* ```typescript
|
|
7
|
+
* import { API } from './api';
|
|
8
|
+
*
|
|
9
|
+
* const api = new API('https://api.example.com');
|
|
10
|
+
*
|
|
11
|
+
* // Set JWT token
|
|
12
|
+
* api.setToken('your-jwt-token', 'refresh-token');
|
|
13
|
+
*
|
|
14
|
+
* // Use API
|
|
15
|
+
* const posts = await api.posts.list();
|
|
16
|
+
* const user = await api.users.retrieve(1);
|
|
17
|
+
*
|
|
18
|
+
* // Check authentication
|
|
19
|
+
* if (api.isAuthenticated()) {
|
|
20
|
+
* // ...
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* // Custom storage with logging (for Electron/Node.js)
|
|
24
|
+
* import { MemoryStorageAdapter, APILogger } from './storage';
|
|
25
|
+
* const logger = new APILogger({ enabled: true, logLevel: 'debug' });
|
|
26
|
+
* const api = new API('https://api.example.com', {
|
|
27
|
+
* storage: new MemoryStorageAdapter(logger),
|
|
28
|
+
* loggerConfig: { enabled: true, logLevel: 'debug' }
|
|
29
|
+
* });
|
|
30
|
+
*
|
|
31
|
+
* // Get OpenAPI schema
|
|
32
|
+
* const schema = api.getSchema();
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
import { APIClient } from "./client";
|
|
37
|
+
import {
|
|
38
|
+
StorageAdapter,
|
|
39
|
+
LocalStorageAdapter,
|
|
40
|
+
CookieStorageAdapter,
|
|
41
|
+
MemoryStorageAdapter
|
|
42
|
+
} from "./storage";
|
|
43
|
+
import type { RetryConfig } from "./retry";
|
|
44
|
+
import type { LoggerConfig } from "./logger";
|
|
45
|
+
import { APILogger } from "./logger";
|
|
46
|
+
import { 2faManagement } from "./totp__2fa_management/client";
|
|
47
|
+
import { 2faSetup } from "./totp__2fa_setup/client";
|
|
48
|
+
import { 2faVerification } from "./totp__2fa_verification/client";
|
|
49
|
+
import { BackupCodes } from "./totp__backup_codes/client";
|
|
50
|
+
import { Totp } from "./totp/client";
|
|
51
|
+
export * as 2faManagementTypes from "./totp__2fa_management/models";
|
|
52
|
+
// Note: Direct exports (export * from) are removed to avoid duplicate type conflicts
|
|
53
|
+
// Use namespace exports like CfgAccountsTypes.User or import from specific modules
|
|
54
|
+
export * as 2faSetupTypes from "./totp__2fa_setup/models";
|
|
55
|
+
// Note: Direct exports (export * from) are removed to avoid duplicate type conflicts
|
|
56
|
+
// Use namespace exports like CfgAccountsTypes.User or import from specific modules
|
|
57
|
+
export * as 2faVerificationTypes from "./totp__2fa_verification/models";
|
|
58
|
+
// Note: Direct exports (export * from) are removed to avoid duplicate type conflicts
|
|
59
|
+
// Use namespace exports like CfgAccountsTypes.User or import from specific modules
|
|
60
|
+
export * as BackupCodesTypes from "./totp__backup_codes/models";
|
|
61
|
+
// Note: Direct exports (export * from) are removed to avoid duplicate type conflicts
|
|
62
|
+
// Use namespace exports like CfgAccountsTypes.User or import from specific modules
|
|
63
|
+
export * as TotpTypes from "./totp/models";
|
|
64
|
+
// Note: Direct exports (export * from) are removed to avoid duplicate type conflicts
|
|
65
|
+
// Use namespace exports like CfgAccountsTypes.User or import from specific modules
|
|
66
|
+
export * as Enums from "./enums";
|
|
67
|
+
|
|
68
|
+
// Re-export Zod schemas for runtime validation
|
|
69
|
+
export * as Schemas from "./_utils/schemas";
|
|
70
|
+
// Also export all schemas directly for convenience
|
|
71
|
+
export * from "./_utils/schemas";
|
|
72
|
+
|
|
73
|
+
// Re-export Zod validation events for browser integration
|
|
74
|
+
export type { ValidationErrorDetail, ValidationErrorEvent } from "./validation-events";
|
|
75
|
+
export { dispatchValidationError, onValidationError, formatZodError } from "./validation-events";
|
|
76
|
+
|
|
77
|
+
// Re-export typed fetchers for universal usage
|
|
78
|
+
export * as Fetchers from "./_utils/fetchers";
|
|
79
|
+
export * from "./_utils/fetchers";
|
|
80
|
+
|
|
81
|
+
// Re-export API instance configuration functions
|
|
82
|
+
export {
|
|
83
|
+
configureAPI,
|
|
84
|
+
getAPIInstance,
|
|
85
|
+
reconfigureAPI,
|
|
86
|
+
clearAPITokens,
|
|
87
|
+
resetAPI,
|
|
88
|
+
isAPIConfigured
|
|
89
|
+
} from "./api-instance";
|
|
90
|
+
// NOTE: SWR hooks are generated in ./_utils/hooks/ but NOT exported here to keep
|
|
91
|
+
// the main bundle server-safe. Import hooks directly from the hooks directory:
|
|
92
|
+
// import { useUsers } from './_utils/hooks';
|
|
93
|
+
// Or use a separate entry point like '@djangocfg/api/hooks' for client components.
|
|
94
|
+
|
|
95
|
+
// Re-export core client
|
|
96
|
+
export { APIClient };
|
|
97
|
+
|
|
98
|
+
// Re-export storage adapters for convenience
|
|
99
|
+
export type { StorageAdapter };
|
|
100
|
+
export { LocalStorageAdapter, CookieStorageAdapter, MemoryStorageAdapter };
|
|
101
|
+
|
|
102
|
+
// Re-export error classes for convenience
|
|
103
|
+
export { APIError, NetworkError } from "./errors";
|
|
104
|
+
|
|
105
|
+
// Re-export HTTP adapters for custom implementations
|
|
106
|
+
export type { HttpClientAdapter, HttpRequest, HttpResponse } from "./http";
|
|
107
|
+
export { FetchAdapter } from "./http";
|
|
108
|
+
|
|
109
|
+
// Re-export logger types and classes
|
|
110
|
+
export type { LoggerConfig, RequestLog, ResponseLog, ErrorLog } from "./logger";
|
|
111
|
+
export { APILogger } from "./logger";
|
|
112
|
+
|
|
113
|
+
// Re-export retry configuration and utilities
|
|
114
|
+
export type { RetryConfig, FailedAttemptInfo } from "./retry";
|
|
115
|
+
export { withRetry, shouldRetry, DEFAULT_RETRY_CONFIG } from "./retry";
|
|
116
|
+
|
|
117
|
+
export const TOKEN_KEY = "auth_token";
|
|
118
|
+
export const REFRESH_TOKEN_KEY = "refresh_token";
|
|
119
|
+
|
|
120
|
+
export interface APIOptions {
|
|
121
|
+
/** Custom storage adapter (defaults to LocalStorageAdapter) */
|
|
122
|
+
storage?: StorageAdapter;
|
|
123
|
+
/** Retry configuration for failed requests */
|
|
124
|
+
retryConfig?: RetryConfig;
|
|
125
|
+
/** Logger configuration */
|
|
126
|
+
loggerConfig?: Partial<LoggerConfig>;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export class API {
|
|
130
|
+
private baseUrl: string;
|
|
131
|
+
private _client: APIClient;
|
|
132
|
+
private _token: string | null = null;
|
|
133
|
+
private _refreshToken: string | null = null;
|
|
134
|
+
private storage: StorageAdapter;
|
|
135
|
+
private options?: APIOptions;
|
|
136
|
+
|
|
137
|
+
// Sub-clients
|
|
138
|
+
public 2fa_management!: 2faManagement;
|
|
139
|
+
public 2fa_setup!: 2faSetup;
|
|
140
|
+
public 2fa_verification!: 2faVerification;
|
|
141
|
+
public backup_codes!: BackupCodes;
|
|
142
|
+
public totp!: Totp;
|
|
143
|
+
|
|
144
|
+
constructor(baseUrl: string, options?: APIOptions) {
|
|
145
|
+
this.baseUrl = baseUrl;
|
|
146
|
+
this.options = options;
|
|
147
|
+
|
|
148
|
+
// Create logger if config provided
|
|
149
|
+
const logger = options?.loggerConfig ? new APILogger(options.loggerConfig) : undefined;
|
|
150
|
+
|
|
151
|
+
// Initialize storage with logger
|
|
152
|
+
this.storage = options?.storage || new LocalStorageAdapter(logger);
|
|
153
|
+
|
|
154
|
+
this._loadTokensFromStorage();
|
|
155
|
+
|
|
156
|
+
// Initialize APIClient
|
|
157
|
+
this._client = new APIClient(this.baseUrl, {
|
|
158
|
+
retryConfig: this.options?.retryConfig,
|
|
159
|
+
loggerConfig: this.options?.loggerConfig,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// Always inject auth header wrapper (reads token dynamically from storage)
|
|
163
|
+
this._injectAuthHeader();
|
|
164
|
+
|
|
165
|
+
// Initialize sub-clients from APIClient
|
|
166
|
+
this.2fa_management = this._client.2fa_management;
|
|
167
|
+
this.2fa_setup = this._client.2fa_setup;
|
|
168
|
+
this.2fa_verification = this._client.2fa_verification;
|
|
169
|
+
this.backup_codes = this._client.backup_codes;
|
|
170
|
+
this.totp = this._client.totp;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
private _loadTokensFromStorage(): void {
|
|
174
|
+
this._token = this.storage.getItem(TOKEN_KEY);
|
|
175
|
+
this._refreshToken = this.storage.getItem(REFRESH_TOKEN_KEY);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
private _reinitClients(): void {
|
|
179
|
+
this._client = new APIClient(this.baseUrl, {
|
|
180
|
+
retryConfig: this.options?.retryConfig,
|
|
181
|
+
loggerConfig: this.options?.loggerConfig,
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// Always inject auth header wrapper (reads token dynamically from storage)
|
|
185
|
+
this._injectAuthHeader();
|
|
186
|
+
|
|
187
|
+
// Reinitialize sub-clients
|
|
188
|
+
this.2fa_management = this._client.2fa_management;
|
|
189
|
+
this.2fa_setup = this._client.2fa_setup;
|
|
190
|
+
this.2fa_verification = this._client.2fa_verification;
|
|
191
|
+
this.backup_codes = this._client.backup_codes;
|
|
192
|
+
this.totp = this._client.totp;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
private _injectAuthHeader(): void {
|
|
196
|
+
// Override request method to inject auth header
|
|
197
|
+
const originalRequest = this._client.request.bind(this._client);
|
|
198
|
+
this._client.request = async <T>(
|
|
199
|
+
method: string,
|
|
200
|
+
path: string,
|
|
201
|
+
options?: { params?: Record<string, any>; body?: any; formData?: FormData; headers?: Record<string, string> }
|
|
202
|
+
): Promise<T> => {
|
|
203
|
+
// Read token from storage dynamically (supports JWT injection after instantiation)
|
|
204
|
+
const token = this.getToken();
|
|
205
|
+
const mergedOptions = {
|
|
206
|
+
...options,
|
|
207
|
+
headers: {
|
|
208
|
+
...(options?.headers || {}),
|
|
209
|
+
...(token ? { 'Authorization': `Bearer ${token}` } : {}),
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
return originalRequest(method, path, mergedOptions);
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Get current JWT token
|
|
219
|
+
*/
|
|
220
|
+
getToken(): string | null {
|
|
221
|
+
return this.storage.getItem(TOKEN_KEY);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Get current refresh token
|
|
226
|
+
*/
|
|
227
|
+
getRefreshToken(): string | null {
|
|
228
|
+
return this.storage.getItem(REFRESH_TOKEN_KEY);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Set JWT token and refresh token
|
|
233
|
+
* @param token - JWT access token
|
|
234
|
+
* @param refreshToken - JWT refresh token (optional)
|
|
235
|
+
*/
|
|
236
|
+
setToken(token: string, refreshToken?: string): void {
|
|
237
|
+
this._token = token;
|
|
238
|
+
this.storage.setItem(TOKEN_KEY, token);
|
|
239
|
+
|
|
240
|
+
if (refreshToken) {
|
|
241
|
+
this._refreshToken = refreshToken;
|
|
242
|
+
this.storage.setItem(REFRESH_TOKEN_KEY, refreshToken);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Reinitialize clients with new token
|
|
246
|
+
this._reinitClients();
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Clear all tokens
|
|
251
|
+
*/
|
|
252
|
+
clearTokens(): void {
|
|
253
|
+
this._token = null;
|
|
254
|
+
this._refreshToken = null;
|
|
255
|
+
this.storage.removeItem(TOKEN_KEY);
|
|
256
|
+
this.storage.removeItem(REFRESH_TOKEN_KEY);
|
|
257
|
+
|
|
258
|
+
// Reinitialize clients without token
|
|
259
|
+
this._reinitClients();
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Check if user is authenticated
|
|
264
|
+
*/
|
|
265
|
+
isAuthenticated(): boolean {
|
|
266
|
+
return !!this.getToken();
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Update base URL and reinitialize clients
|
|
271
|
+
* @param url - New base URL
|
|
272
|
+
*/
|
|
273
|
+
setBaseUrl(url: string): void {
|
|
274
|
+
this.baseUrl = url;
|
|
275
|
+
this._reinitClients();
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Get current base URL
|
|
280
|
+
*/
|
|
281
|
+
getBaseUrl(): string {
|
|
282
|
+
return this.baseUrl;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Get OpenAPI schema path
|
|
287
|
+
* @returns Path to the OpenAPI schema JSON file
|
|
288
|
+
*
|
|
289
|
+
* Note: The OpenAPI schema is available in the schema.json file.
|
|
290
|
+
* You can load it dynamically using:
|
|
291
|
+
* ```typescript
|
|
292
|
+
* const schema = await fetch('./schema.json').then(r => r.json());
|
|
293
|
+
* // or using fs in Node.js:
|
|
294
|
+
* // const schema = JSON.parse(fs.readFileSync('./schema.json', 'utf-8'));
|
|
295
|
+
* ```
|
|
296
|
+
*/
|
|
297
|
+
getSchemaPath(): string {
|
|
298
|
+
return './schema.json';
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export default API;
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
// Auto-generated by DjangoCFG - see CLAUDE.md
|
|
2
|
+
/**
|
|
3
|
+
* API Logger with Consola
|
|
4
|
+
* Beautiful console logging for API requests and responses
|
|
5
|
+
*
|
|
6
|
+
* Installation:
|
|
7
|
+
* npm install consola
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { type ConsolaInstance, createConsola } from 'consola';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Request log data
|
|
14
|
+
*/
|
|
15
|
+
export interface RequestLog {
|
|
16
|
+
method: string;
|
|
17
|
+
url: string;
|
|
18
|
+
headers?: Record<string, string>;
|
|
19
|
+
body?: any;
|
|
20
|
+
timestamp: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Response log data
|
|
25
|
+
*/
|
|
26
|
+
export interface ResponseLog {
|
|
27
|
+
status: number;
|
|
28
|
+
statusText: string;
|
|
29
|
+
data?: any;
|
|
30
|
+
duration: number;
|
|
31
|
+
timestamp: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Error log data
|
|
36
|
+
*/
|
|
37
|
+
export interface ErrorLog {
|
|
38
|
+
message: string;
|
|
39
|
+
statusCode?: number;
|
|
40
|
+
fieldErrors?: Record<string, string[]>;
|
|
41
|
+
duration: number;
|
|
42
|
+
timestamp: number;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Logger configuration
|
|
47
|
+
*/
|
|
48
|
+
export interface LoggerConfig {
|
|
49
|
+
/** Enable logging */
|
|
50
|
+
enabled: boolean;
|
|
51
|
+
/** Log requests */
|
|
52
|
+
logRequests: boolean;
|
|
53
|
+
/** Log responses */
|
|
54
|
+
logResponses: boolean;
|
|
55
|
+
/** Log errors */
|
|
56
|
+
logErrors: boolean;
|
|
57
|
+
/** Log request/response bodies */
|
|
58
|
+
logBodies: boolean;
|
|
59
|
+
/** Log headers (excluding sensitive ones) */
|
|
60
|
+
logHeaders: boolean;
|
|
61
|
+
/** Custom consola instance */
|
|
62
|
+
consola?: ConsolaInstance;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Default logger configuration
|
|
67
|
+
*/
|
|
68
|
+
const DEFAULT_CONFIG: LoggerConfig = {
|
|
69
|
+
enabled: process.env.NODE_ENV !== 'production',
|
|
70
|
+
logRequests: true,
|
|
71
|
+
logResponses: true,
|
|
72
|
+
logErrors: true,
|
|
73
|
+
logBodies: true,
|
|
74
|
+
logHeaders: false,
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Sensitive header names to filter out
|
|
79
|
+
*/
|
|
80
|
+
const SENSITIVE_HEADERS = [
|
|
81
|
+
'authorization',
|
|
82
|
+
'cookie',
|
|
83
|
+
'set-cookie',
|
|
84
|
+
'x-api-key',
|
|
85
|
+
'x-csrf-token',
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* API Logger class
|
|
90
|
+
*/
|
|
91
|
+
export class APILogger {
|
|
92
|
+
private config: LoggerConfig;
|
|
93
|
+
private consola: ConsolaInstance;
|
|
94
|
+
|
|
95
|
+
constructor(config: Partial<LoggerConfig> = {}) {
|
|
96
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
97
|
+
this.consola = config.consola || createConsola({
|
|
98
|
+
level: this.config.enabled ? 4 : 0,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Enable logging
|
|
104
|
+
*/
|
|
105
|
+
enable(): void {
|
|
106
|
+
this.config.enabled = true;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Disable logging
|
|
111
|
+
*/
|
|
112
|
+
disable(): void {
|
|
113
|
+
this.config.enabled = false;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Update configuration
|
|
118
|
+
*/
|
|
119
|
+
setConfig(config: Partial<LoggerConfig>): void {
|
|
120
|
+
this.config = { ...this.config, ...config };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Filter sensitive headers
|
|
125
|
+
*/
|
|
126
|
+
private filterHeaders(headers?: Record<string, string>): Record<string, string> {
|
|
127
|
+
if (!headers) return {};
|
|
128
|
+
|
|
129
|
+
const filtered: Record<string, string> = {};
|
|
130
|
+
Object.keys(headers).forEach((key) => {
|
|
131
|
+
const lowerKey = key.toLowerCase();
|
|
132
|
+
if (SENSITIVE_HEADERS.includes(lowerKey)) {
|
|
133
|
+
filtered[key] = '***';
|
|
134
|
+
} else {
|
|
135
|
+
filtered[key] = headers[key] || '';
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
return filtered;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Log request
|
|
144
|
+
*/
|
|
145
|
+
logRequest(request: RequestLog): void {
|
|
146
|
+
if (!this.config.enabled || !this.config.logRequests) return;
|
|
147
|
+
|
|
148
|
+
const { method, url, headers, body } = request;
|
|
149
|
+
|
|
150
|
+
this.consola.start(`${method} ${url}`);
|
|
151
|
+
|
|
152
|
+
if (this.config.logHeaders && headers) {
|
|
153
|
+
this.consola.debug('Headers:', this.filterHeaders(headers));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (this.config.logBodies && body) {
|
|
157
|
+
this.consola.debug('Body:', body);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Log response
|
|
163
|
+
*/
|
|
164
|
+
logResponse(request: RequestLog, response: ResponseLog): void {
|
|
165
|
+
if (!this.config.enabled || !this.config.logResponses) return;
|
|
166
|
+
|
|
167
|
+
const { method, url } = request;
|
|
168
|
+
const { status, statusText, data, duration } = response;
|
|
169
|
+
|
|
170
|
+
const statusColor = status >= 500 ? 'red'
|
|
171
|
+
: status >= 400 ? 'yellow'
|
|
172
|
+
: status >= 300 ? 'cyan'
|
|
173
|
+
: 'green';
|
|
174
|
+
|
|
175
|
+
this.consola.success(
|
|
176
|
+
`${method} ${url} ${status} ${statusText} (${duration}ms)`
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
if (this.config.logBodies && data) {
|
|
180
|
+
this.consola.debug('Response:', data);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Log error
|
|
186
|
+
*/
|
|
187
|
+
logError(request: RequestLog, error: ErrorLog): void {
|
|
188
|
+
if (!this.config.enabled || !this.config.logErrors) return;
|
|
189
|
+
|
|
190
|
+
const { method, url } = request;
|
|
191
|
+
const { message, statusCode, fieldErrors, duration } = error;
|
|
192
|
+
|
|
193
|
+
this.consola.error(
|
|
194
|
+
`${method} ${url} ${statusCode || 'Network'} Error (${duration}ms)`
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
this.consola.error('Message:', message);
|
|
198
|
+
|
|
199
|
+
if (fieldErrors && Object.keys(fieldErrors).length > 0) {
|
|
200
|
+
this.consola.error('Field Errors:');
|
|
201
|
+
Object.entries(fieldErrors).forEach(([field, errors]) => {
|
|
202
|
+
errors.forEach((err) => {
|
|
203
|
+
this.consola.error(` • ${field}: ${err}`);
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Log general info
|
|
211
|
+
*/
|
|
212
|
+
info(message: string, ...args: any[]): void {
|
|
213
|
+
if (!this.config.enabled) return;
|
|
214
|
+
this.consola.info(message, ...args);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Log warning
|
|
219
|
+
*/
|
|
220
|
+
warn(message: string, ...args: any[]): void {
|
|
221
|
+
if (!this.config.enabled) return;
|
|
222
|
+
this.consola.warn(message, ...args);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Log error
|
|
227
|
+
*/
|
|
228
|
+
error(message: string, ...args: any[]): void {
|
|
229
|
+
if (!this.config.enabled) return;
|
|
230
|
+
this.consola.error(message, ...args);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Log debug
|
|
235
|
+
*/
|
|
236
|
+
debug(message: string, ...args: any[]): void {
|
|
237
|
+
if (!this.config.enabled) return;
|
|
238
|
+
this.consola.debug(message, ...args);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Log success
|
|
243
|
+
*/
|
|
244
|
+
success(message: string, ...args: any[]): void {
|
|
245
|
+
if (!this.config.enabled) return;
|
|
246
|
+
this.consola.success(message, ...args);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Create a sub-logger with prefix
|
|
251
|
+
*/
|
|
252
|
+
withTag(tag: string): ConsolaInstance {
|
|
253
|
+
return this.consola.withTag(tag);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Default logger instance
|
|
259
|
+
*/
|
|
260
|
+
export const defaultLogger = new APILogger();
|