/** * 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((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', 'TAT_SETTINGS', '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', 'TAT_SETTINGS', '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', 'TAT_SETTINGS', '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', 'TAT_SETTINGS', '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() ), -- AI Configuration (from migration 20251111-add-ai-provider-configs) ( gen_random_uuid(), 'AI_PROVIDER', 'AI_CONFIGURATION', 'claude', 'STRING', 'AI Provider', 'Active AI provider for conclusion generation (claude, openai, or gemini)', 'claude', true, false, '{"enum": ["claude", "openai", "gemini"], "required": true}'::jsonb, 'select', 100, false, NOW(), NOW() ), ( gen_random_uuid(), 'CLAUDE_API_KEY', 'AI_CONFIGURATION', '', 'STRING', 'Claude API Key', 'API key for Claude (Anthropic) - Get from console.anthropic.com', '', true, true, '{"pattern": "^sk-ant-", "minLength": 40}'::jsonb, 'input', 101, false, NOW(), NOW() ), ( gen_random_uuid(), 'OPENAI_API_KEY', 'AI_CONFIGURATION', '', 'STRING', 'OpenAI API Key', 'API key for OpenAI (GPT-4) - Get from platform.openai.com', '', true, true, '{"pattern": "^sk-", "minLength": 40}'::jsonb, 'input', 102, false, NOW(), NOW() ), ( gen_random_uuid(), 'GEMINI_API_KEY', 'AI_CONFIGURATION', '', 'STRING', 'Gemini API Key', 'API key for Gemini (Google) - Get from ai.google.dev', '', true, true, '{"minLength": 20}'::jsonb, 'input', 103, false, NOW(), NOW() ), ( gen_random_uuid(), 'AI_ENABLED', 'AI_CONFIGURATION', 'true', 'BOOLEAN', 'Enable AI Features', 'Master toggle to enable/disable all AI-powered features in the system', 'true', true, false, '{"type": "boolean"}'::jsonb, 'toggle', 104, false, NOW(), NOW() ), ( gen_random_uuid(), 'CLAUDE_MODEL', 'AI_CONFIGURATION', 'claude-sonnet-4-20250514', 'STRING', 'Claude Model', 'Claude (Anthropic) model to use for AI generation', 'claude-sonnet-4-20250514', true, false, '{}'::jsonb, 'input', 105, false, NOW(), NOW() ), ( gen_random_uuid(), 'OPENAI_MODEL', 'AI_CONFIGURATION', 'gpt-4o', 'STRING', 'OpenAI Model', 'OpenAI model to use for AI generation', 'gpt-4o', true, false, '{}'::jsonb, 'input', 106, false, NOW(), NOW() ), ( gen_random_uuid(), 'GEMINI_MODEL', 'AI_CONFIGURATION', 'gemini-2.0-flash-lite', 'STRING', 'Gemini Model', 'Gemini (Google) model to use for AI generation', 'gemini-2.0-flash-lite', true, false, '{}'::jsonb, 'input', 107, false, NOW(), NOW() ), ( gen_random_uuid(), 'AI_REMARK_GENERATION_ENABLED', 'AI_CONFIGURATION', 'true', 'BOOLEAN', 'Enable AI Remark Generation', 'Enable/disable AI-powered conclusion remark generation when requests are approved', 'true', true, false, '{"type": "boolean"}'::jsonb, 'toggle', 108, false, NOW(), NOW() ), ( gen_random_uuid(), 'AI_MAX_REMARK_LENGTH', 'AI_CONFIGURATION', '2000', 'NUMBER', 'AI Max Remark Length', 'Maximum character length for AI-generated conclusion remarks (used as context for AI prompt)', '2000', true, false, '{"type": "number", "min": 500, "max": 5000}'::jsonb, 'number', 109, false, NOW(), NOW() ) ON CONFLICT (config_key) DO UPDATE SET config_value = EXCLUDED.config_value, updated_at = 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;