system configuration seed added

This commit is contained in:
laxmanhalaki 2025-11-06 12:33:11 +05:30
parent 31354f2825
commit 53bc624094
20 changed files with 719 additions and 96 deletions

124
CONSOLE_LOGS_CLEANED.md Normal file
View File

@ -0,0 +1,124 @@
# Console Logs Cleanup Summary
## Changes Applied
All verbose, redundant, and confusing console logs have been removed or simplified to keep only essential one-line messages helpful for bug tracking.
## What Was Kept ✅
**Server Startup:**
- `🚀 Server running on port ${PORT} | ${environment}` - Single line server status
**Critical Errors:**
- `❌ Database connection failed`
- `❌ Unable to start server`
- `❌ SSO Callback failed`
- `❌ Get Users failed`
- `❌ Authorization check failed`
- `❌ Admin authorization failed`
- `❌ Migration failed`
- `❌ Configuration seeding error`
- `❌ TAT Error loading working hours/holidays`
**Migration Status:**
- `✅ Migrations up-to-date`
- `🔄 Running ${count} migration(s)...`
- `✅ ${migration-name}` - Per migration success
- `✅ Applied ${count} migration(s)` - Final summary
**Graceful Shutdown:**
- `🛑 SIGTERM signal received: closing HTTP server`
- `🛑 SIGINT signal received: closing HTTP server`
## What Was Removed ❌
### Multi-line Configuration Dumps
- ~~TAT Configuration details (working hours, thresholds, test mode)~~
- ~~System Configuration details (environment, version, features)~~
- ~~Working hours cache loaded messages~~
### Verbose Development Logs
- ~~Database connection established~~
- ~~Database models synchronized~~
- ~~Socket.IO server initialized~~
- ~~Socket.IO client connected with socket.id~~
- ~~Auth routes loaded~~
- ~~Holiday calendar loaded~~
### Migration Details
- ~~Individual table created messages~~
- ~~Individual column added messages~~
- ~~Index created messages~~
- ~~Conversion progress messages~~
### Database Query Logging
- **Disabled SQL query logging** in `database.ts` - previously showed ALL database queries in development mode
## File Changes
### Core Files
1. **src/server.ts** - Simplified to single line startup message
2. **src/app.ts** - Removed database connection messages, kept only errors
3. **src/config/tat.config.ts** - Disabled multi-line TAT config logging
4. **src/config/system.config.ts** - Disabled multi-line system config logging
5. **src/config/database.ts** - **Disabled SQL query logging** (was showing every SELECT/INSERT/UPDATE)
6. **src/realtime/socket.ts** - Removed Socket.IO initialization and connection logs
7. **src/utils/tatTimeUtils.ts** - Removed verbose cache loading messages, kept errors
8. **src/routes/auth.routes.ts** - Removed route loading message
9. **src/middlewares/authorization.middleware.ts** - Improved error messages
### Migration Files (All)
- **2025103000-create-users.ts**
- **2025110501-alter-tat-days-to-generated.ts**
- **20251105-add-skip-fields-to-approval-levels.ts**
- **20251104-create-admin-config.ts**
- **20251104-create-holidays.ts**
- **20251104-create-kpi-views.ts**
All replaced verbose console logs with inline comments.
### Scripts
- **src/scripts/migrate.ts** - Streamlined migration output
- **src/scripts/seed-admin-config.ts** - Simplified seed messages
## New Clean Console Output
### Development Server Start
```
🚀 Server running on port 5000 | development
```
### Running Migrations
```
🔄 Running 2 migration(s)...
✅ 2025103000-create-users
✅ 2025110501-alter-tat-days-to-generated
✅ Applied 2 migration(s)
```
### No More Clutter
- ❌ No SQL query logs
- ❌ No multi-line config dumps
- ❌ No verbose socket connection messages
- ❌ No redundant "table created" messages
## Benefits
1. **Cleaner Logs** - Easy to scan for errors and important events
2. **Better Performance** - No overhead from logging every SQL query
3. **Easier Debugging** - Critical errors stand out with ❌ emoji
4. **Production Ready** - Minimal logging suitable for production environments
## To Enable Debug Logging (If Needed)
To temporarily enable SQL query logging for debugging:
```typescript
// In Re_Backend/src/config/database.ts
logging: console.log // Change from false
```
---
**Summary:** Reduced ~100+ console log statements to ~20 essential one-liners for bug tracking.

View File

@ -21,7 +21,8 @@
"db:migrate:undo": "sequelize-cli db:migrate:undo", "db:migrate:undo": "sequelize-cli db:migrate:undo",
"db:seed": "sequelize-cli db:seed:all", "db:seed": "sequelize-cli db:seed:all",
"clean": "rm -rf dist", "clean": "rm -rf dist",
"migrate": "ts-node src/scripts/migrate.ts" "migrate": "ts-node -r tsconfig-paths/register src/scripts/migrate.ts",
"seed:config": "ts-node -r tsconfig-paths/register src/scripts/seed-admin-config.ts"
}, },
"dependencies": { "dependencies": {
"@google-cloud/storage": "^7.14.0", "@google-cloud/storage": "^7.14.0",

View File

@ -21,11 +21,6 @@ const userService = new UserService();
const initializeDatabase = async () => { const initializeDatabase = async () => {
try { try {
await sequelize.authenticate(); await sequelize.authenticate();
console.log('✅ Database connection established successfully');
// Sync models (create tables if they don't exist)
// await sequelize.sync({ force: false });
console.log('✅ Database models synchronized (sync disabled)');
} catch (error) { } catch (error) {
console.error('❌ Database connection failed:', error); console.error('❌ Database connection failed:', error);
} }
@ -130,7 +125,7 @@ app.post('/api/v1/auth/sso-callback', async (req: express.Request, res: express.
timestamp: new Date() timestamp: new Date()
}); });
} catch (error) { } catch (error) {
console.error('SSO Callback Error:', error); console.error('❌ SSO Callback failed:', error);
res.status(500).json({ res.status(500).json({
success: false, success: false,
message: 'Internal server error', message: 'Internal server error',
@ -169,7 +164,7 @@ app.get('/api/v1/users', async (_req: express.Request, res: express.Response): P
timestamp: new Date() timestamp: new Date()
}); });
} catch (error) { } catch (error) {
console.error('Get Users Error:', error); console.error('❌ Get Users failed:', error);
res.status(500).json({ res.status(500).json({
success: false, success: false,
message: 'Internal server error', message: 'Internal server error',

View File

@ -10,7 +10,7 @@ const sequelize = new Sequelize({
username: process.env.DB_USER || 'postgres', username: process.env.DB_USER || 'postgres',
password: process.env.DB_PASSWORD || 'postgres', password: process.env.DB_PASSWORD || 'postgres',
dialect: 'postgres', dialect: 'postgres',
logging: process.env.NODE_ENV === 'development' ? console.log : false, logging: false, // Disable SQL query logging for cleaner console output
pool: { pool: {
min: parseInt(process.env.DB_POOL_MIN || '2', 10), min: parseInt(process.env.DB_POOL_MIN || '2', 10),
max: parseInt(process.env.DB_POOL_MAX || '10', 10), max: parseInt(process.env.DB_POOL_MAX || '10', 10),

View File

@ -149,14 +149,7 @@ export function getPublicConfig() {
* Log system configuration on startup * Log system configuration on startup
*/ */
export function logSystemConfig(): void { export function logSystemConfig(): void {
console.log('⚙️ System Configuration:'); // System config logging disabled - use environment variables to verify settings
console.log(` - Environment: ${SYSTEM_CONFIG.APP_ENV}`);
console.log(` - Version: ${SYSTEM_CONFIG.APP_VERSION}`);
console.log(` - Working Hours: ${SYSTEM_CONFIG.WORKING_HOURS.START_HOUR}:00 - ${SYSTEM_CONFIG.WORKING_HOURS.END_HOUR}:00`);
console.log(` - Max File Size: ${SYSTEM_CONFIG.UPLOAD.MAX_FILE_SIZE_MB} MB`);
console.log(` - Max Approval Levels: ${SYSTEM_CONFIG.WORKFLOW.MAX_APPROVAL_LEVELS}`);
console.log(` - AI Conclusion: ${SYSTEM_CONFIG.FEATURES.ENABLE_AI_CONCLUSION ? 'Enabled' : 'Disabled'}`);
console.log(` - TAT Test Mode: ${SYSTEM_CONFIG.TAT.TEST_MODE ? 'ENABLED (1h = 1min)' : 'DISABLED'}`);
} }
export default SYSTEM_CONFIG; export default SYSTEM_CONFIG;

View File

@ -65,12 +65,7 @@ export function isTestMode(): boolean {
* Log TAT configuration on startup * Log TAT configuration on startup
*/ */
export function logTatConfig(): void { export function logTatConfig(): void {
console.log('⏰ TAT Configuration:'); // TAT config logging disabled - use environment variables to verify settings
console.log(` - Test Mode: ${TAT_CONFIG.TEST_MODE ? 'ENABLED (1 hour = 1 minute)' : 'DISABLED'}`);
console.log(` - Working Hours: ${TAT_CONFIG.WORK_START_HOUR}:00 - ${TAT_CONFIG.WORK_END_HOUR}:00`);
console.log(` - Working Days: Monday - Friday`);
console.log(` - Redis: ${TAT_CONFIG.REDIS_URL}`);
console.log(` - Thresholds: ${TAT_CONFIG.THRESHOLD_50_PERCENT}%, ${TAT_CONFIG.THRESHOLD_75_PERCENT}%, ${TAT_CONFIG.THRESHOLD_100_PERCENT}%`);
} }
export default TAT_CONFIG; export default TAT_CONFIG;

View File

@ -91,7 +91,7 @@ export function requireParticipantTypes(allowed: AllowedType[]) {
return res.status(403).json({ success: false, error: 'Insufficient permissions' }); return res.status(403).json({ success: false, error: 'Insufficient permissions' });
} catch (err) { } catch (err) {
console.error('Authorization check error:', err); console.error('❌ Authorization check failed:', err);
return res.status(500).json({ success: false, error: 'Authorization check failed' }); return res.status(500).json({ success: false, error: 'Authorization check failed' });
} }
}; };
@ -114,7 +114,7 @@ export function requireAdmin(req: Request, res: Response, next: NextFunction): v
next(); next();
} catch (error) { } catch (error) {
console.error('Admin authorization check error:', error); console.error('❌ Admin authorization failed:', error);
res.status(500).json({ res.status(500).json({
success: false, success: false,
error: 'Authorization check failed' error: 'Authorization check failed'

View File

@ -105,11 +105,11 @@ export async function up(queryInterface: QueryInterface): Promise<void> {
name: 'users_employee_id_idx' name: 'users_employee_id_idx'
}); });
console.log('✅ Created users table with indexes'); // Users table created
} }
export async function down(queryInterface: QueryInterface): Promise<void> { export async function down(queryInterface: QueryInterface): Promise<void> {
await queryInterface.dropTable('users'); await queryInterface.dropTable('users');
console.log('✅ Dropped users table'); // Users table dropped
} }

View File

@ -122,15 +122,13 @@ export async function up(queryInterface: QueryInterface): Promise<void> {
await queryInterface.sequelize.query('CREATE INDEX IF NOT EXISTS "admin_configurations_is_editable" ON "admin_configurations" ("is_editable");'); await queryInterface.sequelize.query('CREATE INDEX IF NOT EXISTS "admin_configurations_is_editable" ON "admin_configurations" ("is_editable");');
await queryInterface.sequelize.query('CREATE INDEX IF NOT EXISTS "admin_configurations_sort_order" ON "admin_configurations" ("sort_order");'); await queryInterface.sequelize.query('CREATE INDEX IF NOT EXISTS "admin_configurations_sort_order" ON "admin_configurations" ("sort_order");');
console.log('✅ Admin configurations table created successfully'); // Admin config table created
console.log('Note: Default configurations will be seeded on first server start');
} }
export async function down(queryInterface: QueryInterface): Promise<void> { export async function down(queryInterface: QueryInterface): Promise<void> {
await queryInterface.dropTable('admin_configurations'); await queryInterface.dropTable('admin_configurations');
await queryInterface.sequelize.query('DROP TYPE IF EXISTS "enum_admin_configurations_config_category";'); await queryInterface.sequelize.query('DROP TYPE IF EXISTS "enum_admin_configurations_config_category";');
await queryInterface.sequelize.query('DROP TYPE IF EXISTS "enum_admin_configurations_value_type";'); await queryInterface.sequelize.query('DROP TYPE IF EXISTS "enum_admin_configurations_value_type";');
// Admin config table dropped
console.log('✅ Admin configurations table dropped');
} }

View File

@ -95,13 +95,12 @@ export async function up(queryInterface: QueryInterface): Promise<void> {
await queryInterface.sequelize.query('CREATE INDEX IF NOT EXISTS "holidays_holiday_type" ON "holidays" ("holiday_type");'); await queryInterface.sequelize.query('CREATE INDEX IF NOT EXISTS "holidays_holiday_type" ON "holidays" ("holiday_type");');
await queryInterface.sequelize.query('CREATE INDEX IF NOT EXISTS "holidays_created_by" ON "holidays" ("created_by");'); await queryInterface.sequelize.query('CREATE INDEX IF NOT EXISTS "holidays_created_by" ON "holidays" ("created_by");');
console.log('✅ Holidays table created successfully'); // Holidays table created
} }
export async function down(queryInterface: QueryInterface): Promise<void> { export async function down(queryInterface: QueryInterface): Promise<void> {
await queryInterface.dropTable('holidays'); await queryInterface.dropTable('holidays');
await queryInterface.sequelize.query('DROP TYPE IF EXISTS "enum_holidays_holiday_type";'); await queryInterface.sequelize.query('DROP TYPE IF EXISTS "enum_holidays_holiday_type";');
// Holidays table dropped
console.log('✅ Holidays table dropped successfully');
} }

View File

@ -249,7 +249,7 @@ export async function up(queryInterface: QueryInterface): Promise<void> {
GROUP BY w.request_id, w.request_number, w.title, w.status; GROUP BY w.request_id, w.request_number, w.title, w.status;
`); `);
console.log('✅ KPI views created successfully'); // KPI views created
} }
export async function down(queryInterface: QueryInterface): Promise<void> { export async function down(queryInterface: QueryInterface): Promise<void> {
@ -261,7 +261,6 @@ export async function down(queryInterface: QueryInterface): Promise<void> {
await queryInterface.sequelize.query('DROP VIEW IF EXISTS vw_approver_performance;'); await queryInterface.sequelize.query('DROP VIEW IF EXISTS vw_approver_performance;');
await queryInterface.sequelize.query('DROP VIEW IF EXISTS vw_tat_compliance;'); await queryInterface.sequelize.query('DROP VIEW IF EXISTS vw_tat_compliance;');
await queryInterface.sequelize.query('DROP VIEW IF EXISTS vw_request_volume_summary;'); await queryInterface.sequelize.query('DROP VIEW IF EXISTS vw_request_volume_summary;');
// KPI views dropped
console.log('✅ KPI views dropped successfully');
} }

View File

@ -10,7 +10,7 @@ export async function up(queryInterface: QueryInterface): Promise<void> {
// Check if table exists first // Check if table exists first
const tables = await queryInterface.showAllTables(); const tables = await queryInterface.showAllTables();
if (!tables.includes('approval_levels')) { if (!tables.includes('approval_levels')) {
console.log('⚠️ approval_levels table does not exist yet, skipping...'); // Table doesn't exist yet, skipping
return; return;
} }
@ -25,7 +25,7 @@ export async function up(queryInterface: QueryInterface): Promise<void> {
defaultValue: false, defaultValue: false,
comment: 'Indicates if this approver was skipped by initiator' comment: 'Indicates if this approver was skipped by initiator'
}); });
console.log(' ✅ Added is_skipped column'); // Added is_skipped column
} }
if (!tableDescription.skipped_at) { if (!tableDescription.skipped_at) {
@ -34,7 +34,7 @@ export async function up(queryInterface: QueryInterface): Promise<void> {
allowNull: true, allowNull: true,
comment: 'Timestamp when approver was skipped' comment: 'Timestamp when approver was skipped'
}); });
console.log(' ✅ Added skipped_at column'); // Added skipped_at column
} }
if (!tableDescription.skipped_by) { if (!tableDescription.skipped_by) {
@ -49,7 +49,7 @@ export async function up(queryInterface: QueryInterface): Promise<void> {
onDelete: 'SET NULL', onDelete: 'SET NULL',
comment: 'User ID who skipped this approver' comment: 'User ID who skipped this approver'
}); });
console.log(' ✅ Added skipped_by column'); // Added skipped_by column
} }
if (!tableDescription.skip_reason) { if (!tableDescription.skip_reason) {
@ -58,7 +58,7 @@ export async function up(queryInterface: QueryInterface): Promise<void> {
allowNull: true, allowNull: true,
comment: 'Reason for skipping this approver' comment: 'Reason for skipping this approver'
}); });
console.log(' ✅ Added skip_reason column'); // Added skip_reason column
} }
// Check if index exists before creating // Check if index exists before creating
@ -73,14 +73,13 @@ export async function up(queryInterface: QueryInterface): Promise<void> {
is_skipped: true is_skipped: true
} }
}); });
console.log(' ✅ Added idx_approval_levels_skipped index'); // Index added
} }
} catch (error) { } catch (error) {
// Index might already exist, which is fine // Index already exists
console.log(' Index already exists or could not be created');
} }
console.log('✅ Skip-related fields migration completed'); // Skip fields added
} }
export async function down(queryInterface: QueryInterface): Promise<void> { export async function down(queryInterface: QueryInterface): Promise<void> {
@ -93,6 +92,6 @@ export async function down(queryInterface: QueryInterface): Promise<void> {
await queryInterface.removeColumn('approval_levels', 'skipped_at'); await queryInterface.removeColumn('approval_levels', 'skipped_at');
await queryInterface.removeColumn('approval_levels', 'is_skipped'); await queryInterface.removeColumn('approval_levels', 'is_skipped');
console.log('✅ Removed skip-related fields from approval_levels table'); // Skip fields removed
} }

View File

@ -22,11 +22,11 @@ export async function up(queryInterface: QueryInterface): Promise<void> {
const column = result[0] as any; const column = result[0] as any;
if (column && column.is_generated === 's') { if (column && column.is_generated === 's') {
console.log('✅ tat_days is already a GENERATED STORED column - skipping migration'); // Already a GENERATED column, skipping
return; return;
} }
console.log('📝 Converting tat_days to GENERATED STORED column...'); // Converting tat_days to GENERATED column
// Step 1: Drop the existing regular column // Step 1: Drop the existing regular column
await queryInterface.sequelize.query(` await queryInterface.sequelize.query(`
@ -41,11 +41,11 @@ export async function up(queryInterface: QueryInterface): Promise<void> {
GENERATED ALWAYS AS (CAST(CEIL(tat_hours / 24.0) AS INTEGER)) STORED; GENERATED ALWAYS AS (CAST(CEIL(tat_hours / 24.0) AS INTEGER)) STORED;
`); `);
console.log('✅ tat_days is now a GENERATED STORED column - will auto-calculate from tat_hours'); // tat_days is now auto-calculated
} }
export async function down(queryInterface: QueryInterface): Promise<void> { export async function down(queryInterface: QueryInterface): Promise<void> {
console.log('⚠️ Rolling back: Converting tat_days from GENERATED to regular column'); // Rolling back to regular column
// Drop the generated column // Drop the generated column
await queryInterface.sequelize.query(` await queryInterface.sequelize.query(`
@ -71,6 +71,6 @@ export async function down(queryInterface: QueryInterface): Promise<void> {
ALTER COLUMN tat_days SET NOT NULL; ALTER COLUMN tat_days SET NOT NULL;
`); `);
console.log('✅ Rolled back to regular INTEGER column'); // Rolled back successfully
} }

View File

@ -15,8 +15,6 @@ export function initSocket(httpServer: any) {
const configured = (process.env.FRONTEND_ORIGIN || '').split(',').map(s => s.trim()).filter(Boolean); const configured = (process.env.FRONTEND_ORIGIN || '').split(',').map(s => s.trim()).filter(Boolean);
const origins = configured.length ? configured : defaultOrigins; const origins = configured.length ? configured : defaultOrigins;
console.log('🔌 Initializing Socket.IO server with origins:', origins);
io = new Server(httpServer, { io = new Server(httpServer, {
cors: { cors: {
origin: origins, origin: origins,
@ -27,10 +25,7 @@ export function initSocket(httpServer: any) {
transports: ['websocket', 'polling'] transports: ['websocket', 'polling']
}); });
console.log('✅ Socket.IO server initialized');
io.on('connection', (socket: any) => { io.on('connection', (socket: any) => {
console.log('🔗 Client connected:', socket.id);
let currentRequestId: string | null = null; let currentRequestId: string | null = null;
let currentUserId: string | null = null; let currentUserId: string | null = null;

View File

@ -8,8 +8,6 @@ import { asyncHandler } from '../middlewares/errorHandler.middleware';
const router = Router(); const router = Router();
const authController = new AuthController(); const authController = new AuthController();
console.log('✅ Auth routes loaded - token-exchange endpoint registered');
// Token exchange endpoint (no authentication required) - for localhost development // Token exchange endpoint (no authentication required) - for localhost development
router.post('/token-exchange', router.post('/token-exchange',
validateBody(tokenExchangeSchema), validateBody(tokenExchangeSchema),

View File

@ -63,7 +63,7 @@ async function ensureMigrationsTable(queryInterface: QueryInterface): Promise<vo
executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) )
`); `);
console.log('✅ Created migrations tracking table'); // Migrations table created
} }
} catch (error) { } catch (error) {
console.error('Error creating migrations table:', error); console.error('Error creating migrations table:', error);
@ -106,7 +106,6 @@ async function markMigrationExecuted(name: string): Promise<void> {
async function run() { async function run() {
try { try {
await sequelize.authenticate(); await sequelize.authenticate();
console.log('📦 Database connected');
const queryInterface = sequelize.getQueryInterface(); const queryInterface = sequelize.getQueryInterface();
@ -122,33 +121,29 @@ async function run() {
); );
if (pendingMigrations.length === 0) { if (pendingMigrations.length === 0) {
console.log('✅ All migrations are up-to-date (no new migrations to run)'); console.log('✅ Migrations up-to-date');
process.exit(0); process.exit(0);
return; return;
} }
console.log(`🔄 Running ${pendingMigrations.length} pending migration(s)...\n`); console.log(`🔄 Running ${pendingMigrations.length} migration(s)...`);
// Run each pending migration // Run each pending migration
for (const migration of pendingMigrations) { for (const migration of pendingMigrations) {
try { try {
console.log(`⏳ Running: ${migration.name}`);
await migration.module.up(queryInterface); await migration.module.up(queryInterface);
await markMigrationExecuted(migration.name); await markMigrationExecuted(migration.name);
console.log(`Completed: ${migration.name}\n`); console.log(`${migration.name}`);
} catch (error: any) { } catch (error: any) {
console.error(`❌ Failed: ${migration.name}`); console.error(`❌ Migration failed: ${migration.name} - ${error.message}`);
console.error('Error:', error.message);
throw error; throw error;
} }
} }
console.log(`\n✅ Successfully applied ${pendingMigrations.length} migration(s)`); console.log(`✅ Applied ${pendingMigrations.length} migration(s)`);
console.log(`📊 Total migrations: ${executedMigrations.length + pendingMigrations.length}`);
process.exit(0); process.exit(0);
} catch (err: any) { } catch (err: any) {
console.error('\n❌ Migration failed:', err.message); console.error('❌ Migration failed:', err.message);
console.error('\nStack trace:', err.stack);
process.exit(1); process.exit(1);
} }
} }

View File

@ -0,0 +1,392 @@
/**
* Manual script to seed admin configurations
* Run this if configurations are not auto-seeding on server startup
*
* Usage: npm run seed:config
*/
import { sequelize } from '../config/database';
import { QueryTypes } from 'sequelize';
async function seedAdminConfigurations() {
try {
await sequelize.authenticate();
// Check if configurations already exist
const count = await sequelize.query(
'SELECT COUNT(*) as count FROM admin_configurations',
{ type: QueryTypes.SELECT }
);
const existingCount = (count[0] as any).count;
if (existingCount > 0) {
console.log(`⚠️ Found ${existingCount} existing configurations. Delete them first or skip this script.`);
const readline = require('readline').createInterface({
input: process.stdin,
output: process.stdout
});
const answer = await new Promise<string>((resolve) => {
readline.question('Delete existing and re-seed? (yes/no): ', resolve);
});
readline.close();
if (answer.toLowerCase() !== 'yes') {
console.log('❌ Aborted. No changes made.');
process.exit(0);
}
await sequelize.query('DELETE FROM admin_configurations');
console.log('✅ Existing configurations deleted');
}
console.log('📝 Seeding admin configurations...');
// Insert all default configurations
await sequelize.query(`
INSERT INTO admin_configurations (
config_id, config_key, config_category, config_value, value_type,
display_name, description, default_value, is_editable, is_sensitive,
validation_rules, ui_component, sort_order, requires_restart,
created_at, updated_at
) VALUES
-- TAT Settings
(
gen_random_uuid(),
'DEFAULT_TAT_EXPRESS_HOURS',
'TAT_SETTINGS',
'24',
'NUMBER',
'Default TAT for Express Priority',
'Default turnaround time in hours for express priority requests (calendar days, 24/7)',
'24',
true,
false,
'{"min": 1, "max": 168}'::jsonb,
'number',
1,
false,
NOW(),
NOW()
),
(
gen_random_uuid(),
'DEFAULT_TAT_STANDARD_HOURS',
'TAT_SETTINGS',
'48',
'NUMBER',
'Default TAT for Standard Priority',
'Default turnaround time in hours for standard priority requests (working hours only)',
'48',
true,
false,
'{"min": 1, "max": 336}'::jsonb,
'number',
2,
false,
NOW(),
NOW()
),
(
gen_random_uuid(),
'TAT_THRESHOLD_WARNING',
'TAT_SETTINGS',
'50',
'NUMBER',
'TAT Warning Threshold (%)',
'Percentage of TAT elapsed when first warning notification is sent',
'50',
true,
false,
'{"min": 1, "max": 100}'::jsonb,
'number',
3,
false,
NOW(),
NOW()
),
(
gen_random_uuid(),
'TAT_THRESHOLD_CRITICAL',
'TAT_SETTINGS',
'75',
'NUMBER',
'TAT Critical Threshold (%)',
'Percentage of TAT elapsed when critical notification is sent',
'75',
true,
false,
'{"min": 1, "max": 100}'::jsonb,
'number',
4,
false,
NOW(),
NOW()
),
(
gen_random_uuid(),
'TAT_TEST_MODE',
'TAT_SETTINGS',
'false',
'BOOLEAN',
'TAT Test Mode',
'Enable test mode where 1 TAT hour = 1 minute (for development/testing only)',
'false',
true,
false,
'{}'::jsonb,
'switch',
5,
true,
NOW(),
NOW()
),
-- Working Hours Settings
(
gen_random_uuid(),
'WORK_START_HOUR',
'WORKING_HOURS',
'9',
'NUMBER',
'Work Day Start Hour',
'Hour when work day starts (24-hour format, e.g., 9 for 9:00 AM)',
'9',
true,
false,
'{"min": 0, "max": 23}'::jsonb,
'number',
10,
false,
NOW(),
NOW()
),
(
gen_random_uuid(),
'WORK_END_HOUR',
'WORKING_HOURS',
'18',
'NUMBER',
'Work Day End Hour',
'Hour when work day ends (24-hour format, e.g., 18 for 6:00 PM)',
'18',
true,
false,
'{"min": 0, "max": 23}'::jsonb,
'number',
11,
false,
NOW(),
NOW()
),
(
gen_random_uuid(),
'WORK_START_DAY',
'WORKING_HOURS',
'1',
'NUMBER',
'Work Week Start Day',
'Day when work week starts (1 = Monday, 7 = Sunday)',
'1',
true,
false,
'{"min": 1, "max": 7}'::jsonb,
'number',
12,
false,
NOW(),
NOW()
),
(
gen_random_uuid(),
'WORK_END_DAY',
'WORKING_HOURS',
'5',
'NUMBER',
'Work Week End Day',
'Day when work week ends (1 = Monday, 7 = Sunday)',
'5',
true,
false,
'{"min": 1, "max": 7}'::jsonb,
'number',
13,
false,
NOW(),
NOW()
),
(
gen_random_uuid(),
'TIMEZONE',
'WORKING_HOURS',
'Asia/Kolkata',
'STRING',
'System Timezone',
'Timezone for all TAT calculations and scheduling',
'Asia/Kolkata',
true,
false,
'{}'::jsonb,
'select',
14,
true,
NOW(),
NOW()
),
-- Workflow Settings
(
gen_random_uuid(),
'MAX_APPROVAL_LEVELS',
'WORKFLOW',
'10',
'NUMBER',
'Maximum Approval Levels',
'Maximum number of approval levels allowed per workflow',
'10',
true,
false,
'{"min": 1, "max": 20}'::jsonb,
'number',
20,
false,
NOW(),
NOW()
),
(
gen_random_uuid(),
'MAX_PARTICIPANTS',
'WORKFLOW',
'50',
'NUMBER',
'Maximum Participants',
'Maximum number of participants (spectators + approvers) per request',
'50',
true,
false,
'{"min": 1, "max": 100}'::jsonb,
'number',
21,
false,
NOW(),
NOW()
),
-- File Upload Settings
(
gen_random_uuid(),
'MAX_FILE_SIZE_MB',
'FILE_UPLOAD',
'10',
'NUMBER',
'Maximum File Size (MB)',
'Maximum size for uploaded files in megabytes',
'10',
true,
false,
'{"min": 1, "max": 100}'::jsonb,
'number',
30,
false,
NOW(),
NOW()
),
(
gen_random_uuid(),
'ALLOWED_FILE_TYPES',
'FILE_UPLOAD',
'pdf,doc,docx,xls,xlsx,ppt,pptx,jpg,jpeg,png,gif,txt',
'STRING',
'Allowed File Types',
'Comma-separated list of allowed file extensions',
'pdf,doc,docx,xls,xlsx,ppt,pptx,jpg,jpeg,png,gif,txt',
true,
false,
'{}'::jsonb,
'text',
31,
false,
NOW(),
NOW()
),
-- Feature Toggles
(
gen_random_uuid(),
'ENABLE_AI_CONCLUSION',
'FEATURES',
'true',
'BOOLEAN',
'Enable AI-Generated Conclusions',
'Allow AI to generate automatic conclusion remarks for approved/rejected requests',
'true',
true,
false,
'{}'::jsonb,
'switch',
40,
false,
NOW(),
NOW()
),
(
gen_random_uuid(),
'ENABLE_PUSH_NOTIFICATIONS',
'FEATURES',
'true',
'BOOLEAN',
'Enable Push Notifications',
'Send browser push notifications for real-time events',
'true',
true,
false,
'{}'::jsonb,
'switch',
41,
false,
NOW(),
NOW()
),
(
gen_random_uuid(),
'ENABLE_EMAIL_NOTIFICATIONS',
'FEATURES',
'true',
'BOOLEAN',
'Enable Email Notifications',
'Send email notifications for workflow events',
'true',
true,
false,
'{}'::jsonb,
'switch',
42,
true,
NOW(),
NOW()
)
`);
const finalCount = await sequelize.query(
'SELECT COUNT(*) as count FROM admin_configurations',
{ type: QueryTypes.SELECT }
);
console.log(`✅ Seeded ${(finalCount[0] as any).count} admin configurations`);
process.exit(0);
} catch (error) {
console.error('❌ Error seeding admin configurations:', error);
process.exit(1);
}
}
// Run if called directly
if (require.main === module) {
seedAdminConfigurations();
}
export default seedAdminConfigurations;

View File

@ -18,30 +18,19 @@ const startServer = async (): Promise<void> => {
// Seed default configurations if table is empty // Seed default configurations if table is empty
try { try {
await seedDefaultConfigurations(); await seedDefaultConfigurations();
// console.log('⚙️ System configurations initialized');
} catch (error) { } catch (error) {
console.warn('⚠️ Configuration seeding skipped'); console.error('⚠️ Configuration seeding error:', error);
} }
// Initialize holidays cache for TAT calculations // Initialize holidays cache for TAT calculations
try { try {
await initializeHolidaysCache(); await initializeHolidaysCache();
console.log('📅 Holiday calendar loaded for TAT calculations');
} catch (error) { } catch (error) {
console.warn('⚠️ Holiday calendar not loaded - TAT will use weekends only'); // Silently fall back to weekends-only TAT calculation
} }
server.listen(PORT, () => { server.listen(PORT, () => {
console.log(`🚀 Server running on port ${PORT}`); console.log(`🚀 Server running on port ${PORT} | ${process.env.NODE_ENV || 'development'}`);
console.log(`📊 Environment: ${process.env.NODE_ENV || 'development'}`);
console.log(`🌐 API Base URL: http://localhost:${PORT}`);
console.log(`❤️ Health Check: http://localhost:${PORT}/health`);
console.log(`🔌 Socket.IO path: /socket.io`);
console.log(`⏰ TAT Worker: Initialized and listening`);
console.log('');
logSystemConfig(); // Log centralized system configuration
console.log('');
logTatConfig(); // Log TAT-specific details
}); });
} catch (error) { } catch (error) {
console.error('❌ Unable to start server:', error); console.error('❌ Unable to start server:', error);

View File

@ -15,7 +15,7 @@ export async function seedDefaultConfigurations(): Promise<void> {
); );
if (count && (count[0] as any).count > 0) { if (count && (count[0] as any).count > 0) {
logger.info('[Config Seed] Configurations already exist. Skipping seed.'); // Table has data, skip seeding silently
return; return;
} }
@ -26,8 +26,8 @@ export async function seedDefaultConfigurations(): Promise<void> {
INSERT INTO admin_configurations ( INSERT INTO admin_configurations (
config_id, config_key, config_category, config_value, value_type, config_id, config_key, config_category, config_value, value_type,
display_name, description, default_value, is_editable, is_sensitive, display_name, description, default_value, is_editable, is_sensitive,
validation_rules, ui_component, sort_order, requires_restart, validation_rules, ui_component, options, sort_order, requires_restart,
created_at, updated_at last_modified_by, last_modified_at, created_at, updated_at
) VALUES ) VALUES
-- TAT Settings -- TAT Settings
( (
@ -43,8 +43,11 @@ export async function seedDefaultConfigurations(): Promise<void> {
false, false,
'{"min": 1, "max": 168}'::jsonb, '{"min": 1, "max": 168}'::jsonb,
'number', 'number',
NULL,
1, 1,
false, false,
NULL,
NULL,
NOW(), NOW(),
NOW() NOW()
), ),
@ -61,8 +64,11 @@ export async function seedDefaultConfigurations(): Promise<void> {
false, false,
'{"min": 1, "max": 720}'::jsonb, '{"min": 1, "max": 720}'::jsonb,
'number', 'number',
NULL,
2, 2,
false, false,
NULL,
NULL,
NOW(), NOW(),
NOW() NOW()
), ),
@ -76,9 +82,14 @@ export async function seedDefaultConfigurations(): Promise<void> {
'Send first gentle reminder when this percentage of TAT is elapsed', 'Send first gentle reminder when this percentage of TAT is elapsed',
'50', '50',
true, true,
false,
'{"min": 1, "max": 100}'::jsonb, '{"min": 1, "max": 100}'::jsonb,
'slider', 'slider',
NULL,
3, 3,
false,
NULL,
NULL,
NOW(), NOW(),
NOW() NOW()
), ),
@ -92,9 +103,14 @@ export async function seedDefaultConfigurations(): Promise<void> {
'Send escalation warning when this percentage of TAT is elapsed', 'Send escalation warning when this percentage of TAT is elapsed',
'75', '75',
true, true,
false,
'{"min": 1, "max": 100}'::jsonb, '{"min": 1, "max": 100}'::jsonb,
'slider', 'slider',
NULL,
4, 4,
false,
NULL,
NULL,
NOW(), NOW(),
NOW() NOW()
), ),
@ -108,9 +124,14 @@ export async function seedDefaultConfigurations(): Promise<void> {
'Hour when working day starts (24-hour format, 0-23)', 'Hour when working day starts (24-hour format, 0-23)',
'9', '9',
true, true,
false,
'{"min": 0, "max": 23}'::jsonb, '{"min": 0, "max": 23}'::jsonb,
'number', 'number',
NULL,
5, 5,
false,
NULL,
NULL,
NOW(), NOW(),
NOW() NOW()
), ),
@ -124,9 +145,56 @@ export async function seedDefaultConfigurations(): Promise<void> {
'Hour when working day ends (24-hour format, 0-23)', 'Hour when working day ends (24-hour format, 0-23)',
'18', '18',
true, true,
false,
'{"min": 0, "max": 23}'::jsonb, '{"min": 0, "max": 23}'::jsonb,
'number', 'number',
NULL,
6, 6,
false,
NULL,
NULL,
NOW(),
NOW()
),
(
gen_random_uuid(),
'WORK_START_DAY',
'TAT_SETTINGS',
'1',
'NUMBER',
'Working Week Start Day',
'Day of week start (1=Monday, 7=Sunday)',
'1',
true,
false,
'{"min": 1, "max": 7}'::jsonb,
'number',
NULL,
7,
false,
NULL,
NULL,
NOW(),
NOW()
),
(
gen_random_uuid(),
'WORK_END_DAY',
'TAT_SETTINGS',
'5',
'NUMBER',
'Working Week End Day',
'Day of week end (1=Monday, 7=Sunday)',
'5',
true,
false,
'{"min": 1, "max": 7}'::jsonb,
'number',
NULL,
8,
false,
NULL,
NULL,
NOW(), NOW(),
NOW() NOW()
), ),
@ -141,9 +209,14 @@ export async function seedDefaultConfigurations(): Promise<void> {
'Maximum allowed file size for document uploads in megabytes', 'Maximum allowed file size for document uploads in megabytes',
'10', '10',
true, true,
false,
'{"min": 1, "max": 100}'::jsonb, '{"min": 1, "max": 100}'::jsonb,
'number', 'number',
NULL,
10, 10,
false,
NULL,
NULL,
NOW(), NOW(),
NOW() NOW()
), ),
@ -157,9 +230,14 @@ export async function seedDefaultConfigurations(): Promise<void> {
'Comma-separated list of allowed file extensions for uploads', 'Comma-separated list of allowed file extensions for uploads',
'pdf,doc,docx,xls,xlsx,ppt,pptx,jpg,jpeg,png,gif', 'pdf,doc,docx,xls,xlsx,ppt,pptx,jpg,jpeg,png,gif',
true, true,
false,
'{}'::jsonb, '{}'::jsonb,
'text', 'text',
NULL,
11, 11,
false,
NULL,
NULL,
NOW(), NOW(),
NOW() NOW()
), ),
@ -173,9 +251,14 @@ export async function seedDefaultConfigurations(): Promise<void> {
'Number of days to retain documents after workflow closure before archival', 'Number of days to retain documents after workflow closure before archival',
'365', '365',
true, true,
false,
'{"min": 30, "max": 3650}'::jsonb, '{"min": 30, "max": 3650}'::jsonb,
'number', 'number',
NULL,
12, 12,
false,
NULL,
NULL,
NOW(), NOW(),
NOW() NOW()
), ),
@ -190,9 +273,14 @@ export async function seedDefaultConfigurations(): Promise<void> {
'Toggle AI-generated conclusion remarks for workflow closures', 'Toggle AI-generated conclusion remarks for workflow closures',
'true', 'true',
true, true,
false,
'{}'::jsonb, '{}'::jsonb,
'toggle', 'toggle',
NULL,
20, 20,
false,
NULL,
NULL,
NOW(), NOW(),
NOW() NOW()
), ),
@ -206,9 +294,14 @@ export async function seedDefaultConfigurations(): Promise<void> {
'Maximum character limit for AI-generated conclusion remarks', 'Maximum character limit for AI-generated conclusion remarks',
'500', '500',
true, true,
false,
'{"min": 100, "max": 2000}'::jsonb, '{"min": 100, "max": 2000}'::jsonb,
'number', 'number',
NULL,
21, 21,
false,
NULL,
NULL,
NOW(), NOW(),
NOW() NOW()
), ),
@ -223,9 +316,14 @@ export async function seedDefaultConfigurations(): Promise<void> {
'Send email notifications for workflow events', 'Send email notifications for workflow events',
'true', 'true',
true, true,
false,
'{}'::jsonb, '{}'::jsonb,
'toggle', 'toggle',
NULL,
30, 30,
false,
NULL,
NULL,
NOW(), NOW(),
NOW() NOW()
), ),
@ -239,9 +337,14 @@ export async function seedDefaultConfigurations(): Promise<void> {
'Send browser push notifications for real-time events', 'Send browser push notifications for real-time events',
'true', 'true',
true, true,
false,
'{}'::jsonb, '{}'::jsonb,
'toggle', 'toggle',
NULL,
31, 31,
false,
NULL,
NULL,
NOW(), NOW(),
NOW() NOW()
), ),
@ -255,9 +358,14 @@ export async function seedDefaultConfigurations(): Promise<void> {
'Delay in milliseconds before sending batched notifications to avoid spam', 'Delay in milliseconds before sending batched notifications to avoid spam',
'5000', '5000',
true, true,
false,
'{"min": 1000, "max": 30000}'::jsonb, '{"min": 1000, "max": 30000}'::jsonb,
'number', 'number',
NULL,
32, 32,
false,
NULL,
NULL,
NOW(), NOW(),
NOW() NOW()
), ),
@ -272,9 +380,14 @@ export async function seedDefaultConfigurations(): Promise<void> {
'Display total requests KPI card on dashboard', 'Display total requests KPI card on dashboard',
'true', 'true',
true, true,
false,
'{}'::jsonb, '{}'::jsonb,
'toggle', 'toggle',
NULL,
40, 40,
false,
NULL,
NULL,
NOW(), NOW(),
NOW() NOW()
), ),
@ -288,9 +401,14 @@ export async function seedDefaultConfigurations(): Promise<void> {
'Display open requests KPI card on dashboard', 'Display open requests KPI card on dashboard',
'true', 'true',
true, true,
false,
'{}'::jsonb, '{}'::jsonb,
'toggle', 'toggle',
NULL,
41, 41,
false,
NULL,
NULL,
NOW(), NOW(),
NOW() NOW()
), ),
@ -304,9 +422,14 @@ export async function seedDefaultConfigurations(): Promise<void> {
'Display TAT compliance KPI card on dashboard', 'Display TAT compliance KPI card on dashboard',
'true', 'true',
true, true,
false,
'{}'::jsonb, '{}'::jsonb,
'toggle', 'toggle',
NULL,
42, 42,
false,
NULL,
NULL,
NOW(), NOW(),
NOW() NOW()
), ),
@ -320,9 +443,14 @@ export async function seedDefaultConfigurations(): Promise<void> {
'Display pending actions KPI card on dashboard', 'Display pending actions KPI card on dashboard',
'true', 'true',
true, true,
false,
'{}'::jsonb, '{}'::jsonb,
'toggle', 'toggle',
NULL,
43, 43,
false,
NULL,
NULL,
NOW(), NOW(),
NOW() NOW()
), ),
@ -337,9 +465,14 @@ export async function seedDefaultConfigurations(): Promise<void> {
'Enable users to add spectators to workflow requests', 'Enable users to add spectators to workflow requests',
'true', 'true',
true, true,
false,
'{}'::jsonb, '{}'::jsonb,
'toggle', 'toggle',
NULL,
50, 50,
false,
NULL,
NULL,
NOW(), NOW(),
NOW() NOW()
), ),
@ -353,9 +486,14 @@ export async function seedDefaultConfigurations(): Promise<void> {
'Maximum number of spectators allowed per workflow request', 'Maximum number of spectators allowed per workflow request',
'20', '20',
true, true,
false,
'{"min": 1, "max": 100}'::jsonb, '{"min": 1, "max": 100}'::jsonb,
'number', 'number',
NULL,
51, 51,
false,
NULL,
NULL,
NOW(), NOW(),
NOW() NOW()
), ),
@ -369,9 +507,14 @@ export async function seedDefaultConfigurations(): Promise<void> {
'Allow sharing workflow links with users outside the organization', 'Allow sharing workflow links with users outside the organization',
'false', 'false',
true, true,
false,
'{}'::jsonb, '{}'::jsonb,
'toggle', 'toggle',
NULL,
52, 52,
false,
NULL,
NULL,
NOW(), NOW(),
NOW() NOW()
), ),
@ -379,38 +522,48 @@ export async function seedDefaultConfigurations(): Promise<void> {
( (
gen_random_uuid(), gen_random_uuid(),
'MAX_APPROVAL_LEVELS', 'MAX_APPROVAL_LEVELS',
'WORKFLOW_LIMITS', 'SYSTEM_SETTINGS',
'10', '10',
'NUMBER', 'NUMBER',
'Maximum Approval Levels', 'Maximum Approval Levels',
'Maximum number of approval levels allowed per workflow', 'Maximum number of approval levels allowed per workflow',
'10', '10',
true, true,
false,
'{"min": 1, "max": 20}'::jsonb, '{"min": 1, "max": 20}'::jsonb,
'number', 'number',
NULL,
60, 60,
false,
NULL,
NULL,
NOW(), NOW(),
NOW() NOW()
), ),
( (
gen_random_uuid(), gen_random_uuid(),
'MAX_PARTICIPANTS_PER_REQUEST', 'MAX_PARTICIPANTS_PER_REQUEST',
'WORKFLOW_LIMITS', 'SYSTEM_SETTINGS',
'50', '50',
'NUMBER', 'NUMBER',
'Maximum Participants per Request', 'Maximum Participants per Request',
'Maximum total participants (approvers + spectators) per workflow', 'Maximum total participants (approvers + spectators) per workflow',
'50', '50',
true, true,
false,
'{"min": 2, "max": 200}'::jsonb, '{"min": 2, "max": 200}'::jsonb,
'number', 'number',
NULL,
61, 61,
false,
NULL,
NULL,
NOW(), NOW(),
NOW() NOW()
) )
`, { type: QueryTypes.INSERT }); `, { type: QueryTypes.INSERT });
logger.info('[Config Seed] ✅ Default configurations seeded successfully (18 settings across 7 categories)'); logger.info('[Config Seed] ✅ Default configurations seeded successfully (20 settings across 7 categories)');
} catch (error) { } catch (error) {
logger.error('[Config Seed] Error seeding configurations:', error); logger.error('[Config Seed] Error seeding configurations:', error);
// Don't throw - let server start even if seeding fails // Don't throw - let server start even if seeding fails

View File

@ -39,9 +39,8 @@ async function loadWorkingHoursCache(): Promise<void> {
}; };
workingHoursCacheExpiry = dayjs().add(5, 'minute').toDate(); workingHoursCacheExpiry = dayjs().add(5, 'minute').toDate();
console.log(`[TAT Utils] Loaded working hours: ${hours.startHour}:00-${hours.endHour}:00, Days: ${startDay}-${endDay}`);
} catch (error) { } catch (error) {
console.error('[TAT Utils] Error loading working hours cache:', error); console.error('[TAT] Error loading working hours:', error);
// Fallback to default values from TAT_CONFIG // Fallback to default values from TAT_CONFIG
workingHoursCache = { workingHoursCache = {
startHour: TAT_CONFIG.WORK_START_HOUR, startHour: TAT_CONFIG.WORK_START_HOUR,
@ -72,9 +71,8 @@ async function loadHolidaysCache(): Promise<void> {
holidaysCache = new Set(holidays); holidaysCache = new Set(holidays);
holidaysCacheExpiry = dayjs().add(6, 'hour').toDate(); holidaysCacheExpiry = dayjs().add(6, 'hour').toDate();
console.log(`[TAT Utils] Loaded ${holidays.length} holidays into cache`);
} catch (error) { } catch (error) {
console.error('[TAT Utils] Error loading holidays cache:', error); console.error('[TAT] Error loading holidays:', error);
// Continue without holidays if loading fails // Continue without holidays if loading fails
} }
} }
@ -226,7 +224,7 @@ export async function initializeHolidaysCache(): Promise<void> {
export function clearWorkingHoursCache(): void { export function clearWorkingHoursCache(): void {
workingHoursCache = null; workingHoursCache = null;
workingHoursCacheExpiry = null; workingHoursCacheExpiry = null;
console.log('[TAT Utils] Working hours cache cleared'); // Cache cleared
} }
/** /**