Release 2.8.0
This commit is contained in:
+109
-1
@@ -385,6 +385,113 @@ const VERSIONED_MIGRATIONS = [
|
||||
await pool.query('DROP TABLE i_schedule_groups');
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
version: '2.8.0',
|
||||
label: 'v2.8.0 combined application schema',
|
||||
run: async function (pool) {
|
||||
await pool.query(`
|
||||
CREATE TABLE IF NOT EXISTS o_app_settings (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
setting_key VARCHAR(191) NOT NULL UNIQUE,
|
||||
setting_value JSON NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
created_by INT NULL,
|
||||
modified_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
modified_by INT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
`);
|
||||
await pool.query(`
|
||||
CREATE TABLE IF NOT EXISTS o_app_state (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
state_key VARCHAR(191) NOT NULL UNIQUE,
|
||||
state_value MEDIUMTEXT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
modified_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
`);
|
||||
|
||||
const numericIdTables = [
|
||||
['d_announcement_screens', 'uq_announcement_screens_pair', '(announcement_id, screen_id)'],
|
||||
['d_onboarding_devices', 'uq_onboarding_devices_device_id', '(device_id)'],
|
||||
['a_role_permissions', 'uq_role_permissions_pair', '(role_id, permission_id)'],
|
||||
['a_user_roles', 'uq_user_roles_pair', '(user_id, role_id)'],
|
||||
['a_sessions', 'uq_sessions_hash', '(session_hash)'],
|
||||
['o_app_state', 'uq_app_state_key', '(state_key)']
|
||||
];
|
||||
|
||||
for (const [tableName, uniqueKeyName, uniqueColumns] of numericIdTables) {
|
||||
if (!(await tableExists(pool, tableName)) || await columnExists(pool, tableName, 'id')) {
|
||||
continue;
|
||||
}
|
||||
await pool.query('ALTER TABLE ' + tableName + ' DROP PRIMARY KEY, ADD COLUMN id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST, ADD UNIQUE KEY ' + uniqueKeyName + ' ' + uniqueColumns);
|
||||
}
|
||||
await ensureColumn(pool, 'a_users', 'must_change_password', 'TINYINT(1) NOT NULL DEFAULT 0', 'password_iterations');
|
||||
await pool.query(`
|
||||
CREATE TABLE IF NOT EXISTS a_login_attempts (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
rate_key VARCHAR(600) NOT NULL UNIQUE,
|
||||
failed_count INT NOT NULL DEFAULT 0,
|
||||
last_failed_at DATETIME NULL,
|
||||
locked_until DATETIME NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
modified_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
`);
|
||||
await ensureColumn(pool, 'a_users', 'account_locked', 'TINYINT(1) NOT NULL DEFAULT 0', 'must_change_password');
|
||||
await ensureColumn(pool, 'a_sessions', 'ip_address', 'VARCHAR(255) NULL', 'user_id');
|
||||
await ensureColumn(pool, 'a_sessions', 'user_agent', 'VARCHAR(512) NULL', 'ip_address');
|
||||
await pool.query(`
|
||||
CREATE TABLE IF NOT EXISTS o_audit_events (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
occurred_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
category VARCHAR(64) NOT NULL,
|
||||
event_type VARCHAR(128) NOT NULL,
|
||||
actor_user_id INT NULL,
|
||||
target_type VARCHAR(64) NULL,
|
||||
target_id VARCHAR(191) NULL,
|
||||
target_label VARCHAR(255) NULL,
|
||||
ip_address VARCHAR(255) NULL,
|
||||
user_agent VARCHAR(512) NULL,
|
||||
details_json JSON NULL,
|
||||
INDEX idx_audit_events_occurred_at (occurred_at),
|
||||
INDEX idx_audit_events_category_type (category, event_type),
|
||||
INDEX idx_audit_events_actor (actor_user_id),
|
||||
INDEX idx_audit_events_target (target_type, target_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
`);
|
||||
await pool.query(
|
||||
`INSERT INTO o_app_settings (setting_key, setting_value, created_by, modified_by)
|
||||
VALUES (?, ?, NULL, NULL) ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value)`,
|
||||
['audit.retention_days', JSON.stringify(30)]
|
||||
);
|
||||
const settings = [
|
||||
['audit.enabled', true],
|
||||
['audit.categories', ['authentication', 'security', 'sessions', 'users', 'roles', 'system-settings']],
|
||||
['audit.include_request_metadata', true]
|
||||
];
|
||||
for (const [key, value] of settings) {
|
||||
await pool.query(
|
||||
`INSERT INTO o_app_settings (setting_key, setting_value, created_by, modified_by)
|
||||
VALUES (?, ?, NULL, NULL) ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value)`,
|
||||
[key, JSON.stringify(value)]
|
||||
);
|
||||
}
|
||||
await pool.query(
|
||||
`INSERT IGNORE INTO a_permissions
|
||||
(permission_key, name, section_name, description, created_by, modified_by)
|
||||
VALUES (?, ?, ?, ?, NULL, NULL)`,
|
||||
['audit-log.allow', 'Audit log', 'Settings', 'Download filtered audit events.']
|
||||
);
|
||||
await pool.query(
|
||||
`INSERT IGNORE INTO a_role_permissions (role_id, permission_id, created_by, modified_by)
|
||||
SELECT roles.id, permissions.id, NULL, NULL
|
||||
FROM a_roles roles
|
||||
CROSS JOIN a_permissions permissions
|
||||
WHERE roles.role_key = 'administrators'
|
||||
AND permissions.permission_key = 'audit-log.allow'`
|
||||
);
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
@@ -441,7 +548,8 @@ async function detectSchemaVersion(pool) {
|
||||
async function recordSchemaVersion(pool, version) {
|
||||
await pool.query(
|
||||
`CREATE TABLE IF NOT EXISTS ${APP_STATE_TABLE} (
|
||||
state_key VARCHAR(191) NOT NULL PRIMARY KEY,
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
state_key VARCHAR(191) NOT NULL UNIQUE,
|
||||
state_value MEDIUMTEXT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
modified_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
|
||||
Reference in New Issue
Block a user