using System.Data; using System.Text.RegularExpressions; using Insight.Database; namespace Database.Tenants { public class Setup { private readonly IDbConnection _db; public Setup(IDbConnection db) { _db = db ?? throw new ArgumentNullException(nameof(db)); } /// /// Creates the system tables in the specified schema within a transaction. /// /// The schema name where the tables will be created. public async Task CreateSystem(string schema) { if (!IsValidSchemaName(schema)) throw new ArgumentException("Invalid schema name", nameof(schema)); using (var transaction = _db.BeginTransaction()) { try { await CreateRolesTable(schema, transaction).ConfigureAwait(false); await CreatePermissionsTable(schema, transaction).ConfigureAwait(false); await CreatePermissionTypesTable(schema, transaction).ConfigureAwait(false); await CreateRolePermissionsTable(schema, transaction).ConfigureAwait(false); transaction.Commit(); } catch (Exception ex) { transaction.Rollback(); throw new InvalidOperationException("Failed to create system tables.", ex); } } } private bool IsValidSchemaName(string schema) { return !string.IsNullOrEmpty(schema) && Regex.IsMatch(schema, "^[a-zA-Z0-9_]+$"); } private async Task ExecuteSqlAsync(string sql, IDbTransaction transaction) { if (string.IsNullOrEmpty(sql)) throw new ArgumentNullException(nameof(sql)); await _db.ExecuteAsync(sql, transaction: transaction).ConfigureAwait(false); } private async Task CreatePermissionTypesTable(string schema, IDbTransaction transaction) { var sql = $@" CREATE TABLE IF NOT EXISTS {schema}.permission_types ( id SERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL UNIQUE )"; await ExecuteSqlAsync(sql, transaction).ConfigureAwait(false); } private async Task CreatePermissionsTable(string schema, IDbTransaction transaction) { var sql = $@" CREATE TABLE IF NOT EXISTS {schema}.permissions ( id SERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL UNIQUE, type_id INTEGER NOT NULL, FOREIGN KEY (type_id) REFERENCES {schema}.permission_types(id) )"; await ExecuteSqlAsync(sql, transaction).ConfigureAwait(false); } private async Task CreateRolesTable(string schema, IDbTransaction transaction) { var sql = $@" CREATE TABLE IF NOT EXISTS {schema}.roles ( id SERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL UNIQUE )"; await ExecuteSqlAsync(sql, transaction).ConfigureAwait(false); } private async Task CreateRolePermissionsTable(string schema, IDbTransaction transaction) { var sql = $@" CREATE TABLE IF NOT EXISTS {schema}.role_permissions ( role_id INTEGER NOT NULL, permission_id INTEGER NOT NULL, PRIMARY KEY (role_id, permission_id), FOREIGN KEY (role_id) REFERENCES {schema}.roles(id), FOREIGN KEY (permission_id) REFERENCES {schema}.permissions(id) )"; await ExecuteSqlAsync(sql, transaction).ConfigureAwait(false); } } }