@naman_deep_singh/server-utils 1.0.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/README.md +238 -0
- package/dist/health.d.ts +5 -0
- package/dist/health.js +45 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +40 -0
- package/dist/middleware.d.ts +37 -0
- package/dist/middleware.js +217 -0
- package/dist/server.d.ts +5 -0
- package/dist/server.js +43 -0
- package/dist/shutdown.d.ts +5 -0
- package/dist/shutdown.js +50 -0
- package/dist/types.d.ts +24 -0
- package/dist/types.js +2 -0
- package/dist/utils.d.ts +3 -0
- package/dist/utils.js +37 -0
- package/package.json +32 -0
- package/src/health.ts +47 -0
- package/src/index.ts +46 -0
- package/src/middleware.ts +272 -0
- package/src/server.ts +46 -0
- package/src/shutdown.ts +60 -0
- package/src/types.ts +29 -0
- package/src/utils.ts +34 -0
- package/tsconfig.json +17 -0
package/README.md
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# @naman_deep_singh/server-utils
|
|
2
|
+
|
|
3
|
+
Extensible server utilities for Express.js microservices with TypeScript. Provides a plugin-based architecture for building consistent and maintainable server applications.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🚀 **Express Server Factory** - Quick server setup with sensible defaults
|
|
8
|
+
- 🔌 **Plugin System** - Extensible architecture for custom functionality
|
|
9
|
+
- 🏥 **Health Checks** - Built-in health monitoring with custom checks
|
|
10
|
+
- 🛑 **Graceful Shutdown** - Proper cleanup and shutdown handling
|
|
11
|
+
- 📝 **Logging Middleware** - Request logging with customizable formats
|
|
12
|
+
- 🛡️ **Error Handling** - Centralized error handling middleware
|
|
13
|
+
- 🆔 **Request ID** - Automatic request ID generation and tracking
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @naman_deep_singh/server-utils
|
|
19
|
+
# or
|
|
20
|
+
pnpm add @naman_deep_singh/server-utils
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
### Basic Server
|
|
26
|
+
```typescript
|
|
27
|
+
import { createServer, startServerWithShutdown } from '@naman_deep_singh/server-utils';
|
|
28
|
+
|
|
29
|
+
const app = createServer({
|
|
30
|
+
cors: true,
|
|
31
|
+
helmet: true,
|
|
32
|
+
healthCheck: true
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
app.get('/', (req, res) => {
|
|
36
|
+
res.json({ message: 'Hello World!' });
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
startServerWithShutdown(app, 3000);
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Plugin-Based Architecture
|
|
43
|
+
```typescript
|
|
44
|
+
import {
|
|
45
|
+
createServerWithPlugins,
|
|
46
|
+
withHealthCheck,
|
|
47
|
+
withLogging,
|
|
48
|
+
withErrorHandler,
|
|
49
|
+
withGracefulShutdown,
|
|
50
|
+
startServerWithShutdown
|
|
51
|
+
} from '@naman_deep_singh/server-utils';
|
|
52
|
+
|
|
53
|
+
const app = createServerWithPlugins(
|
|
54
|
+
{ cors: true, helmet: true },
|
|
55
|
+
withLogging('detailed'),
|
|
56
|
+
withHealthCheck('/health', {
|
|
57
|
+
customChecks: [
|
|
58
|
+
{
|
|
59
|
+
name: 'database',
|
|
60
|
+
check: async () => {
|
|
61
|
+
// Your database health check
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
]
|
|
66
|
+
}),
|
|
67
|
+
withErrorHandler(),
|
|
68
|
+
withGracefulShutdown({
|
|
69
|
+
timeout: 15000,
|
|
70
|
+
onShutdown: async () => {
|
|
71
|
+
console.log('Cleaning up resources...');
|
|
72
|
+
// Your cleanup logic
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
// Add your routes
|
|
78
|
+
app.get('/api/users', (req, res) => {
|
|
79
|
+
res.json({ users: [] });
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
startServerWithShutdown(app, 3000);
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## API Reference
|
|
86
|
+
|
|
87
|
+
### Server Factory
|
|
88
|
+
|
|
89
|
+
#### `createServer(config?: ServerConfig)`
|
|
90
|
+
Creates an Express application with default middleware.
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
interface ServerConfig {
|
|
94
|
+
port?: number;
|
|
95
|
+
cors?: boolean | CorsOptions;
|
|
96
|
+
helmet?: boolean;
|
|
97
|
+
json?: boolean;
|
|
98
|
+
customMiddleware?: express.RequestHandler[];
|
|
99
|
+
healthCheck?: boolean | string;
|
|
100
|
+
gracefulShutdown?: boolean;
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
#### `createServerWithPlugins(config, ...plugins)`
|
|
105
|
+
Creates a server and applies multiple plugins.
|
|
106
|
+
|
|
107
|
+
### Health Checks
|
|
108
|
+
|
|
109
|
+
#### `withHealthCheck(path?, config?)`
|
|
110
|
+
Adds health check endpoint as a plugin.
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
withHealthCheck('/health', {
|
|
114
|
+
customChecks: [
|
|
115
|
+
{
|
|
116
|
+
name: 'redis',
|
|
117
|
+
check: async () => await redis.ping()
|
|
118
|
+
}
|
|
119
|
+
]
|
|
120
|
+
})
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
#### `addHealthCheck(app, path?, config?)`
|
|
124
|
+
Directly adds health check to existing app.
|
|
125
|
+
|
|
126
|
+
### Graceful Shutdown
|
|
127
|
+
|
|
128
|
+
#### `withGracefulShutdown(config?)`
|
|
129
|
+
Adds graceful shutdown as a plugin.
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
withGracefulShutdown({
|
|
133
|
+
timeout: 10000,
|
|
134
|
+
onShutdown: async () => {
|
|
135
|
+
await database.close();
|
|
136
|
+
await redis.disconnect();
|
|
137
|
+
}
|
|
138
|
+
})
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
#### `startServerWithShutdown(app, port, config?)`
|
|
142
|
+
Starts server with automatic graceful shutdown setup.
|
|
143
|
+
|
|
144
|
+
### Middleware Plugins
|
|
145
|
+
|
|
146
|
+
#### `withLogging(format?)`
|
|
147
|
+
Adds request logging middleware.
|
|
148
|
+
- `format`: 'simple' | 'detailed'
|
|
149
|
+
|
|
150
|
+
#### `withErrorHandler()`
|
|
151
|
+
Adds centralized error handling middleware.
|
|
152
|
+
|
|
153
|
+
#### `withRequestId()`
|
|
154
|
+
Adds request ID generation and tracking.
|
|
155
|
+
|
|
156
|
+
### Custom Plugins
|
|
157
|
+
|
|
158
|
+
Create your own plugins:
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
import { ServerPlugin } from '@naman_deep_singh/server-utils';
|
|
162
|
+
|
|
163
|
+
const withCustomAuth = (secretKey: string): ServerPlugin => {
|
|
164
|
+
return (app, config) => {
|
|
165
|
+
app.use((req, res, next) => {
|
|
166
|
+
// Your custom auth logic
|
|
167
|
+
next();
|
|
168
|
+
});
|
|
169
|
+
};
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
// Use it
|
|
173
|
+
const app = createServerWithPlugins(
|
|
174
|
+
{},
|
|
175
|
+
withCustomAuth('my-secret'),
|
|
176
|
+
withLogging()
|
|
177
|
+
);
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Examples
|
|
181
|
+
|
|
182
|
+
### Microservice Template
|
|
183
|
+
```typescript
|
|
184
|
+
import {
|
|
185
|
+
createServerWithPlugins,
|
|
186
|
+
withHealthCheck,
|
|
187
|
+
withLogging,
|
|
188
|
+
withErrorHandler,
|
|
189
|
+
withRequestId,
|
|
190
|
+
startServerWithShutdown
|
|
191
|
+
} from '@naman_deep_singh/server-utils';
|
|
192
|
+
|
|
193
|
+
const app = createServerWithPlugins(
|
|
194
|
+
{
|
|
195
|
+
cors: {
|
|
196
|
+
origin: process.env.ALLOWED_ORIGINS?.split(',') || '*',
|
|
197
|
+
credentials: true
|
|
198
|
+
},
|
|
199
|
+
helmet: true
|
|
200
|
+
},
|
|
201
|
+
withRequestId(),
|
|
202
|
+
withLogging(process.env.NODE_ENV === 'production' ? 'simple' : 'detailed'),
|
|
203
|
+
withHealthCheck('/health', {
|
|
204
|
+
customChecks: [
|
|
205
|
+
{
|
|
206
|
+
name: 'database',
|
|
207
|
+
check: async () => {
|
|
208
|
+
try {
|
|
209
|
+
await prisma.$queryRaw`SELECT 1`;
|
|
210
|
+
return true;
|
|
211
|
+
} catch {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
]
|
|
217
|
+
}),
|
|
218
|
+
withErrorHandler()
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
// Your routes
|
|
222
|
+
app.use('/api', apiRoutes);
|
|
223
|
+
|
|
224
|
+
const PORT = Number(process.env.PORT) || 3000;
|
|
225
|
+
startServerWithShutdown(app, PORT, {
|
|
226
|
+
onShutdown: async () => {
|
|
227
|
+
await prisma.$disconnect();
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## TypeScript Support
|
|
233
|
+
|
|
234
|
+
Full TypeScript support with comprehensive type definitions for all utilities and plugins.
|
|
235
|
+
|
|
236
|
+
## License
|
|
237
|
+
|
|
238
|
+
ISC
|
package/dist/health.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import { HealthCheckConfig, ServerPlugin } from './types';
|
|
3
|
+
export declare function createHealthCheck(config?: HealthCheckConfig): express.RequestHandler;
|
|
4
|
+
export declare function withHealthCheck(path?: string, config?: HealthCheckConfig): ServerPlugin;
|
|
5
|
+
export declare function addHealthCheck(app: express.Application, path?: string, config?: HealthCheckConfig): void;
|
package/dist/health.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createHealthCheck = createHealthCheck;
|
|
4
|
+
exports.withHealthCheck = withHealthCheck;
|
|
5
|
+
exports.addHealthCheck = addHealthCheck;
|
|
6
|
+
function createHealthCheck(config = {}) {
|
|
7
|
+
const { customChecks = [] } = config;
|
|
8
|
+
return async (req, res) => {
|
|
9
|
+
try {
|
|
10
|
+
const checks = {
|
|
11
|
+
server: true,
|
|
12
|
+
timestamp: Date.now()
|
|
13
|
+
};
|
|
14
|
+
// Run custom health checks
|
|
15
|
+
for (const check of customChecks) {
|
|
16
|
+
try {
|
|
17
|
+
checks[check.name] = await check.check();
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
checks[check.name] = false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const isHealthy = Object.values(checks).every(status => status === true || typeof status === 'number');
|
|
24
|
+
res.status(isHealthy ? 200 : 503).json({
|
|
25
|
+
status: isHealthy ? 'healthy' : 'unhealthy',
|
|
26
|
+
checks
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
res.status(503).json({
|
|
31
|
+
status: 'unhealthy',
|
|
32
|
+
error: 'Health check failed'
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function withHealthCheck(path = '/health', config = {}) {
|
|
38
|
+
return (app) => {
|
|
39
|
+
app.get(path, createHealthCheck(config));
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
// Convenience function for direct use
|
|
43
|
+
function addHealthCheck(app, path = '/health', config = {}) {
|
|
44
|
+
app.get(path, createHealthCheck(config));
|
|
45
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { createServer, withPlugin, createServerWithPlugins } from './server';
|
|
2
|
+
export { createHealthCheck, withHealthCheck, addHealthCheck } from './health';
|
|
3
|
+
export { createGracefulShutdown, withGracefulShutdown, startServerWithShutdown } from './shutdown';
|
|
4
|
+
export { createLoggingMiddleware, createErrorHandler, createRequestIdMiddleware, createValidationMiddleware, createRateLimitMiddleware, createAuthMiddleware, withLogging, withErrorHandler, withRequestId, withValidation, withRateLimit, withAuth, validateFields, rateLimit, requireAuth, type ValidationRule, type RateLimitConfig, type AuthConfig } from './middleware';
|
|
5
|
+
export { getEnv, getEnvNumber, getEnvBoolean } from './utils';
|
|
6
|
+
export type { ServerConfig, HealthCheckConfig, HealthCheck, GracefulShutdownConfig, ServerPlugin } from './types';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getEnvBoolean = exports.getEnvNumber = exports.getEnv = exports.requireAuth = exports.rateLimit = exports.validateFields = exports.withAuth = exports.withRateLimit = exports.withValidation = exports.withRequestId = exports.withErrorHandler = exports.withLogging = exports.createAuthMiddleware = exports.createRateLimitMiddleware = exports.createValidationMiddleware = exports.createRequestIdMiddleware = exports.createErrorHandler = exports.createLoggingMiddleware = exports.startServerWithShutdown = exports.withGracefulShutdown = exports.createGracefulShutdown = exports.addHealthCheck = exports.withHealthCheck = exports.createHealthCheck = exports.createServerWithPlugins = exports.withPlugin = exports.createServer = void 0;
|
|
4
|
+
// Core server utilities
|
|
5
|
+
var server_1 = require("./server");
|
|
6
|
+
Object.defineProperty(exports, "createServer", { enumerable: true, get: function () { return server_1.createServer; } });
|
|
7
|
+
Object.defineProperty(exports, "withPlugin", { enumerable: true, get: function () { return server_1.withPlugin; } });
|
|
8
|
+
Object.defineProperty(exports, "createServerWithPlugins", { enumerable: true, get: function () { return server_1.createServerWithPlugins; } });
|
|
9
|
+
// Health check utilities
|
|
10
|
+
var health_1 = require("./health");
|
|
11
|
+
Object.defineProperty(exports, "createHealthCheck", { enumerable: true, get: function () { return health_1.createHealthCheck; } });
|
|
12
|
+
Object.defineProperty(exports, "withHealthCheck", { enumerable: true, get: function () { return health_1.withHealthCheck; } });
|
|
13
|
+
Object.defineProperty(exports, "addHealthCheck", { enumerable: true, get: function () { return health_1.addHealthCheck; } });
|
|
14
|
+
// Graceful shutdown utilities
|
|
15
|
+
var shutdown_1 = require("./shutdown");
|
|
16
|
+
Object.defineProperty(exports, "createGracefulShutdown", { enumerable: true, get: function () { return shutdown_1.createGracefulShutdown; } });
|
|
17
|
+
Object.defineProperty(exports, "withGracefulShutdown", { enumerable: true, get: function () { return shutdown_1.withGracefulShutdown; } });
|
|
18
|
+
Object.defineProperty(exports, "startServerWithShutdown", { enumerable: true, get: function () { return shutdown_1.startServerWithShutdown; } });
|
|
19
|
+
// Middleware utilities
|
|
20
|
+
var middleware_1 = require("./middleware");
|
|
21
|
+
Object.defineProperty(exports, "createLoggingMiddleware", { enumerable: true, get: function () { return middleware_1.createLoggingMiddleware; } });
|
|
22
|
+
Object.defineProperty(exports, "createErrorHandler", { enumerable: true, get: function () { return middleware_1.createErrorHandler; } });
|
|
23
|
+
Object.defineProperty(exports, "createRequestIdMiddleware", { enumerable: true, get: function () { return middleware_1.createRequestIdMiddleware; } });
|
|
24
|
+
Object.defineProperty(exports, "createValidationMiddleware", { enumerable: true, get: function () { return middleware_1.createValidationMiddleware; } });
|
|
25
|
+
Object.defineProperty(exports, "createRateLimitMiddleware", { enumerable: true, get: function () { return middleware_1.createRateLimitMiddleware; } });
|
|
26
|
+
Object.defineProperty(exports, "createAuthMiddleware", { enumerable: true, get: function () { return middleware_1.createAuthMiddleware; } });
|
|
27
|
+
Object.defineProperty(exports, "withLogging", { enumerable: true, get: function () { return middleware_1.withLogging; } });
|
|
28
|
+
Object.defineProperty(exports, "withErrorHandler", { enumerable: true, get: function () { return middleware_1.withErrorHandler; } });
|
|
29
|
+
Object.defineProperty(exports, "withRequestId", { enumerable: true, get: function () { return middleware_1.withRequestId; } });
|
|
30
|
+
Object.defineProperty(exports, "withValidation", { enumerable: true, get: function () { return middleware_1.withValidation; } });
|
|
31
|
+
Object.defineProperty(exports, "withRateLimit", { enumerable: true, get: function () { return middleware_1.withRateLimit; } });
|
|
32
|
+
Object.defineProperty(exports, "withAuth", { enumerable: true, get: function () { return middleware_1.withAuth; } });
|
|
33
|
+
Object.defineProperty(exports, "validateFields", { enumerable: true, get: function () { return middleware_1.validateFields; } });
|
|
34
|
+
Object.defineProperty(exports, "rateLimit", { enumerable: true, get: function () { return middleware_1.rateLimit; } });
|
|
35
|
+
Object.defineProperty(exports, "requireAuth", { enumerable: true, get: function () { return middleware_1.requireAuth; } });
|
|
36
|
+
// Utility functions
|
|
37
|
+
var utils_1 = require("./utils");
|
|
38
|
+
Object.defineProperty(exports, "getEnv", { enumerable: true, get: function () { return utils_1.getEnv; } });
|
|
39
|
+
Object.defineProperty(exports, "getEnvNumber", { enumerable: true, get: function () { return utils_1.getEnvNumber; } });
|
|
40
|
+
Object.defineProperty(exports, "getEnvBoolean", { enumerable: true, get: function () { return utils_1.getEnvBoolean; } });
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import { ServerPlugin } from './types';
|
|
3
|
+
export declare function createLoggingMiddleware(format?: 'simple' | 'detailed'): express.RequestHandler;
|
|
4
|
+
export declare function createErrorHandler(): express.ErrorRequestHandler;
|
|
5
|
+
export declare function createRequestIdMiddleware(): express.RequestHandler;
|
|
6
|
+
export interface ValidationRule {
|
|
7
|
+
field: string;
|
|
8
|
+
required?: boolean;
|
|
9
|
+
type?: 'string' | 'number' | 'email' | 'boolean';
|
|
10
|
+
minLength?: number;
|
|
11
|
+
maxLength?: number;
|
|
12
|
+
pattern?: RegExp;
|
|
13
|
+
custom?: (value: any) => boolean | string;
|
|
14
|
+
}
|
|
15
|
+
export declare function createValidationMiddleware(rules: ValidationRule[]): express.RequestHandler;
|
|
16
|
+
export interface RateLimitConfig {
|
|
17
|
+
windowMs?: number;
|
|
18
|
+
maxRequests?: number;
|
|
19
|
+
message?: string;
|
|
20
|
+
keyGenerator?: (req: express.Request) => string;
|
|
21
|
+
}
|
|
22
|
+
export declare function createRateLimitMiddleware(config?: RateLimitConfig): express.RequestHandler;
|
|
23
|
+
export interface AuthConfig {
|
|
24
|
+
tokenExtractor?: (req: express.Request) => string | null;
|
|
25
|
+
tokenValidator?: (token: string) => Promise<any> | any;
|
|
26
|
+
unauthorizedMessage?: string;
|
|
27
|
+
}
|
|
28
|
+
export declare function createAuthMiddleware(config: AuthConfig): express.RequestHandler;
|
|
29
|
+
export declare function withLogging(format?: 'simple' | 'detailed'): ServerPlugin;
|
|
30
|
+
export declare function withErrorHandler(): ServerPlugin;
|
|
31
|
+
export declare function withRequestId(): ServerPlugin;
|
|
32
|
+
export declare function withValidation(rules: ValidationRule[]): ServerPlugin;
|
|
33
|
+
export declare function withRateLimit(config?: RateLimitConfig): ServerPlugin;
|
|
34
|
+
export declare function withAuth(config: AuthConfig): ServerPlugin;
|
|
35
|
+
export declare function validateFields(rules: ValidationRule[]): express.RequestHandler;
|
|
36
|
+
export declare function rateLimit(config?: RateLimitConfig): express.RequestHandler;
|
|
37
|
+
export declare function requireAuth(config: AuthConfig): express.RequestHandler;
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createLoggingMiddleware = createLoggingMiddleware;
|
|
4
|
+
exports.createErrorHandler = createErrorHandler;
|
|
5
|
+
exports.createRequestIdMiddleware = createRequestIdMiddleware;
|
|
6
|
+
exports.createValidationMiddleware = createValidationMiddleware;
|
|
7
|
+
exports.createRateLimitMiddleware = createRateLimitMiddleware;
|
|
8
|
+
exports.createAuthMiddleware = createAuthMiddleware;
|
|
9
|
+
exports.withLogging = withLogging;
|
|
10
|
+
exports.withErrorHandler = withErrorHandler;
|
|
11
|
+
exports.withRequestId = withRequestId;
|
|
12
|
+
exports.withValidation = withValidation;
|
|
13
|
+
exports.withRateLimit = withRateLimit;
|
|
14
|
+
exports.withAuth = withAuth;
|
|
15
|
+
exports.validateFields = validateFields;
|
|
16
|
+
exports.rateLimit = rateLimit;
|
|
17
|
+
exports.requireAuth = requireAuth;
|
|
18
|
+
// Logging middleware
|
|
19
|
+
function createLoggingMiddleware(format = 'simple') {
|
|
20
|
+
return (req, res, next) => {
|
|
21
|
+
const start = Date.now();
|
|
22
|
+
res.on('finish', () => {
|
|
23
|
+
const duration = Date.now() - start;
|
|
24
|
+
if (format === 'detailed') {
|
|
25
|
+
console.log(`${req.method} ${req.url} - ${res.statusCode} - ${duration}ms - ${req.ip}`);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
console.log(`${req.method} ${req.url} - ${res.statusCode}`);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
next();
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
// Error handling middleware
|
|
35
|
+
function createErrorHandler() {
|
|
36
|
+
return (err, req, res, next) => {
|
|
37
|
+
console.error('Error:', err);
|
|
38
|
+
if (res.headersSent) {
|
|
39
|
+
return next(err);
|
|
40
|
+
}
|
|
41
|
+
const status = err.status || err.statusCode || 500;
|
|
42
|
+
const message = process.env.NODE_ENV === 'production'
|
|
43
|
+
? 'Internal Server Error'
|
|
44
|
+
: err.message;
|
|
45
|
+
res.status(status).json({
|
|
46
|
+
status: false,
|
|
47
|
+
message,
|
|
48
|
+
...(process.env.NODE_ENV !== 'production' && { stack: err.stack })
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
// Request ID middleware
|
|
53
|
+
function createRequestIdMiddleware() {
|
|
54
|
+
return (req, res, next) => {
|
|
55
|
+
const requestId = Math.random().toString(36).substring(2, 15);
|
|
56
|
+
req.requestId = requestId;
|
|
57
|
+
res.setHeader('X-Request-ID', requestId);
|
|
58
|
+
next();
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function createValidationMiddleware(rules) {
|
|
62
|
+
return (req, res, next) => {
|
|
63
|
+
const errors = [];
|
|
64
|
+
for (const rule of rules) {
|
|
65
|
+
const value = req.body[rule.field];
|
|
66
|
+
if (rule.required && (value === undefined || value === null || value === '')) {
|
|
67
|
+
errors.push(`${rule.field} is required`);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (value === undefined || value === null)
|
|
71
|
+
continue;
|
|
72
|
+
if (rule.type) {
|
|
73
|
+
switch (rule.type) {
|
|
74
|
+
case 'email':
|
|
75
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
76
|
+
if (!emailRegex.test(value)) {
|
|
77
|
+
errors.push(`${rule.field} must be a valid email`);
|
|
78
|
+
}
|
|
79
|
+
break;
|
|
80
|
+
case 'string':
|
|
81
|
+
if (typeof value !== 'string') {
|
|
82
|
+
errors.push(`${rule.field} must be a string`);
|
|
83
|
+
}
|
|
84
|
+
break;
|
|
85
|
+
case 'number':
|
|
86
|
+
if (typeof value !== 'number' && isNaN(Number(value))) {
|
|
87
|
+
errors.push(`${rule.field} must be a number`);
|
|
88
|
+
}
|
|
89
|
+
break;
|
|
90
|
+
case 'boolean':
|
|
91
|
+
if (typeof value !== 'boolean') {
|
|
92
|
+
errors.push(`${rule.field} must be a boolean`);
|
|
93
|
+
}
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (rule.minLength && value.length < rule.minLength) {
|
|
98
|
+
errors.push(`${rule.field} must be at least ${rule.minLength} characters`);
|
|
99
|
+
}
|
|
100
|
+
if (rule.maxLength && value.length > rule.maxLength) {
|
|
101
|
+
errors.push(`${rule.field} must be no more than ${rule.maxLength} characters`);
|
|
102
|
+
}
|
|
103
|
+
if (rule.pattern && !rule.pattern.test(value)) {
|
|
104
|
+
errors.push(`${rule.field} format is invalid`);
|
|
105
|
+
}
|
|
106
|
+
if (rule.custom) {
|
|
107
|
+
const result = rule.custom(value);
|
|
108
|
+
if (result !== true) {
|
|
109
|
+
errors.push(typeof result === 'string' ? result : `${rule.field} is invalid`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (errors.length > 0) {
|
|
114
|
+
return res.status(400).json({
|
|
115
|
+
status: false,
|
|
116
|
+
message: 'Validation failed',
|
|
117
|
+
errors
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
next();
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
const rateLimitStore = new Map();
|
|
124
|
+
function createRateLimitMiddleware(config = {}) {
|
|
125
|
+
const { windowMs = 15 * 60 * 1000, maxRequests = 100, message = 'Too many requests, please try again later', keyGenerator = (req) => req.ip || 'unknown' } = config;
|
|
126
|
+
return (req, res, next) => {
|
|
127
|
+
const key = keyGenerator(req);
|
|
128
|
+
const now = Date.now();
|
|
129
|
+
const record = rateLimitStore.get(key);
|
|
130
|
+
if (!record || now > record.resetTime) {
|
|
131
|
+
rateLimitStore.set(key, {
|
|
132
|
+
count: 1,
|
|
133
|
+
resetTime: now + windowMs
|
|
134
|
+
});
|
|
135
|
+
return next();
|
|
136
|
+
}
|
|
137
|
+
if (record.count >= maxRequests) {
|
|
138
|
+
return res.status(429).json({
|
|
139
|
+
status: false,
|
|
140
|
+
message,
|
|
141
|
+
retryAfter: Math.ceil((record.resetTime - now) / 1000)
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
record.count++;
|
|
145
|
+
next();
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
function createAuthMiddleware(config) {
|
|
149
|
+
const { tokenExtractor = (req) => {
|
|
150
|
+
const authHeader = req.headers.authorization;
|
|
151
|
+
if (authHeader && authHeader.startsWith('Bearer ')) {
|
|
152
|
+
return authHeader.substring(7);
|
|
153
|
+
}
|
|
154
|
+
return req.cookies?.token || null;
|
|
155
|
+
}, tokenValidator = () => { throw new Error('Token validator not implemented'); }, unauthorizedMessage = 'Unauthorized access' } = config;
|
|
156
|
+
return async (req, res, next) => {
|
|
157
|
+
try {
|
|
158
|
+
const token = tokenExtractor(req);
|
|
159
|
+
if (!token) {
|
|
160
|
+
return res.status(401).json({
|
|
161
|
+
status: false,
|
|
162
|
+
message: unauthorizedMessage
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
const user = await tokenValidator(token);
|
|
166
|
+
req.user = user;
|
|
167
|
+
next();
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
return res.status(401).json({
|
|
171
|
+
status: false,
|
|
172
|
+
message: unauthorizedMessage
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
// Plugin versions
|
|
178
|
+
function withLogging(format = 'simple') {
|
|
179
|
+
return (app) => {
|
|
180
|
+
app.use(createLoggingMiddleware(format));
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
function withErrorHandler() {
|
|
184
|
+
return (app) => {
|
|
185
|
+
app.use(createErrorHandler());
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
function withRequestId() {
|
|
189
|
+
return (app) => {
|
|
190
|
+
app.use(createRequestIdMiddleware());
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
function withValidation(rules) {
|
|
194
|
+
return (app) => {
|
|
195
|
+
app.use(createValidationMiddleware(rules));
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
function withRateLimit(config = {}) {
|
|
199
|
+
return (app) => {
|
|
200
|
+
app.use(createRateLimitMiddleware(config));
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
function withAuth(config) {
|
|
204
|
+
return (app) => {
|
|
205
|
+
app.use(createAuthMiddleware(config));
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
// Convenience functions for route-specific middleware
|
|
209
|
+
function validateFields(rules) {
|
|
210
|
+
return createValidationMiddleware(rules);
|
|
211
|
+
}
|
|
212
|
+
function rateLimit(config = {}) {
|
|
213
|
+
return createRateLimitMiddleware(config);
|
|
214
|
+
}
|
|
215
|
+
function requireAuth(config) {
|
|
216
|
+
return createAuthMiddleware(config);
|
|
217
|
+
}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import { ServerConfig, ServerPlugin } from './types';
|
|
3
|
+
export declare function createServer(config?: ServerConfig): express.Application;
|
|
4
|
+
export declare function withPlugin(app: express.Application, plugin: ServerPlugin, config?: ServerConfig): express.Application;
|
|
5
|
+
export declare function createServerWithPlugins(config?: ServerConfig, ...plugins: ServerPlugin[]): express.Application;
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createServer = createServer;
|
|
7
|
+
exports.withPlugin = withPlugin;
|
|
8
|
+
exports.createServerWithPlugins = createServerWithPlugins;
|
|
9
|
+
const express_1 = __importDefault(require("express"));
|
|
10
|
+
const cors_1 = __importDefault(require("cors"));
|
|
11
|
+
const helmet_1 = __importDefault(require("helmet"));
|
|
12
|
+
function createServer(config = {}) {
|
|
13
|
+
const app = (0, express_1.default)();
|
|
14
|
+
// Apply default middleware
|
|
15
|
+
if (config.helmet !== false) {
|
|
16
|
+
app.use((0, helmet_1.default)());
|
|
17
|
+
}
|
|
18
|
+
if (config.cors !== false) {
|
|
19
|
+
const corsOptions = typeof config.cors === 'object' ? config.cors : undefined;
|
|
20
|
+
app.use((0, cors_1.default)(corsOptions));
|
|
21
|
+
}
|
|
22
|
+
if (config.json !== false) {
|
|
23
|
+
app.use(express_1.default.json());
|
|
24
|
+
}
|
|
25
|
+
// Apply custom middleware
|
|
26
|
+
if (config.customMiddleware) {
|
|
27
|
+
config.customMiddleware.forEach(middleware => {
|
|
28
|
+
app.use(middleware);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
return app;
|
|
32
|
+
}
|
|
33
|
+
function withPlugin(app, plugin, config = {}) {
|
|
34
|
+
plugin(app, config);
|
|
35
|
+
return app;
|
|
36
|
+
}
|
|
37
|
+
function createServerWithPlugins(config = {}, ...plugins) {
|
|
38
|
+
const app = createServer(config);
|
|
39
|
+
plugins.forEach(plugin => {
|
|
40
|
+
plugin(app, config);
|
|
41
|
+
});
|
|
42
|
+
return app;
|
|
43
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Server } from 'http';
|
|
2
|
+
import { GracefulShutdownConfig, ServerPlugin } from './types';
|
|
3
|
+
export declare function createGracefulShutdown(server: Server, config?: GracefulShutdownConfig): void;
|
|
4
|
+
export declare function withGracefulShutdown(config?: GracefulShutdownConfig): ServerPlugin;
|
|
5
|
+
export declare function startServerWithShutdown(app: any, port: number, shutdownConfig?: GracefulShutdownConfig): Server;
|