Fix schema migration audit columns
This commit is contained in:
@@ -32,6 +32,69 @@ async function addColumnIfMissing(pool, tableName, columnName, columnDefinition)
|
||||
await pool.query(`ALTER TABLE \`${tableName}\` ADD COLUMN \`${columnName}\` ${columnDefinition}`);
|
||||
}
|
||||
|
||||
async function dropColumnIfPresent(pool, tableName, columnName) {
|
||||
const [rows] = await pool.query(
|
||||
`SELECT COUNT(*) AS column_count
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = ?
|
||||
AND column_name = ?`,
|
||||
[tableName, columnName]
|
||||
);
|
||||
|
||||
if (!rows.length || Number(rows[0].column_count) === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
await pool.query(`ALTER TABLE \`${tableName}\` DROP COLUMN \`${columnName}\``);
|
||||
}
|
||||
|
||||
async function addForeignKeyIfMissing(pool, tableName, columnName, constraintName, referencedTable, referencedColumn, onDeleteAction) {
|
||||
const [rows] = await pool.query(
|
||||
`SELECT COUNT(*) AS constraint_count
|
||||
FROM information_schema.table_constraints
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = ?
|
||||
AND constraint_name = ?`,
|
||||
[tableName, constraintName]
|
||||
);
|
||||
|
||||
if (rows.length && Number(rows[0].constraint_count) > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
await pool.query(
|
||||
`ALTER TABLE \`${tableName}\`
|
||||
ADD CONSTRAINT \`${constraintName}\`
|
||||
FOREIGN KEY (\`${columnName}\`) REFERENCES \`${referencedTable}\`(\`${referencedColumn}\`)
|
||||
ON DELETE ${onDeleteAction}
|
||||
ON UPDATE CASCADE`
|
||||
);
|
||||
}
|
||||
|
||||
async function addUserAuditColumns(pool, tableName) {
|
||||
await addColumnIfMissing(pool, tableName, 'created_by', 'INT NULL');
|
||||
await addColumnIfMissing(pool, tableName, 'modified_by', 'INT NULL');
|
||||
|
||||
await pool.query(
|
||||
`UPDATE \`${tableName}\` t
|
||||
LEFT JOIN users created_user ON created_user.id = t.created_by
|
||||
SET t.created_by = NULL
|
||||
WHERE t.created_by IS NOT NULL
|
||||
AND created_user.id IS NULL`
|
||||
);
|
||||
await pool.query(
|
||||
`UPDATE \`${tableName}\` t
|
||||
LEFT JOIN users modified_user ON modified_user.id = t.modified_by
|
||||
SET t.modified_by = NULL
|
||||
WHERE t.modified_by IS NOT NULL
|
||||
AND modified_user.id IS NULL`
|
||||
);
|
||||
|
||||
await addForeignKeyIfMissing(pool, tableName, 'created_by', `fk_${tableName}_created_by`, 'users', 'id', 'SET NULL');
|
||||
await addForeignKeyIfMissing(pool, tableName, 'modified_by', `fk_${tableName}_modified_by`, 'users', 'id', 'SET NULL');
|
||||
}
|
||||
|
||||
async function pruneStaleOnboardingDevices(pool) {
|
||||
await pool.query(
|
||||
`DELETE FROM player_onboarding_devices
|
||||
@@ -54,7 +117,7 @@ async function getTableColumnNames(pool, tableName) {
|
||||
}
|
||||
|
||||
function getPermissionKey(row) {
|
||||
return String((row && (row.permission_key || row.perm_key)) || '').trim().toLowerCase();
|
||||
return String((row && row.permission_key) || '').trim().toLowerCase();
|
||||
}
|
||||
|
||||
function getLegacyPermissionTargets(permissionKey) {
|
||||
@@ -81,9 +144,6 @@ function buildPermissionSeedColumns(columnNames) {
|
||||
if (columnNames.has('permission_key')) {
|
||||
columns.push('permission_key');
|
||||
}
|
||||
if (columnNames.has('perm_key')) {
|
||||
columns.push('perm_key');
|
||||
}
|
||||
if (columnNames.has('name')) {
|
||||
columns.push('name');
|
||||
}
|
||||
@@ -104,12 +164,10 @@ function buildPermissionSeedColumns(columnNames) {
|
||||
|
||||
async function backfillLegacyRbacSchema(pool) {
|
||||
const permissionColumnNames = await getTableColumnNames(pool, 'permissions');
|
||||
const hasPermissionKeyColumn = permissionColumnNames.has('permission_key');
|
||||
const hasLegacyPermissionKeyColumn = permissionColumnNames.has('perm_key');
|
||||
|
||||
const [permissionRows] = await pool.query('SELECT id, permission_key, perm_key, name, section_name FROM permissions ORDER BY id ASC');
|
||||
const [permissionRows] = await pool.query('SELECT id, permission_key, name, section_name FROM permissions ORDER BY id ASC');
|
||||
const [rolePermissionRows] = await pool.query(
|
||||
`SELECT rp.role_id, p.permission_key, p.perm_key
|
||||
`SELECT rp.role_id, p.permission_key
|
||||
FROM role_permissions rp
|
||||
JOIN permissions p ON p.id = rp.permission_id`
|
||||
);
|
||||
@@ -176,9 +234,6 @@ async function backfillLegacyRbacSchema(pool) {
|
||||
if (permissionColumnNames.has('permission_key')) {
|
||||
seedValues.push(permission.key);
|
||||
}
|
||||
if (permissionColumnNames.has('perm_key')) {
|
||||
seedValues.push(permission.key);
|
||||
}
|
||||
if (permissionColumnNames.has('name')) {
|
||||
seedValues.push(permission.name);
|
||||
}
|
||||
@@ -205,9 +260,6 @@ async function backfillLegacyRbacSchema(pool) {
|
||||
if (permissionColumnNames.has('description')) {
|
||||
updateAssignments.push('description = VALUES(description)');
|
||||
}
|
||||
if (permissionColumnNames.has('perm_key')) {
|
||||
updateAssignments.push('perm_key = VALUES(perm_key)');
|
||||
}
|
||||
if (permissionColumnNames.has('permission_key')) {
|
||||
updateAssignments.push('permission_key = VALUES(permission_key)');
|
||||
}
|
||||
@@ -224,7 +276,7 @@ async function backfillLegacyRbacSchema(pool) {
|
||||
await pool.query('DELETE FROM permissions WHERE id IN (?)', [legacyPermissionRowIds]);
|
||||
}
|
||||
|
||||
const [currentPermissionRows] = await pool.query('SELECT id, permission_key, perm_key FROM permissions');
|
||||
const [currentPermissionRows] = await pool.query('SELECT id, permission_key FROM permissions');
|
||||
const permissionIdByKey = new Map();
|
||||
for (const row of currentPermissionRows || []) {
|
||||
const currentKey = getPermissionKey(row);
|
||||
@@ -275,8 +327,7 @@ async function ensureSchema(pool) {
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
`);
|
||||
await addColumnIfMissing(pool, 'canvas_sizes', 'modified_at', 'TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP');
|
||||
await addColumnIfMissing(pool, 'canvas_sizes', 'created_by', 'INT NULL');
|
||||
await addColumnIfMissing(pool, 'canvas_sizes', 'modified_by', 'INT NULL');
|
||||
await addUserAuditColumns(pool, 'canvas_sizes');
|
||||
|
||||
await pool.query(`
|
||||
CREATE TABLE IF NOT EXISTS playlists (
|
||||
@@ -289,8 +340,7 @@ async function ensureSchema(pool) {
|
||||
`);
|
||||
await addColumnIfMissing(pool, 'playlists', 'fade_between_slides', 'TINYINT(1) NOT NULL DEFAULT 0');
|
||||
await addColumnIfMissing(pool, 'playlists', 'modified_at', 'TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP');
|
||||
await addColumnIfMissing(pool, 'playlists', 'created_by', 'INT NULL');
|
||||
await addColumnIfMissing(pool, 'playlists', 'modified_by', 'INT NULL');
|
||||
await addUserAuditColumns(pool, 'playlists');
|
||||
|
||||
await pool.query(`
|
||||
CREATE TABLE IF NOT EXISTS slide_templates (
|
||||
@@ -308,8 +358,7 @@ async function ensureSchema(pool) {
|
||||
await addColumnIfMissing(pool, 'slide_templates', 'background_image_path', 'VARCHAR(512) NULL');
|
||||
await addColumnIfMissing(pool, 'slide_templates', 'background_color', 'VARCHAR(32) NULL');
|
||||
await addColumnIfMissing(pool, 'slide_templates', 'modified_at', 'TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP');
|
||||
await addColumnIfMissing(pool, 'slide_templates', 'created_by', 'INT NULL');
|
||||
await addColumnIfMissing(pool, 'slide_templates', 'modified_by', 'INT NULL');
|
||||
await addUserAuditColumns(pool, 'slide_templates');
|
||||
|
||||
await pool.query(`
|
||||
INSERT IGNORE INTO canvas_sizes (name, width, height) VALUES
|
||||
@@ -355,8 +404,7 @@ async function ensureSchema(pool) {
|
||||
|
||||
await addColumnIfMissing(pool, 'slide_template_regions', 'font_family', 'VARCHAR(100) NULL');
|
||||
await addColumnIfMissing(pool, 'slide_template_regions', 'modified_at', 'TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP');
|
||||
await addColumnIfMissing(pool, 'slide_template_regions', 'created_by', 'INT NULL');
|
||||
await addColumnIfMissing(pool, 'slide_template_regions', 'modified_by', 'INT NULL');
|
||||
await addUserAuditColumns(pool, 'slide_template_regions');
|
||||
|
||||
await pool.query(`
|
||||
CREATE TABLE IF NOT EXISTS slides (
|
||||
@@ -378,8 +426,7 @@ async function ensureSchema(pool) {
|
||||
await addColumnIfMissing(pool, 'slides', 'media_path', 'VARCHAR(512) NULL');
|
||||
await addColumnIfMissing(pool, 'slides', 'media_type', 'VARCHAR(100) NULL');
|
||||
await addColumnIfMissing(pool, 'slides', 'modified_at', 'TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP');
|
||||
await addColumnIfMissing(pool, 'slides', 'created_by', 'INT NULL');
|
||||
await addColumnIfMissing(pool, 'slides', 'modified_by', 'INT NULL');
|
||||
await addUserAuditColumns(pool, 'slides');
|
||||
|
||||
await pool.query(`
|
||||
CREATE TABLE IF NOT EXISTS playlist_slides (
|
||||
@@ -409,8 +456,7 @@ async function ensureSchema(pool) {
|
||||
await addColumnIfMissing(pool, 'playlist_slides', 'schedule_end_time', 'TIME NULL');
|
||||
await addColumnIfMissing(pool, 'playlist_slides', 'schedule_days_json', 'JSON NULL');
|
||||
await addColumnIfMissing(pool, 'playlist_slides', 'modified_at', 'TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP');
|
||||
await addColumnIfMissing(pool, 'playlist_slides', 'created_by', 'INT NULL');
|
||||
await addColumnIfMissing(pool, 'playlist_slides', 'modified_by', 'INT NULL');
|
||||
await addUserAuditColumns(pool, 'playlist_slides');
|
||||
|
||||
await pool.query(`
|
||||
CREATE TABLE IF NOT EXISTS screens (
|
||||
@@ -426,8 +472,7 @@ async function ensureSchema(pool) {
|
||||
|
||||
await addColumnIfMissing(pool, 'screens', 'playlist_id', 'INT NULL');
|
||||
await addColumnIfMissing(pool, 'screens', 'modified_at', 'TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP');
|
||||
await addColumnIfMissing(pool, 'screens', 'created_by', 'INT NULL');
|
||||
await addColumnIfMissing(pool, 'screens', 'modified_by', 'INT NULL');
|
||||
await addUserAuditColumns(pool, 'screens');
|
||||
|
||||
await pool.query(`
|
||||
CREATE TABLE IF NOT EXISTS player_onboarding_devices (
|
||||
@@ -442,6 +487,7 @@ async function ensureSchema(pool) {
|
||||
await addColumnIfMissing(pool, 'player_onboarding_devices', 'client_name', 'VARCHAR(255) NULL');
|
||||
await addColumnIfMissing(pool, 'player_onboarding_devices', 'screen_id', 'INT NULL');
|
||||
await addColumnIfMissing(pool, 'player_onboarding_devices', 'modified_at', 'TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP');
|
||||
await addUserAuditColumns(pool, 'player_onboarding_devices');
|
||||
|
||||
await pool.query(`
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
@@ -460,8 +506,7 @@ async function ensureSchema(pool) {
|
||||
await addColumnIfMissing(pool, 'users', 'password_salt', 'VARCHAR(64) NOT NULL');
|
||||
await addColumnIfMissing(pool, 'users', 'password_iterations', 'INT NOT NULL');
|
||||
await addColumnIfMissing(pool, 'users', 'modified_at', 'TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP');
|
||||
await addColumnIfMissing(pool, 'users', 'created_by', 'INT NULL');
|
||||
await addColumnIfMissing(pool, 'users', 'modified_by', 'INT NULL');
|
||||
await addUserAuditColumns(pool, 'users');
|
||||
|
||||
await pool.query(`
|
||||
CREATE TABLE IF NOT EXISTS roles (
|
||||
@@ -476,14 +521,12 @@ async function ensureSchema(pool) {
|
||||
await addColumnIfMissing(pool, 'roles', 'role_key', 'VARCHAR(100) NULL');
|
||||
await addColumnIfMissing(pool, 'roles', 'description', 'TEXT NULL');
|
||||
await addColumnIfMissing(pool, 'roles', 'modified_at', 'TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP');
|
||||
await addColumnIfMissing(pool, 'roles', 'created_by', 'INT NULL');
|
||||
await addColumnIfMissing(pool, 'roles', 'modified_by', 'INT NULL');
|
||||
await addUserAuditColumns(pool, 'roles');
|
||||
|
||||
await pool.query(`
|
||||
CREATE TABLE IF NOT EXISTS permissions (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
permission_key VARCHAR(100) NOT NULL UNIQUE,
|
||||
perm_key VARCHAR(100) NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
section_name VARCHAR(255) NOT NULL,
|
||||
description TEXT NULL,
|
||||
@@ -491,14 +534,13 @@ async function ensureSchema(pool) {
|
||||
modified_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
`);
|
||||
await addColumnIfMissing(pool, 'permissions', 'perm_key', 'VARCHAR(100) NULL');
|
||||
await addColumnIfMissing(pool, 'permissions', 'permission_key', 'VARCHAR(100) NULL');
|
||||
await addColumnIfMissing(pool, 'permissions', 'name', 'VARCHAR(255) NULL');
|
||||
await addColumnIfMissing(pool, 'permissions', 'section_name', 'VARCHAR(255) NULL');
|
||||
await addColumnIfMissing(pool, 'permissions', 'description', 'TEXT NULL');
|
||||
await addColumnIfMissing(pool, 'permissions', 'modified_at', 'TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP');
|
||||
await addColumnIfMissing(pool, 'permissions', 'created_by', 'INT NULL');
|
||||
await addColumnIfMissing(pool, 'permissions', 'modified_by', 'INT NULL');
|
||||
await addUserAuditColumns(pool, 'permissions');
|
||||
await dropColumnIfPresent(pool, 'permissions', 'perm_key');
|
||||
|
||||
await pool.query(`
|
||||
CREATE TABLE IF NOT EXISTS role_permissions (
|
||||
@@ -512,8 +554,7 @@ async function ensureSchema(pool) {
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
`);
|
||||
await addColumnIfMissing(pool, 'role_permissions', 'modified_at', 'TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP');
|
||||
await addColumnIfMissing(pool, 'role_permissions', 'created_by', 'INT NULL');
|
||||
await addColumnIfMissing(pool, 'role_permissions', 'modified_by', 'INT NULL');
|
||||
await addUserAuditColumns(pool, 'role_permissions');
|
||||
|
||||
await pool.query(`
|
||||
CREATE TABLE IF NOT EXISTS user_roles (
|
||||
@@ -527,8 +568,7 @@ async function ensureSchema(pool) {
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
`);
|
||||
await addColumnIfMissing(pool, 'user_roles', 'modified_at', 'TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP');
|
||||
await addColumnIfMissing(pool, 'user_roles', 'created_by', 'INT NULL');
|
||||
await addColumnIfMissing(pool, 'user_roles', 'modified_by', 'INT NULL');
|
||||
await addUserAuditColumns(pool, 'user_roles');
|
||||
|
||||
await pool.query(`
|
||||
CREATE TABLE IF NOT EXISTS auth_sessions (
|
||||
@@ -540,8 +580,7 @@ async function ensureSchema(pool) {
|
||||
CONSTRAINT fk_auth_sessions_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
`);
|
||||
await addColumnIfMissing(pool, 'auth_sessions', 'created_by', 'INT NULL');
|
||||
await addColumnIfMissing(pool, 'auth_sessions', 'modified_by', 'INT NULL');
|
||||
await addUserAuditColumns(pool, 'auth_sessions');
|
||||
|
||||
const [userCountRows] = await pool.query('SELECT COUNT(*) AS user_count FROM users');
|
||||
if (!userCountRows.length || Number(userCountRows[0].user_count) === 0) {
|
||||
|
||||
Reference in New Issue
Block a user