create-arktos 1.2.0 โ 1.4.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/bin/cli.js +71 -10
- package/eslint.config.js.template +31 -0
- package/package.json +4 -1
- package/src-js/app.js +97 -0
- package/src-js/config/env.validation.js +15 -0
- package/src-js/config/logger.js +76 -0
- package/src-js/constants/errorCodes.js +57 -0
- package/src-js/constants/messages.js +240 -0
- package/src-js/controllers/auth.controller.js +533 -0
- package/src-js/middleware/index.js +284 -0
- package/src-js/routes/auth.routes.js +28 -0
- package/src-js/routes/index.js +18 -0
- package/src-js/schemas/index.js +60 -0
- package/src-js/services/database.service.js +89 -0
- package/src-js/services/email.service.js +161 -0
- package/src-js/services/jwt.service.js +177 -0
- package/src-js/utils/response.js +53 -0
- package/src-js/views/emails/notification.html +87 -0
- package/src-js/views/emails/resetPassword.html +118 -0
- package/src-js/views/emails/verification.html +107 -0
- package/src-js/views/emails/welcome.html +113 -0
- package/template.js.package.json +77 -0
package/bin/cli.js
CHANGED
|
@@ -3,11 +3,13 @@
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const { execSync } = require('child_process');
|
|
6
|
+
const { input, select } = require('inquirer');
|
|
6
7
|
|
|
7
|
-
const
|
|
8
|
+
const TEMPLATE_TS_DIR = path.join(__dirname, '../src');
|
|
9
|
+
const TEMPLATE_JS_DIR = path.join(__dirname, '../src-js');
|
|
8
10
|
const ROOT_DIR = path.join(__dirname, '..');
|
|
9
11
|
|
|
10
|
-
function createProject(projectName) {
|
|
12
|
+
async function createProject(projectName) {
|
|
11
13
|
console.log(`๐ Creating Arktos project: ${projectName}`);
|
|
12
14
|
console.log();
|
|
13
15
|
|
|
@@ -23,15 +25,29 @@ function createProject(projectName) {
|
|
|
23
25
|
process.exit(1);
|
|
24
26
|
}
|
|
25
27
|
|
|
28
|
+
// Ask user for language preference
|
|
29
|
+
const language = await select({
|
|
30
|
+
message: '๐ Which language would you like to use?',
|
|
31
|
+
choices: [
|
|
32
|
+
{ name: 'TypeScript (Recommended)', value: 'typescript' },
|
|
33
|
+
{ name: 'JavaScript', value: 'javascript' }
|
|
34
|
+
],
|
|
35
|
+
default: 'typescript'
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const useTypeScript = language === 'typescript';
|
|
39
|
+
const templateDir = useTypeScript ? TEMPLATE_TS_DIR : TEMPLATE_JS_DIR;
|
|
40
|
+
const packageTemplate = useTypeScript ? 'template.package.json' : 'template.js.package.json';
|
|
41
|
+
|
|
26
42
|
try {
|
|
27
43
|
// Create project directory
|
|
28
44
|
console.log('๐ Creating project directory...');
|
|
29
45
|
fs.mkdirSync(projectName);
|
|
30
46
|
|
|
31
47
|
// Copy src directory
|
|
32
|
-
console.log(
|
|
48
|
+
console.log(`๐ Copying ${useTypeScript ? 'TypeScript' : 'JavaScript'} source files...`);
|
|
33
49
|
const targetSrcDir = path.join(projectName, 'src');
|
|
34
|
-
copyDir(
|
|
50
|
+
copyDir(templateDir, targetSrcDir);
|
|
35
51
|
|
|
36
52
|
// Copy prisma directory
|
|
37
53
|
console.log('๐๏ธ Copying database schema...');
|
|
@@ -43,11 +59,11 @@ function createProject(projectName) {
|
|
|
43
59
|
|
|
44
60
|
// Copy template config files
|
|
45
61
|
console.log('๐ Copying configuration files...');
|
|
46
|
-
copyTemplateFiles(projectName);
|
|
62
|
+
copyTemplateFiles(projectName, useTypeScript);
|
|
47
63
|
|
|
48
64
|
// Copy and update package.json
|
|
49
65
|
console.log('๐ง Updating project configuration...');
|
|
50
|
-
const templatePackageJsonPath = path.join(ROOT_DIR,
|
|
66
|
+
const templatePackageJsonPath = path.join(ROOT_DIR, packageTemplate);
|
|
51
67
|
const targetPackageJsonPath = path.join(projectName, 'package.json');
|
|
52
68
|
|
|
53
69
|
if (fs.existsSync(templatePackageJsonPath)) {
|
|
@@ -59,6 +75,12 @@ function createProject(projectName) {
|
|
|
59
75
|
fs.writeFileSync(targetPackageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
60
76
|
}
|
|
61
77
|
|
|
78
|
+
// Clean up TypeScript specific files if JavaScript is selected
|
|
79
|
+
if (!useTypeScript) {
|
|
80
|
+
console.log('๐งน Cleaning up TypeScript specific files...');
|
|
81
|
+
cleanupTypeScriptFiles(projectName);
|
|
82
|
+
}
|
|
83
|
+
|
|
62
84
|
console.log('โ
Project created successfully!');
|
|
63
85
|
console.log();
|
|
64
86
|
console.log('๐ฏ Next steps:');
|
|
@@ -122,7 +144,30 @@ function copyDir(src, dest) {
|
|
|
122
144
|
}
|
|
123
145
|
}
|
|
124
146
|
|
|
125
|
-
function
|
|
147
|
+
function cleanupTypeScriptFiles(projectPath) {
|
|
148
|
+
try {
|
|
149
|
+
// Remove TypeScript specific directories and files
|
|
150
|
+
const tsSpecificPaths = [
|
|
151
|
+
path.join(projectPath, 'src', 'types'),
|
|
152
|
+
path.join(projectPath, 'tsconfig.json')
|
|
153
|
+
];
|
|
154
|
+
|
|
155
|
+
for (const tsPath of tsSpecificPaths) {
|
|
156
|
+
if (fs.existsSync(tsPath)) {
|
|
157
|
+
const stats = fs.statSync(tsPath);
|
|
158
|
+
if (stats.isDirectory()) {
|
|
159
|
+
fs.rmSync(tsPath, { recursive: true, force: true });
|
|
160
|
+
} else {
|
|
161
|
+
fs.unlinkSync(tsPath);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
} catch (error) {
|
|
166
|
+
console.warn('โ ๏ธ Warning: Could not clean up some TypeScript files:', error.message);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function copyTemplateFiles(dest, useTypeScript = true) {
|
|
126
171
|
// Create common config files
|
|
127
172
|
const configFiles = {
|
|
128
173
|
'.gitignore': `# Dependencies
|
|
@@ -263,8 +308,11 @@ Happy coding! ๐`
|
|
|
263
308
|
fs.writeFileSync(path.join(dest, fileName), content);
|
|
264
309
|
}
|
|
265
310
|
|
|
266
|
-
// Copy template files from root
|
|
267
|
-
const
|
|
311
|
+
// Copy template files from root based on language choice
|
|
312
|
+
const commonTemplateFiles = ['.env.example', 'vercel.json'];
|
|
313
|
+
const templateFiles = useTypeScript
|
|
314
|
+
? [...commonTemplateFiles, 'tsconfig.json', 'eslint.config.js']
|
|
315
|
+
: [...commonTemplateFiles];
|
|
268
316
|
|
|
269
317
|
for (const templateFile of templateFiles) {
|
|
270
318
|
const srcPath = path.join(ROOT_DIR, templateFile);
|
|
@@ -274,6 +322,16 @@ Happy coding! ๐`
|
|
|
274
322
|
fs.copyFileSync(srcPath, destPath);
|
|
275
323
|
}
|
|
276
324
|
}
|
|
325
|
+
|
|
326
|
+
// Copy appropriate ESLint config for JavaScript projects
|
|
327
|
+
if (!useTypeScript) {
|
|
328
|
+
const jsEslintConfigPath = path.join(ROOT_DIR, 'eslint.config.js.template');
|
|
329
|
+
const destEslintConfigPath = path.join(dest, 'eslint.config.js');
|
|
330
|
+
|
|
331
|
+
if (fs.existsSync(jsEslintConfigPath)) {
|
|
332
|
+
fs.copyFileSync(jsEslintConfigPath, destEslintConfigPath);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
277
335
|
}
|
|
278
336
|
|
|
279
337
|
function showHelp() {
|
|
@@ -324,4 +382,7 @@ if (args[0] === '-v' || args[0] === '--version') {
|
|
|
324
382
|
}
|
|
325
383
|
|
|
326
384
|
const projectName = args[0];
|
|
327
|
-
createProject(projectName)
|
|
385
|
+
createProject(projectName).catch((error) => {
|
|
386
|
+
console.error('โ Error creating project:', error.message);
|
|
387
|
+
process.exit(1);
|
|
388
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export default [
|
|
2
|
+
{
|
|
3
|
+
files: ['**/*.js'],
|
|
4
|
+
languageOptions: {
|
|
5
|
+
ecmaVersion: 'latest',
|
|
6
|
+
sourceType: 'module',
|
|
7
|
+
globals: {
|
|
8
|
+
process: 'readonly',
|
|
9
|
+
Buffer: 'readonly',
|
|
10
|
+
__dirname: 'readonly',
|
|
11
|
+
__filename: 'readonly',
|
|
12
|
+
console: 'readonly',
|
|
13
|
+
global: 'readonly',
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
rules: {
|
|
17
|
+
'no-unused-vars': ['warn', {
|
|
18
|
+
argsIgnorePattern: '^_',
|
|
19
|
+
varsIgnorePattern: '^_'
|
|
20
|
+
}],
|
|
21
|
+
'no-console': 'off',
|
|
22
|
+
'prefer-const': 'error',
|
|
23
|
+
'no-var': 'error',
|
|
24
|
+
'no-undef': 'error',
|
|
25
|
+
'semi': ['error', 'always'],
|
|
26
|
+
'quotes': ['error', 'single'],
|
|
27
|
+
'indent': ['error', 2],
|
|
28
|
+
'comma-dangle': ['error', 'always-multiline'],
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-arktos",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "๐ A modern Node.js backend boilerplate with TypeScript, Express, JWT authentication, Prisma ORM, PostgreSQL, and Resend email service. Includes complete authentication flow, security middleware, and database management.",
|
|
5
5
|
"main": "bin/cli.js",
|
|
6
6
|
"bin": {
|
|
@@ -9,12 +9,15 @@
|
|
|
9
9
|
"files": [
|
|
10
10
|
"bin/",
|
|
11
11
|
"src/",
|
|
12
|
+
"src-js/",
|
|
12
13
|
"prisma/",
|
|
13
14
|
"template.package.json",
|
|
15
|
+
"template.js.package.json",
|
|
14
16
|
".env.example",
|
|
15
17
|
"vercel.json",
|
|
16
18
|
"tsconfig.json",
|
|
17
19
|
"eslint.config.js",
|
|
20
|
+
"eslint.config.js.template",
|
|
18
21
|
"README.md",
|
|
19
22
|
"LICENSE"
|
|
20
23
|
],
|
package/src-js/app.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import dotenv from 'dotenv';
|
|
3
|
+
import routes from './routes/index.js';
|
|
4
|
+
import { errorHandling, securityMiddleware, rateLimiter } from './middleware/index.js';
|
|
5
|
+
import DatabaseService from './services/database.service.js';
|
|
6
|
+
import logger from './config/logger.js';
|
|
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('*', (req, res) => {
|
|
52
|
+
res.status(404).json({
|
|
53
|
+
success: false,
|
|
54
|
+
message: 'Route not found',
|
|
55
|
+
error: `Cannot ${req.method} ${req.originalUrl}`,
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Global error handler
|
|
60
|
+
app.use(errorHandling);
|
|
61
|
+
|
|
62
|
+
// Graceful shutdown
|
|
63
|
+
const gracefulShutdown = async (signal) => {
|
|
64
|
+
logger.info(`Received ${signal}, shutting down gracefully...`);
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
const dbService = DatabaseService.getInstance();
|
|
68
|
+
await dbService.disconnect();
|
|
69
|
+
logger.info('Database connection closed');
|
|
70
|
+
} catch (error) {
|
|
71
|
+
logger.error('Error during graceful shutdown:', error);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
process.exit(0);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
|
|
78
|
+
process.on('SIGINT', () => gracefulShutdown('SIGINT'));
|
|
79
|
+
|
|
80
|
+
const startServer = async () => {
|
|
81
|
+
try {
|
|
82
|
+
// Initialize database connection
|
|
83
|
+
const dbService = DatabaseService.getInstance();
|
|
84
|
+
await dbService.connect();
|
|
85
|
+
|
|
86
|
+
app.listen(PORT, () => {
|
|
87
|
+
logger.info(`Server running on port ${PORT}`);
|
|
88
|
+
logger.info(`Health check: http://localhost:${PORT}/health`);
|
|
89
|
+
logger.info(`API base URL: http://localhost:${PORT}/api`);
|
|
90
|
+
});
|
|
91
|
+
} catch (error) {
|
|
92
|
+
logger.error('Failed to start server:', error);
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
startServer();
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import logger from './logger.js';
|
|
2
|
+
import { envSchema } from '../schemas/index.js';
|
|
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
|
+
import fs from 'fs';
|
|
4
|
+
|
|
5
|
+
const logLevel = process.env.LOG_LEVEL || 'info';
|
|
6
|
+
const logFile = process.env.LOG_FILE || 'logs/app.log';
|
|
7
|
+
|
|
8
|
+
// Ensure logs directory exists
|
|
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,57 @@
|
|
|
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
|
+
};
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { ERROR_CODES } from './errorCodes.js';
|
|
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
|
+
TOKEN_INVALID: {
|
|
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: 'Email address is not verified',
|
|
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 to access this resource',
|
|
83
|
+
statusCode: 403,
|
|
84
|
+
},
|
|
85
|
+
USER_EXISTS: {
|
|
86
|
+
code: ERROR_CODES.AUTH_USER_EXISTS,
|
|
87
|
+
message: 'User already exists with this email',
|
|
88
|
+
statusCode: 409,
|
|
89
|
+
},
|
|
90
|
+
USERNAME_TAKEN: {
|
|
91
|
+
code: ERROR_CODES.AUTH_USERNAME_TAKEN,
|
|
92
|
+
message: 'Username is already taken',
|
|
93
|
+
statusCode: 409,
|
|
94
|
+
},
|
|
95
|
+
TOKEN_REQUIRED: {
|
|
96
|
+
code: ERROR_CODES.AUTH_TOKEN_REQUIRED,
|
|
97
|
+
message: 'Authentication token is required',
|
|
98
|
+
statusCode: 401,
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
// Validation messages
|
|
103
|
+
VALIDATION: {
|
|
104
|
+
REQUIRED_FIELD: {
|
|
105
|
+
code: ERROR_CODES.VALIDATION_REQUIRED_FIELD,
|
|
106
|
+
message: 'Required field is missing',
|
|
107
|
+
statusCode: 400,
|
|
108
|
+
},
|
|
109
|
+
INVALID_FORMAT: {
|
|
110
|
+
code: ERROR_CODES.VALIDATION_INVALID_FORMAT,
|
|
111
|
+
message: 'Invalid format provided',
|
|
112
|
+
statusCode: 400,
|
|
113
|
+
},
|
|
114
|
+
INVALID_LENGTH: {
|
|
115
|
+
code: ERROR_CODES.VALIDATION_INVALID_LENGTH,
|
|
116
|
+
message: 'Invalid length for field',
|
|
117
|
+
statusCode: 400,
|
|
118
|
+
},
|
|
119
|
+
INVALID_TYPE: {
|
|
120
|
+
code: ERROR_CODES.VALIDATION_INVALID_TYPE,
|
|
121
|
+
message: 'Invalid type provided',
|
|
122
|
+
statusCode: 400,
|
|
123
|
+
},
|
|
124
|
+
VALIDATION_ERROR: {
|
|
125
|
+
code: ERROR_CODES.VALIDATION_ERROR,
|
|
126
|
+
message: 'Validation failed',
|
|
127
|
+
statusCode: 400,
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
// Database messages
|
|
132
|
+
DATABASE: {
|
|
133
|
+
CONNECTION_ERROR: {
|
|
134
|
+
code: ERROR_CODES.DB_CONNECTION_ERROR,
|
|
135
|
+
message: 'Database connection failed',
|
|
136
|
+
statusCode: 503,
|
|
137
|
+
},
|
|
138
|
+
QUERY_ERROR: {
|
|
139
|
+
code: ERROR_CODES.DB_QUERY_ERROR,
|
|
140
|
+
message: 'Database query failed',
|
|
141
|
+
statusCode: 500,
|
|
142
|
+
},
|
|
143
|
+
CONSTRAINT_VIOLATION: {
|
|
144
|
+
code: ERROR_CODES.DB_CONSTRAINT_VIOLATION,
|
|
145
|
+
message: 'Database constraint violation',
|
|
146
|
+
statusCode: 409,
|
|
147
|
+
},
|
|
148
|
+
RECORD_NOT_FOUND: {
|
|
149
|
+
code: ERROR_CODES.DB_RECORD_NOT_FOUND,
|
|
150
|
+
message: 'Record not found',
|
|
151
|
+
statusCode: 404,
|
|
152
|
+
},
|
|
153
|
+
DUPLICATE_ENTRY: {
|
|
154
|
+
code: ERROR_CODES.DB_DUPLICATE_ENTRY,
|
|
155
|
+
message: 'Duplicate entry detected',
|
|
156
|
+
statusCode: 409,
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
// File upload messages
|
|
161
|
+
FILE: {
|
|
162
|
+
TOO_LARGE: {
|
|
163
|
+
code: ERROR_CODES.FILE_TOO_LARGE,
|
|
164
|
+
message: 'File size exceeds maximum limit',
|
|
165
|
+
statusCode: 413,
|
|
166
|
+
},
|
|
167
|
+
INVALID_TYPE: {
|
|
168
|
+
code: ERROR_CODES.FILE_INVALID_TYPE,
|
|
169
|
+
message: 'Invalid file type',
|
|
170
|
+
statusCode: 400,
|
|
171
|
+
},
|
|
172
|
+
UPLOAD_FAILED: {
|
|
173
|
+
code: ERROR_CODES.FILE_UPLOAD_FAILED,
|
|
174
|
+
message: 'File upload failed',
|
|
175
|
+
statusCode: 500,
|
|
176
|
+
},
|
|
177
|
+
NOT_FOUND: {
|
|
178
|
+
code: ERROR_CODES.FILE_NOT_FOUND,
|
|
179
|
+
message: 'File not found',
|
|
180
|
+
statusCode: 404,
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
// Rate limiting messages
|
|
185
|
+
RATE_LIMIT: {
|
|
186
|
+
EXCEEDED: {
|
|
187
|
+
code: ERROR_CODES.RATE_LIMIT_EXCEEDED,
|
|
188
|
+
message: 'Rate limit exceeded. Please try again later',
|
|
189
|
+
statusCode: 429,
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
// Server messages
|
|
194
|
+
SERVER: {
|
|
195
|
+
INTERNAL_ERROR: {
|
|
196
|
+
code: ERROR_CODES.INTERNAL_SERVER_ERROR,
|
|
197
|
+
message: 'Internal server error occurred',
|
|
198
|
+
statusCode: 500,
|
|
199
|
+
},
|
|
200
|
+
SERVICE_UNAVAILABLE: {
|
|
201
|
+
code: ERROR_CODES.SERVICE_UNAVAILABLE,
|
|
202
|
+
message: 'Service temporarily unavailable',
|
|
203
|
+
statusCode: 503,
|
|
204
|
+
},
|
|
205
|
+
EXTERNAL_SERVICE_ERROR: {
|
|
206
|
+
code: ERROR_CODES.EXTERNAL_SERVICE_ERROR,
|
|
207
|
+
message: 'External service error',
|
|
208
|
+
statusCode: 502,
|
|
209
|
+
},
|
|
210
|
+
GENERIC_ERROR: {
|
|
211
|
+
code: ERROR_CODES.GENERIC_ERROR,
|
|
212
|
+
message: 'An unexpected error occurred',
|
|
213
|
+
statusCode: 500,
|
|
214
|
+
},
|
|
215
|
+
ROUTE_NOT_FOUND: {
|
|
216
|
+
code: ERROR_CODES.ROUTE_NOT_FOUND,
|
|
217
|
+
message: 'Route not found',
|
|
218
|
+
statusCode: 404,
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
|
|
222
|
+
// Email service messages
|
|
223
|
+
EMAIL: {
|
|
224
|
+
SEND_FAILED: {
|
|
225
|
+
code: ERROR_CODES.EMAIL_SEND_FAILED,
|
|
226
|
+
message: 'Failed to send email',
|
|
227
|
+
statusCode: 500,
|
|
228
|
+
},
|
|
229
|
+
TEMPLATE_NOT_FOUND: {
|
|
230
|
+
code: ERROR_CODES.EMAIL_TEMPLATE_NOT_FOUND,
|
|
231
|
+
message: 'Email template not found',
|
|
232
|
+
statusCode: 404,
|
|
233
|
+
},
|
|
234
|
+
INVALID_RECIPIENT: {
|
|
235
|
+
code: ERROR_CODES.EMAIL_INVALID_RECIPIENT,
|
|
236
|
+
message: 'Invalid email recipient',
|
|
237
|
+
statusCode: 400,
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
};
|