diff --git a/server/docker-entrypoint.sh b/server/docker-entrypoint.sh index ad670fafa..09727ccb6 100644 --- a/server/docker-entrypoint.sh +++ b/server/docker-entrypoint.sh @@ -26,8 +26,15 @@ wait_for_postgres() { # 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 database migrations using npm script +echo "Running database migrations..." +npm run migrate -# Run the server normally +if [ $? -eq 0 ]; then + echo "✅ Migrations completed successfully!" +else + echo "⚠️ Migration failed, but continuing to start server..." +fi + +# Run the server normally exec "$@" \ No newline at end of file diff --git a/server/src/db/migrate.js b/server/src/db/migrate.js index a76c04ebb..3580b5816 100644 --- a/server/src/db/migrate.js +++ b/server/src/db/migrate.js @@ -1,23 +1,14 @@ '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); +const { execSync } = require('child_process'); +const path = require('path'); 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', { + execSync('npx sequelize-cli db:migrate', { stdio: 'inherit', - cwd: path.resolve(__dirname, '../../..') + cwd: path.resolve(__dirname, '../../..') }); console.log('Migrations completed successfully'); return true; diff --git a/server/src/db/migrations/20250202000000-add-retry-count.js b/server/src/db/migrations/20250202000000-add-retry-count.js new file mode 100644 index 000000000..398b9239d --- /dev/null +++ b/server/src/db/migrations/20250202000000-add-retry-count.js @@ -0,0 +1,20 @@ +'use strict'; + +module.exports = { + up: async (queryInterface, Sequelize) => { + const tableInfo = await queryInterface.describeTable('run'); + + // Only add the column if it doesn't exist + if (!tableInfo.retryCount) { + await queryInterface.addColumn('run', 'retryCount', { + type: Sequelize.INTEGER, + allowNull: true, + defaultValue: 0, + }); + } + }, + + down: async (queryInterface, Sequelize) => { + await queryInterface.removeColumn('run', 'retryCount'); + } +}; diff --git a/server/src/db/migrations/20250327111003-add-airtable-columns.js b/server/src/db/migrations/20250327111003-add-airtable-columns.js index 7cbb1e02e..1302aaf20 100644 --- a/server/src/db/migrations/20250327111003-add-airtable-columns.js +++ b/server/src/db/migrations/20250327111003-add-airtable-columns.js @@ -67,14 +67,28 @@ module.exports = { // Remove Airtable related columns return queryInterface.sequelize.transaction(async (transaction) => { try { - // Remove columns in reverse order - await queryInterface.removeColumn('robot', 'airtable_refresh_token', { transaction }); - await queryInterface.removeColumn('robot', 'airtable_access_token', { transaction }); - await queryInterface.removeColumn('robot', 'airtable_table_id', { transaction }); - await queryInterface.removeColumn('robot', 'airtable_table_name', { transaction }); - await queryInterface.removeColumn('robot', 'airtable_base_name', { transaction }); - await queryInterface.removeColumn('robot', 'airtable_base_id', { transaction }); - + const tableInfo = await queryInterface.describeTable('robot', { transaction }); + + // Remove columns in reverse order, only if they exist + if (tableInfo.airtable_refresh_token) { + await queryInterface.removeColumn('robot', 'airtable_refresh_token', { transaction }); + } + if (tableInfo.airtable_access_token) { + await queryInterface.removeColumn('robot', 'airtable_access_token', { transaction }); + } + if (tableInfo.airtable_table_id) { + await queryInterface.removeColumn('robot', 'airtable_table_id', { transaction }); + } + if (tableInfo.airtable_table_name) { + await queryInterface.removeColumn('robot', 'airtable_table_name', { transaction }); + } + if (tableInfo.airtable_base_name) { + await queryInterface.removeColumn('robot', 'airtable_base_name', { transaction }); + } + if (tableInfo.airtable_base_id) { + await queryInterface.removeColumn('robot', 'airtable_base_id', { transaction }); + } + return Promise.resolve(); } catch (error) { return Promise.reject(error); diff --git a/server/src/db/migrations/20250527105655-add-webhooks.js b/server/src/db/migrations/20250527105655-add-webhooks.js index 60eefd193..23d93fa4b 100644 --- a/server/src/db/migrations/20250527105655-add-webhooks.js +++ b/server/src/db/migrations/20250527105655-add-webhooks.js @@ -2,26 +2,44 @@ module.exports = { async up(queryInterface, Sequelize) { - await queryInterface.addColumn('robot', 'webhooks', { - type: Sequelize.JSONB, - allowNull: true, - defaultValue: null, - comment: 'Webhook configurations for the robot' - }); + const tableInfo = await queryInterface.describeTable('robot'); - // Optional: Add an index for better query performance if you plan to search within webhook data - await queryInterface.addIndex('robot', { - fields: ['webhooks'], - using: 'gin', // GIN index for JSONB columns - name: 'robot_webhooks_gin_idx' - }); + // Only add the column if it doesn't exist + if (!tableInfo.webhooks) { + await queryInterface.addColumn('robot', 'webhooks', { + type: Sequelize.JSONB, + allowNull: true, + defaultValue: null, + comment: 'Webhook configurations for the robot' + }); + } + + // Check if index exists before adding + const indexes = await queryInterface.showIndex('robot'); + const indexExists = indexes.some(index => index.name === 'robot_webhooks_gin_idx'); + + if (!indexExists && tableInfo.webhooks) { + await queryInterface.addIndex('robot', { + fields: ['webhooks'], + using: 'gin', // GIN index for JSONB columns + name: 'robot_webhooks_gin_idx' + }); + } }, async down(queryInterface, Sequelize) { - // Remove the index first - await queryInterface.removeIndex('robot', 'robot_webhooks_gin_idx'); - - // Then remove the column - await queryInterface.removeColumn('robot', 'webhooks'); + // Check if index exists before removing + const indexes = await queryInterface.showIndex('robot'); + const indexExists = indexes.some(index => index.name === 'robot_webhooks_gin_idx'); + + if (indexExists) { + await queryInterface.removeIndex('robot', 'robot_webhooks_gin_idx'); + } + + // Check if column exists before removing + const tableInfo = await queryInterface.describeTable('robot'); + if (tableInfo.webhooks) { + await queryInterface.removeColumn('robot', 'webhooks'); + } } }; \ No newline at end of file diff --git a/server/src/models/Run.ts b/server/src/models/Run.ts index 1e292dbbf..74f1b9bac 100644 --- a/server/src/models/Run.ts +++ b/server/src/models/Run.ts @@ -47,7 +47,7 @@ class Run extends Model implements RunAttr public runByAPI!: boolean; public serializableOutput!: Record; public binaryOutput!: Record; - public retryCount!: number; + public retryCount?: number; } Run.init(