627 lines
15 KiB
TypeScript
627 lines
15 KiB
TypeScript
import { sequelize } from '@config/database';
|
||
import { QueryTypes } from 'sequelize';
|
||
import logger from '@utils/logger';
|
||
|
||
/**
|
||
* Seed default admin configurations if table is empty
|
||
* Called automatically on server startup
|
||
*/
|
||
export async function seedDefaultConfigurations(): Promise<void> {
|
||
try {
|
||
// Ensure pgcrypto extension is available for gen_random_uuid()
|
||
try {
|
||
await sequelize.query('CREATE EXTENSION IF NOT EXISTS "pgcrypto"', { type: QueryTypes.RAW });
|
||
} catch (extError: any) {
|
||
// Extension might already exist or user might not have permission - continue
|
||
logger.debug('[Config Seed] pgcrypto extension check:', extError?.message || 'already exists');
|
||
}
|
||
|
||
logger.info('[Config Seed] Seeding default configurations (duplicates will be skipped automatically)...');
|
||
|
||
// Insert default configurations with ON CONFLICT handling
|
||
// This allows re-running the seed without errors if configs already exist
|
||
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, options, sort_order, requires_restart,
|
||
last_modified_by, last_modified_at, 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',
|
||
NULL,
|
||
1,
|
||
false,
|
||
NULL,
|
||
NULL,
|
||
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 days only, excludes weekends and holidays)',
|
||
'48',
|
||
true,
|
||
false,
|
||
'{"min": 1, "max": 720}'::jsonb,
|
||
'number',
|
||
NULL,
|
||
2,
|
||
false,
|
||
NULL,
|
||
NULL,
|
||
NOW(),
|
||
NOW()
|
||
),
|
||
(
|
||
gen_random_uuid(),
|
||
'TAT_REMINDER_THRESHOLD_1',
|
||
'TAT_SETTINGS',
|
||
'50',
|
||
'NUMBER',
|
||
'First TAT Reminder Threshold (%)',
|
||
'Send first gentle reminder when this percentage of TAT is elapsed',
|
||
'50',
|
||
true,
|
||
false,
|
||
'{"min": 1, "max": 100}'::jsonb,
|
||
'slider',
|
||
NULL,
|
||
3,
|
||
false,
|
||
NULL,
|
||
NULL,
|
||
NOW(),
|
||
NOW()
|
||
),
|
||
(
|
||
gen_random_uuid(),
|
||
'TAT_REMINDER_THRESHOLD_2',
|
||
'TAT_SETTINGS',
|
||
'75',
|
||
'NUMBER',
|
||
'Second TAT Reminder Threshold (%)',
|
||
'Send escalation warning when this percentage of TAT is elapsed',
|
||
'75',
|
||
true,
|
||
false,
|
||
'{"min": 1, "max": 100}'::jsonb,
|
||
'slider',
|
||
NULL,
|
||
4,
|
||
false,
|
||
NULL,
|
||
NULL,
|
||
NOW(),
|
||
NOW()
|
||
),
|
||
(
|
||
gen_random_uuid(),
|
||
'WORK_START_HOUR',
|
||
'TAT_SETTINGS',
|
||
'9',
|
||
'NUMBER',
|
||
'Working Day Start Hour',
|
||
'Hour when working day starts (24-hour format, 0-23)',
|
||
'9',
|
||
true,
|
||
false,
|
||
'{"min": 0, "max": 23}'::jsonb,
|
||
'number',
|
||
NULL,
|
||
5,
|
||
false,
|
||
NULL,
|
||
NULL,
|
||
NOW(),
|
||
NOW()
|
||
),
|
||
(
|
||
gen_random_uuid(),
|
||
'WORK_END_HOUR',
|
||
'TAT_SETTINGS',
|
||
'18',
|
||
'NUMBER',
|
||
'Working Day End Hour',
|
||
'Hour when working day ends (24-hour format, 0-23)',
|
||
'18',
|
||
true,
|
||
false,
|
||
'{"min": 0, "max": 23}'::jsonb,
|
||
'number',
|
||
NULL,
|
||
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()
|
||
),
|
||
-- Document Policy
|
||
(
|
||
gen_random_uuid(),
|
||
'MAX_FILE_SIZE_MB',
|
||
'DOCUMENT_POLICY',
|
||
'10',
|
||
'NUMBER',
|
||
'Maximum File Upload Size (MB)',
|
||
'Maximum allowed file size for document uploads in megabytes',
|
||
'10',
|
||
true,
|
||
false,
|
||
'{"min": 1, "max": 100}'::jsonb,
|
||
'number',
|
||
NULL,
|
||
10,
|
||
false,
|
||
NULL,
|
||
NULL,
|
||
NOW(),
|
||
NOW()
|
||
),
|
||
(
|
||
gen_random_uuid(),
|
||
'ALLOWED_FILE_TYPES',
|
||
'DOCUMENT_POLICY',
|
||
'pdf,doc,docx,xls,xlsx,ppt,pptx,jpg,jpeg,png,gif',
|
||
'STRING',
|
||
'Allowed File Types',
|
||
'Comma-separated list of allowed file extensions for uploads',
|
||
'pdf,doc,docx,xls,xlsx,ppt,pptx,jpg,jpeg,png,gif',
|
||
true,
|
||
false,
|
||
'{}'::jsonb,
|
||
'text',
|
||
NULL,
|
||
11,
|
||
false,
|
||
NULL,
|
||
NULL,
|
||
NOW(),
|
||
NOW()
|
||
),
|
||
(
|
||
gen_random_uuid(),
|
||
'DOCUMENT_RETENTION_DAYS',
|
||
'DOCUMENT_POLICY',
|
||
'365',
|
||
'NUMBER',
|
||
'Document Retention Period (Days)',
|
||
'Number of days to retain documents after workflow closure before archival',
|
||
'365',
|
||
true,
|
||
false,
|
||
'{"min": 30, "max": 3650}'::jsonb,
|
||
'number',
|
||
NULL,
|
||
12,
|
||
false,
|
||
NULL,
|
||
NULL,
|
||
NOW(),
|
||
NOW()
|
||
),
|
||
-- AI Configuration (Vertex AI Gemini)
|
||
(
|
||
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',
|
||
NULL,
|
||
20,
|
||
false,
|
||
NULL,
|
||
NULL,
|
||
NOW(),
|
||
NOW()
|
||
),
|
||
(
|
||
gen_random_uuid(),
|
||
'AI_REMARK_GENERATION_ENABLED',
|
||
'AI_CONFIGURATION',
|
||
'true',
|
||
'BOOLEAN',
|
||
'Enable AI Remark Generation',
|
||
'Toggle AI-generated conclusion remarks for workflow closures',
|
||
'true',
|
||
true,
|
||
false,
|
||
'{}'::jsonb,
|
||
'toggle',
|
||
NULL,
|
||
21,
|
||
false,
|
||
NULL,
|
||
NULL,
|
||
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',
|
||
'2000',
|
||
true,
|
||
false,
|
||
'{"min": 500, "max": 5000}'::jsonb,
|
||
'number',
|
||
NULL,
|
||
24,
|
||
false,
|
||
NULL,
|
||
NULL,
|
||
NOW(),
|
||
NOW()
|
||
),
|
||
-- Notification Rules
|
||
(
|
||
gen_random_uuid(),
|
||
'ENABLE_EMAIL_NOTIFICATIONS',
|
||
'NOTIFICATION_RULES',
|
||
'true',
|
||
'BOOLEAN',
|
||
'Enable Email Notifications',
|
||
'Send email notifications for workflow events',
|
||
'true',
|
||
true,
|
||
false,
|
||
'{}'::jsonb,
|
||
'toggle',
|
||
NULL,
|
||
31,
|
||
false,
|
||
NULL,
|
||
NULL,
|
||
NOW(),
|
||
NOW()
|
||
),
|
||
(
|
||
gen_random_uuid(),
|
||
'ENABLE_IN_APP_NOTIFICATIONS',
|
||
'NOTIFICATION_RULES',
|
||
'true',
|
||
'BOOLEAN',
|
||
'Enable In-App Notifications',
|
||
'Show notifications within the application portal',
|
||
'true',
|
||
true,
|
||
false,
|
||
'{}'::jsonb,
|
||
'toggle',
|
||
NULL,
|
||
32,
|
||
false,
|
||
NULL,
|
||
NULL,
|
||
NOW(),
|
||
NOW()
|
||
),
|
||
(
|
||
gen_random_uuid(),
|
||
'NOTIFICATION_BATCH_DELAY_MS',
|
||
'NOTIFICATION_RULES',
|
||
'5000',
|
||
'NUMBER',
|
||
'Notification Batch Delay (ms)',
|
||
'Delay in milliseconds before sending batched notifications to avoid spam',
|
||
'5000',
|
||
true,
|
||
false,
|
||
'{"min": 1000, "max": 30000}'::jsonb,
|
||
'number',
|
||
NULL,
|
||
33,
|
||
false,
|
||
NULL,
|
||
NULL,
|
||
NOW(),
|
||
NOW()
|
||
),
|
||
-- Dashboard Layout
|
||
(
|
||
gen_random_uuid(),
|
||
'DASHBOARD_SHOW_TOTAL_REQUESTS',
|
||
'DASHBOARD_LAYOUT',
|
||
'true',
|
||
'BOOLEAN',
|
||
'Show Total Requests Card',
|
||
'Display total requests KPI card on dashboard',
|
||
'true',
|
||
true,
|
||
false,
|
||
'{}'::jsonb,
|
||
'toggle',
|
||
NULL,
|
||
40,
|
||
false,
|
||
NULL,
|
||
NULL,
|
||
NOW(),
|
||
NOW()
|
||
),
|
||
(
|
||
gen_random_uuid(),
|
||
'DASHBOARD_SHOW_OPEN_REQUESTS',
|
||
'DASHBOARD_LAYOUT',
|
||
'true',
|
||
'BOOLEAN',
|
||
'Show Open Requests Card',
|
||
'Display open requests KPI card on dashboard',
|
||
'true',
|
||
true,
|
||
false,
|
||
'{}'::jsonb,
|
||
'toggle',
|
||
NULL,
|
||
41,
|
||
false,
|
||
NULL,
|
||
NULL,
|
||
NOW(),
|
||
NOW()
|
||
),
|
||
(
|
||
gen_random_uuid(),
|
||
'DASHBOARD_SHOW_TAT_COMPLIANCE',
|
||
'DASHBOARD_LAYOUT',
|
||
'true',
|
||
'BOOLEAN',
|
||
'Show TAT Compliance Card',
|
||
'Display TAT compliance KPI card on dashboard',
|
||
'true',
|
||
true,
|
||
false,
|
||
'{}'::jsonb,
|
||
'toggle',
|
||
NULL,
|
||
42,
|
||
false,
|
||
NULL,
|
||
NULL,
|
||
NOW(),
|
||
NOW()
|
||
),
|
||
(
|
||
gen_random_uuid(),
|
||
'DASHBOARD_SHOW_PENDING_ACTIONS',
|
||
'DASHBOARD_LAYOUT',
|
||
'true',
|
||
'BOOLEAN',
|
||
'Show Pending Actions Card',
|
||
'Display pending actions KPI card on dashboard',
|
||
'true',
|
||
true,
|
||
false,
|
||
'{}'::jsonb,
|
||
'toggle',
|
||
NULL,
|
||
43,
|
||
false,
|
||
NULL,
|
||
NULL,
|
||
NOW(),
|
||
NOW()
|
||
),
|
||
-- Workflow Sharing Policy
|
||
(
|
||
gen_random_uuid(),
|
||
'ALLOW_ADD_SPECTATOR',
|
||
'WORKFLOW_SHARING',
|
||
'true',
|
||
'BOOLEAN',
|
||
'Allow Adding Spectators',
|
||
'Enable users to add spectators to workflow requests',
|
||
'true',
|
||
true,
|
||
false,
|
||
'{}'::jsonb,
|
||
'toggle',
|
||
NULL,
|
||
50,
|
||
false,
|
||
NULL,
|
||
NULL,
|
||
NOW(),
|
||
NOW()
|
||
),
|
||
(
|
||
gen_random_uuid(),
|
||
'MAX_SPECTATORS_PER_REQUEST',
|
||
'WORKFLOW_SHARING',
|
||
'20',
|
||
'NUMBER',
|
||
'Maximum Spectators per Request',
|
||
'Maximum number of spectators allowed per workflow request',
|
||
'20',
|
||
true,
|
||
false,
|
||
'{"min": 1, "max": 100}'::jsonb,
|
||
'number',
|
||
NULL,
|
||
51,
|
||
false,
|
||
NULL,
|
||
NULL,
|
||
NOW(),
|
||
NOW()
|
||
),
|
||
(
|
||
gen_random_uuid(),
|
||
'ALLOW_EXTERNAL_SHARING',
|
||
'WORKFLOW_SHARING',
|
||
'false',
|
||
'BOOLEAN',
|
||
'Allow External Sharing',
|
||
'Allow sharing workflow links with users outside the organization',
|
||
'false',
|
||
true,
|
||
false,
|
||
'{}'::jsonb,
|
||
'toggle',
|
||
NULL,
|
||
52,
|
||
false,
|
||
NULL,
|
||
NULL,
|
||
NOW(),
|
||
NOW()
|
||
),
|
||
-- User Roles (Read-only settings for reference)
|
||
(
|
||
gen_random_uuid(),
|
||
'MAX_APPROVAL_LEVELS',
|
||
'SYSTEM_SETTINGS',
|
||
'10',
|
||
'NUMBER',
|
||
'Maximum Approval Levels',
|
||
'Maximum number of approval levels allowed per workflow',
|
||
'10',
|
||
true,
|
||
false,
|
||
'{"min": 1, "max": 20}'::jsonb,
|
||
'number',
|
||
NULL,
|
||
60,
|
||
false,
|
||
NULL,
|
||
NULL,
|
||
NOW(),
|
||
NOW()
|
||
),
|
||
(
|
||
gen_random_uuid(),
|
||
'MAX_PARTICIPANTS_PER_REQUEST',
|
||
'SYSTEM_SETTINGS',
|
||
'50',
|
||
'NUMBER',
|
||
'Maximum Participants per Request',
|
||
'Maximum total participants (approvers + spectators) per workflow',
|
||
'50',
|
||
true,
|
||
false,
|
||
'{"min": 2, "max": 200}'::jsonb,
|
||
'number',
|
||
NULL,
|
||
61,
|
||
false,
|
||
NULL,
|
||
NULL,
|
||
NOW(),
|
||
NOW()
|
||
),
|
||
-- Form 16 admin (submission viewers, 26AS viewers, notifications) – same admin_configurations table
|
||
(
|
||
gen_random_uuid(),
|
||
'FORM16_ADMIN_CONFIG',
|
||
'SYSTEM_SETTINGS',
|
||
'{"submissionViewerEmails":[],"twentySixAsViewerEmails":[],"reminderEnabled":true,"reminderDays":7,"notification26AsDataAdded":{"enabled":true,"template":"26AS data has been added. Please review."},"notificationForm16SuccessCreditNote":{"enabled":true,"template":"Form 16 submitted successfully. Credit note: [CreditNoteRef]."},"notificationForm16Unsuccessful":{"enabled":true,"template":"Form 16 submission was unsuccessful. Issue: [Issue]."},"alertSubmitForm16Enabled":true,"alertSubmitForm16FrequencyDays":0,"alertSubmitForm16FrequencyHours":24,"alertSubmitForm16Template":"Dear [Name], please submit Form 16A for the pending period. Due: [DueDate].","reminderNotificationEnabled":true,"reminderFrequencyDays":0,"reminderFrequencyHours":12,"reminderNotificationTemplate":"Reminder: Dear [Name], your Form 16A submission is pending for request [Request ID]. Please complete it.","debitNoteNotification":{"enabled":true,"template":"Debit note issued: [DebitNoteRef]. Please review."}}',
|
||
'JSON',
|
||
'Form 16 Admin Config',
|
||
'Form 16 visibility (submission data viewers, 26AS viewers), reminders and notification settings',
|
||
'{"submissionViewerEmails":[],"twentySixAsViewerEmails":[],"reminderEnabled":true,"reminderDays":7}',
|
||
true,
|
||
false,
|
||
'{}'::jsonb,
|
||
NULL,
|
||
NULL,
|
||
62,
|
||
false,
|
||
NULL,
|
||
NULL,
|
||
NOW(),
|
||
NOW()
|
||
)
|
||
ON CONFLICT (config_key) DO NOTHING
|
||
`, { type: QueryTypes.INSERT });
|
||
|
||
// Verify how many were actually inserted
|
||
const result = await sequelize.query(
|
||
'SELECT COUNT(*) as count FROM admin_configurations',
|
||
{ type: QueryTypes.SELECT }
|
||
);
|
||
const totalCount = result && (result[0] as any).count ? (result[0] as any).count : 0;
|
||
|
||
logger.info(`[Config Seed] ✅ Configuration seeding complete. Total configurations: ${totalCount}`);
|
||
} catch (error: any) {
|
||
logger.error('[Config Seed] ❌ Error seeding configurations:', {
|
||
message: error?.message || String(error),
|
||
stack: error?.stack,
|
||
name: error?.name
|
||
});
|
||
// Don't throw - let server start even if seeding fails
|
||
// User can manually run seed script if needed: npm run seed:config
|
||
}
|
||
}
|
||
|