From 7f07d5c2707d3632a150e208805b940abf3d3a45 Mon Sep 17 00:00:00 2001 From: Rohit Date: Mon, 24 Mar 2025 22:49:54 +0530 Subject: [PATCH 01/11] feat: add db migration config --- server/src/db/config/database.js | 31 +++++++++++++++++++++++ server/src/db/models/index.js | 43 ++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 server/src/db/config/database.js create mode 100644 server/src/db/models/index.js diff --git a/server/src/db/config/database.js b/server/src/db/config/database.js new file mode 100644 index 00000000..c3ce5402 --- /dev/null +++ b/server/src/db/config/database.js @@ -0,0 +1,31 @@ +require('dotenv').config({ path: './.env' }); + +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.TEST_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, + } +}; \ No newline at end of file diff --git a/server/src/db/models/index.js b/server/src/db/models/index.js new file mode 100644 index 00000000..dc97bee9 --- /dev/null +++ b/server/src/db/models/index.js @@ -0,0 +1,43 @@ +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const Sequelize = require('sequelize'); +const process = require('process'); +const basename = path.basename(__filename); +const env = process.env.NODE_ENV || 'development'; +const config = require('../config/database.js')[env]; +const db = {}; + +let sequelize; +if (config.use_env_variable) { + sequelize = new Sequelize(process.env[config.use_env_variable], config); +} else { + sequelize = new Sequelize(config.database, config.username, config.password, config); +} + +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; \ No newline at end of file From 957ed52166e972424d2ea5c4ecb97645f08017ab Mon Sep 17 00:00:00 2001 From: Rohit Date: Mon, 24 Mar 2025 22:50:49 +0530 Subject: [PATCH 02/11] feat: set sequelize export paths --- .sequelizerc | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .sequelizerc diff --git a/.sequelizerc b/.sequelizerc new file mode 100644 index 00000000..86d27dbc --- /dev/null +++ b/.sequelizerc @@ -0,0 +1,8 @@ +const path = require('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') +}; \ No newline at end of file From 0ccf7f91181c5febb1ab1c105c5a3fb2c4cb1b6e Mon Sep 17 00:00:00 2001 From: Rohit Date: Mon, 24 Mar 2025 22:53:34 +0530 Subject: [PATCH 03/11] feat: add migration commands --- package.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index d9818fcc..3e8c3bc1 100644 --- a/package.json +++ b/package.json @@ -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": [ From 9a75ea374b6967c9ff113b9dfb18b3c13c3e17dc Mon Sep 17 00:00:00 2001 From: Rohit Date: Tue, 25 Mar 2025 16:50:58 +0530 Subject: [PATCH 04/11] feat: add run migrations script --- server/src/db/migrate.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 server/src/db/migrate.js diff --git a/server/src/db/migrate.js b/server/src/db/migrate.js new file mode 100644 index 00000000..c432be3f --- /dev/null +++ b/server/src/db/migrate.js @@ -0,0 +1,26 @@ +'use strict'; + +const { execSync } = require('child_process'); +const path = require('path'); +const db = require('./models'); + +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; \ No newline at end of file From acaf514f2507d1273e8fbefe2aba5e240602154f Mon Sep 17 00:00:00 2001 From: Rohit Date: Tue, 25 Mar 2025 16:51:33 +0530 Subject: [PATCH 05/11] feat: add docker entrypoint script --- server/docker-entrypoint.sh | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 server/docker-entrypoint.sh diff --git a/server/docker-entrypoint.sh b/server/docker-entrypoint.sh new file mode 100644 index 00000000..89ada4f3 --- /dev/null +++ b/server/docker-entrypoint.sh @@ -0,0 +1,30 @@ +#!/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(success => { if (success || process.env.CONTINUE_ON_MIGRATION_FAILURE === 'true') { require('./server/src/index'); } else { console.error('Migration failed. Exiting.'); process.exit(1); } })" \ No newline at end of file From dc93f63fd7e3061fc199f317cd42c2bcc514ddd5 Mon Sep 17 00:00:00 2001 From: Rohit Date: Tue, 25 Mar 2025 16:52:24 +0530 Subject: [PATCH 06/11] feat: modify to automate migrations --- server/Dockerfile | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/server/Dockerfile b/server/Dockerfile index 65e472eb..8770b78e 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -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"] From 39833f5fee6c6d8e6186e4d306b97317a8642495 Mon Sep 17 00:00:00 2001 From: Rohit Date: Tue, 25 Mar 2025 16:53:06 +0530 Subject: [PATCH 07/11] feat: add db host env var --- docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose.yml b/docker-compose.yml index b571cc6f..45f70a33 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -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 From cee88e3b624bf228f59bc884ca8864d354536422 Mon Sep 17 00:00:00 2001 From: Rohit Date: Tue, 25 Mar 2025 21:42:47 +0530 Subject: [PATCH 08/11] feat: add db vars validation --- server/src/db/config/database.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/server/src/db/config/database.js b/server/src/db/config/database.js index c3ce5402..f8d77160 100644 --- a/server/src/db/config/database.js +++ b/server/src/db/config/database.js @@ -1,5 +1,15 @@ require('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, @@ -13,7 +23,7 @@ module.exports = { test: { username: process.env.DB_USER, password: process.env.DB_PASSWORD, - database: process.env.TEST_DB_NAME, + database: process.env.DB_NAME, host: process.env.DB_HOST, port: process.env.DB_PORT, dialect: 'postgres', From 198aa1f60e08f5b437cfb3e3fc0d8cc1afc77f2f Mon Sep 17 00:00:00 2001 From: Rohit Date: Tue, 25 Mar 2025 21:44:28 +0530 Subject: [PATCH 09/11] feat: add error handling for db conn --- server/src/db/models/index.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/server/src/db/models/index.js b/server/src/db/models/index.js index dc97bee9..1f044a8d 100644 --- a/server/src/db/models/index.js +++ b/server/src/db/models/index.js @@ -11,9 +11,21 @@ const db = {}; let sequelize; if (config.use_env_variable) { - sequelize = new Sequelize(process.env[config.use_env_variable], config); + 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 { - sequelize = new Sequelize(config.database, config.username, config.password, config); + 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 From ed9af243d4a7e26098b55e72916d9a84406fbc07 Mon Sep 17 00:00:00 2001 From: Rohit Date: Tue, 25 Mar 2025 22:05:28 +0530 Subject: [PATCH 10/11] feat: rm invalid path --- server/docker-entrypoint.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/server/docker-entrypoint.sh b/server/docker-entrypoint.sh index 89ada4f3..ad670faf 100644 --- a/server/docker-entrypoint.sh +++ b/server/docker-entrypoint.sh @@ -27,4 +27,7 @@ wait_for_postgres() { 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(success => { if (success || process.env.CONTINUE_ON_MIGRATION_FAILURE === 'true') { require('./server/src/index'); } else { console.error('Migration failed. Exiting.'); process.exit(1); } })" \ No newline at end of file +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 "$@" \ No newline at end of file From 091c1daa27fb0931a97bff40bc3e844bb42dfd0f Mon Sep 17 00:00:00 2001 From: Rohit Date: Thu, 27 Mar 2025 00:09:45 +0530 Subject: [PATCH 11/11] feat: change require syntax to import --- .sequelizerc | 2 +- server/src/db/config/database.js | 3 ++- server/src/db/migrate.js | 10 +++++++--- server/src/db/models/index.js | 14 +++++++++----- 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/.sequelizerc b/.sequelizerc index 86d27dbc..a6f14ffd 100644 --- a/.sequelizerc +++ b/.sequelizerc @@ -1,4 +1,4 @@ -const path = require('path'); +import path from 'path'; module.exports = { 'config': path.resolve('server/src/db/config', 'database.js'), diff --git a/server/src/db/config/database.js b/server/src/db/config/database.js index f8d77160..ae6972d1 100644 --- a/server/src/db/config/database.js +++ b/server/src/db/config/database.js @@ -1,4 +1,5 @@ -require('dotenv').config({ path: './.env' }); +import dotenv from 'dotenv'; +dotenv.config({ path: './.env' }); // Validate required environment variables const requiredEnvVars = ['DB_USER', 'DB_PASSWORD', 'DB_NAME', 'DB_HOST', 'DB_PORT']; diff --git a/server/src/db/migrate.js b/server/src/db/migrate.js index c432be3f..a76c04eb 100644 --- a/server/src/db/migrate.js +++ b/server/src/db/migrate.js @@ -1,8 +1,12 @@ 'use strict'; -const { execSync } = require('child_process'); -const path = require('path'); -const db = require('./models'); +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 { diff --git a/server/src/db/models/index.js b/server/src/db/models/index.js index 1f044a8d..1f26e70b 100644 --- a/server/src/db/models/index.js +++ b/server/src/db/models/index.js @@ -1,12 +1,16 @@ 'use strict'; -const fs = require('fs'); -const path = require('path'); -const Sequelize = require('sequelize'); -const process = require('process'); +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 = require('../config/database.js')[env]; +const config = databaseConfig[env]; const db = {}; let sequelize;