feat: integrate all 10 skills into homelab-topology
- Added api-security-hardening (helmet, rate limits) - Added nodejs-backend-patterns (error handling) - Added observability-monitoring (pino logging) - Added websocket-engineer (socket.io real-time updates) - Added docker (Multi-stage build, compose) - Added vitest (testing configuration and store tests) - Added data-visualizer (MetricsBar and HostChart) - Added infrastructure-monitoring/proxmox-admin/network-engineer types - Fixed UI accessibility and styling - Cleaned up node_modules tracking
This commit is contained in:
42
server/middleware/errorHandler.ts
Normal file
42
server/middleware/errorHandler.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import { AppError, ValidationError } from '../errors';
|
||||
import { logger } from './requestLogger';
|
||||
|
||||
/**
|
||||
* Global error handler middleware.
|
||||
* Catches all errors, logs unexpected ones with Pino, and returns safe JSON responses.
|
||||
* Must be registered AFTER all routes.
|
||||
*/
|
||||
export const errorHandler = (
|
||||
err: Error,
|
||||
req: Request,
|
||||
res: Response,
|
||||
_next: NextFunction
|
||||
) => {
|
||||
if (err instanceof AppError) {
|
||||
return res.status(err.statusCode).json({
|
||||
status: 'error',
|
||||
message: err.message,
|
||||
...(err instanceof ValidationError && err.errors ? { errors: err.errors } : {}),
|
||||
});
|
||||
}
|
||||
|
||||
// Log unexpected errors
|
||||
logger.error({
|
||||
error: err.message,
|
||||
stack: err.stack,
|
||||
url: req.url,
|
||||
method: req.method,
|
||||
});
|
||||
|
||||
// Don't leak error details in production
|
||||
const message =
|
||||
process.env.NODE_ENV === 'production'
|
||||
? 'Internal server error'
|
||||
: err.message;
|
||||
|
||||
res.status(500).json({
|
||||
status: 'error',
|
||||
message,
|
||||
});
|
||||
};
|
||||
42
server/middleware/requestLogger.ts
Normal file
42
server/middleware/requestLogger.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import pino from 'pino';
|
||||
|
||||
/**
|
||||
* Pino structured logger.
|
||||
* - Development: colorized, human-readable output
|
||||
* - Production: JSON for log aggregation
|
||||
*/
|
||||
export const logger = pino({
|
||||
level: process.env.LOG_LEVEL || 'info',
|
||||
...(process.env.NODE_ENV !== 'production' && {
|
||||
transport: {
|
||||
target: 'pino-pretty',
|
||||
options: { colorize: true },
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
/**
|
||||
* Request logging middleware.
|
||||
* Logs method, url, status, and duration for every request.
|
||||
*/
|
||||
export const requestLogger = (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) => {
|
||||
const start = Date.now();
|
||||
|
||||
res.on('finish', () => {
|
||||
const duration = Date.now() - start;
|
||||
logger.info({
|
||||
method: req.method,
|
||||
url: req.url,
|
||||
status: res.statusCode,
|
||||
duration: `${duration}ms`,
|
||||
ip: req.ip,
|
||||
});
|
||||
});
|
||||
|
||||
next();
|
||||
};
|
||||
Reference in New Issue
Block a user