diff --git a/build.ts b/build.ts index 79584d4..b61b146 100644 --- a/build.ts +++ b/build.ts @@ -106,7 +106,7 @@ import pkg from "./package.json"; logger.info('✅ Build completed successfully!'); logger.info('📦 Package ready in ./dist folder'); } catch (err) { - logger.err('❌ Build failed:', err); + logger.err('❌ Build failed:', err as any); process.exit(1); } })(); diff --git a/tsconfig.json b/tsconfig.json index 537aff7..89d06d7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,6 +2,7 @@ "compilerOptions": { "declaration": true, "declarationDir": "dist", + "ignoreDeprecations": "6.0", "target": "ES2020", "module": "commonjs", "moduleResolution": "node", diff --git a/v2/core/Application.ts b/v2/core/Application.ts index 2daab40..7f42442 100644 --- a/v2/core/Application.ts +++ b/v2/core/Application.ts @@ -276,6 +276,13 @@ export class ApplicationBuilder { * Build and return the application */ async build(): Promise { + // Import middleware initializers + const { initHelmet } = await import('../middlewares/security/helmet') + const { initCors } = await import('../middlewares/security/cors') + const { initRateLimiter } = await import('../middlewares/security/rateLimiter') + const { initCookieParser } = await import('../middlewares/utils/cookieParser') + const { initBodyParser } = await import('../middlewares/utils/bodyParser') + // Load configuration let finalConfig: AppConfig @@ -349,10 +356,41 @@ export class ApplicationBuilder { application.plugins(this.pluginRegistrations) } - // Initialize and start + // Initialize and start plugins await application.initialize() await application.start() + // Auto-inject middlewares from config (AFTER plugins so logger is available) + const logger = application.getContainer().tryResolve('logger') + + // Inject middlewares in order (IMPORTANT: body parsers first, then security, then auth) + if (finalConfig.middlewares) { + // 1. Body parser (must be early to parse request bodies) + if (finalConfig.middlewares.bodyParser) { + initBodyParser(this.app, finalConfig.middlewares.bodyParser as any, logger) + } + + // 2. Cookie parser (must be early to parse cookies) + if (finalConfig.middlewares.cookieParser) { + initCookieParser(this.app, finalConfig.middlewares.cookieParser as any, logger) + } + + // 3. Security middlewares + if (finalConfig.middlewares.helmet) { + initHelmet(this.app, finalConfig.middlewares.helmet as any, logger) + } + + if (finalConfig.middlewares.cors) { + initCors(this.app, finalConfig.middlewares.cors as any, logger) + } + + // 4. Rate limiting (after CORS) + const rateLimitConfig = finalConfig.middlewares.rateLimit || finalConfig.middlewares.rateLimiter + if (rateLimitConfig) { + initRateLimiter(this.app, rateLimitConfig as any, logger) + } + } + return application } } diff --git a/v2/core/ConfigSchema.ts b/v2/core/ConfigSchema.ts index 4a9f30b..ba3fc20 100644 --- a/v2/core/ConfigSchema.ts +++ b/v2/core/ConfigSchema.ts @@ -153,6 +153,29 @@ const RateLimiterConfigSchema = z.object({ handler: z.function().optional(), }).passthrough() +/** + * Cookie parser middleware configuration schema + */ +const CookieParserConfigSchema = z.object({ + enabled: z.boolean().optional(), + secret: z.union([z.string(), z.array(z.string())]).optional(), + options: z.record(z.unknown()).optional(), +}).passthrough() + +/** + * Body parser middleware configuration schema + */ +const BodyParserConfigSchema = z.object({ + json: z.object({ + enabled: z.boolean().optional(), + options: z.record(z.unknown()).optional(), + }).optional(), + urlencoded: z.object({ + enabled: z.boolean().optional(), + options: z.record(z.unknown()).optional(), + }).optional(), +}).passthrough() + /** * Middleware configuration schema */ @@ -162,6 +185,9 @@ const MiddlewareConfigSchema = z.object({ csrf: CsrfConfigSchema.optional(), jwt: JwtConfigSchema.optional(), rateLimiter: RateLimiterConfigSchema.optional(), + rateLimit: RateLimiterConfigSchema.optional(), // Alias for rateLimiter + cookieParser: CookieParserConfigSchema.optional(), + bodyParser: BodyParserConfigSchema.optional(), }).passthrough() /** diff --git a/v2/index.ts b/v2/index.ts index 569776f..72fa64e 100644 --- a/v2/index.ts +++ b/v2/index.ts @@ -46,6 +46,8 @@ export { initCors, type CorsConfig } from './middlewares/security/cors' export { initCsrf, type CsrfConfig } from './middlewares/security/csrf' export { initRateLimiter, type RateLimiterConfig } from './middlewares/security/rateLimiter' export { initJwt, signToken, type JwtConfig } from './middlewares/auth/jwt' +export { initCookieParser, type CookieParserConfig } from './middlewares/utils/cookieParser' +export { initBodyParser, type BodyParserConfig } from './middlewares/utils/bodyParser' // Re-export Application.create as default export { Application as default } from './core/Application' diff --git a/v2/middlewares/security/cors.ts b/v2/middlewares/security/cors.ts index 94eb906..2b6ddd1 100644 --- a/v2/middlewares/security/cors.ts +++ b/v2/middlewares/security/cors.ts @@ -19,11 +19,11 @@ export interface CorsConfig { */ export function initCors(app: Application, config: CorsConfig, logger?: Logger): void { if (config.enabled === false) { - logger?.debug('CORS middleware disabled') + logger?.debug('[NSK][CORS] Middleware disabled') return } - logger?.info('Initializing CORS middleware') + logger?.info('[NSK][CORS] Initializing...') const corsOptions: CorsOptions = { credentials: config.credentials ?? true, @@ -49,11 +49,11 @@ export function initCors(app: Application, config: CorsConfig, logger?: Logger): } } - logger?.info('CORS allowed origins:', { origins: config.allowedOrigins }) + logger?.info('[NSK][CORS] Allowed origins:', { origins: config.allowedOrigins }) } else { // Default: allow all origins corsOptions.origin = true - logger?.warn('CORS allowing all origins (not recommended for production)') + logger?.warn('[NSK][CORS] Allowing all origins (not recommended for production)') } if (config.allowedMethods) { @@ -70,5 +70,5 @@ export function initCors(app: Application, config: CorsConfig, logger?: Logger): app.use(cors(corsOptions) as RequestHandler) - logger?.info('CORS middleware initialized') + logger?.info('[NSK][CORS] Initialized') } diff --git a/v2/middlewares/security/helmet.ts b/v2/middlewares/security/helmet.ts index 6ac1c75..5ad8f8f 100644 --- a/v2/middlewares/security/helmet.ts +++ b/v2/middlewares/security/helmet.ts @@ -17,11 +17,11 @@ export interface HelmetConfig { */ export function initHelmet(app: Application, config: HelmetConfig, logger?: Logger): void { if (config.enabled === false) { - logger?.debug('Helmet middleware disabled') + logger?.debug('[NSK][HELMET] Middleware disabled') return } - logger?.info('Initializing Helmet middleware') + logger?.info('[NSK][HELMET] Initializing...') const options: HelmetOptions = config.options || {} @@ -40,5 +40,5 @@ export function initHelmet(app: Application, config: HelmetConfig, logger?: Logg app.use(helmet(options) as RequestHandler) - logger?.info('Helmet middleware initialized') + logger?.info('[NSK][HELMET] Initialized') } diff --git a/v2/middlewares/security/rateLimiter.ts b/v2/middlewares/security/rateLimiter.ts index 2645a4b..5e934e5 100644 --- a/v2/middlewares/security/rateLimiter.ts +++ b/v2/middlewares/security/rateLimiter.ts @@ -21,11 +21,11 @@ export interface RateLimiterConfig { */ export function initRateLimiter(app: Application, config: RateLimiterConfig, logger?: Logger): void { if (config.enabled === false) { - logger?.debug('Rate limiter middleware disabled') + logger?.debug('[NSK][RATELIMITER] Middleware disabled') return } - logger?.info('Initializing rate limiter middleware') + logger?.info('[NSK][RATELIMITER] Initializing...') const skipPaths = config.skipPaths || [] @@ -43,7 +43,7 @@ export function initRateLimiter(app: Application, config: RateLimiterConfig, log return skipPaths.some(path => req.path.startsWith(path)) }), handler: config.handler || ((req: Request, res: Response) => { - logger?.warn('Rate limit exceeded', { + logger?.warn('[NSK][RATELIMITER] Rate limit exceeded', { ip: req.ip, path: req.path, method: req.method @@ -58,7 +58,7 @@ export function initRateLimiter(app: Application, config: RateLimiterConfig, log app.use(rateLimit(options)) - logger?.info('Rate limiter middleware initialized', { + logger?.info('[NSK][RATELIMITER] Initialized', { windowMs: options.windowMs, max: options.max, }) diff --git a/v2/middlewares/utils/bodyParser.ts b/v2/middlewares/utils/bodyParser.ts new file mode 100644 index 0000000..ebd0ad8 --- /dev/null +++ b/v2/middlewares/utils/bodyParser.ts @@ -0,0 +1,37 @@ +import express, { Application } from 'express' +import { Logger } from '../../types/Logger' + +export interface BodyParserConfig { + json?: { + enabled?: boolean + options?: any + } + urlencoded?: { + enabled?: boolean + options?: any + } +} + +/** + * Initialize body parser middleware + * Parses request body as JSON and URL-encoded + */ +export function initBodyParser(app: Application, config: BodyParserConfig, logger?: Logger): void { + logger?.info('[NSK][BODYPARSER] Initializing...') + + // JSON parser + if (config.json?.enabled !== false) { + const jsonOptions = config.json?.options || {} + app.use(express.json(jsonOptions)) + logger?.debug('[NSK][BODYPARSER] JSON parser enabled') + } + + // URL-encoded parser + if (config.urlencoded?.enabled !== false) { + const urlencodedOptions = config.urlencoded?.options || { extended: true } + app.use(express.urlencoded(urlencodedOptions)) + logger?.debug('[NSK][BODYPARSER] URL-encoded parser enabled') + } + + logger?.info('[NSK][BODYPARSER] Initialized') +} diff --git a/v2/middlewares/utils/cookieParser.ts b/v2/middlewares/utils/cookieParser.ts new file mode 100644 index 0000000..2509c1b --- /dev/null +++ b/v2/middlewares/utils/cookieParser.ts @@ -0,0 +1,30 @@ +import cookieParser from 'cookie-parser' +import { Application, RequestHandler } from 'express' +import { Logger } from '../../types/Logger' + +export interface CookieParserConfig { + enabled?: boolean + secret?: string | string[] + options?: cookieParser.CookieParseOptions +} + +/** + * Initialize cookie parser middleware + * Parses cookies from request headers + */ +export function initCookieParser(app: Application, config: CookieParserConfig, logger?: Logger): void { + if (config.enabled === false) { + logger?.debug('[NSK][COOKIEPARSER] Middleware disabled') + return + } + + logger?.info('[NSK][COOKIEPARSER] Initializing...') + + if (config.secret) { + app.use(cookieParser(config.secret, config.options) as RequestHandler) + logger?.info('[NSK][COOKIEPARSER] Initialized with secret') + } else { + app.use(cookieParser(undefined, config.options) as RequestHandler) + logger?.info('[NSK][COOKIEPARSER] Initialized without secret') + } +}