@groundbrick/express-adapter 0.0.1
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/README.md +449 -0
- package/dist/core/BaseController.d.ts +23 -0
- package/dist/core/BaseController.d.ts.map +1 -0
- package/dist/core/BaseController.js +50 -0
- package/dist/core/BaseController.js.map +1 -0
- package/dist/core/ExpressApp.d.ts +20 -0
- package/dist/core/ExpressApp.d.ts.map +1 -0
- package/dist/core/ExpressApp.js +183 -0
- package/dist/core/ExpressApp.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/ErrorHandler.d.ts +8 -0
- package/dist/middleware/ErrorHandler.d.ts.map +1 -0
- package/dist/middleware/ErrorHandler.js +64 -0
- package/dist/middleware/ErrorHandler.js.map +1 -0
- package/dist/middleware/JwtMiddleware.d.ts +17 -0
- package/dist/middleware/JwtMiddleware.d.ts.map +1 -0
- package/dist/middleware/JwtMiddleware.js +140 -0
- package/dist/middleware/JwtMiddleware.js.map +1 -0
- package/dist/middleware/RequestLogger.d.ts +8 -0
- package/dist/middleware/RequestLogger.d.ts.map +1 -0
- package/dist/middleware/RequestLogger.js +39 -0
- package/dist/middleware/RequestLogger.js.map +1 -0
- package/dist/middleware/ResponseFormatter.d.ts +5 -0
- package/dist/middleware/ResponseFormatter.d.ts.map +1 -0
- package/dist/middleware/ResponseFormatter.js +39 -0
- package/dist/middleware/ResponseFormatter.js.map +1 -0
- package/dist/types/ApiResponse.d.ts +17 -0
- package/dist/types/ApiResponse.d.ts.map +1 -0
- package/dist/types/ApiResponse.js +2 -0
- package/dist/types/ApiResponse.js.map +1 -0
- package/dist/types/Auth.d.ts +21 -0
- package/dist/types/Auth.d.ts.map +1 -0
- package/dist/types/Auth.js +2 -0
- package/dist/types/Auth.js.map +1 -0
- package/dist/types/Controller.d.ts +23 -0
- package/dist/types/Controller.d.ts.map +1 -0
- package/dist/types/Controller.js +2 -0
- package/dist/types/Controller.js.map +1 -0
- package/dist/types/ExpressConfig.d.ts +35 -0
- package/dist/types/ExpressConfig.d.ts.map +1 -0
- package/dist/types/ExpressConfig.js +2 -0
- package/dist/types/ExpressConfig.js.map +1 -0
- package/dist/types/RouteHandler.d.ts +9 -0
- package/dist/types/RouteHandler.d.ts.map +1 -0
- package/dist/types/RouteHandler.js +2 -0
- package/dist/types/RouteHandler.js.map +1 -0
- package/dist/types/index.d.ts +56 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/index.d.ts +53 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +263 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +81 -0
package/README.md
ADDED
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
# @groundbrick/express-adapter
|
|
2
|
+
|
|
3
|
+
Express.js integration adapter para o microframework TypeScript com middleware estruturado, controllers base e tratamento de erros.
|
|
4
|
+
|
|
5
|
+
## Features Implementadas
|
|
6
|
+
|
|
7
|
+
- **š ExpressApp Class** - Classe principal com configuração Express prĆ©-configurada
|
|
8
|
+
- **š BaseController** - Controller base com async handlers e utilitĆ”rios
|
|
9
|
+
- **š”ļø Middleware Stack** - Error handling, request logging e response formatting
|
|
10
|
+
- **š JWT Authentication** - Middleware de autenticação JWT com role-based access
|
|
11
|
+
- **š Response Helpers** - Formatação padronizada de respostas da API
|
|
12
|
+
- **ā
Validation Utilities** - Helpers de validação e sanitização
|
|
13
|
+
- **š Pagination Support** - UtilitĆ”rios de paginação e ordenação
|
|
14
|
+
|
|
15
|
+
## Instalação
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @groundbrick/express-adapter
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Peer Dependencies
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install express cors helmet compression express-rate-limit jsonwebtoken
|
|
25
|
+
npm install --save-dev @types/express @types/cors @types/compression @types/jsonwebtoken
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
### 1. Configuração BÔsica da Aplicação
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
// src/server.ts
|
|
34
|
+
import { ExpressApp } from '@groundbrick/express-adapter';
|
|
35
|
+
import { createLogger } from '@groundbrick/logger';
|
|
36
|
+
|
|
37
|
+
const app = new ExpressApp({
|
|
38
|
+
port: 3000,
|
|
39
|
+
host: '0.0.0.0',
|
|
40
|
+
cors: {
|
|
41
|
+
origin: process.env.CORS_ORIGIN || 'http://localhost:5173',
|
|
42
|
+
credentials: true
|
|
43
|
+
},
|
|
44
|
+
trustProxy: true,
|
|
45
|
+
security: {
|
|
46
|
+
helmet: true,
|
|
47
|
+
rateLimit: {
|
|
48
|
+
windowMs: 15 * 60 * 1000,
|
|
49
|
+
max: 100
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
app.start().then(() => {
|
|
55
|
+
console.log('Server started successfully');
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### 2. JWT Authentication Setup
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
// src/middleware/auth.ts
|
|
63
|
+
import { JwtMiddleware } from '@groundbrick/express-adapter';
|
|
64
|
+
import { createLogger } from '@groundbrick/logger';
|
|
65
|
+
|
|
66
|
+
const logger = createLogger({ context: 'auth' });
|
|
67
|
+
|
|
68
|
+
export const jwtAuth = new JwtMiddleware({
|
|
69
|
+
secret: process.env.JWT_SECRET || 'your-secret-key',
|
|
70
|
+
algorithms: ['HS256'],
|
|
71
|
+
skipPaths: ['/auth/login', '/auth/register', '/health'],
|
|
72
|
+
headerName: 'authorization',
|
|
73
|
+
cookieName: 'token'
|
|
74
|
+
}, logger);
|
|
75
|
+
|
|
76
|
+
// Usage in routes
|
|
77
|
+
import { Router } from 'express';
|
|
78
|
+
const router = Router();
|
|
79
|
+
|
|
80
|
+
// Protected route
|
|
81
|
+
router.get('/profile', jwtAuth.authenticate, userController.getProfile);
|
|
82
|
+
|
|
83
|
+
// Optional auth (user context if token present)
|
|
84
|
+
router.get('/public', jwtAuth.optional, contentController.getPublicContent);
|
|
85
|
+
|
|
86
|
+
// Role-based access
|
|
87
|
+
router.delete('/admin/users/:id',
|
|
88
|
+
jwtAuth.authenticate,
|
|
89
|
+
jwtAuth.requireRole(['admin', 'super-admin']),
|
|
90
|
+
adminController.deleteUser
|
|
91
|
+
);
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### 3. Controller Implementation
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
// src/controllers/UserController.ts
|
|
98
|
+
import { Request, Response, NextFunction } from 'express';
|
|
99
|
+
import { BaseController, AuthenticatedRequest } from '@groundbrick/express-adapter';
|
|
100
|
+
import { Logger } from '@groundbrick/logger';
|
|
101
|
+
import { ServiceResult } from '@groundbrick/service-base';
|
|
102
|
+
|
|
103
|
+
export class UserController extends BaseController {
|
|
104
|
+
constructor(logger: Logger, private userService: UserService) {
|
|
105
|
+
super(logger);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
getProfile = this.asyncHandler(async (req: AuthenticatedRequest, res: Response): Promise<ServiceResult<User>> => {
|
|
109
|
+
const userId = req.user!.userId; // Safe because of JWT middleware
|
|
110
|
+
const user = await this.userService.getUserById(userId);
|
|
111
|
+
return ServiceResult.success(user);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
login = this.asyncHandler(async (req: Request, res: Response): Promise<ServiceResult<LoginResponse>> => {
|
|
115
|
+
const { email, password } = req.body;
|
|
116
|
+
|
|
117
|
+
const user = await this.userService.authenticateUser(email, password);
|
|
118
|
+
const token = JwtMiddleware.generateToken(
|
|
119
|
+
{ userId: user.id, email: user.email, roles: user.roles },
|
|
120
|
+
process.env.JWT_SECRET!,
|
|
121
|
+
'24h'
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
return ServiceResult.success({ user, token });
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### 4. Login/Logout Implementation
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
// src/controllers/AuthController.ts
|
|
133
|
+
export class AuthController extends BaseController {
|
|
134
|
+
login = this.asyncHandler(async (req: Request, res: Response): Promise<void> => {
|
|
135
|
+
const { email, password } = req.body;
|
|
136
|
+
|
|
137
|
+
const user = await this.authService.login(email, password);
|
|
138
|
+
const token = JwtMiddleware.generateToken(
|
|
139
|
+
{
|
|
140
|
+
userId: user.id,
|
|
141
|
+
email: user.email,
|
|
142
|
+
roles: user.roles || []
|
|
143
|
+
},
|
|
144
|
+
process.env.JWT_SECRET!,
|
|
145
|
+
'24h'
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
// Set as cookie (optional)
|
|
149
|
+
res.cookie('token', token, {
|
|
150
|
+
httpOnly: true,
|
|
151
|
+
secure: process.env.NODE_ENV === 'production',
|
|
152
|
+
maxAge: 24 * 60 * 60 * 1000 // 24 hours
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
res.json({
|
|
156
|
+
success: true,
|
|
157
|
+
data: { user, token },
|
|
158
|
+
meta: { timestamp: new Date().toISOString() }
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
logout = this.asyncHandler(async (req: Request, res: Response): Promise<void> => {
|
|
163
|
+
res.clearCookie('token');
|
|
164
|
+
res.json({
|
|
165
|
+
success: true,
|
|
166
|
+
message: 'Logged out successfully',
|
|
167
|
+
meta: { timestamp: new Date().toISOString() }
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## API Reference
|
|
174
|
+
|
|
175
|
+
### JwtMiddleware
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
interface JwtMiddlewareConfig {
|
|
179
|
+
secret?: string;
|
|
180
|
+
algorithms?: string[];
|
|
181
|
+
skipPaths?: string[];
|
|
182
|
+
cookieName?: string;
|
|
183
|
+
headerName?: string;
|
|
184
|
+
onTokenExpired?: (req: Request, res: Response) => void;
|
|
185
|
+
onTokenInvalid?: (req: Request, res: Response) => void;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
class JwtMiddleware {
|
|
189
|
+
constructor(config: JwtMiddlewareConfig, logger: Logger);
|
|
190
|
+
|
|
191
|
+
// Required authentication
|
|
192
|
+
authenticate: RequestHandler;
|
|
193
|
+
|
|
194
|
+
// Optional authentication
|
|
195
|
+
optional: RequestHandler;
|
|
196
|
+
|
|
197
|
+
// Role-based access control
|
|
198
|
+
requireRole(roles: string | string[]): RequestHandler;
|
|
199
|
+
|
|
200
|
+
// Static helpers
|
|
201
|
+
static generateToken(payload: JwtPayload, secret: string, expiresIn?: string): string;
|
|
202
|
+
static decodeToken(token: string): JwtPayload | null;
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### JWT Payload Structure
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
interface JwtPayload {
|
|
210
|
+
userId: number;
|
|
211
|
+
email: string;
|
|
212
|
+
roles?: string[];
|
|
213
|
+
[key: string]: unknown;
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Authentication Patterns
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
// 1. Basic protected route
|
|
221
|
+
router.get('/protected', jwtAuth.authenticate, controller.method);
|
|
222
|
+
|
|
223
|
+
// 2. Optional authentication
|
|
224
|
+
router.get('/mixed', jwtAuth.optional, (req: AuthenticatedRequest, res) => {
|
|
225
|
+
if (req.user) {
|
|
226
|
+
// User is authenticated
|
|
227
|
+
} else {
|
|
228
|
+
// Anonymous access
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// 3. Role-based access
|
|
233
|
+
router.post('/admin',
|
|
234
|
+
jwtAuth.authenticate,
|
|
235
|
+
jwtAuth.requireRole('admin'),
|
|
236
|
+
adminController.method
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
// 4. Multiple roles
|
|
240
|
+
router.delete('/resource/:id',
|
|
241
|
+
jwtAuth.authenticate,
|
|
242
|
+
jwtAuth.requireRole(['admin', 'moderator']),
|
|
243
|
+
controller.method
|
|
244
|
+
);
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### ExpressApp
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
interface ExpressConfig {
|
|
251
|
+
port?: number;
|
|
252
|
+
host?: string;
|
|
253
|
+
cors?: {
|
|
254
|
+
origin?: string | string[] | boolean;
|
|
255
|
+
credentials?: boolean;
|
|
256
|
+
methods?: string[];
|
|
257
|
+
allowedHeaders?: string[];
|
|
258
|
+
};
|
|
259
|
+
security?: {
|
|
260
|
+
helmet?: boolean;
|
|
261
|
+
rateLimit?: {
|
|
262
|
+
windowMs?: number;
|
|
263
|
+
max?: number;
|
|
264
|
+
message?: string;
|
|
265
|
+
};
|
|
266
|
+
};
|
|
267
|
+
compression?: boolean;
|
|
268
|
+
bodyParser?: {
|
|
269
|
+
json?: { limit?: string };
|
|
270
|
+
urlencoded?: { limit?: string; extended?: boolean };
|
|
271
|
+
};
|
|
272
|
+
logger?: Partial<LoggerConfig>;
|
|
273
|
+
trustProxy?: boolean;
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### BaseController
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
abstract class BaseController {
|
|
281
|
+
constructor(logger: Logger);
|
|
282
|
+
|
|
283
|
+
protected asyncHandler(handler: RouteHandler): RouteHandler;
|
|
284
|
+
protected getIdFromParams(req: Request, paramName?: string): string;
|
|
285
|
+
protected getQueryParam(req: Request, paramName: string, defaultValue?: string): string | undefined;
|
|
286
|
+
protected getQueryParamAsNumber(req: Request, paramName: string, defaultValue?: number): number | undefined;
|
|
287
|
+
protected getPaginationParams(req: Request): { page: number; limit: number; offset: number };
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Response Helpers
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
// Success responses
|
|
295
|
+
ResponseHelper.success(res, data, 'Operation successful');
|
|
296
|
+
ResponseHelper.created(res, newUser, 'User created');
|
|
297
|
+
ResponseHelper.noContent(res);
|
|
298
|
+
|
|
299
|
+
// Error responses
|
|
300
|
+
ResponseHelper.error(res, 'Something went wrong', 400);
|
|
301
|
+
ResponseHelper.notFound(res, 'User not found');
|
|
302
|
+
ResponseHelper.unauthorized(res, 'Invalid credentials');
|
|
303
|
+
ResponseHelper.forbidden(res, 'Access denied');
|
|
304
|
+
|
|
305
|
+
// Authentication responses
|
|
306
|
+
ResponseHelper.unauthorized(res); // 401
|
|
307
|
+
ResponseHelper.forbidden(res); // 403
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
## Authentication Error Responses
|
|
311
|
+
|
|
312
|
+
JWT middleware returns standardized error responses:
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
// No token provided
|
|
316
|
+
{
|
|
317
|
+
"success": false,
|
|
318
|
+
"error": {
|
|
319
|
+
"message": "No token provided",
|
|
320
|
+
"code": "NO_TOKEN"
|
|
321
|
+
},
|
|
322
|
+
"meta": {
|
|
323
|
+
"timestamp": "2024-01-15T10:30:00.000Z",
|
|
324
|
+
"requestId": "abc123"
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Token expired
|
|
329
|
+
{
|
|
330
|
+
"success": false,
|
|
331
|
+
"error": {
|
|
332
|
+
"message": "Token has expired",
|
|
333
|
+
"code": "TOKEN_EXPIRED"
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Invalid token
|
|
338
|
+
{
|
|
339
|
+
"success": false,
|
|
340
|
+
"error": {
|
|
341
|
+
"message": "Invalid token",
|
|
342
|
+
"code": "TOKEN_INVALID"
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Insufficient permissions
|
|
347
|
+
{
|
|
348
|
+
"success": false,
|
|
349
|
+
"error": {
|
|
350
|
+
"message": "Insufficient permissions",
|
|
351
|
+
"code": "INSUFFICIENT_PERMISSIONS",
|
|
352
|
+
"details": {
|
|
353
|
+
"requiredRoles": ["admin"],
|
|
354
|
+
"userRoles": ["user"]
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
## Middleware Stack AutomƔtico
|
|
361
|
+
|
|
362
|
+
A classe `ExpressApp` configura automaticamente:
|
|
363
|
+
|
|
364
|
+
- **Security**: Helmet para headers de seguranƧa
|
|
365
|
+
- **Rate Limiting**: Proteção contra ataques de força bruta
|
|
366
|
+
- **CORS**: Configuração de Cross-Origin Resource Sharing
|
|
367
|
+
- **Compression**: Compressão gzip/deflate
|
|
368
|
+
- **Body Parsing**: JSON e URL-encoded parsing
|
|
369
|
+
- **Request Logging**: Log automƔtico de requests com timing
|
|
370
|
+
- **Response Formatting**: Formatação padronizada de respostas
|
|
371
|
+
- **Error Handling**: Tratamento global de erros
|
|
372
|
+
|
|
373
|
+
## Exemplo Completo com Autenticação
|
|
374
|
+
|
|
375
|
+
```typescript
|
|
376
|
+
// src/server.ts
|
|
377
|
+
import { ExpressApp, JwtMiddleware } from '@groundbrick/express-adapter';
|
|
378
|
+
import { createLogger } from '@groundbrick/logger';
|
|
379
|
+
import { UserController } from './controllers/UserController';
|
|
380
|
+
import { AuthController } from './controllers/AuthController';
|
|
381
|
+
|
|
382
|
+
const logger = createLogger({ context: 'api-server' });
|
|
383
|
+
|
|
384
|
+
// JWT Setup
|
|
385
|
+
const jwtAuth = new JwtMiddleware({
|
|
386
|
+
secret: process.env.JWT_SECRET!,
|
|
387
|
+
skipPaths: ['/auth/login', '/auth/register', '/health'],
|
|
388
|
+
algorithms: ['HS256']
|
|
389
|
+
}, logger);
|
|
390
|
+
|
|
391
|
+
// Express App
|
|
392
|
+
const app = new ExpressApp({
|
|
393
|
+
port: 3000,
|
|
394
|
+
cors: { origin: true, credentials: true },
|
|
395
|
+
trustProxy: true
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
// Auth routes (no JWT required)
|
|
399
|
+
app.addRoutes([
|
|
400
|
+
{ method: 'post', path: '/auth/login', handler: authController.login },
|
|
401
|
+
{ method: 'post', path: '/auth/register', handler: authController.register },
|
|
402
|
+
{ method: 'post', path: '/auth/logout', handler: authController.logout }
|
|
403
|
+
]);
|
|
404
|
+
|
|
405
|
+
// Protected routes
|
|
406
|
+
app.addRoutes([
|
|
407
|
+
{
|
|
408
|
+
method: 'get',
|
|
409
|
+
path: '/api/profile',
|
|
410
|
+
handler: userController.getProfile,
|
|
411
|
+
middleware: [jwtAuth.authenticate]
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
method: 'get',
|
|
415
|
+
path: '/api/admin/users',
|
|
416
|
+
handler: userController.getAllUsers,
|
|
417
|
+
middleware: [jwtAuth.authenticate, jwtAuth.requireRole('admin')]
|
|
418
|
+
}
|
|
419
|
+
]);
|
|
420
|
+
|
|
421
|
+
app.start();
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
## Integração com Outros Packages
|
|
425
|
+
|
|
426
|
+
Este adapter funciona perfeitamente com:
|
|
427
|
+
|
|
428
|
+
- `@groundbrick/logger` - Sistema de logging (obrigatório)
|
|
429
|
+
- `@groundbrick/service-base` - Base classes para services (obrigatório)
|
|
430
|
+
- `@groundbrick/db-core` - Abstração de banco de dados
|
|
431
|
+
|
|
432
|
+
## Dependencies
|
|
433
|
+
|
|
434
|
+
### Required
|
|
435
|
+
- `@groundbrick/logger` - Logging functionality
|
|
436
|
+
- `@groundbrick/service-base` - Service base classes
|
|
437
|
+
- `@groundbrick/db-core` - Database core abstractions
|
|
438
|
+
|
|
439
|
+
### Peer Dependencies
|
|
440
|
+
- `express` - Express.js framework
|
|
441
|
+
- `cors` - CORS middleware
|
|
442
|
+
- `helmet` - Security middleware
|
|
443
|
+
- `compression` - Response compression
|
|
444
|
+
- `express-rate-limit` - Rate limiting
|
|
445
|
+
- `jsonwebtoken` - JWT token handling
|
|
446
|
+
|
|
447
|
+
## License
|
|
448
|
+
|
|
449
|
+
MIT
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Request } from 'express';
|
|
2
|
+
import { Logger } from '@groundbrick/logger';
|
|
3
|
+
import { RouteHandler } from '../types/RouteHandler.js';
|
|
4
|
+
import { IBaseController } from '../types/Controller.js';
|
|
5
|
+
export declare abstract class BaseController implements IBaseController {
|
|
6
|
+
readonly logger: Logger;
|
|
7
|
+
constructor(logger: Logger);
|
|
8
|
+
/**
|
|
9
|
+
* Wraps a request handler with error handling and async support
|
|
10
|
+
* @param handler The route handler function
|
|
11
|
+
* @returns A wrapped handler function
|
|
12
|
+
*/
|
|
13
|
+
readonly asyncHandler: (handler: RouteHandler) => RouteHandler;
|
|
14
|
+
protected getIdFromParams(req: Request, paramName?: string): string;
|
|
15
|
+
protected getQueryParam(req: Request, paramName: string, defaultValue?: string): string | undefined;
|
|
16
|
+
protected getQueryParamAsNumber(req: Request, paramName: string, defaultValue?: number): number | undefined;
|
|
17
|
+
protected getPaginationParams(req: Request): {
|
|
18
|
+
page: number;
|
|
19
|
+
limit: number;
|
|
20
|
+
offset: number;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=BaseController.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BaseController.d.ts","sourceRoot":"","sources":["../../src/core/BaseController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAA0B,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAE7C,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,8BAAsB,cAAe,YAAW,eAAe;IAC3D,SAAgB,MAAM,EAAE,MAAM,CAAC;gBAEnB,MAAM,EAAE,MAAM;IAI1B;;;;OAIG;IACH,SAAgB,YAAY,GAAI,SAAS,YAAY,KAAG,YAAY,CAUlE;IAEF,SAAS,CAAC,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,GAAE,MAAa,GAAG,MAAM;IAQzE,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAInG,SAAS,CAAC,qBAAqB,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAY3G,SAAS,CAAC,mBAAmB,CAAC,GAAG,EAAE,OAAO,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;CAO/F"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export class BaseController {
|
|
2
|
+
logger;
|
|
3
|
+
constructor(logger) {
|
|
4
|
+
this.logger = logger;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Wraps a request handler with error handling and async support
|
|
8
|
+
* @param handler The route handler function
|
|
9
|
+
* @returns A wrapped handler function
|
|
10
|
+
*/
|
|
11
|
+
asyncHandler = (handler) => {
|
|
12
|
+
return async (req, res, next) => {
|
|
13
|
+
try {
|
|
14
|
+
const result = await handler(req, res, next);
|
|
15
|
+
// If the result is a Response object or void, it means the response has been handled
|
|
16
|
+
return result;
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
next(error);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
getIdFromParams(req, paramName = 'id') {
|
|
24
|
+
const id = req.params[paramName];
|
|
25
|
+
if (!id) {
|
|
26
|
+
throw new Error(`Missing required parameter: ${paramName}`);
|
|
27
|
+
}
|
|
28
|
+
return id;
|
|
29
|
+
}
|
|
30
|
+
getQueryParam(req, paramName, defaultValue) {
|
|
31
|
+
return req.query[paramName] || defaultValue;
|
|
32
|
+
}
|
|
33
|
+
getQueryParamAsNumber(req, paramName, defaultValue) {
|
|
34
|
+
const value = req.query[paramName];
|
|
35
|
+
if (!value)
|
|
36
|
+
return defaultValue;
|
|
37
|
+
const num = parseInt(value, 10);
|
|
38
|
+
if (isNaN(num)) {
|
|
39
|
+
throw new Error(`Invalid number format for parameter: ${paramName}`);
|
|
40
|
+
}
|
|
41
|
+
return num;
|
|
42
|
+
}
|
|
43
|
+
getPaginationParams(req) {
|
|
44
|
+
const page = Math.max(1, this.getQueryParamAsNumber(req, 'page', 1) || 1);
|
|
45
|
+
const limit = Math.min(100, Math.max(1, this.getQueryParamAsNumber(req, 'limit', 10) || 10));
|
|
46
|
+
const offset = (page - 1) * limit;
|
|
47
|
+
return { page, limit, offset };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=BaseController.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BaseController.js","sourceRoot":"","sources":["../../src/core/BaseController.ts"],"names":[],"mappings":"AAMA,MAAM,OAAgB,cAAc;IAChB,MAAM,CAAS;IAE/B,YAAY,MAAc;QACtB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACzB,CAAC;IAED;;;;OAIG;IACa,YAAY,GAAG,CAAC,OAAqB,EAAgB,EAAE;QACnE,OAAO,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;YAC7D,IAAI,CAAC;gBACD,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;gBAC7C,qFAAqF;gBACrF,OAAO,MAAM,CAAC;YAClB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACb,IAAI,CAAC,KAAK,CAAC,CAAC;YAChB,CAAC;QACL,CAAC,CAAC;IACN,CAAC,CAAC;IAEQ,eAAe,CAAC,GAAY,EAAE,YAAoB,IAAI;QAC5D,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACjC,IAAI,CAAC,EAAE,EAAE,CAAC;YACN,MAAM,IAAI,KAAK,CAAC,+BAA+B,SAAS,EAAE,CAAC,CAAC;QAChE,CAAC;QACD,OAAO,EAAE,CAAC;IACd,CAAC;IAES,aAAa,CAAC,GAAY,EAAE,SAAiB,EAAE,YAAqB;QAC1E,OAAO,GAAG,CAAC,KAAK,CAAC,SAAS,CAAW,IAAI,YAAY,CAAC;IAC1D,CAAC;IAES,qBAAqB,CAAC,GAAY,EAAE,SAAiB,EAAE,YAAqB;QAClF,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,SAAS,CAAW,CAAC;QAC7C,IAAI,CAAC,KAAK;YAAE,OAAO,YAAY,CAAC;QAEhC,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAChC,IAAI,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,wCAAwC,SAAS,EAAE,CAAC,CAAC;QACzE,CAAC;QAED,OAAO,GAAG,CAAC;IACf,CAAC;IAES,mBAAmB,CAAC,GAAY;QACtC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QAC1E,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,qBAAqB,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAC7F,MAAM,MAAM,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC;QAElC,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IACnC,CAAC;CACJ"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Express } from 'express';
|
|
2
|
+
import { ExpressConfig } from '../types/ExpressConfig.js';
|
|
3
|
+
import { RouteDefinition } from '../types/RouteHandler.js';
|
|
4
|
+
export declare class ExpressApp {
|
|
5
|
+
private app;
|
|
6
|
+
private logger;
|
|
7
|
+
private config;
|
|
8
|
+
private errorHandler;
|
|
9
|
+
private requestLogger;
|
|
10
|
+
constructor(config?: ExpressConfig);
|
|
11
|
+
private setupMiddleware;
|
|
12
|
+
private setupStaticFiles;
|
|
13
|
+
private setupRoutes;
|
|
14
|
+
addRoutes(routes: RouteDefinition[]): void;
|
|
15
|
+
addRoute(route: RouteDefinition): void;
|
|
16
|
+
getApp(): Express;
|
|
17
|
+
setupErrorHandling(): void;
|
|
18
|
+
start(): Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=ExpressApp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExpressApp.d.ts","sourceRoot":"","sources":["../../src/core/ExpressApp.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAE,OAAO,EAAqB,MAAM,SAAS,CAAC;AAO9D,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAK3D,qBAAa,UAAU;IACnB,OAAO,CAAC,GAAG,CAAU;IACrB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,aAAa,CAAgB;gBAEzB,MAAM,GAAE,aAAkB;IAqCtC,OAAO,CAAC,eAAe;IA6DvB,OAAO,CAAC,gBAAgB;IASxB,OAAO,CAAC,WAAW;IAoBZ,SAAS,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,IAAI;IAS1C,QAAQ,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI;IAItC,MAAM,IAAI,OAAO;IAIjB,kBAAkB,IAAI,IAAI;IAoB1B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CA6BhC"}
|