Files
Debian 890543fb77
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled
Add frontend service with auth, MFA, and content management
- Node.js/Express backend with TypeScript
- SQLite database for users, sessions, and content metadata
- Authentication with TOTP and WebAuthn MFA support
- Admin user auto-created on first startup
- User content gallery with view/delete functionality
- RunPod API proxy (keeps API keys server-side)
- Docker setup with CI/CD for Gitea registry

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 04:57:08 +00:00

65 lines
1.7 KiB
TypeScript

import type { Request, Response, NextFunction, ErrorRequestHandler } from 'express';
import { logger } from '../utils/logger.js';
import { ZodError } from 'zod';
export interface AppError extends Error {
statusCode?: number;
isOperational?: boolean;
}
export function createError(message: string, statusCode: number = 500): AppError {
const error: AppError = new Error(message);
error.statusCode = statusCode;
error.isOperational = true;
return error;
}
export const errorHandler: ErrorRequestHandler = (
err: AppError | Error,
req: Request,
res: Response,
_next: NextFunction
): void => {
// Handle Zod validation errors
if (err instanceof ZodError) {
const message = err.errors.map(e => e.message).join(', ');
res.status(400).json({ error: message });
return;
}
// Get status code and message
const statusCode = 'statusCode' in err ? err.statusCode || 500 : 500;
const isOperational = 'isOperational' in err ? err.isOperational : false;
// Log error
if (statusCode >= 500 || !isOperational) {
logger.error({
err,
method: req.method,
url: req.url,
statusCode,
}, 'Unhandled error');
} else {
logger.warn({
message: err.message,
method: req.method,
url: req.url,
statusCode,
}, 'Operational error');
}
// Send response
res.status(statusCode).json({
error: isOperational ? err.message : 'Internal server error',
});
};
// Async route handler wrapper
export function asyncHandler<T>(
fn: (req: Request, res: Response, next: NextFunction) => Promise<T>
): (req: Request, res: Response, next: NextFunction) => void {
return (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
}