Skip to content

Commit

Permalink
Add MySQL2 support and implement database migration setup with initia…
Browse files Browse the repository at this point in the history
…l SMTP configuration
  • Loading branch information
garanda21 committed Jan 5, 2025
1 parent 2e165c3 commit 07c7159
Show file tree
Hide file tree
Showing 5 changed files with 523 additions and 243 deletions.
122 changes: 122 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
"axios": "^1.7.9",
"cors": "^2.8.5",
"express": "^4.21.2",
"lucide-react": "^0.344.0",
"mysql": "^2.18.1",
"lucide-react": "^0.344.0",
"mysql2": "^3.12.0",
"nodemailer": "^6.9.12",
"react": "^18.3.1",
"react-dom": "^18.3.1",
Expand Down
31 changes: 31 additions & 0 deletions server/database/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { createPool } from 'mysql2/promise';

// Validate DB environment variables
const requiredDbVars = ['DB_HOST', 'DB_USER', 'DB_PASSWORD', 'DB_NAME'];
const missingVars = requiredDbVars.filter(varName => !process.env[varName]);

if (missingVars.length > 0) {
throw new Error(`Missing required database environment variables: ${missingVars.join(', ')}`);
}

export const dbConfig = {
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0,
};

// Create a pool without database selection for initial setup
export const initPool = createPool({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
waitForConnections: true,
connectionLimit: 1,
queueLimit: 0
});

export const pool = createPool(dbConfig);
155 changes: 155 additions & 0 deletions server/database/migrate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import { pool, initPool } from './config.js';

const migrations = [
// Create tables in order of dependencies
`CREATE TABLE IF NOT EXISTS smtp_config (
id int NOT NULL AUTO_INCREMENT,
host varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
port int NOT NULL,
username varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
password varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
fromEmail varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
fromName varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
UNIQUE KEY idx (id) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`,

`CREATE TABLE IF NOT EXISTS templates (
id varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
name varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
content text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`,

`CREATE TABLE IF NOT EXISTS contact_lists (
id varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
name varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`,

`CREATE TABLE IF NOT EXISTS contacts (
id varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
name varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
email varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
contact_list_id varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
PRIMARY KEY (id),
KEY contact_list_id (contact_list_id),
CONSTRAINT contacts_ibfk_1 FOREIGN KEY (contact_list_id) REFERENCES contact_lists (id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`,

`CREATE TABLE IF NOT EXISTS campaigns (
id varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
name varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
subject varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
template_id varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
contact_list_id varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
status varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
sent_count int DEFAULT NULL,
total_count int DEFAULT NULL,
create_date datetime DEFAULT NULL,
PRIMARY KEY (id),
KEY template_id (template_id),
KEY contact_list_id (contact_list_id),
CONSTRAINT campaigns_ibfk_3 FOREIGN KEY (template_id) REFERENCES templates (id) ON DELETE CASCADE,
CONSTRAINT campaigns_ibfk_4 FOREIGN KEY (contact_list_id) REFERENCES contact_lists (id) ON DELETE SET NULL ON UPDATE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`,

`CREATE TABLE IF NOT EXISTS errors (
id int NOT NULL AUTO_INCREMENT,
email text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
error text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
campaign_id varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
PRIMARY KEY (id),
KEY campaign_id (campaign_id) USING BTREE,
CONSTRAINT errors_ibfk_1 FOREIGN KEY (campaign_id) REFERENCES campaigns (id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`,

// Add initial SMTP config if needed
`INSERT INTO smtp_config (id, host, port, username, password, fromEmail, fromName)
SELECT 1, 'smtp.example.com', 587, 'default', 'default', '[email protected]', 'System'
WHERE NOT EXISTS (SELECT 1 FROM smtp_config WHERE id = 1)`
];

async function runMigrations() {
const connection = await pool.getConnection();

try {
// Create migrations table if it doesn't exist
await connection.query(`
CREATE TABLE IF NOT EXISTS migrations (
id int NOT NULL AUTO_INCREMENT,
migration_name varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
executed_at timestamp DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
`);

// Run each migration in a transaction
for (const [index, migration] of migrations.entries()) {
const tableName = migration.match(/CREATE TABLE IF NOT EXISTS (\w+)/)?.[1] || 'other';
const migrationName = `migration_${tableName}_${index + 1}`;

// Check if migration was already executed
const [executed] = await connection.query(
'SELECT 1 FROM migrations WHERE migration_name = ?',
[migrationName]
);

if (executed.length === 0) {
await connection.beginTransaction();

try {
// Run the migration
await connection.query(migration);

// Record the migration
await connection.query(
'INSERT INTO migrations (migration_name) VALUES (?)',
[migrationName]
);

await connection.commit();
console.log(`Migration ${migrationName} executed successfully`);

} catch (error) {
await connection.rollback();
throw error;
}
}
}

console.log('All migrations completed successfully');

} catch (error) {
console.error('Migration failed:', error);
throw error;

} finally {
connection.release();
}
}

export async function ensureDatabase() {
const connection = await initPool.getConnection();
try {
await connection.query(`CREATE DATABASE IF NOT EXISTS ${process.env.DB_NAME}
CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci`);
console.log(`Database ${process.env.DB_NAME} ensured`);
} catch (error) {
console.error('Failed to create database:', error);
throw error;
} finally {
connection.release();
await initPool.end();
}
}

export async function initializeDatabase() {
try {
await ensureDatabase();
await runMigrations();
return true;
} catch (error) {
console.error('Database initialization failed:', error);
return false;
}
}
Loading

0 comments on commit 07c7159

Please sign in to comment.