Merge pull request #500 from getmaxun/auto-migrate

feat: automate db migrations
This commit is contained in:
Karishma Shukla
2025-03-27 16:16:36 +05:30
committed by GitHub
8 changed files with 188 additions and 1 deletions

8
.sequelizerc Normal file
View File

@@ -0,0 +1,8 @@
import path from 'path';
module.exports = {
'config': path.resolve('server/src/db/config', 'database.js'),
'models-path': path.resolve('server/src/db/models'),
'seeders-path': path.resolve('server/src/db/seeders'),
'migrations-path': path.resolve('server/src/db/migrations')
};

View File

@@ -48,6 +48,7 @@ services:
- "${BACKEND_PORT:-8080}:${BACKEND_PORT:-8080}"
env_file: .env
environment:
DB_HOST: ${DB_HOST}
BACKEND_URL: ${BACKEND_URL}
# to ensure Playwright works in Docker
PLAYWRIGHT_BROWSERS_PATH: /ms-playwright

View File

@@ -96,7 +96,13 @@
"build:server": "tsc -p server/tsconfig.json",
"start:server": "cross-env NODE_OPTIONS='--max-old-space-size=8000' server/dist/server/src/server.js",
"preview": "vite preview",
"lint": "./node_modules/.bin/eslint ."
"lint": "./node_modules/.bin/eslint .",
"migrate": "sequelize-cli db:migrate",
"migrate:undo": "sequelize-cli db:migrate:undo",
"migrate:undo:all": "sequelize-cli db:migrate:undo:all",
"seed": "sequelize-cli db:seed:all",
"seed:undo:all": "sequelize-cli db:seed:undo:all",
"migration:generate": "sequelize-cli migration:generate --name"
},
"eslintConfig": {
"extends": [

View File

@@ -11,6 +11,7 @@ COPY public ./public
COPY server ./server
COPY tsconfig.json ./
COPY server/tsconfig.json ./server/
COPY .sequelizerc ./
# COPY server/start.sh ./
# Install dependencies
@@ -41,11 +42,18 @@ RUN apt-get update && apt-get install -y \
libxext6 \
libxi6 \
libxtst6 \
postgresql-client \
netcat-openbsd \
&& rm -rf /var/lib/apt/lists/* \
&& mkdir -p /tmp/.X11-unix && chmod 1777 /tmp/.X11-unix
COPY server/docker-entrypoint.sh /app/
RUN chmod +x /app/docker-entrypoint.sh
# Expose the backend port
EXPOSE ${BACKEND_PORT:-8080}
ENTRYPOINT ["/app/docker-entrypoint.sh"]
# Start the backend using the start script
CMD ["npm", "run", "server"]

View File

@@ -0,0 +1,33 @@
#!/bin/bash
set -e
# Function to wait for PostgreSQL
wait_for_postgres() {
echo "Waiting for PostgreSQL at $DB_HOST:$DB_PORT..."
max_retries=30
retries=0
while ! nc -z $DB_HOST $DB_PORT; do
retries=$((retries+1))
if [ $retries -eq $max_retries ]; then
echo "Error: PostgreSQL not available after $max_retries attempts. Continuing anyway..."
break
fi
echo "PostgreSQL not available yet (attempt $retries/$max_retries), retrying..."
sleep 2
done
if [ $retries -lt $max_retries ]; then
echo "PostgreSQL is ready!"
fi
}
# Wait for PostgreSQL to be ready
wait_for_postgres
# Run the application with migrations before startup
NODE_OPTIONS="--max-old-space-size=4096" node -e "require('./server/src/db/migrate')().then(() => { console.log('Migration process completed.'); })"
# Run the server normally
exec "$@"

View File

@@ -0,0 +1,42 @@
import dotenv from 'dotenv';
dotenv.config({ path: './.env' });
// Validate required environment variables
const requiredEnvVars = ['DB_USER', 'DB_PASSWORD', 'DB_NAME', 'DB_HOST', 'DB_PORT'];
requiredEnvVars.forEach(envVar => {
if (!process.env[envVar]) {
console.error(`Error: Environment variable ${envVar} is not set.`);
process.exit(1);
}
});
module.exports = {
development: {
username: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
host: process.env.DB_HOST,
port: process.env.DB_PORT,
dialect: 'postgres',
logging: console.log,
},
test: {
username: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
host: process.env.DB_HOST,
port: process.env.DB_PORT,
dialect: 'postgres',
logging: false,
},
production: {
username: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
host: process.env.DB_HOST,
port: process.env.DB_PORT,
dialect: 'postgres',
logging: false,
}
};

30
server/src/db/migrate.js Normal file
View File

@@ -0,0 +1,30 @@
'use strict';
import { execSync } from 'child_process';
import path from 'path';
import { fileURLToPath } from 'url';
import db from './models/index.js';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
async function runMigrations() {
try {
console.log('Testing database connection...');
await db.sequelize.authenticate();
console.log('Database connection established successfully.');
console.log('Running database migrations...');
execSync('npx sequelize-cli db:migrate', {
stdio: 'inherit',
cwd: path.resolve(__dirname, '../../..')
});
console.log('Migrations completed successfully');
return true;
} catch (error) {
console.error('Migration error:', error);
return false;
}
}
module.exports = runMigrations;

View File

@@ -0,0 +1,59 @@
'use strict';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import Sequelize from 'sequelize';
import databaseConfig from '../config/database.js';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const basename = path.basename(__filename);
const env = process.env.NODE_ENV || 'development';
const config = databaseConfig[env];
const db = {};
let sequelize;
if (config.use_env_variable) {
try {
sequelize = new Sequelize(process.env[config.use_env_variable], config);
console.log(`Connected to database using ${config.use_env_variable}`);
} catch (error) {
console.error('Unable to connect to the database using environment variable:', error);
process.exit(1);
}
} else {
try {
sequelize = new Sequelize(config.database, config.username, config.password, config);
console.log(`Connected to database: ${config.database}`);
} catch (error) {
console.error('Unable to connect to the database:', error);
process.exit(1);
}
}
fs
.readdirSync(__dirname)
.filter(file => {
return (
file.indexOf('.') !== 0 &&
file !== basename &&
file.slice(-3) === '.js' &&
file.indexOf('.test.js') === -1
);
})
.forEach(file => {
const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
db[model.name] = model;
});
Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;