UNPKG

perchance-ai-prompt-library

Version:

🎨 Complete AI prompt library and generator with advanced CLI, batch processing, analytics, and multi-format export capabilities

253 lines (233 loc) • 6.52 kB
const { v4: uuidv4 } = require('uuid'); const logger = require('./logger'); /** * Custom error class for API errors */ class ApiError extends Error { constructor(message, statusCode, code, details = {}) { super(message); this.name = 'ApiError'; this.statusCode = statusCode || 500; this.code = code || 'INTERNAL_ERROR'; this.details = details; this.timestamp = new Date().toISOString(); this.errorId = uuidv4(); // Capture stack trace Error.captureStackTrace(this, this.constructor); } /** * Convert error to a plain object * @returns {Object} Plain object representation of the error */ toJSON() { return { error: { id: this.errorId, message: this.message, code: this.code, statusCode: this.statusCode, timestamp: this.timestamp, ...(process.env.NODE_ENV === 'development' && { stack: this.stack, details: this.details }) } }; } } /** * Error types with default messages and status codes */ const ERROR_TYPES = { // 4xx Errors BAD_REQUEST: { statusCode: 400, code: 'BAD_REQUEST', message: 'The request could not be understood or was missing required parameters.' }, UNAUTHORIZED: { statusCode: 401, code: 'UNAUTHORIZED', message: 'Authentication failed or user does not have permissions for the requested operation.' }, FORBIDDEN: { statusCode: 403, code: 'FORBIDDEN', message: 'Access to the requested resource is forbidden.' }, NOT_FOUND: { statusCode: 404, code: 'NOT_FOUND', message: 'The requested resource could not be found.' }, VALIDATION_ERROR: { statusCode: 422, code: 'VALIDATION_ERROR', message: 'The request data is invalid.' }, RATE_LIMIT_EXCEEDED: { statusCode: 429, code: 'RATE_LIMIT_EXCEEDED', message: 'Too many requests, please try again later.' }, // 5xx Errors INTERNAL_SERVER_ERROR: { statusCode: 500, code: 'INTERNAL_SERVER_ERROR', message: 'An unexpected error occurred on the server.' }, SERVICE_UNAVAILABLE: { statusCode: 503, code: 'SERVICE_UNAVAILABLE', message: 'Service is temporarily unavailable, please try again later.' }, // Custom errors POLLINATIONS_API_ERROR: { statusCode: 502, code: 'POLLINATIONS_API_ERROR', message: 'An error occurred while communicating with the Pollinations.ai API.' }, IMAGE_GENERATION_FAILED: { statusCode: 500, code: 'IMAGE_GENERATION_FAILED', message: 'Failed to generate the requested image.' }, INVALID_IMAGE_FORMAT: { statusCode: 400, code: 'INVALID_IMAGE_FORMAT', message: 'The provided image format is not supported.' } }; /** * Create a new API error * @param {string} type - Error type (key from ERROR_TYPES) * @param {string} [message] - Custom error message (overrides default) * @param {Object} [details] - Additional error details * @returns {ApiError} The created error */ function createError(type, message, details = {}) { const errorType = ERROR_TYPES[type] || ERROR_TYPES.INTERNAL_SERVER_ERROR; const errorMessage = message || errorType.message; return new ApiError( errorMessage, errorType.statusCode, errorType.code, details ); } /** * Error handling middleware for Express * @param {Error} err - The error object * @param {Object} req - Express request object * @param {Object} res - Express response object * @param {Function} next - Next middleware function */ function errorHandler(err, req, res, next) { // Log the error const errorId = err.errorId || uuidv4(); const statusCode = err.statusCode || 500; // Log the error with request details logger.error({ errorId, message: err.message, name: err.name, code: err.code, statusCode, stack: process.env.NODE_ENV === 'development' ? err.stack : undefined, request: { method: req.method, url: req.originalUrl, params: req.params, query: req.query, // Don't log sensitive data like passwords body: req.body && Object.fromEntries( Object.entries(req.body) .filter(([key]) => !['password', 'token', 'apiKey'].includes(key.toLowerCase())) ), user: req.user ? { id: req.user.id, email: req.user.email } : undefined, ip: req.ip }, details: err.details }, 'API Error'); // Prepare error response const response = { error: { id: errorId, code: err.code || 'INTERNAL_ERROR', message: err.message || 'An unexpected error occurred', ...(process.env.NODE_ENV === 'development' && { stack: err.stack, details: err.details }) } }; // Send the error response res.status(statusCode).json(response); } /** * Async error handler wrapper for Express routes * @param {Function} fn - The async route handler function * @returns {Function} Wrapped route handler with error handling */ function asyncHandler(fn) { return (req, res, next) => { Promise.resolve(fn(req, res, next)).catch(next); }; } /** * Handle uncaught exceptions */ function handleUncaughtExceptions() { process.on('uncaughtException', (error) => { const errorId = uuidv4(); logger.fatal({ errorId, name: error.name, message: error.message, stack: error.stack, timestamp: new Date().toISOString() }, 'Uncaught Exception'); // Give time to flush logs before exiting setTimeout(() => process.exit(1), 1000); }); } /** * Handle unhandled promise rejections */ function handleUnhandledRejections() { process.on('unhandledRejection', (reason, promise) => { const errorId = uuidv4(); logger.error({ errorId, reason: reason instanceof Error ? { name: reason.name, message: reason.message, stack: reason.stack } : reason, timestamp: new Date().toISOString() }, 'Unhandled Promise Rejection'); }); } /** * Handle process termination signals */ function handleProcessSignals() { const signals = ['SIGINT', 'SIGTERM', 'SIGQUIT']; signals.forEach(signal => { process.on(signal, () => { logger.info(`Received ${signal}. Gracefully shutting down...`); // Perform cleanup if needed process.exit(0); }); }); } module.exports = { ApiError, ERROR_TYPES, createError, errorHandler, asyncHandler, handleUncaughtExceptions, handleUnhandledRejections, handleProcessSignals };