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/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;