@naman_deep_singh/server-utils 1.0.0 → 1.0.2
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 +220 -177
- package/dist/index.d.ts +37 -2
- package/dist/index.js +43 -3
- package/dist/middleware.d.ts +2 -2
- package/dist/middleware.js +5 -3
- package/dist/server.d.ts +66 -4
- package/dist/server.js +236 -32
- package/dist/types.d.ts +33 -1
- package/package.json +3 -8
- package/src/index.ts +70 -3
- package/src/middleware.ts +11 -8
- package/src/server.ts +356 -30
- package/src/types.ts +36 -1
- package/tsconfig.json +6 -2
package/dist/server.js
CHANGED
|
@@ -3,41 +3,245 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ExpressServer = void 0;
|
|
6
7
|
exports.createServer = createServer;
|
|
7
|
-
exports.withPlugin = withPlugin;
|
|
8
|
-
exports.createServerWithPlugins = createServerWithPlugins;
|
|
9
8
|
const express_1 = __importDefault(require("express"));
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
9
|
+
const shutdown_1 = require("./shutdown");
|
|
10
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
11
|
+
class ExpressServer {
|
|
12
|
+
constructor(name = 'Express Server', version = '1.0.0', config = {}) {
|
|
13
|
+
this.status = 'stopped';
|
|
14
|
+
this.grpcServices = [];
|
|
15
|
+
this.rpcMethods = {};
|
|
16
|
+
this.app = (0, express_1.default)();
|
|
17
|
+
this.config = {
|
|
18
|
+
name,
|
|
19
|
+
version,
|
|
20
|
+
startTime: new Date(),
|
|
21
|
+
port: config.port || 3000,
|
|
22
|
+
cors: config.cors ?? true,
|
|
23
|
+
helmet: config.helmet ?? true,
|
|
24
|
+
json: config.json ?? true,
|
|
25
|
+
customMiddleware: config.customMiddleware || [],
|
|
26
|
+
healthCheck: config.healthCheck ?? true,
|
|
27
|
+
gracefulShutdown: config.gracefulShutdown ?? true,
|
|
28
|
+
socketIO: config.socketIO
|
|
29
|
+
};
|
|
30
|
+
// Apply middleware based on configuration
|
|
31
|
+
this.setupMiddleware();
|
|
32
|
+
}
|
|
33
|
+
setupMiddleware() {
|
|
34
|
+
// Apply CORS if enabled
|
|
35
|
+
if (this.config.cors) {
|
|
36
|
+
try {
|
|
37
|
+
const cors = require('cors');
|
|
38
|
+
const corsOptions = typeof this.config.cors === 'object' ? this.config.cors : undefined;
|
|
39
|
+
this.app.use(cors(corsOptions));
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
console.warn('CORS middleware not available. Install cors package.');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// Apply Helmet if enabled
|
|
46
|
+
if (this.config.helmet) {
|
|
47
|
+
try {
|
|
48
|
+
const helmet = require('helmet');
|
|
49
|
+
this.app.use(helmet());
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
console.warn('Helmet middleware not available. Install helmet package.');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// Apply JSON parser if enabled
|
|
56
|
+
if (this.config.json) {
|
|
57
|
+
this.app.use(express_1.default.json());
|
|
58
|
+
}
|
|
59
|
+
// Apply custom middleware
|
|
60
|
+
if (this.config.customMiddleware && this.config.customMiddleware.length > 0) {
|
|
61
|
+
this.config.customMiddleware.forEach(middleware => {
|
|
62
|
+
this.app.use(middleware);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
// Add health check if enabled
|
|
66
|
+
if (this.config.healthCheck) {
|
|
67
|
+
const healthPath = typeof this.config.healthCheck === 'string' ? this.config.healthCheck : '/health';
|
|
68
|
+
this.app.get(healthPath, (req, res) => {
|
|
69
|
+
res.status(200).json({
|
|
70
|
+
status: 'healthy',
|
|
71
|
+
service: this.config.name,
|
|
72
|
+
version: this.config.version,
|
|
73
|
+
uptime: Date.now() - this.config.startTime.getTime(),
|
|
74
|
+
timestamp: new Date().toISOString()
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async start() {
|
|
80
|
+
this.status = 'starting';
|
|
81
|
+
return new Promise((resolve, reject) => {
|
|
82
|
+
try {
|
|
83
|
+
this.server = this.app.listen(this.config.port, () => {
|
|
84
|
+
this.status = 'running';
|
|
85
|
+
console.log(`🚀 ${this.config.name} v${this.config.version} running on http://localhost:${this.config.port}`);
|
|
86
|
+
if (this.config.gracefulShutdown) {
|
|
87
|
+
(0, shutdown_1.createGracefulShutdown)(this.server, {
|
|
88
|
+
onShutdown: async () => {
|
|
89
|
+
this.status = 'stopping';
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
resolve(this);
|
|
94
|
+
});
|
|
95
|
+
this.server.on('error', reject);
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
this.status = 'stopped';
|
|
99
|
+
reject(error);
|
|
100
|
+
}
|
|
29
101
|
});
|
|
30
102
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
103
|
+
async stop() {
|
|
104
|
+
this.status = 'stopping';
|
|
105
|
+
// Stop gRPC server if running
|
|
106
|
+
if (this.grpcServer) {
|
|
107
|
+
this.grpcServer.forceShutdown();
|
|
108
|
+
}
|
|
109
|
+
// Stop Socket.IO server if running
|
|
110
|
+
if (this.socketIO) {
|
|
111
|
+
this.socketIO.close();
|
|
112
|
+
}
|
|
113
|
+
if (!this.server) {
|
|
114
|
+
this.status = 'stopped';
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
return new Promise((resolve) => {
|
|
118
|
+
this.server.close(() => {
|
|
119
|
+
this.status = 'stopped';
|
|
120
|
+
console.log(`👋 ${this.config.name} stopped`);
|
|
121
|
+
resolve();
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
getInfo() {
|
|
126
|
+
return {
|
|
127
|
+
name: this.config.name,
|
|
128
|
+
version: this.config.version,
|
|
129
|
+
port: this.config.port,
|
|
130
|
+
uptime: Date.now() - this.config.startTime.getTime(),
|
|
131
|
+
status: this.status,
|
|
132
|
+
startTime: this.config.startTime
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
addGrpcService(service, implementation, port = 50051) {
|
|
136
|
+
this.grpcServices.push({ service, implementation });
|
|
137
|
+
// Lazy load gRPC to avoid dependency issues
|
|
138
|
+
if (!this.grpcServer) {
|
|
139
|
+
try {
|
|
140
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
141
|
+
const grpc = require('@grpc/grpc-js');
|
|
142
|
+
this.grpcServer = new grpc.Server();
|
|
143
|
+
// Add all services
|
|
144
|
+
this.grpcServices.forEach(({ service, implementation }) => {
|
|
145
|
+
this.grpcServer.addService(service, implementation);
|
|
146
|
+
});
|
|
147
|
+
this.grpcServer.bindAsync(`0.0.0.0:${port}`, grpc.ServerCredentials.createInsecure(), () => {
|
|
148
|
+
this.grpcServer.start();
|
|
149
|
+
console.log(`🔗 gRPC server running on port ${port}`);
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
console.warn('gRPC not available. Install @grpc/grpc-js to use gRPC features.');
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
addRpcMethods(methods, path = '/rpc') {
|
|
158
|
+
Object.assign(this.rpcMethods, methods);
|
|
159
|
+
try {
|
|
160
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
161
|
+
const jayson = require('jayson');
|
|
162
|
+
const rpcServer = jayson.server(this.rpcMethods);
|
|
163
|
+
this.app.use(path, rpcServer.middleware());
|
|
164
|
+
console.log(`📡 JSON-RPC server mounted on ${path}`);
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
console.warn('JSON-RPC not available. Install jayson to use RPC features.');
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
addWebhook(config) {
|
|
171
|
+
this.app.post(config.path, express_1.default.raw({ type: 'application/json' }), async (req, res) => {
|
|
172
|
+
try {
|
|
173
|
+
// Verify signature if secret provided
|
|
174
|
+
if (config.secret) {
|
|
175
|
+
const signature = req.headers['x-hub-signature-256'] || req.headers['x-signature-256'];
|
|
176
|
+
if (signature) {
|
|
177
|
+
const expectedSignature = crypto_1.default
|
|
178
|
+
.createHmac('sha256', config.secret)
|
|
179
|
+
.update(req.body)
|
|
180
|
+
.digest('hex');
|
|
181
|
+
const providedSignature = Array.isArray(signature) ? signature[0] : signature;
|
|
182
|
+
if (!providedSignature.includes(expectedSignature)) {
|
|
183
|
+
return res.status(401).json({ error: 'Invalid signature' });
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// Parse JSON payload
|
|
188
|
+
const payload = JSON.parse(req.body.toString());
|
|
189
|
+
// Call handler
|
|
190
|
+
await config.handler(payload, req.headers);
|
|
191
|
+
res.status(200).json({ success: true });
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
console.error('Webhook error:', error);
|
|
195
|
+
res.status(500).json({ error: 'Webhook processing failed' });
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
console.log(`🪝 Webhook registered at ${config.path}`);
|
|
199
|
+
}
|
|
200
|
+
addSocketIO(config = {}) {
|
|
201
|
+
if (!this.server) {
|
|
202
|
+
throw new Error('Server must be started before adding Socket.IO');
|
|
203
|
+
}
|
|
204
|
+
try {
|
|
205
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
206
|
+
const { Server } = require('socket.io');
|
|
207
|
+
// Configure CORS
|
|
208
|
+
const corsConfig = config.cors === true
|
|
209
|
+
? { origin: '*', methods: ['GET', 'POST'] }
|
|
210
|
+
: config.cors || undefined;
|
|
211
|
+
// Create Socket.IO server
|
|
212
|
+
const io = new Server(this.server, {
|
|
213
|
+
cors: config.cors ? corsConfig : undefined,
|
|
214
|
+
path: config.path || '/socket.io'
|
|
215
|
+
});
|
|
216
|
+
// Store reference for cleanup
|
|
217
|
+
this.socketIO = io;
|
|
218
|
+
// Handle connections
|
|
219
|
+
io.on('connection', (socket) => {
|
|
220
|
+
const typedSocket = socket;
|
|
221
|
+
console.log(`🔌 Socket connected: ${typedSocket.id}`);
|
|
222
|
+
// Call user-defined connection handler
|
|
223
|
+
if (config.onConnection) {
|
|
224
|
+
config.onConnection(socket);
|
|
225
|
+
}
|
|
226
|
+
// Handle disconnection
|
|
227
|
+
typedSocket.on('disconnect', (reason) => {
|
|
228
|
+
console.log(`🔌 Socket disconnected: ${typedSocket.id} - ${reason}`);
|
|
229
|
+
// Call user-defined disconnection handler
|
|
230
|
+
if (config.onDisconnection) {
|
|
231
|
+
config.onDisconnection(socket, reason);
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
console.log(`🔌 Socket.IO server attached${config.path ? ` at ${config.path}` : ''}`);
|
|
236
|
+
return io;
|
|
237
|
+
}
|
|
238
|
+
catch (error) {
|
|
239
|
+
console.warn('Socket.IO not available. Install socket.io to use WebSocket features.');
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
36
243
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
plugin(app, config);
|
|
41
|
-
});
|
|
42
|
-
return app;
|
|
244
|
+
exports.ExpressServer = ExpressServer;
|
|
245
|
+
function createServer(name, version, config) {
|
|
246
|
+
return new ExpressServer(name, version, config);
|
|
43
247
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import express from 'express';
|
|
2
|
-
|
|
2
|
+
export interface CorsOptions {
|
|
3
|
+
origin?: string | string[] | boolean | RegExp | ((origin: string | undefined, callback: (err: Error | null, allow?: boolean) => void) => void);
|
|
4
|
+
methods?: string | string[];
|
|
5
|
+
allowedHeaders?: string | string[];
|
|
6
|
+
exposedHeaders?: string | string[];
|
|
7
|
+
credentials?: boolean;
|
|
8
|
+
maxAge?: number;
|
|
9
|
+
preflightContinue?: boolean;
|
|
10
|
+
optionsSuccessStatus?: number;
|
|
11
|
+
}
|
|
3
12
|
export interface ServerConfig {
|
|
4
13
|
port?: number;
|
|
5
14
|
cors?: boolean | CorsOptions;
|
|
@@ -8,6 +17,20 @@ export interface ServerConfig {
|
|
|
8
17
|
customMiddleware?: express.RequestHandler[];
|
|
9
18
|
healthCheck?: boolean | string;
|
|
10
19
|
gracefulShutdown?: boolean;
|
|
20
|
+
socketIO?: SocketIOConfig;
|
|
21
|
+
name?: string;
|
|
22
|
+
version?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface SocketIOConfig {
|
|
25
|
+
enabled?: boolean;
|
|
26
|
+
cors?: boolean | {
|
|
27
|
+
origin?: string | string[] | boolean;
|
|
28
|
+
methods?: string[];
|
|
29
|
+
credentials?: boolean;
|
|
30
|
+
};
|
|
31
|
+
onConnection?: (socket: unknown) => void;
|
|
32
|
+
onDisconnection?: (socket: unknown, reason: string) => void;
|
|
33
|
+
path?: string;
|
|
11
34
|
}
|
|
12
35
|
export interface HealthCheckConfig {
|
|
13
36
|
path?: string;
|
|
@@ -21,4 +44,13 @@ export interface GracefulShutdownConfig {
|
|
|
21
44
|
timeout?: number;
|
|
22
45
|
onShutdown?: () => Promise<void>;
|
|
23
46
|
}
|
|
47
|
+
export interface SocketInstance {
|
|
48
|
+
id: string;
|
|
49
|
+
emit: (event: string, data?: unknown) => void;
|
|
50
|
+
on: (event: string, handler: (...args: unknown[]) => void) => void;
|
|
51
|
+
broadcast: {
|
|
52
|
+
emit: (event: string, data?: unknown) => void;
|
|
53
|
+
};
|
|
54
|
+
disconnect: () => void;
|
|
55
|
+
}
|
|
24
56
|
export type ServerPlugin = (app: express.Application, config: ServerConfig) => void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@naman_deep_singh/server-utils",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Extensible server utilities for Express.js microservices with TypeScript",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -18,15 +18,10 @@
|
|
|
18
18
|
"license": "ISC",
|
|
19
19
|
"packageManager": "pnpm@10.20.0",
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"express": "^
|
|
22
|
-
"cors": "^2.8.5",
|
|
23
|
-
"helmet": "^8.1.0",
|
|
24
|
-
"cookie-parser": "^1.4.6"
|
|
21
|
+
"express": "^4.21.1"
|
|
25
22
|
},
|
|
26
23
|
"devDependencies": {
|
|
27
|
-
"@types/express": "^
|
|
28
|
-
"@types/cors": "^2.8.19",
|
|
29
|
-
"@types/cookie-parser": "^1.4.7",
|
|
24
|
+
"@types/express": "^4.17.21",
|
|
30
25
|
"typescript": "^5.9.3"
|
|
31
26
|
}
|
|
32
27
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Core server utilities
|
|
2
|
-
export {
|
|
2
|
+
export { ExpressServer, createServer } from './server';
|
|
3
|
+
export type { ServerInstance, ServerInfo, GrpcService, RpcMethod, WebhookConfig } from './server';
|
|
3
4
|
|
|
4
5
|
// Health check utilities
|
|
5
6
|
export { createHealthCheck, withHealthCheck, addHealthCheck } from './health';
|
|
@@ -42,5 +43,71 @@ export type {
|
|
|
42
43
|
HealthCheckConfig,
|
|
43
44
|
HealthCheck,
|
|
44
45
|
GracefulShutdownConfig,
|
|
45
|
-
ServerPlugin
|
|
46
|
-
|
|
46
|
+
ServerPlugin,
|
|
47
|
+
SocketIOConfig,
|
|
48
|
+
SocketInstance
|
|
49
|
+
} from './types';
|
|
50
|
+
|
|
51
|
+
// Import all exports for default export
|
|
52
|
+
import { ExpressServer, createServer } from './server';
|
|
53
|
+
import { createHealthCheck, withHealthCheck, addHealthCheck } from './health';
|
|
54
|
+
import { createGracefulShutdown, withGracefulShutdown, startServerWithShutdown } from './shutdown';
|
|
55
|
+
import {
|
|
56
|
+
createLoggingMiddleware,
|
|
57
|
+
createErrorHandler,
|
|
58
|
+
createRequestIdMiddleware,
|
|
59
|
+
createValidationMiddleware,
|
|
60
|
+
createRateLimitMiddleware,
|
|
61
|
+
createAuthMiddleware,
|
|
62
|
+
withLogging,
|
|
63
|
+
withErrorHandler,
|
|
64
|
+
withRequestId,
|
|
65
|
+
withValidation,
|
|
66
|
+
withRateLimit,
|
|
67
|
+
withAuth,
|
|
68
|
+
validateFields,
|
|
69
|
+
rateLimit,
|
|
70
|
+
requireAuth
|
|
71
|
+
} from './middleware';
|
|
72
|
+
import { getEnv, getEnvNumber, getEnvBoolean } from './utils';
|
|
73
|
+
|
|
74
|
+
// Default export for namespace usage
|
|
75
|
+
const ServerUtils = {
|
|
76
|
+
// Server creation
|
|
77
|
+
createServer,
|
|
78
|
+
ExpressServer,
|
|
79
|
+
|
|
80
|
+
// Health checks
|
|
81
|
+
createHealthCheck,
|
|
82
|
+
withHealthCheck,
|
|
83
|
+
addHealthCheck,
|
|
84
|
+
|
|
85
|
+
// Graceful shutdown
|
|
86
|
+
createGracefulShutdown,
|
|
87
|
+
withGracefulShutdown,
|
|
88
|
+
startServerWithShutdown,
|
|
89
|
+
|
|
90
|
+
// Middleware
|
|
91
|
+
createLoggingMiddleware,
|
|
92
|
+
createErrorHandler,
|
|
93
|
+
createRequestIdMiddleware,
|
|
94
|
+
createValidationMiddleware,
|
|
95
|
+
createRateLimitMiddleware,
|
|
96
|
+
createAuthMiddleware,
|
|
97
|
+
withLogging,
|
|
98
|
+
withErrorHandler,
|
|
99
|
+
withRequestId,
|
|
100
|
+
withValidation,
|
|
101
|
+
withRateLimit,
|
|
102
|
+
withAuth,
|
|
103
|
+
validateFields,
|
|
104
|
+
rateLimit,
|
|
105
|
+
requireAuth,
|
|
106
|
+
|
|
107
|
+
// Utils
|
|
108
|
+
getEnv,
|
|
109
|
+
getEnvNumber,
|
|
110
|
+
getEnvBoolean
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export default ServerUtils;
|
package/src/middleware.ts
CHANGED
|
@@ -22,22 +22,25 @@ export function createLoggingMiddleware(format: 'simple' | 'detailed' = 'simple'
|
|
|
22
22
|
|
|
23
23
|
// Error handling middleware
|
|
24
24
|
export function createErrorHandler(): express.ErrorRequestHandler {
|
|
25
|
-
return (err:
|
|
25
|
+
return (err: unknown, req: express.Request, res: express.Response, next: express.NextFunction) => {
|
|
26
26
|
console.error('Error:', err);
|
|
27
27
|
|
|
28
28
|
if (res.headersSent) {
|
|
29
29
|
return next(err);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
// Type guard for error objects
|
|
33
|
+
const errorObj = err as { status?: number; statusCode?: number; message?: string; stack?: string };
|
|
34
|
+
|
|
35
|
+
const status = errorObj.status || errorObj.statusCode || 500;
|
|
33
36
|
const message = process.env.NODE_ENV === 'production'
|
|
34
37
|
? 'Internal Server Error'
|
|
35
|
-
:
|
|
38
|
+
: errorObj.message || 'Unknown error';
|
|
36
39
|
|
|
37
40
|
res.status(status).json({
|
|
38
41
|
status: false,
|
|
39
42
|
message,
|
|
40
|
-
...(process.env.NODE_ENV !== 'production' && { stack:
|
|
43
|
+
...(process.env.NODE_ENV !== 'production' && { stack: errorObj.stack })
|
|
41
44
|
});
|
|
42
45
|
};
|
|
43
46
|
}
|
|
@@ -46,7 +49,7 @@ export function createErrorHandler(): express.ErrorRequestHandler {
|
|
|
46
49
|
export function createRequestIdMiddleware(): express.RequestHandler {
|
|
47
50
|
return (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
|
48
51
|
const requestId = Math.random().toString(36).substring(2, 15);
|
|
49
|
-
(req as
|
|
52
|
+
(req as express.Request & { requestId: string }).requestId = requestId;
|
|
50
53
|
res.setHeader('X-Request-ID', requestId);
|
|
51
54
|
next();
|
|
52
55
|
};
|
|
@@ -60,7 +63,7 @@ export interface ValidationRule {
|
|
|
60
63
|
minLength?: number;
|
|
61
64
|
maxLength?: number;
|
|
62
65
|
pattern?: RegExp;
|
|
63
|
-
custom?: (value:
|
|
66
|
+
custom?: (value: unknown) => boolean | string;
|
|
64
67
|
}
|
|
65
68
|
|
|
66
69
|
export function createValidationMiddleware(rules: ValidationRule[]): express.RequestHandler {
|
|
@@ -181,7 +184,7 @@ export function createRateLimitMiddleware(config: RateLimitConfig = {}): express
|
|
|
181
184
|
// Authentication middleware helper
|
|
182
185
|
export interface AuthConfig {
|
|
183
186
|
tokenExtractor?: (req: express.Request) => string | null;
|
|
184
|
-
tokenValidator?: (token: string) => Promise<
|
|
187
|
+
tokenValidator?: (token: string) => Promise<unknown> | unknown;
|
|
185
188
|
unauthorizedMessage?: string;
|
|
186
189
|
}
|
|
187
190
|
|
|
@@ -210,7 +213,7 @@ export function createAuthMiddleware(config: AuthConfig): express.RequestHandler
|
|
|
210
213
|
}
|
|
211
214
|
|
|
212
215
|
const user = await tokenValidator(token);
|
|
213
|
-
(req as
|
|
216
|
+
(req as express.Request & { user: unknown }).user = user;
|
|
214
217
|
next();
|
|
215
218
|
} catch (error) {
|
|
216
219
|
return res.status(401).json({
|