@naman_deep_singh/server-utils 1.3.0 → 1.4.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 +19 -5
- package/dist/cjs/core/health.d.ts +5 -0
- package/dist/cjs/{health.js → core/health.js} +4 -4
- package/dist/cjs/{periodic-health.d.ts → core/periodic-health.d.ts} +1 -1
- package/dist/cjs/{periodic-health.js → core/periodic-health.js} +4 -4
- package/dist/{types → cjs/core}/server.d.ts +8 -7
- package/dist/cjs/{server.js → core/server.js} +70 -25
- package/dist/cjs/{shutdown.d.ts → core/shutdown.d.ts} +2 -2
- package/dist/cjs/{shutdown.js → core/shutdown.js} +2 -1
- package/dist/cjs/index.d.ts +14 -14
- package/dist/cjs/index.js +10 -10
- package/dist/cjs/middleware/auth.middleware.d.ts +7 -0
- package/dist/cjs/middleware/auth.middleware.js +41 -0
- package/dist/cjs/middleware/cache.middleware.d.ts +2 -0
- package/dist/cjs/middleware/cache.middleware.js +58 -0
- package/dist/cjs/middleware/errorHandler.middleware.d.ts +2 -0
- package/dist/cjs/middleware/errorHandler.middleware.js +30 -0
- package/dist/cjs/middleware/index.d.ts +10 -0
- package/dist/cjs/middleware/index.js +40 -0
- package/dist/cjs/middleware/logging.middleware.d.ts +2 -0
- package/dist/cjs/middleware/logging.middleware.js +19 -0
- package/dist/cjs/middleware/plugins.middleware.d.ts +14 -0
- package/dist/cjs/middleware/plugins.middleware.js +58 -0
- package/dist/cjs/middleware/rateLimiter.middleware.d.ts +8 -0
- package/dist/cjs/middleware/rateLimiter.middleware.js +35 -0
- package/dist/cjs/middleware/requestId.middleware.d.ts +2 -0
- package/dist/cjs/middleware/requestId.middleware.js +12 -0
- package/dist/cjs/middleware/session.middleware.d.ts +2 -0
- package/dist/cjs/middleware/session.middleware.js +53 -0
- package/dist/cjs/middleware/validation.middleware.d.ts +11 -0
- package/dist/cjs/middleware/validation.middleware.js +67 -0
- package/dist/{esm/types.d.ts → cjs/types/index.d.ts} +1 -1
- package/dist/cjs/{utils.js → utils/utils.js} +2 -2
- package/dist/esm/core/health.d.ts +5 -0
- package/dist/esm/{health.js → core/health.js} +4 -4
- package/dist/esm/{periodic-health.d.ts → core/periodic-health.d.ts} +1 -1
- package/dist/esm/{periodic-health.js → core/periodic-health.js} +4 -4
- package/dist/esm/{server.d.ts → core/server.d.ts} +8 -7
- package/dist/esm/{server.js → core/server.js} +36 -24
- package/dist/esm/{shutdown.d.ts → core/shutdown.d.ts} +2 -2
- package/dist/esm/{shutdown.js → core/shutdown.js} +2 -1
- package/dist/esm/index.d.ts +14 -14
- package/dist/esm/index.js +12 -12
- package/dist/esm/middleware/auth.middleware.d.ts +7 -0
- package/dist/esm/middleware/auth.middleware.js +38 -0
- package/dist/esm/middleware/cache.middleware.d.ts +2 -0
- package/dist/esm/middleware/cache.middleware.js +55 -0
- package/dist/esm/middleware/errorHandler.middleware.d.ts +2 -0
- package/dist/esm/middleware/errorHandler.middleware.js +27 -0
- package/dist/esm/middleware/index.d.ts +10 -0
- package/dist/esm/middleware/index.js +20 -0
- package/dist/esm/middleware/logging.middleware.d.ts +2 -0
- package/dist/esm/middleware/logging.middleware.js +16 -0
- package/dist/esm/middleware/plugins.middleware.d.ts +14 -0
- package/dist/esm/middleware/plugins.middleware.js +47 -0
- package/dist/esm/middleware/rateLimiter.middleware.d.ts +8 -0
- package/dist/esm/middleware/rateLimiter.middleware.js +32 -0
- package/dist/esm/middleware/requestId.middleware.d.ts +2 -0
- package/dist/esm/middleware/requestId.middleware.js +9 -0
- package/dist/esm/middleware/session.middleware.d.ts +2 -0
- package/dist/esm/middleware/session.middleware.js +50 -0
- package/dist/esm/middleware/validation.middleware.d.ts +11 -0
- package/dist/esm/middleware/validation.middleware.js +64 -0
- package/dist/{types/types.d.ts → esm/types/index.d.ts} +1 -1
- package/dist/esm/{utils.js → utils/utils.js} +2 -2
- package/dist/types/core/health.d.ts +5 -0
- package/dist/types/{periodic-health.d.ts → core/periodic-health.d.ts} +1 -1
- package/dist/{cjs → types/core}/server.d.ts +8 -7
- package/dist/types/{shutdown.d.ts → core/shutdown.d.ts} +2 -2
- package/dist/types/index.d.ts +14 -14
- package/dist/types/middleware/auth.middleware.d.ts +7 -0
- package/dist/types/middleware/cache.middleware.d.ts +2 -0
- package/dist/types/middleware/errorHandler.middleware.d.ts +2 -0
- package/dist/types/middleware/index.d.ts +10 -0
- package/dist/types/middleware/logging.middleware.d.ts +2 -0
- package/dist/types/middleware/plugins.middleware.d.ts +14 -0
- package/dist/types/middleware/rateLimiter.middleware.d.ts +8 -0
- package/dist/types/middleware/requestId.middleware.d.ts +2 -0
- package/dist/types/middleware/session.middleware.d.ts +2 -0
- package/dist/types/middleware/validation.middleware.d.ts +11 -0
- package/dist/{cjs/types.d.ts → types/types/index.d.ts} +1 -1
- package/package.json +4 -2
- package/dist/cjs/health.d.ts +0 -5
- package/dist/cjs/middleware.d.ts +0 -39
- package/dist/cjs/middleware.js +0 -348
- package/dist/esm/health.d.ts +0 -5
- package/dist/esm/middleware.d.ts +0 -39
- package/dist/esm/middleware.js +0 -329
- package/dist/types/health.d.ts +0 -5
- package/dist/types/middleware.d.ts +0 -39
- /package/dist/cjs/{types.js → types/index.js} +0 -0
- /package/dist/cjs/{utils.d.ts → utils/utils.d.ts} +0 -0
- /package/dist/esm/{types.js → types/index.js} +0 -0
- /package/dist/esm/{utils.d.ts → utils/utils.d.ts} +0 -0
- /package/dist/types/{utils.d.ts → utils/utils.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @naman_deep_singh/server-utils
|
|
2
2
|
|
|
3
|
-
**Version:** 1.
|
|
3
|
+
**Version:** 1.4.1 (with integrated cache & session support)
|
|
4
4
|
|
|
5
5
|
Extensible server utilities for Express.js microservices with multi-protocol support, integrated caching, session management, and TypeScript.
|
|
6
6
|
|
|
@@ -158,15 +158,29 @@ server.app.use('/api', rateLimit({
|
|
|
158
158
|
}));
|
|
159
159
|
```
|
|
160
160
|
|
|
161
|
+
|
|
161
162
|
### Authentication Middleware
|
|
162
163
|
```typescript
|
|
163
164
|
import { requireAuth } from '@naman_deep_singh/server-utils';
|
|
164
165
|
|
|
165
166
|
server.app.use('/protected', requireAuth({
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
167
|
+
secret: process.env.JWT_SECRET!,
|
|
168
|
+
unauthorizedMessage: 'Invalid or expired token'
|
|
169
|
+
}));
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**Advanced Configuration:**
|
|
173
|
+
```typescript
|
|
174
|
+
import { createAuthMiddleware } from '@naman_deep_singh/server-utils';
|
|
175
|
+
|
|
176
|
+
server.app.use('/api', createAuthMiddleware({
|
|
177
|
+
secret: process.env.JWT_SECRET!,
|
|
178
|
+
unauthorizedMessage: 'Access denied',
|
|
179
|
+
tokenExtractor: (req) => {
|
|
180
|
+
// Custom token extraction logic
|
|
181
|
+
// By default, uses sophisticated extractToken from @naman_deep_singh/security
|
|
182
|
+
// that checks Authorization headers, cookies, query params, and body
|
|
183
|
+
return req.headers.authorization?.substring(7);
|
|
170
184
|
}
|
|
171
185
|
}));
|
|
172
186
|
```
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Application, RequestHandler } from 'express';
|
|
2
|
+
import type { HealthCheckConfig, ServerPlugin } from '../types';
|
|
3
|
+
export declare function createHealthCheck(config?: HealthCheckConfig): RequestHandler;
|
|
4
|
+
export declare function withHealthCheck(path?: string, config?: HealthCheckConfig): ServerPlugin;
|
|
5
|
+
export declare function addHealthCheck(app: Application, path?: string, config?: HealthCheckConfig): void;
|
|
@@ -9,7 +9,7 @@ function createHealthCheck(config = {}) {
|
|
|
9
9
|
try {
|
|
10
10
|
const checks = {
|
|
11
11
|
server: true,
|
|
12
|
-
timestamp: Date.now()
|
|
12
|
+
timestamp: Date.now(),
|
|
13
13
|
};
|
|
14
14
|
// Run custom health checks
|
|
15
15
|
for (const check of customChecks) {
|
|
@@ -20,16 +20,16 @@ function createHealthCheck(config = {}) {
|
|
|
20
20
|
checks[check.name] = false;
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
|
-
const isHealthy = Object.values(checks).every(status => status === true || typeof status === 'number');
|
|
23
|
+
const isHealthy = Object.values(checks).every((status) => status === true || typeof status === 'number');
|
|
24
24
|
res.status(isHealthy ? 200 : 503).json({
|
|
25
25
|
status: isHealthy ? 'healthy' : 'unhealthy',
|
|
26
|
-
checks
|
|
26
|
+
checks,
|
|
27
27
|
});
|
|
28
28
|
}
|
|
29
29
|
catch (error) {
|
|
30
30
|
res.status(503).json({
|
|
31
31
|
status: 'unhealthy',
|
|
32
|
-
error: 'Health check failed'
|
|
32
|
+
error: 'Health check failed',
|
|
33
33
|
});
|
|
34
34
|
}
|
|
35
35
|
};
|
|
@@ -12,7 +12,7 @@ class PeriodicHealthMonitor {
|
|
|
12
12
|
return;
|
|
13
13
|
}
|
|
14
14
|
const interval = this.config.interval || 30000;
|
|
15
|
-
this.config.services.forEach(service => {
|
|
15
|
+
this.config.services.forEach((service) => {
|
|
16
16
|
const intervalId = setInterval(async () => {
|
|
17
17
|
await this.checkServiceHealth(service);
|
|
18
18
|
}, interval);
|
|
@@ -21,7 +21,7 @@ class PeriodicHealthMonitor {
|
|
|
21
21
|
console.log(`📊 ${this.serviceName}: Periodic health monitoring enabled (${interval}ms interval) for ${this.config.services.length} service(s)`);
|
|
22
22
|
}
|
|
23
23
|
stop() {
|
|
24
|
-
this.intervals.forEach(interval => clearInterval(interval));
|
|
24
|
+
this.intervals.forEach((interval) => clearInterval(interval));
|
|
25
25
|
this.intervals = [];
|
|
26
26
|
console.log(`🛑 ${this.serviceName}: Periodic health monitoring stopped`);
|
|
27
27
|
}
|
|
@@ -34,8 +34,8 @@ class PeriodicHealthMonitor {
|
|
|
34
34
|
method: 'GET',
|
|
35
35
|
signal: controller.signal,
|
|
36
36
|
headers: {
|
|
37
|
-
'User-Agent': `${this.serviceName}-health-monitor
|
|
38
|
-
}
|
|
37
|
+
'User-Agent': `${this.serviceName}-health-monitor`,
|
|
38
|
+
},
|
|
39
39
|
});
|
|
40
40
|
clearTimeout(timeoutId);
|
|
41
41
|
if (response.ok) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { Server } from 'http';
|
|
3
|
-
import {
|
|
1
|
+
import { ICache, SessionStore } from '@naman_deep_singh/cache';
|
|
2
|
+
import type { Server } from 'http';
|
|
3
|
+
import type { Application } from 'express';
|
|
4
|
+
import type { ServerConfig, SocketIOConfig } from '../types';
|
|
4
5
|
export interface GrpcService {
|
|
5
6
|
service: Record<string, unknown>;
|
|
6
7
|
implementation: Record<string, (...args: unknown[]) => unknown>;
|
|
@@ -26,7 +27,7 @@ export interface ServerInstanceConfig extends Required<Omit<ServerConfig, 'socke
|
|
|
26
27
|
socketIO?: SocketIOConfig;
|
|
27
28
|
}
|
|
28
29
|
export interface ServerInstance {
|
|
29
|
-
app:
|
|
30
|
+
app: Application;
|
|
30
31
|
server?: Server;
|
|
31
32
|
config: ServerInstanceConfig;
|
|
32
33
|
start(): Promise<ServerInstance>;
|
|
@@ -46,11 +47,11 @@ export interface ServerInfo {
|
|
|
46
47
|
startTime: Date;
|
|
47
48
|
}
|
|
48
49
|
export declare class ExpressServer implements ServerInstance {
|
|
49
|
-
app:
|
|
50
|
+
app: Application;
|
|
50
51
|
server?: Server;
|
|
51
52
|
config: ServerInstanceConfig;
|
|
52
|
-
cache?:
|
|
53
|
-
sessionStore?:
|
|
53
|
+
cache?: ICache<unknown>;
|
|
54
|
+
sessionStore?: SessionStore | undefined;
|
|
54
55
|
private status;
|
|
55
56
|
private grpcServices;
|
|
56
57
|
private grpcServer?;
|
|
@@ -1,15 +1,49 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
5
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
39
|
exports.ExpressServer = void 0;
|
|
7
40
|
exports.createServer = createServer;
|
|
8
|
-
const express_1 = __importDefault(require("express"));
|
|
9
|
-
const shutdown_1 = require("./shutdown");
|
|
10
|
-
const periodic_health_1 = require("./periodic-health");
|
|
11
41
|
const crypto_1 = __importDefault(require("crypto"));
|
|
12
42
|
const cache_1 = require("@naman_deep_singh/cache");
|
|
43
|
+
const express_1 = __importStar(require("express"));
|
|
44
|
+
const periodic_health_1 = require("./periodic-health");
|
|
45
|
+
const shutdown_1 = require("./shutdown");
|
|
46
|
+
const middleware_1 = require("../middleware");
|
|
13
47
|
class ExpressServer {
|
|
14
48
|
constructor(name = 'Express Server', version = '1.0.0', config = {}) {
|
|
15
49
|
this.status = 'stopped';
|
|
@@ -31,7 +65,7 @@ class ExpressServer {
|
|
|
31
65
|
socketIO: config.socketIO,
|
|
32
66
|
periodicHealthCheck: config.periodicHealthCheck || { enabled: false },
|
|
33
67
|
cache: config.cache || { enabled: false },
|
|
34
|
-
session: config.session || { enabled: false }
|
|
68
|
+
session: config.session || { enabled: false },
|
|
35
69
|
};
|
|
36
70
|
// Initialize locals for cache/session
|
|
37
71
|
this.app.locals.cache = undefined;
|
|
@@ -66,7 +100,7 @@ class ExpressServer {
|
|
|
66
100
|
}
|
|
67
101
|
// Apply JSON parser if enabled
|
|
68
102
|
if (this.config.json) {
|
|
69
|
-
this.app.use(express_1.
|
|
103
|
+
this.app.use((0, express_1.json)());
|
|
70
104
|
}
|
|
71
105
|
// Apply cookie parser if enabled
|
|
72
106
|
if (this.config.cookieParser) {
|
|
@@ -79,21 +113,24 @@ class ExpressServer {
|
|
|
79
113
|
}
|
|
80
114
|
}
|
|
81
115
|
// Apply custom middleware
|
|
82
|
-
if (this.config.customMiddleware &&
|
|
83
|
-
this.config.customMiddleware.
|
|
116
|
+
if (this.config.customMiddleware &&
|
|
117
|
+
this.config.customMiddleware.length > 0) {
|
|
118
|
+
this.config.customMiddleware.forEach((middleware) => {
|
|
84
119
|
this.app.use(middleware);
|
|
85
120
|
});
|
|
86
121
|
}
|
|
87
122
|
// Add health check if enabled
|
|
88
123
|
if (this.config.healthCheck) {
|
|
89
|
-
const healthPath = typeof this.config.healthCheck === 'string'
|
|
124
|
+
const healthPath = typeof this.config.healthCheck === 'string'
|
|
125
|
+
? this.config.healthCheck
|
|
126
|
+
: '/health';
|
|
90
127
|
this.app.get(healthPath, async (req, res) => {
|
|
91
128
|
const base = {
|
|
92
129
|
status: 'healthy',
|
|
93
130
|
service: this.config.name,
|
|
94
131
|
version: this.config.version,
|
|
95
132
|
uptime: Date.now() - this.config.startTime.getTime(),
|
|
96
|
-
timestamp: new Date().toISOString()
|
|
133
|
+
timestamp: new Date().toISOString(),
|
|
97
134
|
};
|
|
98
135
|
// If cache is enabled, include its health
|
|
99
136
|
const cache = req.app.locals.cache;
|
|
@@ -102,7 +139,12 @@ class ExpressServer {
|
|
|
102
139
|
base.cache = await cache.isAlive();
|
|
103
140
|
}
|
|
104
141
|
catch (e) {
|
|
105
|
-
base.cache = {
|
|
142
|
+
base.cache = {
|
|
143
|
+
isAlive: false,
|
|
144
|
+
adapter: 'unknown',
|
|
145
|
+
timestamp: new Date(),
|
|
146
|
+
error: e.message,
|
|
147
|
+
};
|
|
106
148
|
}
|
|
107
149
|
}
|
|
108
150
|
res.status(200).json(base);
|
|
@@ -121,7 +163,11 @@ class ExpressServer {
|
|
|
121
163
|
}
|
|
122
164
|
console.log(`🔄 [${serverName}] Initializing cache adapter: ${config.cache.adapter || 'memory'}...`);
|
|
123
165
|
// Use createWithFallback to prefer primary and fall back to memory when configured
|
|
124
|
-
const cfg = {
|
|
166
|
+
const cfg = {
|
|
167
|
+
...(cacheConfig || {}),
|
|
168
|
+
ttl: cacheConfig?.ttl ??
|
|
169
|
+
config.cache?.defaultTTL,
|
|
170
|
+
};
|
|
125
171
|
const cache = await cache_1.CacheFactory.createWithFallback(cfg);
|
|
126
172
|
this.app.locals.cache = cache;
|
|
127
173
|
this.cache = cache;
|
|
@@ -131,7 +177,7 @@ class ExpressServer {
|
|
|
131
177
|
req.cache = cache;
|
|
132
178
|
next();
|
|
133
179
|
});
|
|
134
|
-
console.log(`✅ [${serverName}] Cache initialized successfully (adapter: ${
|
|
180
|
+
console.log(`✅ [${serverName}] Cache initialized successfully (adapter: ${cacheConfig.adapter || 'memory'})`);
|
|
135
181
|
}
|
|
136
182
|
catch (err) {
|
|
137
183
|
console.error(`❌ [${serverName}] Failed to initialize cache (fallback to memory if enabled):`, err instanceof Error ? err.message : err);
|
|
@@ -140,7 +186,8 @@ class ExpressServer {
|
|
|
140
186
|
}
|
|
141
187
|
// Initialize session if enabled
|
|
142
188
|
if (config.session && config.session.enabled) {
|
|
143
|
-
const cookieName = config.session.cookieName ||
|
|
189
|
+
const cookieName = config.session.cookieName ||
|
|
190
|
+
`${serverName.replace(/\s+/g, '_').toLowerCase()}.sid`;
|
|
144
191
|
const ttl = config.session.ttl ?? 3600;
|
|
145
192
|
let cache = this.app.locals.cache;
|
|
146
193
|
if (!cache) {
|
|
@@ -168,9 +215,7 @@ class ExpressServer {
|
|
|
168
215
|
this.sessionStore = store;
|
|
169
216
|
// attach session middleware globally so req.sessionStore is available
|
|
170
217
|
try {
|
|
171
|
-
|
|
172
|
-
const { useSession } = require('./middleware');
|
|
173
|
-
this.app.use(useSession(cookieName));
|
|
218
|
+
this.app.use((0, middleware_1.useSession)(cookieName));
|
|
174
219
|
console.log(`✅ [${serverName}] Session middleware enabled (cookie: ${cookieName}, TTL: ${ttl}s)`);
|
|
175
220
|
}
|
|
176
221
|
catch (err) {
|
|
@@ -224,7 +269,7 @@ class ExpressServer {
|
|
|
224
269
|
catch (e) {
|
|
225
270
|
// SessionStore may not have close; ignore
|
|
226
271
|
}
|
|
227
|
-
}
|
|
272
|
+
},
|
|
228
273
|
});
|
|
229
274
|
}
|
|
230
275
|
// Start periodic health monitoring after server is running
|
|
@@ -274,7 +319,7 @@ class ExpressServer {
|
|
|
274
319
|
port: this.config.port,
|
|
275
320
|
uptime: Date.now() - this.config.startTime.getTime(),
|
|
276
321
|
status: this.status,
|
|
277
|
-
startTime: this.config.startTime
|
|
322
|
+
startTime: this.config.startTime,
|
|
278
323
|
};
|
|
279
324
|
}
|
|
280
325
|
addGrpcService(service, implementation, port = 50051) {
|
|
@@ -282,7 +327,6 @@ class ExpressServer {
|
|
|
282
327
|
// Lazy load gRPC to avoid dependency issues
|
|
283
328
|
if (!this.grpcServer) {
|
|
284
329
|
try {
|
|
285
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
286
330
|
const grpc = require('@grpc/grpc-js');
|
|
287
331
|
this.grpcServer = new grpc.Server();
|
|
288
332
|
// Add all services
|
|
@@ -302,7 +346,6 @@ class ExpressServer {
|
|
|
302
346
|
addRpcMethods(methods, path = '/rpc') {
|
|
303
347
|
Object.assign(this.rpcMethods, methods);
|
|
304
348
|
try {
|
|
305
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
306
349
|
const jayson = require('jayson');
|
|
307
350
|
const rpcServer = jayson.server(this.rpcMethods);
|
|
308
351
|
this.app.use(path, rpcServer.middleware());
|
|
@@ -313,17 +356,20 @@ class ExpressServer {
|
|
|
313
356
|
}
|
|
314
357
|
}
|
|
315
358
|
addWebhook(config) {
|
|
316
|
-
this.app.post(config.path, express_1.
|
|
359
|
+
this.app.post(config.path, (0, express_1.raw)({ type: 'application/json' }), async (req, res) => {
|
|
317
360
|
try {
|
|
318
361
|
// Verify signature if secret provided
|
|
319
362
|
if (config.secret) {
|
|
320
|
-
const signature = req.headers['x-hub-signature-256'] ||
|
|
363
|
+
const signature = req.headers['x-hub-signature-256'] ||
|
|
364
|
+
req.headers['x-signature-256'];
|
|
321
365
|
if (signature) {
|
|
322
366
|
const expectedSignature = crypto_1.default
|
|
323
367
|
.createHmac('sha256', config.secret)
|
|
324
368
|
.update(req.body)
|
|
325
369
|
.digest('hex');
|
|
326
|
-
const providedSignature = Array.isArray(signature)
|
|
370
|
+
const providedSignature = Array.isArray(signature)
|
|
371
|
+
? signature[0]
|
|
372
|
+
: signature;
|
|
327
373
|
if (!providedSignature.includes(expectedSignature)) {
|
|
328
374
|
return res.status(401).json({ error: 'Invalid signature' });
|
|
329
375
|
}
|
|
@@ -347,7 +393,6 @@ class ExpressServer {
|
|
|
347
393
|
throw new Error(`${this.config.name}: Server must be started before adding Socket.IO`);
|
|
348
394
|
}
|
|
349
395
|
try {
|
|
350
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
351
396
|
const { Server } = require('socket.io');
|
|
352
397
|
// Configure CORS
|
|
353
398
|
const corsConfig = config.cors === true
|
|
@@ -356,7 +401,7 @@ class ExpressServer {
|
|
|
356
401
|
// Create Socket.IO server
|
|
357
402
|
const io = new Server(this.server, {
|
|
358
403
|
cors: config.cors ? corsConfig : undefined,
|
|
359
|
-
path: config.path || '/socket.io'
|
|
404
|
+
path: config.path || '/socket.io',
|
|
360
405
|
});
|
|
361
406
|
// Store reference for cleanup
|
|
362
407
|
this.socketIO = io;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Server } from 'http';
|
|
2
|
-
import { GracefulShutdownConfig, ServerPlugin } from '
|
|
1
|
+
import type { Server } from 'http';
|
|
2
|
+
import type { GracefulShutdownConfig, ServerPlugin } from '../types';
|
|
3
3
|
export declare function createGracefulShutdown(server: Server, config?: GracefulShutdownConfig): void;
|
|
4
4
|
export declare function withGracefulShutdown(config?: GracefulShutdownConfig): ServerPlugin;
|
|
5
5
|
export declare function startServerWithShutdown(app: import('express').Application, port: number, shutdownConfig?: GracefulShutdownConfig, serverName?: string, serverVersion?: string): Server;
|
|
@@ -37,6 +37,7 @@ function withGracefulShutdown(config = {}) {
|
|
|
37
37
|
return (app, serverConfig) => {
|
|
38
38
|
// This plugin needs to be applied after server.listen()
|
|
39
39
|
// Store config for later use
|
|
40
|
+
;
|
|
40
41
|
app.__gracefulShutdownConfig = config;
|
|
41
42
|
};
|
|
42
43
|
}
|
|
@@ -50,7 +51,7 @@ function startServerWithShutdown(app, port, shutdownConfig = {}, serverName, ser
|
|
|
50
51
|
const enhancedConfig = {
|
|
51
52
|
...config,
|
|
52
53
|
serverName,
|
|
53
|
-
serverVersion
|
|
54
|
+
serverVersion,
|
|
54
55
|
};
|
|
55
56
|
createGracefulShutdown(server, enhancedConfig);
|
|
56
57
|
return server;
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
export { ExpressServer, createServer } from './server';
|
|
2
|
-
export type { ServerInstance, ServerInfo, GrpcService, RpcMethod, WebhookConfig } from './server';
|
|
1
|
+
export { ExpressServer, createServer } from './core/server';
|
|
2
|
+
export type { ServerInstance, ServerInfo, GrpcService, RpcMethod, WebhookConfig, } from './core/server';
|
|
3
3
|
export { Request, Response, NextFunction, Router, Application } from 'express';
|
|
4
4
|
export type { RequestHandler, ErrorRequestHandler } from 'express';
|
|
5
|
-
export { createHealthCheck, withHealthCheck, addHealthCheck } from './health';
|
|
6
|
-
export { createGracefulShutdown, withGracefulShutdown, startServerWithShutdown } from './shutdown';
|
|
7
|
-
export { createLoggingMiddleware, createErrorHandler, createRequestIdMiddleware, createValidationMiddleware, createRateLimitMiddleware, createAuthMiddleware, withLogging, withErrorHandler, withRequestId, withValidation, withRateLimit, withAuth, validateFields, rateLimit, requireAuth, cacheResponse, useSession, type ValidationRule, type RateLimitConfig, type AuthConfig } from './middleware';
|
|
8
|
-
export { getEnv, getEnvNumber, getEnvBoolean } from './utils';
|
|
9
|
-
export { PeriodicHealthMonitor } from './periodic-health';
|
|
10
|
-
export type { ServerConfig, HealthCheckConfig, HealthCheck, GracefulShutdownConfig, ServerPlugin, SocketIOConfig, SocketInstance, PeriodicHealthCheckConfig, HealthCheckService } from './types';
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
5
|
+
export { createHealthCheck, withHealthCheck, addHealthCheck, } from './core/health';
|
|
6
|
+
export { createGracefulShutdown, withGracefulShutdown, startServerWithShutdown, } from './core/shutdown';
|
|
7
|
+
export { createLoggingMiddleware, createErrorHandler, createRequestIdMiddleware, createValidationMiddleware, createRateLimitMiddleware, createAuthMiddleware, withLogging, withErrorHandler, withRequestId, withValidation, withRateLimit, withAuth, validateFields, rateLimit, requireAuth, cacheResponse, useSession, type ValidationRule, type RateLimitConfig, type AuthConfig, } from './middleware';
|
|
8
|
+
export { getEnv, getEnvNumber, getEnvBoolean, } from './utils/utils';
|
|
9
|
+
export { PeriodicHealthMonitor } from './core/periodic-health';
|
|
10
|
+
export type { ServerConfig, HealthCheckConfig, HealthCheck, GracefulShutdownConfig, ServerPlugin, SocketIOConfig, SocketInstance, PeriodicHealthCheckConfig, HealthCheckService, } from './types';
|
|
11
|
+
import { addHealthCheck, createHealthCheck, withHealthCheck } from './core/health';
|
|
12
|
+
import { PeriodicHealthMonitor } from './core/periodic-health';
|
|
13
|
+
import { ExpressServer, createServer } from './core/server';
|
|
14
|
+
import { createGracefulShutdown, startServerWithShutdown, withGracefulShutdown } from './core/shutdown';
|
|
15
|
+
import { createAuthMiddleware, createErrorHandler, createLoggingMiddleware, createRateLimitMiddleware, createRequestIdMiddleware, createValidationMiddleware, rateLimit, requireAuth, validateFields, withAuth, withErrorHandler, withLogging, withRateLimit, withRequestId, withValidation } from './middleware';
|
|
16
|
+
import { getEnv, getEnvBoolean, getEnvNumber } from './utils/utils';
|
|
17
17
|
declare const ServerUtils: {
|
|
18
18
|
createServer: typeof createServer;
|
|
19
19
|
ExpressServer: typeof ExpressServer;
|
package/dist/cjs/index.js
CHANGED
|
@@ -2,19 +2,19 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PeriodicHealthMonitor = exports.getEnvBoolean = exports.getEnvNumber = exports.getEnv = exports.useSession = exports.cacheResponse = 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.Router = exports.createServer = exports.ExpressServer = void 0;
|
|
4
4
|
// Core server utilities
|
|
5
|
-
var server_1 = require("./server");
|
|
5
|
+
var server_1 = require("./core/server");
|
|
6
6
|
Object.defineProperty(exports, "ExpressServer", { enumerable: true, get: function () { return server_1.ExpressServer; } });
|
|
7
7
|
Object.defineProperty(exports, "createServer", { enumerable: true, get: function () { return server_1.createServer; } });
|
|
8
8
|
// Express re-exports (to avoid direct Express dependency in services)
|
|
9
9
|
var express_1 = require("express");
|
|
10
10
|
Object.defineProperty(exports, "Router", { enumerable: true, get: function () { return express_1.Router; } });
|
|
11
11
|
// Health check utilities
|
|
12
|
-
var health_1 = require("./health");
|
|
12
|
+
var health_1 = require("./core/health");
|
|
13
13
|
Object.defineProperty(exports, "createHealthCheck", { enumerable: true, get: function () { return health_1.createHealthCheck; } });
|
|
14
14
|
Object.defineProperty(exports, "withHealthCheck", { enumerable: true, get: function () { return health_1.withHealthCheck; } });
|
|
15
15
|
Object.defineProperty(exports, "addHealthCheck", { enumerable: true, get: function () { return health_1.addHealthCheck; } });
|
|
16
16
|
// Graceful shutdown utilities
|
|
17
|
-
var shutdown_1 = require("./shutdown");
|
|
17
|
+
var shutdown_1 = require("./core/shutdown");
|
|
18
18
|
Object.defineProperty(exports, "createGracefulShutdown", { enumerable: true, get: function () { return shutdown_1.createGracefulShutdown; } });
|
|
19
19
|
Object.defineProperty(exports, "withGracefulShutdown", { enumerable: true, get: function () { return shutdown_1.withGracefulShutdown; } });
|
|
20
20
|
Object.defineProperty(exports, "startServerWithShutdown", { enumerable: true, get: function () { return shutdown_1.startServerWithShutdown; } });
|
|
@@ -38,20 +38,20 @@ Object.defineProperty(exports, "requireAuth", { enumerable: true, get: function
|
|
|
38
38
|
Object.defineProperty(exports, "cacheResponse", { enumerable: true, get: function () { return middleware_1.cacheResponse; } });
|
|
39
39
|
Object.defineProperty(exports, "useSession", { enumerable: true, get: function () { return middleware_1.useSession; } });
|
|
40
40
|
// Utility functions
|
|
41
|
-
var utils_1 = require("./utils");
|
|
41
|
+
var utils_1 = require("./utils/utils");
|
|
42
42
|
Object.defineProperty(exports, "getEnv", { enumerable: true, get: function () { return utils_1.getEnv; } });
|
|
43
43
|
Object.defineProperty(exports, "getEnvNumber", { enumerable: true, get: function () { return utils_1.getEnvNumber; } });
|
|
44
44
|
Object.defineProperty(exports, "getEnvBoolean", { enumerable: true, get: function () { return utils_1.getEnvBoolean; } });
|
|
45
45
|
// Periodic health monitoring
|
|
46
|
-
var periodic_health_1 = require("./periodic-health");
|
|
46
|
+
var periodic_health_1 = require("./core/periodic-health");
|
|
47
47
|
Object.defineProperty(exports, "PeriodicHealthMonitor", { enumerable: true, get: function () { return periodic_health_1.PeriodicHealthMonitor; } });
|
|
48
|
+
const health_2 = require("./core/health");
|
|
49
|
+
const periodic_health_2 = require("./core/periodic-health");
|
|
48
50
|
// Import all exports for default export
|
|
49
|
-
const server_2 = require("./server");
|
|
50
|
-
const
|
|
51
|
-
const shutdown_2 = require("./shutdown");
|
|
51
|
+
const server_2 = require("./core/server");
|
|
52
|
+
const shutdown_2 = require("./core/shutdown");
|
|
52
53
|
const middleware_2 = require("./middleware");
|
|
53
|
-
const utils_2 = require("./utils");
|
|
54
|
-
const periodic_health_2 = require("./periodic-health");
|
|
54
|
+
const utils_2 = require("./utils/utils");
|
|
55
55
|
// Default export for namespace usage
|
|
56
56
|
const ServerUtils = {
|
|
57
57
|
// Server creation
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Request, RequestHandler } from 'node_modules/@types/express';
|
|
2
|
+
export interface AuthConfig {
|
|
3
|
+
secret: string;
|
|
4
|
+
unauthorizedMessage?: string;
|
|
5
|
+
tokenExtractor?: (req: Request) => string | null;
|
|
6
|
+
}
|
|
7
|
+
export declare function createAuthMiddleware(config: AuthConfig): RequestHandler;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createAuthMiddleware = createAuthMiddleware;
|
|
4
|
+
const errors_utils_1 = require("@naman_deep_singh/errors-utils");
|
|
5
|
+
const security_1 = require("@naman_deep_singh/security");
|
|
6
|
+
function createAuthMiddleware(config) {
|
|
7
|
+
const { secret, unauthorizedMessage = 'Unauthorized access', tokenExtractor = (req) => (0, security_1.extractToken)({
|
|
8
|
+
header: req.headers.authorization || undefined,
|
|
9
|
+
cookies: req.cookies,
|
|
10
|
+
query: req.query,
|
|
11
|
+
body: req.body,
|
|
12
|
+
}), } = config;
|
|
13
|
+
return async (req, res, next) => {
|
|
14
|
+
try {
|
|
15
|
+
// Extract token from request
|
|
16
|
+
const token = tokenExtractor(req);
|
|
17
|
+
if (!token) {
|
|
18
|
+
const error = new errors_utils_1.UnauthorizedError(unauthorizedMessage, {
|
|
19
|
+
reason: 'No token provided',
|
|
20
|
+
});
|
|
21
|
+
return next(error);
|
|
22
|
+
}
|
|
23
|
+
// Use safe verify token from security package
|
|
24
|
+
const result = (0, security_1.safeVerifyToken)(token, secret);
|
|
25
|
+
if (!result.valid) {
|
|
26
|
+
const error = new errors_utils_1.UnauthorizedError(unauthorizedMessage, {
|
|
27
|
+
reason: 'Invalid or expired token',
|
|
28
|
+
originalError: result.error?.message,
|
|
29
|
+
});
|
|
30
|
+
return next(error);
|
|
31
|
+
}
|
|
32
|
+
// Attach the verified payload as user
|
|
33
|
+
req.user = result.payload;
|
|
34
|
+
next();
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
const unauthorizedError = new errors_utils_1.UnauthorizedError(unauthorizedMessage, error instanceof Error ? { originalError: error.message } : error);
|
|
38
|
+
return next(unauthorizedError);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.cacheResponse = cacheResponse;
|
|
4
|
+
// Cache response middleware (per-route opt-in)
|
|
5
|
+
function cacheResponse(ttl) {
|
|
6
|
+
return async (req, res, next) => {
|
|
7
|
+
try {
|
|
8
|
+
if (req.method !== 'GET')
|
|
9
|
+
return next();
|
|
10
|
+
const cache = (req.cache ?? req.app.locals.cache);
|
|
11
|
+
const defaultTTL = req.app.locals.cacheDefaultTTL;
|
|
12
|
+
if (!cache || typeof cache.get !== 'function')
|
|
13
|
+
return next();
|
|
14
|
+
const key = `${req.originalUrl}`;
|
|
15
|
+
try {
|
|
16
|
+
const cached = await cache.get(key);
|
|
17
|
+
if (cached !== null && cached !== undefined) {
|
|
18
|
+
res.setHeader('X-Cache', 'HIT');
|
|
19
|
+
return res.json(cached);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
catch (cacheErr) {
|
|
23
|
+
console.error(`[Cache] Failed to retrieve key "${key}":`, cacheErr);
|
|
24
|
+
// Continue without cache hit
|
|
25
|
+
}
|
|
26
|
+
const originalJson = res.json.bind(res);
|
|
27
|
+
res.json = (body) => {
|
|
28
|
+
try {
|
|
29
|
+
const expiry = ttl ?? defaultTTL;
|
|
30
|
+
if (expiry && cache && typeof cache.set === 'function') {
|
|
31
|
+
;
|
|
32
|
+
cache.set(key, body, expiry).catch((err) => {
|
|
33
|
+
console.error(`[Cache] Failed to set key "${key}" with TTL ${expiry}:`, err);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
else if (cache) {
|
|
37
|
+
if (typeof cache.set === 'function') {
|
|
38
|
+
;
|
|
39
|
+
cache.set(key, body).catch((err) => {
|
|
40
|
+
console.error(`[Cache] Failed to set key "${key}":`, err);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
console.error(`[Cache] Error during cache.set operation:`, e);
|
|
47
|
+
}
|
|
48
|
+
res.setHeader('X-Cache', 'MISS');
|
|
49
|
+
return originalJson(body);
|
|
50
|
+
};
|
|
51
|
+
next();
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
console.error('[Cache] Unexpected error in cacheResponse middleware:', err);
|
|
55
|
+
next();
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createErrorHandler = createErrorHandler;
|
|
4
|
+
// Error handling middleware
|
|
5
|
+
function createErrorHandler() {
|
|
6
|
+
return (err, req, res, next) => {
|
|
7
|
+
console.error('Error:', err);
|
|
8
|
+
if (res.headersSent) {
|
|
9
|
+
return next(err);
|
|
10
|
+
}
|
|
11
|
+
// Type guard for error objects
|
|
12
|
+
const errorObj = err;
|
|
13
|
+
const status = errorObj.status || errorObj.statusCode || 500;
|
|
14
|
+
const message = process.env.NODE_ENV === 'production'
|
|
15
|
+
? 'Internal Server Error'
|
|
16
|
+
: errorObj.message || 'Unknown error';
|
|
17
|
+
res.status(status).json({
|
|
18
|
+
success: false,
|
|
19
|
+
message,
|
|
20
|
+
data: undefined,
|
|
21
|
+
error: {
|
|
22
|
+
message,
|
|
23
|
+
...(process.env.NODE_ENV !== 'production' && {
|
|
24
|
+
details: { stack: errorObj.stack },
|
|
25
|
+
}),
|
|
26
|
+
},
|
|
27
|
+
meta: null,
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
}
|