diff --git a/server/migrations/20250828000001-add-device-approval.js b/server/migrations/20250828000001-add-device-approval.js index 6eb0a31..e5ccecd 100644 --- a/server/migrations/20250828000001-add-device-approval.js +++ b/server/migrations/20250828000001-add-device-approval.js @@ -7,26 +7,33 @@ module.exports = { async up(queryInterface, Sequelize) { - // Add is_approved column to devices table - await queryInterface.addColumn('devices', 'is_approved', { - type: Sequelize.BOOLEAN, - defaultValue: false, - allowNull: false, - comment: 'Whether the device is approved to send data' - }); + // Check if is_approved column already exists + const tableDescription = await queryInterface.describeTable('devices'); + + if (!tableDescription.is_approved) { + // Add is_approved column to devices table + await queryInterface.addColumn('devices', 'is_approved', { + type: Sequelize.BOOLEAN, + defaultValue: false, + allowNull: false, + comment: 'Whether the device is approved to send data' + }); - // Add index for is_approved for better query performance - await queryInterface.addIndex('devices', ['is_approved'], { - name: 'devices_is_approved_idx' - }); + // Add index for is_approved for better query performance + await queryInterface.addIndex('devices', ['is_approved'], { + name: 'devices_is_approved_idx' + }); - // Approve all existing devices by default (backward compatibility) - await queryInterface.sequelize.query( - 'UPDATE devices SET is_approved = true WHERE created_at < NOW()' - ); + // Approve all existing devices by default (backward compatibility) + await queryInterface.sequelize.query( + 'UPDATE devices SET is_approved = true WHERE created_at < NOW()' + ); - console.log('✅ Added is_approved field to devices table'); - console.log('✅ Approved all existing devices for backward compatibility'); + console.log('✅ Added is_approved field to devices table'); + console.log('✅ Approved all existing devices for backward compatibility'); + } else { + console.log('⚠️ Column is_approved already exists, skipping...'); + } }, async down(queryInterface, Sequelize) { diff --git a/server/migrations/20250913-add-auth-session-config.js b/server/migrations/20250913-add-auth-session-config.js index a1391e4..bdde475 100644 --- a/server/migrations/20250913-add-auth-session-config.js +++ b/server/migrations/20250913-add-auth-session-config.js @@ -7,38 +7,77 @@ module.exports = { up: async (queryInterface, Sequelize) => { + // Check if the columns already exist + const tableDescription = await queryInterface.describeTable('tenants'); + // Add session configuration fields - await queryInterface.addColumn('tenants', 'session_timeout', { - type: Sequelize.INTEGER, - defaultValue: 480, // 8 hours in minutes - allowNull: false, - comment: 'Session timeout in minutes' - }); + if (!tableDescription.session_timeout) { + await queryInterface.addColumn('tenants', 'session_timeout', { + type: Sequelize.INTEGER, + defaultValue: 480, // 8 hours in minutes + allowNull: false, + comment: 'Session timeout in minutes' + }); + console.log('✅ Added session_timeout column to tenants table'); + } else { + console.log('⚠️ Column session_timeout already exists, skipping...'); + } - await queryInterface.addColumn('tenants', 'require_mfa', { - type: Sequelize.BOOLEAN, - defaultValue: false, - allowNull: false, - comment: 'Whether multi-factor authentication is required' - }); + if (!tableDescription.require_mfa) { + await queryInterface.addColumn('tenants', 'require_mfa', { + type: Sequelize.BOOLEAN, + defaultValue: false, + allowNull: false, + comment: 'Whether multi-factor authentication is required' + }); + console.log('✅ Added require_mfa column to tenants table'); + } else { + console.log('⚠️ Column require_mfa already exists, skipping...'); + } - await queryInterface.addColumn('tenants', 'allow_concurrent_sessions', { - type: Sequelize.BOOLEAN, - defaultValue: true, - allowNull: false, - comment: 'Whether users can have multiple concurrent sessions' - }); + if (!tableDescription.allow_concurrent_sessions) { + await queryInterface.addColumn('tenants', 'allow_concurrent_sessions', { + type: Sequelize.BOOLEAN, + defaultValue: true, + allowNull: false, + comment: 'Whether users can have multiple concurrent sessions' + }); + console.log('✅ Added allow_concurrent_sessions column to tenants table'); + } else { + console.log('⚠️ Column allow_concurrent_sessions already exists, skipping...'); + } - await queryInterface.addColumn('tenants', 'role_mappings', { - type: Sequelize.JSONB, - allowNull: true, - comment: 'Mapping of external groups/attributes to system roles' - }); + if (!tableDescription.role_mappings) { + await queryInterface.addColumn('tenants', 'role_mappings', { + type: Sequelize.JSONB, + allowNull: true, + comment: 'Mapping of external groups/attributes to system roles' + }); + console.log('✅ Added role_mappings column to tenants table'); + } else { + console.log('⚠️ Column role_mappings already exists, skipping...'); + } - // Update auth_provider enum to include 'ad' - await queryInterface.sequelize.query(` - ALTER TYPE "enum_tenants_auth_provider" ADD VALUE 'ad'; - `); + // Update auth_provider enum to include 'ad' - only if it doesn't exist + try { + await queryInterface.sequelize.query(` + DO $$ + BEGIN + IF NOT EXISTS ( + SELECT 1 FROM pg_enum + WHERE enumlabel = 'ad' + AND enumtypid = ( + SELECT oid FROM pg_type WHERE typname = 'enum_tenants_auth_provider' + ) + ) THEN + ALTER TYPE "enum_tenants_auth_provider" ADD VALUE 'ad'; + END IF; + END$$; + `); + console.log('✅ Added ad to auth_provider enum'); + } catch (error) { + console.log('⚠️ Auth provider enum already includes ad or error occurred:', error.message); + } }, down: async (queryInterface, Sequelize) => { diff --git a/server/migrations/20250913-add-ip-restrictions.js b/server/migrations/20250913-add-ip-restrictions.js index 4c803a9..bcd2de9 100644 --- a/server/migrations/20250913-add-ip-restrictions.js +++ b/server/migrations/20250913-add-ip-restrictions.js @@ -7,27 +7,44 @@ module.exports = { up: async (queryInterface, Sequelize) => { - // Add IP restriction fields - await queryInterface.addColumn('tenants', 'ip_whitelist', { - type: Sequelize.JSONB, - allowNull: true, - defaultValue: null, - comment: 'Array of allowed IP addresses/CIDR blocks for this tenant' - }); + // Check if the columns already exist + const tableDescription = await queryInterface.describeTable('tenants'); + + if (!tableDescription.ip_whitelist) { + await queryInterface.addColumn('tenants', 'ip_whitelist', { + type: Sequelize.JSONB, + allowNull: true, + defaultValue: null, + comment: 'Array of allowed IP addresses/CIDR blocks for this tenant' + }); + console.log('✅ Added ip_whitelist column to tenants table'); + } else { + console.log('⚠️ Column ip_whitelist already exists, skipping...'); + } - await queryInterface.addColumn('tenants', 'ip_restriction_enabled', { - type: Sequelize.BOOLEAN, - defaultValue: false, - allowNull: false, - comment: 'Whether IP restrictions are enabled for this tenant' - }); + if (!tableDescription.ip_restriction_enabled) { + await queryInterface.addColumn('tenants', 'ip_restriction_enabled', { + type: Sequelize.BOOLEAN, + defaultValue: false, + allowNull: false, + comment: 'Whether IP restrictions are enabled for this tenant' + }); + console.log('✅ Added ip_restriction_enabled column to tenants table'); + } else { + console.log('⚠️ Column ip_restriction_enabled already exists, skipping...'); + } - await queryInterface.addColumn('tenants', 'ip_restriction_message', { - type: Sequelize.TEXT, - allowNull: true, - defaultValue: 'Access denied. Your IP address is not authorized to access this tenant.', - comment: 'Custom message shown when IP access is denied' - }); + if (!tableDescription.ip_restriction_message) { + await queryInterface.addColumn('tenants', 'ip_restriction_message', { + type: Sequelize.TEXT, + allowNull: true, + defaultValue: 'Access denied. Your IP address is not authorized to access this tenant.', + comment: 'Custom message shown when IP access is denied' + }); + console.log('✅ Added ip_restriction_message column to tenants table'); + } else { + console.log('⚠️ Column ip_restriction_message already exists, skipping...'); + } }, down: async (queryInterface, Sequelize) => {