Update various configuration files, components, and assets; enhance notification system and API endpoints; improve documentation and styles across the application.
This commit is contained in:
24
scripts/cleanup-connections.js
Normal file
24
scripts/cleanup-connections.js
Normal file
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Cleanup script to disconnect all Prisma connections
|
||||
* Run this before restarting the server if you have connection pool issues
|
||||
*/
|
||||
|
||||
const { PrismaClient } = require('@prisma/client');
|
||||
|
||||
async function cleanup() {
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
try {
|
||||
console.log('🔌 Disconnecting Prisma client...');
|
||||
await prisma.$disconnect();
|
||||
console.log('✅ Prisma client disconnected successfully');
|
||||
process.exit(0);
|
||||
} catch (error) {
|
||||
console.error('❌ Error disconnecting Prisma:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
cleanup();
|
||||
71
scripts/run-migration.js
Normal file
71
scripts/run-migration.js
Normal file
@@ -0,0 +1,71 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const mysql = require('mysql2/promise');
|
||||
|
||||
// Database configuration - you should update these with your actual database credentials
|
||||
const dbConfig = {
|
||||
host: process.env.DB_HOST || 'localhost',
|
||||
port: process.env.DB_PORT || 3306,
|
||||
user: process.env.DB_USER || 'root',
|
||||
password: process.env.DB_PASSWORD || '',
|
||||
database: process.env.DB_NAME || 'corrad_af_2024',
|
||||
multipleStatements: true
|
||||
};
|
||||
|
||||
async function runMigration() {
|
||||
let connection;
|
||||
|
||||
try {
|
||||
console.log('🔄 Connecting to database...');
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
|
||||
console.log('✅ Connected to database successfully');
|
||||
|
||||
// Read the migration file
|
||||
const migrationPath = path.join(__dirname, '..', 'database', 'migrations', '004_create_notification_logs_table.sql');
|
||||
const migrationSQL = fs.readFileSync(migrationPath, 'utf8');
|
||||
|
||||
console.log('📄 Running migration: 004_create_notification_logs_table.sql');
|
||||
|
||||
// Execute the migration
|
||||
const [results] = await connection.execute(migrationSQL);
|
||||
|
||||
console.log('✅ Migration completed successfully');
|
||||
console.log('🎉 Notification logs table created');
|
||||
|
||||
// Optional: Check if the table was created
|
||||
const [tables] = await connection.execute('SHOW TABLES LIKE "notification_logs"');
|
||||
|
||||
if (tables.length > 0) {
|
||||
console.log('✅ Table "notification_logs" confirmed to exist');
|
||||
|
||||
// Show table structure
|
||||
const [columns] = await connection.execute('DESCRIBE notification_logs');
|
||||
console.log('📋 Table structure:');
|
||||
columns.forEach(col => {
|
||||
console.log(` - ${col.Field}: ${col.Type} ${col.Null === 'YES' ? 'NULL' : 'NOT NULL'} ${col.Key ? `(${col.Key})` : ''}`);
|
||||
});
|
||||
} else {
|
||||
console.log('❌ Table "notification_logs" was not found');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Migration failed:', error.message);
|
||||
console.error('Stack trace:', error.stack);
|
||||
process.exit(1);
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
console.log('🔌 Database connection closed');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run the migration
|
||||
console.log('🚀 Starting database migration for notification logs...');
|
||||
runMigration().then(() => {
|
||||
console.log('✅ Migration process completed successfully');
|
||||
}).catch(error => {
|
||||
console.error('❌ Migration process failed:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
184
scripts/seed-templates.js
Normal file
184
scripts/seed-templates.js
Normal file
@@ -0,0 +1,184 @@
|
||||
const { PrismaClient } = require('@prisma/client');
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
const sampleTemplates = [
|
||||
{
|
||||
name: "Welcome Email",
|
||||
value: "welcome_email",
|
||||
description: "Welcome email for new users",
|
||||
subject: "Welcome to our platform!",
|
||||
preheader: "Get started with your new account",
|
||||
email_content: `
|
||||
<h1>Welcome {{user_name}}!</h1>
|
||||
<p>Thank you for joining our platform. We're excited to have you aboard!</p>
|
||||
<p>Here are some things you can do to get started:</p>
|
||||
<ul>
|
||||
<li>Complete your profile</li>
|
||||
<li>Explore our features</li>
|
||||
<li>Connect with other users</li>
|
||||
</ul>
|
||||
<p>If you have any questions, feel free to reach out to our support team.</p>
|
||||
<p>Best regards,<br>The Team</p>
|
||||
`,
|
||||
category: "user_management",
|
||||
channels: ["email"],
|
||||
status: "Active",
|
||||
version: "1.0",
|
||||
is_personal: false,
|
||||
from_name: "Welcome Team",
|
||||
reply_to: "support@example.com",
|
||||
track_opens: true,
|
||||
variables: [
|
||||
{ name: "user_name", description: "User's full name", required: true },
|
||||
{ name: "user_email", description: "User's email address", required: true }
|
||||
],
|
||||
is_active: true,
|
||||
created_by: "system",
|
||||
updated_by: "system"
|
||||
},
|
||||
{
|
||||
name: "Password Reset",
|
||||
value: "password_reset",
|
||||
description: "Password reset notification",
|
||||
subject: "Reset your password",
|
||||
preheader: "You requested a password reset",
|
||||
email_content: `
|
||||
<h1>Password Reset Request</h1>
|
||||
<p>Hi {{user_name}},</p>
|
||||
<p>You recently requested to reset your password. Click the button below to proceed:</p>
|
||||
<p><a href="{{reset_url}}" style="background: #007bff; color: white; padding: 10px 20px; text-decoration: none; border-radius: 5px;">Reset Password</a></p>
|
||||
<p>If you didn't request this, please ignore this email.</p>
|
||||
<p>This link will expire in 24 hours.</p>
|
||||
`,
|
||||
category: "security",
|
||||
channels: ["email"],
|
||||
status: "Active",
|
||||
version: "1.0",
|
||||
is_personal: false,
|
||||
from_name: "Security Team",
|
||||
reply_to: "security@example.com",
|
||||
track_opens: true,
|
||||
variables: [
|
||||
{ name: "user_name", description: "User's full name", required: true },
|
||||
{ name: "reset_url", description: "Password reset URL", required: true }
|
||||
],
|
||||
is_active: true,
|
||||
created_by: "system",
|
||||
updated_by: "system"
|
||||
},
|
||||
{
|
||||
name: "Order Confirmation",
|
||||
value: "order_confirmation",
|
||||
description: "Order confirmation notification",
|
||||
subject: "Order Confirmed - {{order_number}}",
|
||||
preheader: "Your order has been confirmed",
|
||||
email_content: `
|
||||
<h1>Order Confirmation</h1>
|
||||
<p>Hi {{customer_name}},</p>
|
||||
<p>Your order <strong>{{order_number}}</strong> has been confirmed!</p>
|
||||
<p>Order Total: <strong>{{order_total}}</strong></p>
|
||||
<p>Estimated delivery: {{delivery_date}}</p>
|
||||
<p>You can track your order status in your account dashboard.</p>
|
||||
<p>Thank you for your business!</p>
|
||||
`,
|
||||
push_title: "Order Confirmed!",
|
||||
push_body: "Your order {{order_number}} has been confirmed and is being processed.",
|
||||
category: "orders",
|
||||
channels: ["email", "push"],
|
||||
status: "Active",
|
||||
version: "1.0",
|
||||
is_personal: false,
|
||||
from_name: "Orders Team",
|
||||
reply_to: "orders@example.com",
|
||||
track_opens: true,
|
||||
variables: [
|
||||
{ name: "customer_name", description: "Customer's name", required: true },
|
||||
{ name: "order_number", description: "Order number", required: true },
|
||||
{ name: "order_total", description: "Order total amount", required: true },
|
||||
{ name: "delivery_date", description: "Estimated delivery date", required: true }
|
||||
],
|
||||
is_active: true,
|
||||
created_by: "system",
|
||||
updated_by: "system"
|
||||
},
|
||||
{
|
||||
name: "Marketing Newsletter",
|
||||
value: "marketing_newsletter",
|
||||
description: "Monthly marketing newsletter",
|
||||
subject: "{{newsletter_title}} - Don't miss out!",
|
||||
preheader: "Exclusive offers and updates inside",
|
||||
email_content: `
|
||||
<h1>{{newsletter_title}}</h1>
|
||||
<p>Hi {{subscriber_name}},</p>
|
||||
<p>Here are this month's highlights:</p>
|
||||
<div>{{newsletter_content}}</div>
|
||||
<p>Don't forget to follow us on social media for daily updates!</p>
|
||||
<p>Best regards,<br>Marketing Team</p>
|
||||
<p><small><a href="{{unsubscribe_url}}">Unsubscribe</a></small></p>
|
||||
`,
|
||||
category: "marketing",
|
||||
channels: ["email"],
|
||||
status: "Draft",
|
||||
version: "1.0",
|
||||
is_personal: false,
|
||||
from_name: "Marketing Team",
|
||||
reply_to: "marketing@example.com",
|
||||
track_opens: true,
|
||||
variables: [
|
||||
{ name: "subscriber_name", description: "Subscriber's name", required: true },
|
||||
{ name: "newsletter_title", description: "Newsletter title", required: true },
|
||||
{ name: "newsletter_content", description: "Newsletter content", required: true },
|
||||
{ name: "unsubscribe_url", description: "Unsubscribe URL", required: true }
|
||||
],
|
||||
is_active: false,
|
||||
created_by: "system",
|
||||
updated_by: "system"
|
||||
}
|
||||
];
|
||||
|
||||
async function seedTemplates() {
|
||||
try {
|
||||
console.log('Starting to seed notification templates...');
|
||||
|
||||
// Clear existing sample data
|
||||
await prisma.notification_templates.deleteMany({
|
||||
where: {
|
||||
created_by: "system"
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Cleared existing sample templates');
|
||||
|
||||
// Insert sample templates
|
||||
for (const template of sampleTemplates) {
|
||||
const created = await prisma.notification_templates.create({
|
||||
data: template
|
||||
});
|
||||
console.log(`Created template: ${created.name} (${created.id})`);
|
||||
}
|
||||
|
||||
console.log(`Successfully seeded ${sampleTemplates.length} notification templates!`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error seeding templates:', error);
|
||||
throw error;
|
||||
} finally {
|
||||
await prisma.$disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
// Run the seeding
|
||||
if (require.main === module) {
|
||||
seedTemplates()
|
||||
.then(() => {
|
||||
console.log('Seeding completed successfully!');
|
||||
process.exit(0);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Seeding failed:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = seedTemplates;
|
||||
60
scripts/setup-aws-ses-config.sql
Normal file
60
scripts/setup-aws-ses-config.sql
Normal file
@@ -0,0 +1,60 @@
|
||||
-- Setup AWS SES SMTP Configuration in Database
|
||||
-- This script configures the notification_delivery_config table with AWS SES settings
|
||||
|
||||
-- Insert or update AWS SES configuration for email channel
|
||||
INSERT INTO notification_delivery_config
|
||||
(channel_type, is_enabled, provider, provider_config, status, success_rate, created_by, updated_by, created_at, updated_at)
|
||||
VALUES (
|
||||
'email',
|
||||
true,
|
||||
'AWS SES',
|
||||
JSON_OBJECT(
|
||||
'host', 'email-smtp.us-east-1.amazonaws.com',
|
||||
'port', 587,
|
||||
'secure', false,
|
||||
'user', 'YOUR_AWS_SES_SMTP_USERNAME',
|
||||
'pass', 'YOUR_AWS_SES_SMTP_PASSWORD',
|
||||
'senderEmail', 'noreply@yourcompany.com',
|
||||
'senderName', 'Your Company Name',
|
||||
'region', 'us-east-1',
|
||||
'configurationSet', ''
|
||||
),
|
||||
'Active',
|
||||
0.0,
|
||||
1,
|
||||
1,
|
||||
NOW(),
|
||||
NOW()
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
is_enabled = true,
|
||||
provider = 'AWS SES',
|
||||
provider_config = JSON_OBJECT(
|
||||
'host', 'email-smtp.us-east-1.amazonaws.com',
|
||||
'port', 587,
|
||||
'secure', false,
|
||||
'user', 'YOUR_AWS_SES_SMTP_USERNAME',
|
||||
'pass', 'YOUR_AWS_SES_SMTP_PASSWORD',
|
||||
'senderEmail', 'noreply@yourcompany.com',
|
||||
'senderName', 'Your Company Name',
|
||||
'region', 'us-east-1',
|
||||
'configurationSet', ''
|
||||
),
|
||||
status = 'Active',
|
||||
updated_by = 1,
|
||||
updated_at = NOW();
|
||||
|
||||
-- Verify the configuration
|
||||
SELECT
|
||||
id,
|
||||
channel_type,
|
||||
is_enabled,
|
||||
provider,
|
||||
status,
|
||||
JSON_EXTRACT(provider_config, '$.host') as smtp_host,
|
||||
JSON_EXTRACT(provider_config, '$.port') as smtp_port,
|
||||
JSON_EXTRACT(provider_config, '$.region') as aws_region,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM notification_delivery_config
|
||||
WHERE channel_type = 'email';
|
||||
56
scripts/setup-mailtrap-config.sql
Normal file
56
scripts/setup-mailtrap-config.sql
Normal file
@@ -0,0 +1,56 @@
|
||||
-- Setup Mailtrap SMTP Configuration in Database
|
||||
-- This script configures the notification_delivery_config table with Mailtrap settings
|
||||
|
||||
-- Insert or update Mailtrap configuration for email channel
|
||||
INSERT INTO notification_delivery_config
|
||||
(channel_type, is_enabled, provider, provider_config, status, success_rate, created_by, updated_by, created_at, updated_at)
|
||||
VALUES (
|
||||
'email',
|
||||
true,
|
||||
'Mailtrap',
|
||||
JSON_OBJECT(
|
||||
'host', 'live.smtp.mailtrap.io',
|
||||
'port', 587,
|
||||
'secure', false,
|
||||
'user', 'apismtp@mailtrap.io',
|
||||
'pass', '8ec4d16f282740da5ddfc7ef7a3ca87b',
|
||||
'senderEmail', 'noreply@yourcompany.com',
|
||||
'senderName', 'Your Company Name'
|
||||
),
|
||||
'Active',
|
||||
0.0,
|
||||
1,
|
||||
1,
|
||||
NOW(),
|
||||
NOW()
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
is_enabled = true,
|
||||
provider = 'Mailtrap',
|
||||
provider_config = JSON_OBJECT(
|
||||
'host', 'live.smtp.mailtrap.io',
|
||||
'port', 587,
|
||||
'secure', false,
|
||||
'user', 'apismtp@mailtrap.io',
|
||||
'pass', '8ec4d16f282740da5ddfc7ef7a3ca87b',
|
||||
'senderEmail', 'noreply@yourcompany.com',
|
||||
'senderName', 'Your Company Name'
|
||||
),
|
||||
status = 'Active',
|
||||
updated_by = 1,
|
||||
updated_at = NOW();
|
||||
|
||||
-- Verify the configuration
|
||||
SELECT
|
||||
id,
|
||||
channel_type,
|
||||
is_enabled,
|
||||
provider,
|
||||
status,
|
||||
JSON_EXTRACT(provider_config, '$.host') as smtp_host,
|
||||
JSON_EXTRACT(provider_config, '$.port') as smtp_port,
|
||||
JSON_EXTRACT(provider_config, '$.user') as smtp_user,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM notification_delivery_config
|
||||
WHERE channel_type = 'email';
|
||||
206
scripts/test-api.js
Normal file
206
scripts/test-api.js
Normal file
@@ -0,0 +1,206 @@
|
||||
const { PrismaClient } = require('@prisma/client');
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
async function testDatabaseConnection() {
|
||||
try {
|
||||
console.log('Testing database connection...');
|
||||
|
||||
// Test basic connection
|
||||
await prisma.$connect();
|
||||
console.log('✅ Database connection successful');
|
||||
|
||||
// Check if notification_templates table exists
|
||||
const tableInfo = await prisma.$queryRaw`
|
||||
SELECT COUNT(*) as count
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'notification_templates'
|
||||
`;
|
||||
|
||||
console.log('Table exists check:', tableInfo);
|
||||
|
||||
// Check current data in the table
|
||||
const count = await prisma.notification_templates.count();
|
||||
console.log(`📊 Current templates count: ${count}`);
|
||||
|
||||
// Fetch all templates to see what we have
|
||||
const templates = await prisma.notification_templates.findMany({
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
value: true,
|
||||
status: true,
|
||||
category: true,
|
||||
channels: true,
|
||||
created_at: true
|
||||
}
|
||||
});
|
||||
|
||||
console.log('📝 Current templates:', JSON.stringify(templates, null, 2));
|
||||
|
||||
return { success: true, count, templates };
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Database test failed:', error);
|
||||
return { success: false, error: error.message };
|
||||
} finally {
|
||||
await prisma.$disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
async function testAPILogic() {
|
||||
try {
|
||||
console.log('\n🧪 Testing API logic...');
|
||||
|
||||
// Simulate the API endpoint logic
|
||||
const queryParams = {
|
||||
page: 1,
|
||||
limit: 10,
|
||||
sortBy: "created_at",
|
||||
sortOrder: "desc"
|
||||
};
|
||||
|
||||
console.log('Query params:', queryParams);
|
||||
|
||||
// Build where clause (same as API)
|
||||
const whereClause = {};
|
||||
|
||||
// Calculate pagination
|
||||
const skip = (queryParams.page - 1) * queryParams.limit;
|
||||
|
||||
// Get total count
|
||||
const totalCount = await prisma.notification_templates.count({
|
||||
where: whereClause
|
||||
});
|
||||
|
||||
console.log(`📊 Total count: ${totalCount}`);
|
||||
|
||||
// Fetch templates
|
||||
const templates = await prisma.notification_templates.findMany({
|
||||
where: whereClause,
|
||||
orderBy: {
|
||||
[queryParams.sortBy]: queryParams.sortOrder
|
||||
},
|
||||
skip: skip,
|
||||
take: queryParams.limit,
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
value: true,
|
||||
description: true,
|
||||
subject: true,
|
||||
category: true,
|
||||
channels: true,
|
||||
status: true,
|
||||
version: true,
|
||||
tags: true,
|
||||
is_personal: true,
|
||||
is_active: true,
|
||||
created_by: true,
|
||||
created_at: true,
|
||||
updated_at: true
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`📝 Fetched ${templates.length} templates`);
|
||||
|
||||
// Format the response data (same as API)
|
||||
const formattedTemplates = templates.map(template => ({
|
||||
id: template.id,
|
||||
title: template.name,
|
||||
value: template.value,
|
||||
description: template.description,
|
||||
subject: template.subject,
|
||||
category: template.category,
|
||||
channels: template.channels || [],
|
||||
status: template.status,
|
||||
version: template.version,
|
||||
tags: template.tags ? template.tags.split(',').map(tag => tag.trim()) : [],
|
||||
isPersonal: template.is_personal,
|
||||
isActive: template.is_active,
|
||||
createdBy: template.created_by,
|
||||
createdAt: template.created_at,
|
||||
updatedAt: template.updated_at
|
||||
}));
|
||||
|
||||
// Calculate pagination info
|
||||
const totalPages = Math.ceil(totalCount / queryParams.limit);
|
||||
const hasNextPage = queryParams.page < totalPages;
|
||||
const hasPrevPage = queryParams.page > 1;
|
||||
|
||||
const apiResponse = {
|
||||
success: true,
|
||||
data: {
|
||||
templates: formattedTemplates,
|
||||
pagination: {
|
||||
page: queryParams.page,
|
||||
limit: queryParams.limit,
|
||||
totalCount,
|
||||
totalPages,
|
||||
hasNextPage,
|
||||
hasPrevPage
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
console.log('🚀 Simulated API Response structure:');
|
||||
console.log(` success: ${apiResponse.success}`);
|
||||
console.log(` data: ${apiResponse.data ? 'exists' : 'undefined'}`);
|
||||
console.log(` data.templates: ${apiResponse.data?.templates ? `array with ${apiResponse.data.templates.length} items` : 'undefined'}`);
|
||||
console.log(` data.pagination: ${apiResponse.data?.pagination ? 'exists' : 'undefined'}`);
|
||||
|
||||
return apiResponse;
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ API logic test failed:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.message
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function runAllTests() {
|
||||
console.log('🔍 Starting API debugging tests...\n');
|
||||
|
||||
// Test 1: Database connection and data
|
||||
const dbTest = await testDatabaseConnection();
|
||||
|
||||
// Test 2: API logic simulation
|
||||
const apiTest = await testAPILogic();
|
||||
|
||||
console.log('\n📋 Test Summary:');
|
||||
console.log(`Database connection: ${dbTest.success ? '✅ PASS' : '❌ FAIL'}`);
|
||||
console.log(`API logic simulation: ${apiTest.success ? '✅ PASS' : '❌ FAIL'}`);
|
||||
|
||||
if (!dbTest.success || !apiTest.success) {
|
||||
console.log('\n🔧 Recommended actions:');
|
||||
if (!dbTest.success) {
|
||||
console.log(' - Check database connection string');
|
||||
console.log(' - Ensure database server is running');
|
||||
console.log(' - Run database migrations');
|
||||
}
|
||||
if (!apiTest.success) {
|
||||
console.log(' - Check API endpoint implementation');
|
||||
console.log(' - Verify Prisma client setup');
|
||||
}
|
||||
}
|
||||
|
||||
return { dbTest, apiTest };
|
||||
}
|
||||
|
||||
// Run the tests
|
||||
if (require.main === module) {
|
||||
runAllTests()
|
||||
.then(() => {
|
||||
console.log('\n✅ Debugging tests completed!');
|
||||
process.exit(0);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('\n❌ Debugging tests failed:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = runAllTests;
|
||||
Reference in New Issue
Block a user