add changes

This commit is contained in:
AD2025
2025-11-11 00:25:50 +02:00
commit e3ca132c5e
86 changed files with 22238 additions and 0 deletions

113
backend/config/config.js Normal file
View File

@@ -0,0 +1,113 @@
require('dotenv').config();
/**
* Application Configuration
* Centralized configuration management for all environment variables
*/
const config = {
// Server Configuration
server: {
nodeEnv: process.env.NODE_ENV || 'development',
port: parseInt(process.env.PORT) || 3000,
apiPrefix: process.env.API_PREFIX || '/api',
isDevelopment: (process.env.NODE_ENV || 'development') === 'development',
isProduction: process.env.NODE_ENV === 'production',
isTest: process.env.NODE_ENV === 'test'
},
// Database Configuration
database: {
host: process.env.DB_HOST || 'localhost',
port: parseInt(process.env.DB_PORT) || 3306,
name: process.env.DB_NAME || 'interview_quiz_db',
user: process.env.DB_USER || 'root',
password: process.env.DB_PASSWORD || '',
dialect: process.env.DB_DIALECT || 'mysql',
pool: {
max: parseInt(process.env.DB_POOL_MAX) || 10,
min: parseInt(process.env.DB_POOL_MIN) || 0,
acquire: parseInt(process.env.DB_POOL_ACQUIRE) || 30000,
idle: parseInt(process.env.DB_POOL_IDLE) || 10000
}
},
// JWT Configuration
jwt: {
secret: process.env.JWT_SECRET,
expire: process.env.JWT_EXPIRE || '24h',
algorithm: 'HS256'
},
// Rate Limiting Configuration
rateLimit: {
windowMs: parseInt(process.env.RATE_LIMIT_WINDOW_MS) || 900000, // 15 minutes
maxRequests: parseInt(process.env.RATE_LIMIT_MAX_REQUESTS) || 100,
message: 'Too many requests from this IP, please try again later.'
},
// CORS Configuration
cors: {
origin: process.env.CORS_ORIGIN || 'http://localhost:4200',
credentials: true
},
// Guest Session Configuration
guest: {
sessionExpireHours: parseInt(process.env.GUEST_SESSION_EXPIRE_HOURS) || 24,
maxQuizzes: parseInt(process.env.GUEST_MAX_QUIZZES) || 3
},
// Logging Configuration
logging: {
level: process.env.LOG_LEVEL || 'info'
},
// Pagination Defaults
pagination: {
defaultLimit: 10,
maxLimit: 100
},
// Security Configuration
security: {
bcryptRounds: 10,
maxLoginAttempts: 5,
lockoutDuration: 15 * 60 * 1000 // 15 minutes
}
};
/**
* Validate critical configuration values
*/
function validateConfig() {
const errors = [];
if (!config.jwt.secret) {
errors.push('JWT_SECRET is not configured');
}
if (!config.database.name) {
errors.push('DB_NAME is not configured');
}
if (config.server.isProduction && !config.database.password) {
errors.push('DB_PASSWORD is required in production');
}
if (errors.length > 0) {
throw new Error(`Configuration errors:\n - ${errors.join('\n - ')}`);
}
return true;
}
// Validate on module load
try {
validateConfig();
} catch (error) {
console.error('❌ Configuration Error:', error.message);
process.exit(1);
}
module.exports = config;

View File

@@ -0,0 +1,76 @@
require('dotenv').config();
module.exports = {
development: {
username: process.env.DB_USER || 'root',
password: process.env.DB_PASSWORD || '',
database: process.env.DB_NAME || 'interview_quiz_db',
host: process.env.DB_HOST || 'localhost',
port: process.env.DB_PORT || 3306,
dialect: process.env.DB_DIALECT || 'mysql',
logging: console.log,
pool: {
max: parseInt(process.env.DB_POOL_MAX) || 10,
min: parseInt(process.env.DB_POOL_MIN) || 0,
acquire: parseInt(process.env.DB_POOL_ACQUIRE) || 30000,
idle: parseInt(process.env.DB_POOL_IDLE) || 10000
},
define: {
timestamps: true,
underscored: true,
freezeTableName: false,
charset: 'utf8mb4',
collate: 'utf8mb4_unicode_ci'
}
},
test: {
username: process.env.DB_USER || 'root',
password: process.env.DB_PASSWORD || '',
database: process.env.DB_NAME + '_test' || 'interview_quiz_db_test',
host: process.env.DB_HOST || 'localhost',
port: process.env.DB_PORT || 3306,
dialect: process.env.DB_DIALECT || 'mysql',
logging: false,
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000
},
define: {
timestamps: true,
underscored: true,
freezeTableName: false,
charset: 'utf8mb4',
collate: 'utf8mb4_unicode_ci'
}
},
production: {
username: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
host: process.env.DB_HOST,
port: process.env.DB_PORT || 3306,
dialect: process.env.DB_DIALECT || 'mysql',
logging: false,
pool: {
max: parseInt(process.env.DB_POOL_MAX) || 20,
min: parseInt(process.env.DB_POOL_MIN) || 5,
acquire: parseInt(process.env.DB_POOL_ACQUIRE) || 30000,
idle: parseInt(process.env.DB_POOL_IDLE) || 10000
},
define: {
timestamps: true,
underscored: true,
freezeTableName: false,
charset: 'utf8mb4',
collate: 'utf8mb4_unicode_ci'
},
dialectOptions: {
ssl: {
require: true,
rejectUnauthorized: false
}
}
}
};

74
backend/config/db.js Normal file
View File

@@ -0,0 +1,74 @@
const db = require('../models');
/**
* Test database connection
*/
async function testConnection() {
try {
await db.sequelize.authenticate();
console.log('✅ Database connection verified');
return true;
} catch (error) {
console.error('❌ Database connection failed:', error.message);
return false;
}
}
/**
* Sync all models with database
* WARNING: Use with caution in production
*/
async function syncModels(options = {}) {
try {
await db.sequelize.sync(options);
console.log('✅ Models synchronized with database');
return true;
} catch (error) {
console.error('❌ Model synchronization failed:', error.message);
return false;
}
}
/**
* Close database connection
*/
async function closeConnection() {
try {
await db.sequelize.close();
console.log('✅ Database connection closed');
return true;
} catch (error) {
console.error('❌ Failed to close database connection:', error.message);
return false;
}
}
/**
* Get database statistics
*/
async function getDatabaseStats() {
try {
const [tables] = await db.sequelize.query('SHOW TABLES');
const [version] = await db.sequelize.query('SELECT VERSION() as version');
return {
connected: true,
version: version[0].version,
tables: tables.length,
database: db.sequelize.config.database
};
} catch (error) {
return {
connected: false,
error: error.message
};
}
}
module.exports = {
db,
testConnection,
syncModels,
closeConnection,
getDatabaseStats
};