151 lines
6.3 KiB
JavaScript
151 lines
6.3 KiB
JavaScript
'use strict';
|
|
|
|
/**
|
|
* Migration to add foreign key constraints to all model associations.
|
|
* This enforces referential integrity at the database level.
|
|
*/
|
|
|
|
module.exports = {
|
|
async up(queryInterface) {
|
|
const transaction = await queryInterface.sequelize.transaction();
|
|
|
|
try {
|
|
// Helper to add FK constraint safely (checks if exists first)
|
|
const addForeignKey = async (tableName, columnName, references, onDelete = 'CASCADE', onUpdate = 'CASCADE') => {
|
|
const constraintName = `${tableName}_${columnName}_fkey`;
|
|
|
|
// Check if constraint already exists
|
|
const [results] = await queryInterface.sequelize.query(
|
|
`SELECT constraint_name FROM information_schema.table_constraints
|
|
WHERE table_name = '${tableName}' AND constraint_name = '${constraintName}'`,
|
|
{ transaction }
|
|
);
|
|
|
|
if (results.length === 0) {
|
|
await queryInterface.addConstraint(tableName, {
|
|
fields: [columnName],
|
|
type: 'foreign key',
|
|
name: constraintName,
|
|
references: {
|
|
table: references.table,
|
|
field: references.field,
|
|
},
|
|
onDelete,
|
|
onUpdate,
|
|
transaction,
|
|
});
|
|
console.log(`Added FK constraint: ${constraintName}`);
|
|
} else {
|
|
console.log(`FK constraint already exists: ${constraintName}`);
|
|
}
|
|
};
|
|
|
|
// asset_variants -> assets
|
|
await addForeignKey('asset_variants', 'assetId', { table: 'assets', field: 'id' }, 'CASCADE', 'CASCADE');
|
|
|
|
// page_elements -> tour_pages
|
|
await addForeignKey('page_elements', 'pageId', { table: 'tour_pages', field: 'id' }, 'CASCADE', 'CASCADE');
|
|
|
|
// page_links -> tour_pages (from_page)
|
|
await addForeignKey('page_links', 'from_pageId', { table: 'tour_pages', field: 'id' }, 'CASCADE', 'CASCADE');
|
|
|
|
// page_links -> tour_pages (to_page)
|
|
await addForeignKey('page_links', 'to_pageId', { table: 'tour_pages', field: 'id' }, 'SET NULL', 'CASCADE');
|
|
|
|
// page_links -> transitions
|
|
await addForeignKey('page_links', 'transitionId', { table: 'transitions', field: 'id' }, 'SET NULL', 'CASCADE');
|
|
|
|
// assets -> projects
|
|
await addForeignKey('assets', 'projectId', { table: 'projects', field: 'id' }, 'CASCADE', 'CASCADE');
|
|
|
|
// tour_pages -> projects
|
|
await addForeignKey('tour_pages', 'projectId', { table: 'projects', field: 'id' }, 'CASCADE', 'CASCADE');
|
|
|
|
// transitions -> projects
|
|
await addForeignKey('transitions', 'projectId', { table: 'projects', field: 'id' }, 'CASCADE', 'CASCADE');
|
|
|
|
// project_memberships -> projects
|
|
await addForeignKey('project_memberships', 'projectId', { table: 'projects', field: 'id' }, 'CASCADE', 'CASCADE');
|
|
|
|
// project_memberships -> users
|
|
await addForeignKey('project_memberships', 'userId', { table: 'users', field: 'id' }, 'CASCADE', 'CASCADE');
|
|
|
|
// presigned_url_requests -> projects
|
|
await addForeignKey('presigned_url_requests', 'projectId', { table: 'projects', field: 'id' }, 'CASCADE', 'CASCADE');
|
|
|
|
// presigned_url_requests -> users
|
|
await addForeignKey('presigned_url_requests', 'userId', { table: 'users', field: 'id' }, 'CASCADE', 'CASCADE');
|
|
|
|
// project_audio_tracks -> projects
|
|
await addForeignKey('project_audio_tracks', 'projectId', { table: 'projects', field: 'id' }, 'CASCADE', 'CASCADE');
|
|
|
|
// publish_events -> projects
|
|
await addForeignKey('publish_events', 'projectId', { table: 'projects', field: 'id' }, 'CASCADE', 'CASCADE');
|
|
|
|
// publish_events -> users (SET NULL to preserve audit trail)
|
|
await addForeignKey('publish_events', 'userId', { table: 'users', field: 'id' }, 'SET NULL', 'CASCADE');
|
|
|
|
// pwa_caches -> projects
|
|
await addForeignKey('pwa_caches', 'projectId', { table: 'projects', field: 'id' }, 'CASCADE', 'CASCADE');
|
|
|
|
// access_logs -> projects
|
|
await addForeignKey('access_logs', 'projectId', { table: 'projects', field: 'id' }, 'CASCADE', 'CASCADE');
|
|
|
|
// access_logs -> users (SET NULL to preserve audit trail)
|
|
await addForeignKey('access_logs', 'userId', { table: 'users', field: 'id' }, 'SET NULL', 'CASCADE');
|
|
|
|
// users -> roles (SET NULL so deleting role doesn't delete users)
|
|
await addForeignKey('users', 'app_roleId', { table: 'roles', field: 'id' }, 'SET NULL', 'CASCADE');
|
|
|
|
await transaction.commit();
|
|
console.log('All FK constraints added successfully');
|
|
} catch (error) {
|
|
await transaction.rollback();
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
async down(queryInterface) {
|
|
const transaction = await queryInterface.sequelize.transaction();
|
|
|
|
try {
|
|
const dropForeignKey = async (tableName, columnName) => {
|
|
const constraintName = `${tableName}_${columnName}_fkey`;
|
|
try {
|
|
await queryInterface.removeConstraint(tableName, constraintName, { transaction });
|
|
console.log(`Removed FK constraint: ${constraintName}`);
|
|
} catch (error) {
|
|
console.log(`FK constraint not found (may not exist): ${constraintName}`);
|
|
}
|
|
};
|
|
|
|
// Remove all FK constraints in reverse order
|
|
await dropForeignKey('users', 'app_roleId');
|
|
await dropForeignKey('access_logs', 'userId');
|
|
await dropForeignKey('access_logs', 'projectId');
|
|
await dropForeignKey('pwa_caches', 'projectId');
|
|
await dropForeignKey('publish_events', 'userId');
|
|
await dropForeignKey('publish_events', 'projectId');
|
|
await dropForeignKey('project_audio_tracks', 'projectId');
|
|
await dropForeignKey('presigned_url_requests', 'userId');
|
|
await dropForeignKey('presigned_url_requests', 'projectId');
|
|
await dropForeignKey('project_memberships', 'userId');
|
|
await dropForeignKey('project_memberships', 'projectId');
|
|
await dropForeignKey('transitions', 'projectId');
|
|
await dropForeignKey('tour_pages', 'projectId');
|
|
await dropForeignKey('assets', 'projectId');
|
|
await dropForeignKey('page_links', 'transitionId');
|
|
await dropForeignKey('page_links', 'to_pageId');
|
|
await dropForeignKey('page_links', 'from_pageId');
|
|
await dropForeignKey('page_elements', 'pageId');
|
|
await dropForeignKey('asset_variants', 'assetId');
|
|
|
|
await transaction.commit();
|
|
console.log('All FK constraints removed successfully');
|
|
} catch (error) {
|
|
await transaction.rollback();
|
|
throw error;
|
|
}
|
|
}
|
|
};
|