@naman_deep_singh/server-utils 1.2.0 → 1.4.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 +35 -62
- package/dist/cjs/core/health.d.ts +5 -0
- package/dist/cjs/{periodic-health.d.ts → core/periodic-health.d.ts} +1 -1
- package/dist/{types → cjs/core}/server.d.ts +6 -4
- package/dist/cjs/{server.js → core/server.js} +43 -12
- package/dist/cjs/core/shutdown.d.ts +5 -0
- package/dist/cjs/index.d.ts +11 -11
- 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 +56 -0
- package/dist/cjs/middleware/errorHandler.middleware.d.ts +2 -0
- package/dist/cjs/middleware/errorHandler.middleware.js +28 -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 +50 -0
- package/dist/cjs/middleware/validation.middleware.d.ts +11 -0
- package/dist/cjs/middleware/validation.middleware.js +66 -0
- package/dist/esm/core/health.d.ts +5 -0
- package/dist/esm/{periodic-health.d.ts → core/periodic-health.d.ts} +1 -1
- package/dist/esm/{server.d.ts → core/server.d.ts} +6 -4
- package/dist/esm/{server.js → core/server.js} +10 -12
- package/dist/esm/core/shutdown.d.ts +5 -0
- package/dist/esm/index.d.ts +11 -11
- package/dist/esm/index.js +10 -10
- 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 +53 -0
- package/dist/esm/middleware/errorHandler.middleware.d.ts +2 -0
- package/dist/esm/middleware/errorHandler.middleware.js +25 -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 +47 -0
- package/dist/esm/middleware/validation.middleware.d.ts +11 -0
- package/dist/esm/middleware/validation.middleware.js +63 -0
- 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 +6 -4
- package/dist/types/core/shutdown.d.ts +5 -0
- package/dist/types/index.d.ts +11 -11
- 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/package.json +3 -1
- package/dist/cjs/health.d.ts +0 -5
- package/dist/cjs/middleware.d.ts +0 -39
- package/dist/cjs/middleware.js +0 -346
- package/dist/cjs/shutdown.d.ts +0 -5
- package/dist/esm/health.d.ts +0 -5
- package/dist/esm/middleware.d.ts +0 -39
- package/dist/esm/middleware.js +0 -327
- package/dist/esm/shutdown.d.ts +0 -5
- package/dist/types/health.d.ts +0 -5
- package/dist/types/middleware.d.ts +0 -39
- package/dist/types/shutdown.d.ts +0 -5
- /package/dist/cjs/{health.js → core/health.js} +0 -0
- /package/dist/cjs/{periodic-health.js → core/periodic-health.js} +0 -0
- /package/dist/cjs/{shutdown.js → core/shutdown.js} +0 -0
- /package/dist/cjs/{types.d.ts → types/index.d.ts} +0 -0
- /package/dist/cjs/{types.js → types/index.js} +0 -0
- /package/dist/cjs/{utils.d.ts → utils/utils.d.ts} +0 -0
- /package/dist/cjs/{utils.js → utils/utils.js} +0 -0
- /package/dist/esm/{health.js → core/health.js} +0 -0
- /package/dist/esm/{periodic-health.js → core/periodic-health.js} +0 -0
- /package/dist/esm/{shutdown.js → core/shutdown.js} +0 -0
- /package/dist/esm/{types.d.ts → types/index.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/esm/{utils.js → utils/utils.js} +0 -0
- /package/dist/types/{types.d.ts → types/index.d.ts} +0 -0
- /package/dist/types/{utils.d.ts → utils/utils.d.ts} +0 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.withLogging = withLogging;
|
|
4
|
+
exports.withErrorHandler = withErrorHandler;
|
|
5
|
+
exports.withRequestId = withRequestId;
|
|
6
|
+
exports.withValidation = withValidation;
|
|
7
|
+
exports.withRateLimit = withRateLimit;
|
|
8
|
+
exports.withAuth = withAuth;
|
|
9
|
+
exports.validateFields = validateFields;
|
|
10
|
+
exports.rateLimit = rateLimit;
|
|
11
|
+
exports.requireAuth = requireAuth;
|
|
12
|
+
const logging_middleware_1 = require("./logging.middleware");
|
|
13
|
+
const errorHandler_middleware_1 = require("./errorHandler.middleware");
|
|
14
|
+
const requestId_middleware_1 = require("./requestId.middleware");
|
|
15
|
+
const validation_middleware_1 = require("./validation.middleware");
|
|
16
|
+
const rateLimiter_middleware_1 = require("./rateLimiter.middleware");
|
|
17
|
+
const auth_middleware_1 = require("./auth.middleware");
|
|
18
|
+
// Plugin versions
|
|
19
|
+
function withLogging(format = 'simple') {
|
|
20
|
+
return (app) => {
|
|
21
|
+
app.use((0, logging_middleware_1.createLoggingMiddleware)(format));
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function withErrorHandler() {
|
|
25
|
+
return (app) => {
|
|
26
|
+
app.use((0, errorHandler_middleware_1.createErrorHandler)());
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function withRequestId() {
|
|
30
|
+
return (app) => {
|
|
31
|
+
app.use((0, requestId_middleware_1.createRequestIdMiddleware)());
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function withValidation(rules) {
|
|
35
|
+
return (app) => {
|
|
36
|
+
app.use((0, validation_middleware_1.createValidationMiddleware)(rules));
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function withRateLimit(config = {}) {
|
|
40
|
+
return (app) => {
|
|
41
|
+
app.use((0, rateLimiter_middleware_1.createRateLimitMiddleware)(config));
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function withAuth(config) {
|
|
45
|
+
return (app) => {
|
|
46
|
+
app.use((0, auth_middleware_1.createAuthMiddleware)(config));
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
// Convenience functions for route-specific middleware
|
|
50
|
+
function validateFields(rules) {
|
|
51
|
+
return (0, validation_middleware_1.createValidationMiddleware)(rules);
|
|
52
|
+
}
|
|
53
|
+
function rateLimit(config = {}) {
|
|
54
|
+
return (0, rateLimiter_middleware_1.createRateLimitMiddleware)(config);
|
|
55
|
+
}
|
|
56
|
+
function requireAuth(config) {
|
|
57
|
+
return (0, auth_middleware_1.createAuthMiddleware)(config);
|
|
58
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Request, RequestHandler } from "node_modules/@types/express";
|
|
2
|
+
export interface RateLimitConfig {
|
|
3
|
+
windowMs?: number;
|
|
4
|
+
maxRequests?: number;
|
|
5
|
+
message?: string;
|
|
6
|
+
keyGenerator?: (req: Request) => string;
|
|
7
|
+
}
|
|
8
|
+
export declare function createRateLimitMiddleware(config?: RateLimitConfig): RequestHandler;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createRateLimitMiddleware = createRateLimitMiddleware;
|
|
4
|
+
const rateLimitStore = new Map();
|
|
5
|
+
function createRateLimitMiddleware(config = {}) {
|
|
6
|
+
const { windowMs = 15 * 60 * 1000, maxRequests = 100, message = 'Too many requests, please try again later', keyGenerator = (req) => req.ip || 'unknown' } = config;
|
|
7
|
+
return (req, res, next) => {
|
|
8
|
+
const key = keyGenerator(req);
|
|
9
|
+
const now = Date.now();
|
|
10
|
+
const record = rateLimitStore.get(key);
|
|
11
|
+
if (!record || now > record.resetTime) {
|
|
12
|
+
rateLimitStore.set(key, {
|
|
13
|
+
count: 1,
|
|
14
|
+
resetTime: now + windowMs
|
|
15
|
+
});
|
|
16
|
+
return next();
|
|
17
|
+
}
|
|
18
|
+
if (record.count >= maxRequests) {
|
|
19
|
+
return res.status(429).json({
|
|
20
|
+
success: false,
|
|
21
|
+
message,
|
|
22
|
+
data: undefined,
|
|
23
|
+
error: {
|
|
24
|
+
message,
|
|
25
|
+
details: {
|
|
26
|
+
retryAfter: Math.ceil((record.resetTime - now) / 1000)
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
meta: null
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
record.count++;
|
|
33
|
+
next();
|
|
34
|
+
};
|
|
35
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createRequestIdMiddleware = createRequestIdMiddleware;
|
|
4
|
+
// Request ID middleware
|
|
5
|
+
function createRequestIdMiddleware() {
|
|
6
|
+
return (req, res, next) => {
|
|
7
|
+
const requestId = Math.random().toString(36).substring(2, 15);
|
|
8
|
+
req.requestId = requestId;
|
|
9
|
+
res.setHeader('X-Request-ID', requestId);
|
|
10
|
+
next();
|
|
11
|
+
};
|
|
12
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useSession = useSession;
|
|
4
|
+
// Session middleware helper (attaches sessionStore and helpers to req)
|
|
5
|
+
function useSession(cookieName) {
|
|
6
|
+
return async (req, res, next) => {
|
|
7
|
+
try {
|
|
8
|
+
const store = req.app.locals.sessionStore;
|
|
9
|
+
if (!store)
|
|
10
|
+
return next();
|
|
11
|
+
const name = cookieName || req.app.locals.sessionCookieName || 'sid';
|
|
12
|
+
let sid = req.cookies?.[name];
|
|
13
|
+
if (!sid) {
|
|
14
|
+
const cookieHeader = req.headers.cookie;
|
|
15
|
+
if (cookieHeader) {
|
|
16
|
+
const match = cookieHeader.split(';').map(s => s.trim()).find(s => s.startsWith(name + '='));
|
|
17
|
+
if (match)
|
|
18
|
+
sid = match.split('=')[1];
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
req.sessionId = sid;
|
|
22
|
+
req.sessionStore = store;
|
|
23
|
+
req.getSession = async () => {
|
|
24
|
+
if (!sid)
|
|
25
|
+
return null;
|
|
26
|
+
try {
|
|
27
|
+
return await store.get(sid);
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
console.error(`[Session] Failed to get session "${sid}":`, err);
|
|
31
|
+
throw err;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
req.createSession = async (id, data, ttl) => {
|
|
35
|
+
try {
|
|
36
|
+
return await store.create(id, data, ttl);
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
console.error(`[Session] Failed to create session "${id}":`, err);
|
|
40
|
+
throw err;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
next();
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
console.error('[Session] Unexpected error in useSession middleware:', err);
|
|
47
|
+
next();
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { RequestHandler } from "node_modules/@types/express";
|
|
2
|
+
export interface ValidationRule {
|
|
3
|
+
field: string;
|
|
4
|
+
required?: boolean;
|
|
5
|
+
type?: 'string' | 'number' | 'email' | 'boolean';
|
|
6
|
+
minLength?: number;
|
|
7
|
+
maxLength?: number;
|
|
8
|
+
pattern?: RegExp;
|
|
9
|
+
custom?: (value: unknown) => boolean | string;
|
|
10
|
+
}
|
|
11
|
+
export declare function createValidationMiddleware(rules: ValidationRule[]): RequestHandler;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createValidationMiddleware = createValidationMiddleware;
|
|
4
|
+
const errors_utils_1 = require("@naman_deep_singh/errors-utils");
|
|
5
|
+
function createValidationMiddleware(rules) {
|
|
6
|
+
return (req, res, next) => {
|
|
7
|
+
const errors = [];
|
|
8
|
+
for (const rule of rules) {
|
|
9
|
+
const value = req.body[rule.field];
|
|
10
|
+
if (rule.required && (value === undefined || value === null || value === '')) {
|
|
11
|
+
errors.push(`${rule.field} is required`);
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
if (value === undefined || value === null)
|
|
15
|
+
continue;
|
|
16
|
+
if (rule.type) {
|
|
17
|
+
switch (rule.type) {
|
|
18
|
+
case 'email':
|
|
19
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
20
|
+
if (!emailRegex.test(value)) {
|
|
21
|
+
errors.push(`${rule.field} must be a valid email`);
|
|
22
|
+
}
|
|
23
|
+
break;
|
|
24
|
+
case 'string':
|
|
25
|
+
if (typeof value !== 'string') {
|
|
26
|
+
errors.push(`${rule.field} must be a string`);
|
|
27
|
+
}
|
|
28
|
+
break;
|
|
29
|
+
case 'number':
|
|
30
|
+
if (typeof value !== 'number' && isNaN(Number(value))) {
|
|
31
|
+
errors.push(`${rule.field} must be a number`);
|
|
32
|
+
}
|
|
33
|
+
break;
|
|
34
|
+
case 'boolean':
|
|
35
|
+
if (typeof value !== 'boolean') {
|
|
36
|
+
errors.push(`${rule.field} must be a boolean`);
|
|
37
|
+
}
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (rule.minLength && value.length < rule.minLength) {
|
|
42
|
+
errors.push(`${rule.field} must be at least ${rule.minLength} characters`);
|
|
43
|
+
}
|
|
44
|
+
if (rule.maxLength && value.length > rule.maxLength) {
|
|
45
|
+
errors.push(`${rule.field} must be no more than ${rule.maxLength} characters`);
|
|
46
|
+
}
|
|
47
|
+
if (rule.pattern && !rule.pattern.test(value)) {
|
|
48
|
+
errors.push(`${rule.field} format is invalid`);
|
|
49
|
+
}
|
|
50
|
+
if (rule.custom) {
|
|
51
|
+
const result = rule.custom(value);
|
|
52
|
+
if (result !== true) {
|
|
53
|
+
errors.push(typeof result === 'string' ? result : `${rule.field} is invalid`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (errors.length > 0) {
|
|
58
|
+
// Use ValidationError from errors-utils and let error middleware handle response
|
|
59
|
+
const validationError = new errors_utils_1.ValidationError('Validation failed', {
|
|
60
|
+
validationErrors: errors
|
|
61
|
+
});
|
|
62
|
+
return next(validationError);
|
|
63
|
+
}
|
|
64
|
+
next();
|
|
65
|
+
};
|
|
66
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Application, RequestHandler } from 'express';
|
|
2
|
+
import { 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;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import express from 'express';
|
|
2
1
|
import { Server } from 'http';
|
|
3
|
-
import {
|
|
2
|
+
import { Application } from 'express';
|
|
3
|
+
import { ServerConfig, SocketIOConfig } from '../types';
|
|
4
4
|
export interface GrpcService {
|
|
5
5
|
service: Record<string, unknown>;
|
|
6
6
|
implementation: Record<string, (...args: unknown[]) => unknown>;
|
|
@@ -26,7 +26,7 @@ export interface ServerInstanceConfig extends Required<Omit<ServerConfig, 'socke
|
|
|
26
26
|
socketIO?: SocketIOConfig;
|
|
27
27
|
}
|
|
28
28
|
export interface ServerInstance {
|
|
29
|
-
app:
|
|
29
|
+
app: Application;
|
|
30
30
|
server?: Server;
|
|
31
31
|
config: ServerInstanceConfig;
|
|
32
32
|
start(): Promise<ServerInstance>;
|
|
@@ -46,9 +46,11 @@ export interface ServerInfo {
|
|
|
46
46
|
startTime: Date;
|
|
47
47
|
}
|
|
48
48
|
export declare class ExpressServer implements ServerInstance {
|
|
49
|
-
app:
|
|
49
|
+
app: Application;
|
|
50
50
|
server?: Server;
|
|
51
51
|
config: ServerInstanceConfig;
|
|
52
|
+
cache?: import('@naman_deep_singh/cache').ICache<unknown>;
|
|
53
|
+
sessionStore?: import('@naman_deep_singh/cache').SessionStore | undefined;
|
|
52
54
|
private status;
|
|
53
55
|
private grpcServices;
|
|
54
56
|
private grpcServer?;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import express from 'express';
|
|
2
|
-
import { createGracefulShutdown } from './shutdown';
|
|
3
|
-
import { PeriodicHealthMonitor } from './periodic-health';
|
|
4
|
-
import crypto from 'crypto';
|
|
1
|
+
import express, { json, raw } from 'express';
|
|
5
2
|
import { CacheFactory, SessionStore } from '@naman_deep_singh/cache';
|
|
3
|
+
import crypto from 'crypto';
|
|
4
|
+
import { PeriodicHealthMonitor } from './periodic-health';
|
|
5
|
+
import { createGracefulShutdown } from './shutdown';
|
|
6
6
|
export class ExpressServer {
|
|
7
7
|
constructor(name = 'Express Server', version = '1.0.0', config = {}) {
|
|
8
8
|
this.status = 'stopped';
|
|
@@ -59,7 +59,7 @@ export class ExpressServer {
|
|
|
59
59
|
}
|
|
60
60
|
// Apply JSON parser if enabled
|
|
61
61
|
if (this.config.json) {
|
|
62
|
-
this.app.use(
|
|
62
|
+
this.app.use(json());
|
|
63
63
|
}
|
|
64
64
|
// Apply cookie parser if enabled
|
|
65
65
|
if (this.config.cookieParser) {
|
|
@@ -107,17 +107,15 @@ export class ExpressServer {
|
|
|
107
107
|
// Initialize cache if enabled
|
|
108
108
|
if (config.cache && config.cache.enabled) {
|
|
109
109
|
try {
|
|
110
|
-
const provided = config.cache
|
|
110
|
+
const provided = config.cache?.options;
|
|
111
111
|
let cacheConfig = provided && typeof provided === 'object' ? provided : undefined;
|
|
112
112
|
if (!cacheConfig) {
|
|
113
113
|
cacheConfig = { adapter: config.cache.adapter || 'memory' };
|
|
114
114
|
}
|
|
115
115
|
console.log(`🔄 [${serverName}] Initializing cache adapter: ${config.cache.adapter || 'memory'}...`);
|
|
116
116
|
// Use createWithFallback to prefer primary and fall back to memory when configured
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
ttl: cacheConfig?.ttl ?? config.cache?.defaultTTL
|
|
120
|
-
});
|
|
117
|
+
const cfg = { ...(cacheConfig || {}), ttl: cacheConfig?.ttl ?? config.cache?.defaultTTL };
|
|
118
|
+
const cache = await CacheFactory.createWithFallback(cfg);
|
|
121
119
|
this.app.locals.cache = cache;
|
|
122
120
|
this.cache = cache;
|
|
123
121
|
this.app.locals.cacheDefaultTTL = config.cache?.defaultTTL;
|
|
@@ -164,7 +162,7 @@ export class ExpressServer {
|
|
|
164
162
|
// attach session middleware globally so req.sessionStore is available
|
|
165
163
|
try {
|
|
166
164
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
167
|
-
const { useSession } = require('
|
|
165
|
+
const { useSession } = require('../middleware/middleware');
|
|
168
166
|
this.app.use(useSession(cookieName));
|
|
169
167
|
console.log(`✅ [${serverName}] Session middleware enabled (cookie: ${cookieName}, TTL: ${ttl}s)`);
|
|
170
168
|
}
|
|
@@ -308,7 +306,7 @@ export class ExpressServer {
|
|
|
308
306
|
}
|
|
309
307
|
}
|
|
310
308
|
addWebhook(config) {
|
|
311
|
-
this.app.post(config.path,
|
|
309
|
+
this.app.post(config.path, raw({ type: 'application/json' }), async (req, res) => {
|
|
312
310
|
try {
|
|
313
311
|
// Verify signature if secret provided
|
|
314
312
|
if (config.secret) {
|
|
@@ -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: import('express').Application, port: number, shutdownConfig?: GracefulShutdownConfig, serverName?: string, serverVersion?: string): Server;
|
package/dist/esm/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';
|
|
5
|
+
export { createHealthCheck, withHealthCheck, addHealthCheck } from './core/health';
|
|
6
|
+
export { createGracefulShutdown, withGracefulShutdown, startServerWithShutdown } from './core/shutdown';
|
|
7
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';
|
|
8
|
+
export { getEnv, getEnvNumber, getEnvBoolean } from './utils/utils';
|
|
9
|
+
export { PeriodicHealthMonitor } from './core/periodic-health';
|
|
10
10
|
export type { ServerConfig, HealthCheckConfig, HealthCheck, GracefulShutdownConfig, ServerPlugin, SocketIOConfig, SocketInstance, PeriodicHealthCheckConfig, HealthCheckService } from './types';
|
|
11
|
-
import { ExpressServer, createServer } from './server';
|
|
12
|
-
import { createHealthCheck, withHealthCheck, addHealthCheck } from './health';
|
|
13
|
-
import { createGracefulShutdown, withGracefulShutdown, startServerWithShutdown } from './shutdown';
|
|
11
|
+
import { ExpressServer, createServer } from './core/server';
|
|
12
|
+
import { createHealthCheck, withHealthCheck, addHealthCheck } from './core/health';
|
|
13
|
+
import { createGracefulShutdown, withGracefulShutdown, startServerWithShutdown } from './core/shutdown';
|
|
14
14
|
import { createLoggingMiddleware, createErrorHandler, createRequestIdMiddleware, createValidationMiddleware, createRateLimitMiddleware, createAuthMiddleware, withLogging, withErrorHandler, withRequestId, withValidation, withRateLimit, withAuth, validateFields, rateLimit, requireAuth } from './middleware';
|
|
15
|
-
import { getEnv, getEnvNumber, getEnvBoolean } from './utils';
|
|
16
|
-
import { PeriodicHealthMonitor } from './periodic-health';
|
|
15
|
+
import { getEnv, getEnvNumber, getEnvBoolean } from './utils/utils';
|
|
16
|
+
import { PeriodicHealthMonitor } from './core/periodic-health';
|
|
17
17
|
declare const ServerUtils: {
|
|
18
18
|
createServer: typeof createServer;
|
|
19
19
|
ExpressServer: typeof ExpressServer;
|
package/dist/esm/index.js
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
// Core server utilities
|
|
2
|
-
export { ExpressServer, createServer } from './server';
|
|
2
|
+
export { ExpressServer, createServer } from './core/server';
|
|
3
3
|
// Express re-exports (to avoid direct Express dependency in services)
|
|
4
4
|
export { Router } from 'express';
|
|
5
5
|
// Health check utilities
|
|
6
|
-
export { createHealthCheck, withHealthCheck, addHealthCheck } from './health';
|
|
6
|
+
export { createHealthCheck, withHealthCheck, addHealthCheck } from './core/health';
|
|
7
7
|
// Graceful shutdown utilities
|
|
8
|
-
export { createGracefulShutdown, withGracefulShutdown, startServerWithShutdown } from './shutdown';
|
|
8
|
+
export { createGracefulShutdown, withGracefulShutdown, startServerWithShutdown } from './core/shutdown';
|
|
9
9
|
// Middleware utilities
|
|
10
10
|
export { createLoggingMiddleware, createErrorHandler, createRequestIdMiddleware, createValidationMiddleware, createRateLimitMiddleware, createAuthMiddleware, withLogging, withErrorHandler, withRequestId, withValidation, withRateLimit, withAuth, validateFields, rateLimit, requireAuth, cacheResponse, useSession } from './middleware';
|
|
11
11
|
// Utility functions
|
|
12
|
-
export { getEnv, getEnvNumber, getEnvBoolean } from './utils';
|
|
12
|
+
export { getEnv, getEnvNumber, getEnvBoolean } from './utils/utils';
|
|
13
13
|
// Periodic health monitoring
|
|
14
|
-
export { PeriodicHealthMonitor } from './periodic-health';
|
|
14
|
+
export { PeriodicHealthMonitor } from './core/periodic-health';
|
|
15
15
|
// Import all exports for default export
|
|
16
|
-
import { ExpressServer, createServer } from './server';
|
|
17
|
-
import { createHealthCheck, withHealthCheck, addHealthCheck } from './health';
|
|
18
|
-
import { createGracefulShutdown, withGracefulShutdown, startServerWithShutdown } from './shutdown';
|
|
16
|
+
import { ExpressServer, createServer } from './core/server';
|
|
17
|
+
import { createHealthCheck, withHealthCheck, addHealthCheck } from './core/health';
|
|
18
|
+
import { createGracefulShutdown, withGracefulShutdown, startServerWithShutdown } from './core/shutdown';
|
|
19
19
|
import { createLoggingMiddleware, createErrorHandler, createRequestIdMiddleware, createValidationMiddleware, createRateLimitMiddleware, createAuthMiddleware, withLogging, withErrorHandler, withRequestId, withValidation, withRateLimit, withAuth, validateFields, rateLimit, requireAuth } from './middleware';
|
|
20
|
-
import { getEnv, getEnvNumber, getEnvBoolean } from './utils';
|
|
21
|
-
import { PeriodicHealthMonitor } from './periodic-health';
|
|
20
|
+
import { getEnv, getEnvNumber, getEnvBoolean } from './utils/utils';
|
|
21
|
+
import { PeriodicHealthMonitor } from './core/periodic-health';
|
|
22
22
|
// Default export for namespace usage
|
|
23
23
|
const ServerUtils = {
|
|
24
24
|
// Server creation
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { 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,38 @@
|
|
|
1
|
+
import { extractToken, safeVerifyToken } from "@naman_deep_singh/security";
|
|
2
|
+
import { UnauthorizedError } from "@naman_deep_singh/errors-utils";
|
|
3
|
+
export function createAuthMiddleware(config) {
|
|
4
|
+
const { secret, unauthorizedMessage = 'Unauthorized access', tokenExtractor = (req) => extractToken({
|
|
5
|
+
header: req.headers.authorization || undefined,
|
|
6
|
+
cookies: req.cookies,
|
|
7
|
+
query: req.query,
|
|
8
|
+
body: req.body
|
|
9
|
+
}) } = config;
|
|
10
|
+
return async (req, res, next) => {
|
|
11
|
+
try {
|
|
12
|
+
// Extract token from request
|
|
13
|
+
const token = tokenExtractor(req);
|
|
14
|
+
if (!token) {
|
|
15
|
+
const error = new UnauthorizedError(unauthorizedMessage, {
|
|
16
|
+
reason: 'No token provided'
|
|
17
|
+
});
|
|
18
|
+
return next(error);
|
|
19
|
+
}
|
|
20
|
+
// Use safe verify token from security package
|
|
21
|
+
const result = safeVerifyToken(token, secret);
|
|
22
|
+
if (!result.valid) {
|
|
23
|
+
const error = new UnauthorizedError(unauthorizedMessage, {
|
|
24
|
+
reason: 'Invalid or expired token',
|
|
25
|
+
originalError: result.error?.message
|
|
26
|
+
});
|
|
27
|
+
return next(error);
|
|
28
|
+
}
|
|
29
|
+
// Attach the verified payload as user
|
|
30
|
+
req.user = result.payload;
|
|
31
|
+
next();
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
const unauthorizedError = new UnauthorizedError(unauthorizedMessage, error instanceof Error ? { originalError: error.message } : error);
|
|
35
|
+
return next(unauthorizedError);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Cache response middleware (per-route opt-in)
|
|
2
|
+
export function cacheResponse(ttl) {
|
|
3
|
+
return async (req, res, next) => {
|
|
4
|
+
try {
|
|
5
|
+
if (req.method !== 'GET')
|
|
6
|
+
return next();
|
|
7
|
+
const cache = (req.cache ?? req.app.locals.cache);
|
|
8
|
+
const defaultTTL = req.app.locals.cacheDefaultTTL;
|
|
9
|
+
if (!cache || typeof cache.get !== 'function')
|
|
10
|
+
return next();
|
|
11
|
+
const key = `${req.originalUrl}`;
|
|
12
|
+
try {
|
|
13
|
+
const cached = await cache.get(key);
|
|
14
|
+
if (cached !== null && cached !== undefined) {
|
|
15
|
+
res.setHeader('X-Cache', 'HIT');
|
|
16
|
+
return res.json(cached);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
catch (cacheErr) {
|
|
20
|
+
console.error(`[Cache] Failed to retrieve key "${key}":`, cacheErr);
|
|
21
|
+
// Continue without cache hit
|
|
22
|
+
}
|
|
23
|
+
const originalJson = res.json.bind(res);
|
|
24
|
+
res.json = (body) => {
|
|
25
|
+
try {
|
|
26
|
+
const expiry = ttl ?? defaultTTL;
|
|
27
|
+
if (expiry && cache && typeof cache.set === 'function') {
|
|
28
|
+
cache.set(key, body, expiry).catch((err) => {
|
|
29
|
+
console.error(`[Cache] Failed to set key "${key}" with TTL ${expiry}:`, err);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
else if (cache) {
|
|
33
|
+
if (typeof cache.set === 'function') {
|
|
34
|
+
cache.set(key, body).catch((err) => {
|
|
35
|
+
console.error(`[Cache] Failed to set key "${key}":`, err);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
console.error(`[Cache] Error during cache.set operation:`, e);
|
|
42
|
+
}
|
|
43
|
+
res.setHeader('X-Cache', 'MISS');
|
|
44
|
+
return originalJson(body);
|
|
45
|
+
};
|
|
46
|
+
next();
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
console.error('[Cache] Unexpected error in cacheResponse middleware:', err);
|
|
50
|
+
next();
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Error handling middleware
|
|
2
|
+
export function createErrorHandler() {
|
|
3
|
+
return (err, req, res, next) => {
|
|
4
|
+
console.error('Error:', err);
|
|
5
|
+
if (res.headersSent) {
|
|
6
|
+
return next(err);
|
|
7
|
+
}
|
|
8
|
+
// Type guard for error objects
|
|
9
|
+
const errorObj = err;
|
|
10
|
+
const status = errorObj.status || errorObj.statusCode || 500;
|
|
11
|
+
const message = process.env.NODE_ENV === 'production'
|
|
12
|
+
? 'Internal Server Error'
|
|
13
|
+
: errorObj.message || 'Unknown error';
|
|
14
|
+
res.status(status).json({
|
|
15
|
+
success: false,
|
|
16
|
+
message,
|
|
17
|
+
data: undefined,
|
|
18
|
+
error: {
|
|
19
|
+
message,
|
|
20
|
+
...(process.env.NODE_ENV !== 'production' && { details: { stack: errorObj.stack } })
|
|
21
|
+
},
|
|
22
|
+
meta: null
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { AuthConfig, createAuthMiddleware } from './auth.middleware';
|
|
2
|
+
export { cacheResponse } from './cache.middleware';
|
|
3
|
+
export { createErrorHandler } from './errorHandler.middleware';
|
|
4
|
+
export { createLoggingMiddleware } from './logging.middleware';
|
|
5
|
+
export { RateLimitConfig, createRateLimitMiddleware } from './rateLimiter.middleware';
|
|
6
|
+
export { createRequestIdMiddleware } from './requestId.middleware';
|
|
7
|
+
export { useSession } from './session.middleware';
|
|
8
|
+
export { ValidationRule, createValidationMiddleware } from './validation.middleware';
|
|
9
|
+
export { withLogging, withErrorHandler, withRequestId, withValidation, withRateLimit, withAuth } from './plugins.middleware';
|
|
10
|
+
export { validateFields, rateLimit, requireAuth } from './plugins.middleware';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Authentication Middleware
|
|
2
|
+
export { createAuthMiddleware } from './auth.middleware';
|
|
3
|
+
// Cache Middleware
|
|
4
|
+
export { cacheResponse } from './cache.middleware';
|
|
5
|
+
// Error Handler Middleware
|
|
6
|
+
export { createErrorHandler } from './errorHandler.middleware';
|
|
7
|
+
// Logging Middleware
|
|
8
|
+
export { createLoggingMiddleware } from './logging.middleware';
|
|
9
|
+
// Rate Limiter Middleware
|
|
10
|
+
export { createRateLimitMiddleware } from './rateLimiter.middleware';
|
|
11
|
+
// Request ID Middleware
|
|
12
|
+
export { createRequestIdMiddleware } from './requestId.middleware';
|
|
13
|
+
// Session Middleware
|
|
14
|
+
export { useSession } from './session.middleware';
|
|
15
|
+
// Validation Middleware
|
|
16
|
+
export { createValidationMiddleware } from './validation.middleware';
|
|
17
|
+
// Plugin middleware functions (for application-level middleware)
|
|
18
|
+
export { withLogging, withErrorHandler, withRequestId, withValidation, withRateLimit, withAuth } from './plugins.middleware';
|
|
19
|
+
// Convenience middleware functions (for route-level middleware)
|
|
20
|
+
export { validateFields, rateLimit, requireAuth } from './plugins.middleware';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Logging middleware
|
|
2
|
+
export function createLoggingMiddleware(format = 'simple') {
|
|
3
|
+
return (req, res, next) => {
|
|
4
|
+
const start = Date.now();
|
|
5
|
+
res.on('finish', () => {
|
|
6
|
+
const duration = Date.now() - start;
|
|
7
|
+
if (format === 'detailed') {
|
|
8
|
+
console.log(`${req.method} ${req.url} - ${res.statusCode} - ${duration}ms - ${req.ip}`);
|
|
9
|
+
}
|
|
10
|
+
else {
|
|
11
|
+
console.log(`${req.method} ${req.url} - ${res.statusCode}`);
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
next();
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { RequestHandler } from 'express';
|
|
2
|
+
import { ServerPlugin } from '../types';
|
|
3
|
+
import { ValidationRule } from './validation.middleware';
|
|
4
|
+
import { RateLimitConfig } from './rateLimiter.middleware';
|
|
5
|
+
import { AuthConfig } from './auth.middleware';
|
|
6
|
+
export declare function withLogging(format?: 'simple' | 'detailed'): ServerPlugin;
|
|
7
|
+
export declare function withErrorHandler(): ServerPlugin;
|
|
8
|
+
export declare function withRequestId(): ServerPlugin;
|
|
9
|
+
export declare function withValidation(rules: ValidationRule[]): ServerPlugin;
|
|
10
|
+
export declare function withRateLimit(config?: RateLimitConfig): ServerPlugin;
|
|
11
|
+
export declare function withAuth(config: AuthConfig): ServerPlugin;
|
|
12
|
+
export declare function validateFields(rules: ValidationRule[]): RequestHandler;
|
|
13
|
+
export declare function rateLimit(config?: RateLimitConfig): RequestHandler;
|
|
14
|
+
export declare function requireAuth(config: AuthConfig): RequestHandler;
|