create-arktos 1.0.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/.env.example +69 -0
- package/LICENSE +21 -0
- package/README.md +533 -0
- package/bin/cli.js +318 -0
- package/eslint.config.js +79 -0
- package/package.json +108 -0
- package/prisma/schema.prisma +172 -0
- package/src/app.ts +101 -0
- package/src/config/env.validation.ts +15 -0
- package/src/config/logger.ts +76 -0
- package/src/constants/errorCodes.ts +59 -0
- package/src/constants/messages.ts +190 -0
- package/src/controllers/auth.controller.ts +546 -0
- package/src/middleware/index.ts +638 -0
- package/src/routes/auth.routes.ts +29 -0
- package/src/routes/index.ts +63 -0
- package/src/schemas/index.ts +139 -0
- package/src/services/database.service.ts +120 -0
- package/src/services/email.service.ts +244 -0
- package/src/services/jwt.service.ts +227 -0
- package/src/types/express.d.ts +19 -0
- package/src/types/index.ts +156 -0
- package/src/utils/response.ts +55 -0
- package/src/views/emails/notification.html +87 -0
- package/src/views/emails/resetPassword.html +118 -0
- package/src/views/emails/verification.html +107 -0
- package/src/views/emails/welcome.html +113 -0
- package/template.package.json +82 -0
- package/tsconfig.json +35 -0
- package/vercel.json +34 -0
package/src/app.ts
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import dotenv from 'dotenv';
|
|
3
|
+
import routes from './routes';
|
|
4
|
+
import { errorHandling, securityMiddleware, rateLimiter } from './middleware';
|
|
5
|
+
import DatabaseService from './services/database.service';
|
|
6
|
+
import logger from './config/logger';
|
|
7
|
+
|
|
8
|
+
// Load environment variables
|
|
9
|
+
dotenv.config();
|
|
10
|
+
|
|
11
|
+
const app = express();
|
|
12
|
+
const PORT = process.env.PORT || 3001;
|
|
13
|
+
|
|
14
|
+
// Security middleware stack
|
|
15
|
+
app.use(securityMiddleware);
|
|
16
|
+
|
|
17
|
+
// Rate limiting
|
|
18
|
+
app.use('/api', rateLimiter.general);
|
|
19
|
+
|
|
20
|
+
// Body parsing middleware
|
|
21
|
+
app.use(express.json({ limit: '10mb' }));
|
|
22
|
+
app.use(express.urlencoded({ extended: true, limit: '10mb' }));
|
|
23
|
+
|
|
24
|
+
// Trust proxy (for accurate IP addresses)
|
|
25
|
+
app.set('trust proxy', 1);
|
|
26
|
+
|
|
27
|
+
// API routes
|
|
28
|
+
app.use('/api', routes);
|
|
29
|
+
|
|
30
|
+
// Health check route (outside of rate limiting)
|
|
31
|
+
app.get('/health', async (req, res) => {
|
|
32
|
+
try {
|
|
33
|
+
const dbService = DatabaseService.getInstance();
|
|
34
|
+
const dbHealth = await dbService.healthCheck();
|
|
35
|
+
|
|
36
|
+
res.json({
|
|
37
|
+
status: dbHealth.connected ? 'ok' : 'error',
|
|
38
|
+
timestamp: new Date().toISOString(),
|
|
39
|
+
uptime: process.uptime(),
|
|
40
|
+
database: dbHealth,
|
|
41
|
+
});
|
|
42
|
+
} catch (error) {
|
|
43
|
+
res.status(503).json({
|
|
44
|
+
status: 'error',
|
|
45
|
+
message: 'Health check failed',
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// 404 handler
|
|
51
|
+
app.use(errorHandling.notFound);
|
|
52
|
+
|
|
53
|
+
// Global error handler
|
|
54
|
+
app.use(errorHandling.handler);
|
|
55
|
+
|
|
56
|
+
// Initialize database connection
|
|
57
|
+
const initializeApp = async () => {
|
|
58
|
+
try {
|
|
59
|
+
const dbService = DatabaseService.getInstance();
|
|
60
|
+
await dbService.connect();
|
|
61
|
+
|
|
62
|
+
logger.info('✅ Database connected successfully');
|
|
63
|
+
|
|
64
|
+
app.listen(PORT, () => {
|
|
65
|
+
logger.info(`🚀 Server is running on port ${PORT}`);
|
|
66
|
+
logger.info(`🌐 Environment: ${process.env.NODE_ENV || 'development'}`);
|
|
67
|
+
logger.info(`📚 API Documentation: http://localhost:${PORT}/api`);
|
|
68
|
+
logger.info(`❤️ Health Check: http://localhost:${PORT}/health`);
|
|
69
|
+
});
|
|
70
|
+
} catch (error) {
|
|
71
|
+
logger.error('❌ Failed to initialize application:', error);
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// Graceful shutdown
|
|
77
|
+
const gracefulShutdown = async (signal: string) => {
|
|
78
|
+
logger.info(`📡 Received ${signal}, starting graceful shutdown...`);
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
const dbService = DatabaseService.getInstance();
|
|
82
|
+
await dbService.disconnect();
|
|
83
|
+
logger.info('✅ Database disconnected');
|
|
84
|
+
|
|
85
|
+
process.exit(0);
|
|
86
|
+
} catch (error) {
|
|
87
|
+
logger.error('❌ Error during graceful shutdown:', error);
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// Handle shutdown signals
|
|
93
|
+
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
|
|
94
|
+
process.on('SIGINT', () => gracefulShutdown('SIGINT'));
|
|
95
|
+
|
|
96
|
+
// Start the application
|
|
97
|
+
if (require.main === module) {
|
|
98
|
+
initializeApp();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export default app;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import logger from './logger';
|
|
2
|
+
import { envSchema } from '../schemas';
|
|
3
|
+
|
|
4
|
+
export function validateEnv() {
|
|
5
|
+
try {
|
|
6
|
+
const env = envSchema.parse(process.env);
|
|
7
|
+
logger.info('Environment validation successful');
|
|
8
|
+
return env;
|
|
9
|
+
} catch (error) {
|
|
10
|
+
logger.error('Environment validation failed:', error);
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const config = validateEnv();
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import winston from 'winston';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
const logLevel = process.env.LOG_LEVEL || 'info';
|
|
5
|
+
const logFile = process.env.LOG_FILE || 'logs/app.log';
|
|
6
|
+
|
|
7
|
+
// Ensure logs directory exists
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
const logDir = path.dirname(logFile);
|
|
10
|
+
if (!fs.existsSync(logDir)) {
|
|
11
|
+
fs.mkdirSync(logDir, { recursive: true });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const logFormat = winston.format.combine(
|
|
15
|
+
winston.format.timestamp({
|
|
16
|
+
format: 'YYYY-MM-DD HH:mm:ss',
|
|
17
|
+
}),
|
|
18
|
+
winston.format.errors({ stack: true }),
|
|
19
|
+
winston.format.json(),
|
|
20
|
+
winston.format.prettyPrint()
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
const consoleFormat = winston.format.combine(
|
|
24
|
+
winston.format.colorize(),
|
|
25
|
+
winston.format.timestamp({
|
|
26
|
+
format: 'HH:mm:ss',
|
|
27
|
+
}),
|
|
28
|
+
winston.format.printf(({ timestamp, level, message, ...meta }) => {
|
|
29
|
+
return `${timestamp} [${level}]: ${message} ${Object.keys(meta).length ? JSON.stringify(meta, null, 2) : ''}`;
|
|
30
|
+
})
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
const logger = winston.createLogger({
|
|
34
|
+
level: logLevel,
|
|
35
|
+
format: logFormat,
|
|
36
|
+
defaultMeta: { service: 'arktos-backend' },
|
|
37
|
+
transports: [
|
|
38
|
+
// File transport
|
|
39
|
+
new winston.transports.File({
|
|
40
|
+
filename: logFile,
|
|
41
|
+
handleExceptions: true,
|
|
42
|
+
maxsize: 5242880, // 5MB
|
|
43
|
+
maxFiles: 5,
|
|
44
|
+
}),
|
|
45
|
+
// Error file transport
|
|
46
|
+
new winston.transports.File({
|
|
47
|
+
filename: path.join(logDir, 'error.log'),
|
|
48
|
+
level: 'error',
|
|
49
|
+
handleExceptions: true,
|
|
50
|
+
maxsize: 5242880, // 5MB
|
|
51
|
+
maxFiles: 5,
|
|
52
|
+
}),
|
|
53
|
+
],
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Console transport for non-production environments
|
|
57
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
58
|
+
logger.add(
|
|
59
|
+
new winston.transports.Console({
|
|
60
|
+
format: consoleFormat,
|
|
61
|
+
handleExceptions: true,
|
|
62
|
+
})
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Handle uncaught exceptions and unhandled rejections
|
|
67
|
+
process.on('uncaughtException', (error) => {
|
|
68
|
+
logger.error('Uncaught Exception:', error);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
73
|
+
logger.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
export default logger;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export const ERROR_CODES = {
|
|
2
|
+
// Authentication errors
|
|
3
|
+
AUTH_INVALID_CREDENTIALS: 'AUTH_001',
|
|
4
|
+
AUTH_TOKEN_EXPIRED: 'AUTH_002',
|
|
5
|
+
AUTH_TOKEN_INVALID: 'AUTH_003',
|
|
6
|
+
AUTH_USER_NOT_FOUND: 'AUTH_004',
|
|
7
|
+
AUTH_EMAIL_NOT_VERIFIED: 'AUTH_005',
|
|
8
|
+
AUTH_ACCOUNT_DISABLED: 'AUTH_006',
|
|
9
|
+
AUTH_INSUFFICIENT_PERMISSIONS: 'AUTH_007',
|
|
10
|
+
AUTH_USER_EXISTS: 'AUTH_008',
|
|
11
|
+
AUTH_USERNAME_TAKEN: 'AUTH_009',
|
|
12
|
+
AUTH_INVALID_PASSWORD: 'AUTH_010',
|
|
13
|
+
AUTH_TOKEN_REQUIRED: 'AUTH_011',
|
|
14
|
+
AUTH_INVALID_TOKEN: 'AUTH_003', // Alias for AUTH_TOKEN_INVALID
|
|
15
|
+
AUTH_ACCOUNT_DEACTIVATED: 'AUTH_006', // Alias for AUTH_ACCOUNT_DISABLED
|
|
16
|
+
|
|
17
|
+
// Validation errors
|
|
18
|
+
VALIDATION_REQUIRED_FIELD: 'VAL_001',
|
|
19
|
+
VALIDATION_INVALID_FORMAT: 'VAL_002',
|
|
20
|
+
VALIDATION_INVALID_LENGTH: 'VAL_003',
|
|
21
|
+
VALIDATION_INVALID_TYPE: 'VAL_004',
|
|
22
|
+
VALIDATION_ERROR: 'VAL_005',
|
|
23
|
+
|
|
24
|
+
// Database errors
|
|
25
|
+
DB_CONNECTION_ERROR: 'DB_001',
|
|
26
|
+
DB_QUERY_ERROR: 'DB_002',
|
|
27
|
+
DB_CONSTRAINT_VIOLATION: 'DB_003',
|
|
28
|
+
DB_RECORD_NOT_FOUND: 'DB_004',
|
|
29
|
+
DB_DUPLICATE_ENTRY: 'DB_005',
|
|
30
|
+
DATABASE_CONNECTION: 'DB_001', // Alias
|
|
31
|
+
DATABASE_ERROR: 'DB_002', // Alias
|
|
32
|
+
DATABASE_CONSTRAINT: 'DB_003', // Alias
|
|
33
|
+
DATABASE_NOT_FOUND: 'DB_004', // Alias
|
|
34
|
+
DATABASE_CONFLICT: 'DB_005', // Alias
|
|
35
|
+
|
|
36
|
+
// File/Upload errors
|
|
37
|
+
FILE_TOO_LARGE: 'FILE_001',
|
|
38
|
+
FILE_INVALID_TYPE: 'FILE_002',
|
|
39
|
+
FILE_UPLOAD_FAILED: 'FILE_003',
|
|
40
|
+
FILE_NOT_FOUND: 'FILE_004',
|
|
41
|
+
|
|
42
|
+
// Rate limiting errors
|
|
43
|
+
RATE_LIMIT_EXCEEDED: 'RATE_001',
|
|
44
|
+
|
|
45
|
+
// Server errors
|
|
46
|
+
INTERNAL_SERVER_ERROR: 'SRV_001',
|
|
47
|
+
SERVICE_UNAVAILABLE: 'SRV_002',
|
|
48
|
+
EXTERNAL_SERVICE_ERROR: 'SRV_003',
|
|
49
|
+
INTERNAL_ERROR: 'SRV_001', // Alias
|
|
50
|
+
GENERIC_ERROR: 'SRV_004',
|
|
51
|
+
ROUTE_NOT_FOUND: 'SRV_005',
|
|
52
|
+
|
|
53
|
+
// Email service errors
|
|
54
|
+
EMAIL_SEND_FAILED: 'EMAIL_001',
|
|
55
|
+
EMAIL_TEMPLATE_NOT_FOUND: 'EMAIL_002',
|
|
56
|
+
EMAIL_INVALID_RECIPIENT: 'EMAIL_003',
|
|
57
|
+
} as const;
|
|
58
|
+
|
|
59
|
+
export type ErrorCode = typeof ERROR_CODES[keyof typeof ERROR_CODES];
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { ERROR_CODES } from './errorCodes';
|
|
2
|
+
|
|
3
|
+
export const SERVICE_MESSAGES = {
|
|
4
|
+
// Application messages
|
|
5
|
+
APP: {
|
|
6
|
+
API_RUNNING: {
|
|
7
|
+
code: 'APP_001',
|
|
8
|
+
message: 'API is running successfully',
|
|
9
|
+
statusCode: 200,
|
|
10
|
+
},
|
|
11
|
+
HEALTH_CHECK_FAILED: {
|
|
12
|
+
code: 'APP_002',
|
|
13
|
+
message: 'Health check failed',
|
|
14
|
+
statusCode: 503,
|
|
15
|
+
},
|
|
16
|
+
ENDPOINT_NOT_FOUND: {
|
|
17
|
+
code: 'APP_003',
|
|
18
|
+
message: 'Endpoint not found',
|
|
19
|
+
statusCode: 404,
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
// Authentication messages
|
|
24
|
+
AUTH: {
|
|
25
|
+
LOGIN_SUCCESS: {
|
|
26
|
+
code: 'AUTH_S001',
|
|
27
|
+
message: 'Login successful',
|
|
28
|
+
statusCode: 200,
|
|
29
|
+
},
|
|
30
|
+
REGISTER_SUCCESS: {
|
|
31
|
+
code: 'AUTH_S002',
|
|
32
|
+
message: 'Registration successful',
|
|
33
|
+
statusCode: 201,
|
|
34
|
+
},
|
|
35
|
+
LOGOUT_SUCCESS: {
|
|
36
|
+
code: 'AUTH_S003',
|
|
37
|
+
message: 'Logout successful',
|
|
38
|
+
statusCode: 200,
|
|
39
|
+
},
|
|
40
|
+
EMAIL_VERIFICATION_SUCCESS: {
|
|
41
|
+
code: 'AUTH_S004',
|
|
42
|
+
message: 'Email verified successfully',
|
|
43
|
+
statusCode: 200,
|
|
44
|
+
},
|
|
45
|
+
PASSWORD_RESET_SUCCESS: {
|
|
46
|
+
code: 'AUTH_S005',
|
|
47
|
+
message: 'Password reset successful',
|
|
48
|
+
statusCode: 200,
|
|
49
|
+
},
|
|
50
|
+
INVALID_CREDENTIALS: {
|
|
51
|
+
code: ERROR_CODES.AUTH_INVALID_CREDENTIALS,
|
|
52
|
+
message: 'Invalid email or password',
|
|
53
|
+
statusCode: 401,
|
|
54
|
+
},
|
|
55
|
+
TOKEN_EXPIRED: {
|
|
56
|
+
code: ERROR_CODES.AUTH_TOKEN_EXPIRED,
|
|
57
|
+
message: 'Token has expired',
|
|
58
|
+
statusCode: 401,
|
|
59
|
+
},
|
|
60
|
+
INVALID_TOKEN: {
|
|
61
|
+
code: ERROR_CODES.AUTH_TOKEN_INVALID,
|
|
62
|
+
message: 'Invalid token provided',
|
|
63
|
+
statusCode: 401,
|
|
64
|
+
},
|
|
65
|
+
USER_NOT_FOUND: {
|
|
66
|
+
code: ERROR_CODES.AUTH_USER_NOT_FOUND,
|
|
67
|
+
message: 'User not found',
|
|
68
|
+
statusCode: 404,
|
|
69
|
+
},
|
|
70
|
+
EMAIL_NOT_VERIFIED: {
|
|
71
|
+
code: ERROR_CODES.AUTH_EMAIL_NOT_VERIFIED,
|
|
72
|
+
message: 'Please verify your email address',
|
|
73
|
+
statusCode: 403,
|
|
74
|
+
},
|
|
75
|
+
ACCOUNT_DISABLED: {
|
|
76
|
+
code: ERROR_CODES.AUTH_ACCOUNT_DISABLED,
|
|
77
|
+
message: 'Account has been disabled',
|
|
78
|
+
statusCode: 403,
|
|
79
|
+
},
|
|
80
|
+
INSUFFICIENT_PERMISSIONS: {
|
|
81
|
+
code: ERROR_CODES.AUTH_INSUFFICIENT_PERMISSIONS,
|
|
82
|
+
message: 'Insufficient permissions',
|
|
83
|
+
statusCode: 403,
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
// Validation messages
|
|
88
|
+
VALIDATION: {
|
|
89
|
+
REQUIRED_FIELD: {
|
|
90
|
+
code: ERROR_CODES.VALIDATION_REQUIRED_FIELD,
|
|
91
|
+
message: 'Required field is missing',
|
|
92
|
+
statusCode: 400,
|
|
93
|
+
},
|
|
94
|
+
INVALID_FORMAT: {
|
|
95
|
+
code: ERROR_CODES.VALIDATION_INVALID_FORMAT,
|
|
96
|
+
message: 'Invalid format provided',
|
|
97
|
+
statusCode: 400,
|
|
98
|
+
},
|
|
99
|
+
INVALID_EMAIL: {
|
|
100
|
+
code: ERROR_CODES.VALIDATION_INVALID_FORMAT,
|
|
101
|
+
message: 'Invalid email format',
|
|
102
|
+
statusCode: 400,
|
|
103
|
+
},
|
|
104
|
+
PASSWORD_TOO_SHORT: {
|
|
105
|
+
code: ERROR_CODES.VALIDATION_INVALID_LENGTH,
|
|
106
|
+
message: 'Password must be at least 8 characters long',
|
|
107
|
+
statusCode: 400,
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
// Database messages
|
|
112
|
+
DATABASE: {
|
|
113
|
+
CONNECTION_ERROR: {
|
|
114
|
+
code: ERROR_CODES.DB_CONNECTION_ERROR,
|
|
115
|
+
message: 'Database connection failed',
|
|
116
|
+
statusCode: 503,
|
|
117
|
+
},
|
|
118
|
+
RECORD_NOT_FOUND: {
|
|
119
|
+
code: ERROR_CODES.DB_RECORD_NOT_FOUND,
|
|
120
|
+
message: 'Record not found',
|
|
121
|
+
statusCode: 404,
|
|
122
|
+
},
|
|
123
|
+
DUPLICATE_ENTRY: {
|
|
124
|
+
code: ERROR_CODES.DB_DUPLICATE_ENTRY,
|
|
125
|
+
message: 'Record already exists',
|
|
126
|
+
statusCode: 409,
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
// File/Upload messages
|
|
131
|
+
FILE: {
|
|
132
|
+
UPLOAD_SUCCESS: {
|
|
133
|
+
code: 'FILE_S001',
|
|
134
|
+
message: 'File uploaded successfully',
|
|
135
|
+
statusCode: 200,
|
|
136
|
+
},
|
|
137
|
+
TOO_LARGE: {
|
|
138
|
+
code: ERROR_CODES.FILE_TOO_LARGE,
|
|
139
|
+
message: 'File size exceeds maximum limit',
|
|
140
|
+
statusCode: 413,
|
|
141
|
+
},
|
|
142
|
+
INVALID_TYPE: {
|
|
143
|
+
code: ERROR_CODES.FILE_INVALID_TYPE,
|
|
144
|
+
message: 'Invalid file type',
|
|
145
|
+
statusCode: 400,
|
|
146
|
+
},
|
|
147
|
+
NOT_FOUND: {
|
|
148
|
+
code: ERROR_CODES.FILE_NOT_FOUND,
|
|
149
|
+
message: 'File not found',
|
|
150
|
+
statusCode: 404,
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
// Rate limiting messages
|
|
155
|
+
RATE_LIMIT: {
|
|
156
|
+
EXCEEDED: {
|
|
157
|
+
code: ERROR_CODES.RATE_LIMIT_EXCEEDED,
|
|
158
|
+
message: 'Too many requests, please try again later',
|
|
159
|
+
statusCode: 429,
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
|
|
163
|
+
// Email messages
|
|
164
|
+
EMAIL: {
|
|
165
|
+
SEND_SUCCESS: {
|
|
166
|
+
code: 'EMAIL_S001',
|
|
167
|
+
message: 'Email sent successfully',
|
|
168
|
+
statusCode: 200,
|
|
169
|
+
},
|
|
170
|
+
SEND_FAILED: {
|
|
171
|
+
code: ERROR_CODES.EMAIL_SEND_FAILED,
|
|
172
|
+
message: 'Failed to send email',
|
|
173
|
+
statusCode: 503,
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
// Server messages
|
|
178
|
+
SERVER: {
|
|
179
|
+
INTERNAL_ERROR: {
|
|
180
|
+
code: ERROR_CODES.INTERNAL_SERVER_ERROR,
|
|
181
|
+
message: 'Internal server error',
|
|
182
|
+
statusCode: 500,
|
|
183
|
+
},
|
|
184
|
+
SERVICE_UNAVAILABLE: {
|
|
185
|
+
code: ERROR_CODES.SERVICE_UNAVAILABLE,
|
|
186
|
+
message: 'Service temporarily unavailable',
|
|
187
|
+
statusCode: 503,
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
} as const;
|