From e028441b56ea6aaefbb183b16724c18dad2d8a75 Mon Sep 17 00:00:00 2001 From: Ravid-A Date: Sun, 31 May 2026 17:34:53 +0300 Subject: [PATCH 1/2] Add multi-server-per-admin support via sa_admins_servers table --- CS2-SimpleAdmin/CS2-SimpleAdmin.csproj | 6 ++++++ CS2-SimpleAdmin/Database/IDatabaseProvider.cs | 1 + .../Mysql/017_CreateAdminsServersTable.sql | 16 ++++++++++++++++ .../Sqlite/017_CreateAdminsServersTable.sql | 15 +++++++++++++++ .../Database/MysqlDatabaseProvider.cs | 16 +++++++++++----- .../Database/SqliteDatabaseProvider.cs | 14 ++++++++++---- CS2-SimpleAdmin/Managers/PermissionManager.cs | 14 +++++++++++++- 7 files changed, 72 insertions(+), 10 deletions(-) create mode 100644 CS2-SimpleAdmin/Database/Migrations/Mysql/017_CreateAdminsServersTable.sql create mode 100644 CS2-SimpleAdmin/Database/Migrations/Sqlite/017_CreateAdminsServersTable.sql diff --git a/CS2-SimpleAdmin/CS2-SimpleAdmin.csproj b/CS2-SimpleAdmin/CS2-SimpleAdmin.csproj index fe752efe..2af4159d 100644 --- a/CS2-SimpleAdmin/CS2-SimpleAdmin.csproj +++ b/CS2-SimpleAdmin/CS2-SimpleAdmin.csproj @@ -84,6 +84,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -132,6 +135,9 @@ PreserveNewest + + PreserveNewest + diff --git a/CS2-SimpleAdmin/Database/IDatabaseProvider.cs b/CS2-SimpleAdmin/Database/IDatabaseProvider.cs index e2a4ae45..2517ccc2 100644 --- a/CS2-SimpleAdmin/Database/IDatabaseProvider.cs +++ b/CS2-SimpleAdmin/Database/IDatabaseProvider.cs @@ -21,6 +21,7 @@ public interface IDatabaseProvider string GetDeleteAdminQuery(bool globalDelete); string GetAddAdminQuery(); string GetAddAdminFlagsQuery(); + string GetAddAdminServerQuery(); string GetUpdateAdminGroupQuery(); string GetGroupsQuery(); string GetGroupIdByNameQuery(); diff --git a/CS2-SimpleAdmin/Database/Migrations/Mysql/017_CreateAdminsServersTable.sql b/CS2-SimpleAdmin/Database/Migrations/Mysql/017_CreateAdminsServersTable.sql new file mode 100644 index 00000000..c2434155 --- /dev/null +++ b/CS2-SimpleAdmin/Database/Migrations/Mysql/017_CreateAdminsServersTable.sql @@ -0,0 +1,16 @@ +CREATE TABLE IF NOT EXISTS `sa_admins_servers` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `admin_id` int(11) NOT NULL, + `server_id` int(11) NOT NULL, + PRIMARY KEY (`id`), + FOREIGN KEY (`admin_id`) REFERENCES `sa_admins` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +ALTER TABLE `sa_admins` ADD `global` TINYINT(1) NOT NULL DEFAULT 0; + +INSERT INTO `sa_admins_servers` (`admin_id`, `server_id`) +SELECT `id`, `server_id` FROM `sa_admins` WHERE `server_id` IS NOT NULL; + +UPDATE `sa_admins` SET `global` = 1 WHERE `server_id` IS NULL; + +ALTER TABLE `sa_admins` DROP COLUMN `server_id`; diff --git a/CS2-SimpleAdmin/Database/Migrations/Sqlite/017_CreateAdminsServersTable.sql b/CS2-SimpleAdmin/Database/Migrations/Sqlite/017_CreateAdminsServersTable.sql new file mode 100644 index 00000000..0c0155f4 --- /dev/null +++ b/CS2-SimpleAdmin/Database/Migrations/Sqlite/017_CreateAdminsServersTable.sql @@ -0,0 +1,15 @@ +CREATE TABLE IF NOT EXISTS `sa_admins_servers` ( + `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + `admin_id` INTEGER NOT NULL, + `server_id` INTEGER NOT NULL, + FOREIGN KEY (`admin_id`) REFERENCES `sa_admins` (`id`) ON DELETE CASCADE +); + +ALTER TABLE `sa_admins` ADD `global` INTEGER NOT NULL DEFAULT 0; + +INSERT INTO `sa_admins_servers` (`admin_id`, `server_id`) +SELECT `id`, `server_id` FROM `sa_admins` WHERE `server_id` IS NOT NULL; + +UPDATE `sa_admins` SET `global` = 1 WHERE `server_id` IS NULL; + +ALTER TABLE `sa_admins` DROP COLUMN `server_id`; diff --git a/CS2-SimpleAdmin/Database/MysqlDatabaseProvider.cs b/CS2-SimpleAdmin/Database/MysqlDatabaseProvider.cs index fa9a27e4..de0bdeef 100644 --- a/CS2-SimpleAdmin/Database/MysqlDatabaseProvider.cs +++ b/CS2-SimpleAdmin/Database/MysqlDatabaseProvider.cs @@ -127,7 +127,10 @@ public string GetAdminsQuery() FROM sa_admins_flags JOIN sa_admins ON sa_admins_flags.admin_id = sa_admins.id WHERE (sa_admins.ends IS NULL OR sa_admins.ends > @CurrentTime) - AND (sa_admins.server_id IS NULL OR sa_admins.server_id = @serverid) + AND (sa_admins.`global` = 1 + OR EXISTS (SELECT 1 FROM sa_admins_servers + WHERE sa_admins_servers.admin_id = sa_admins.id + AND sa_admins_servers.server_id = @serverid)) ORDER BY sa_admins.player_steamid """; } @@ -135,11 +138,14 @@ ORDER BY sa_admins.player_steamid public string GetDeleteAdminQuery(bool globalDelete) => globalDelete ? "DELETE FROM sa_admins WHERE player_steamid = @PlayerSteamID" - : "DELETE FROM sa_admins WHERE player_steamid = @PlayerSteamID AND server_id = @ServerId"; - + : "DELETE FROM sa_admins_servers WHERE server_id = @ServerId AND admin_id IN (SELECT id FROM sa_admins WHERE player_steamid = @PlayerSteamID)"; + public string GetAddAdminQuery() => - "INSERT INTO sa_admins (player_steamid, player_name, immunity, ends, created, server_id) " + - "VALUES (@playerSteamId, @playerName, @immunity, @ends, @created, @serverid); SELECT LAST_INSERT_ID();"; + "INSERT INTO sa_admins (player_steamid, player_name, immunity, ends, created, `global`) " + + "VALUES (@playerSteamId, @playerName, @immunity, @ends, @created, @isGlobal); SELECT LAST_INSERT_ID();"; + + public string GetAddAdminServerQuery() => + "INSERT INTO sa_admins_servers (admin_id, server_id) VALUES (@adminId, @server_id);"; public string GetGroupsQuery() { diff --git a/CS2-SimpleAdmin/Database/SqliteDatabaseProvider.cs b/CS2-SimpleAdmin/Database/SqliteDatabaseProvider.cs index d6186fb2..fd0bd47f 100644 --- a/CS2-SimpleAdmin/Database/SqliteDatabaseProvider.cs +++ b/CS2-SimpleAdmin/Database/SqliteDatabaseProvider.cs @@ -176,22 +176,28 @@ public string GetAdminsQuery() => FROM sa_admins_flags JOIN sa_admins ON sa_admins_flags.admin_id = sa_admins.id WHERE (sa_admins.ends IS NULL OR sa_admins.ends > @CurrentTime) - AND (sa_admins.server_id IS NULL OR sa_admins.server_id = @serverid) + AND (sa_admins.`global` = 1 + OR EXISTS (SELECT 1 FROM sa_admins_servers + WHERE sa_admins_servers.admin_id = sa_admins.id + AND sa_admins_servers.server_id = @serverid)) ORDER BY sa_admins.player_steamid """; public string GetDeleteAdminQuery(bool globalDelete) => globalDelete ? "DELETE FROM sa_admins WHERE player_steamid = @PlayerSteamID" - : "DELETE FROM sa_admins WHERE player_steamid = @PlayerSteamID AND server_id = @ServerId"; + : "DELETE FROM sa_admins_servers WHERE server_id = @ServerId AND admin_id IN (SELECT id FROM sa_admins WHERE player_steamid = @PlayerSteamID)"; public string GetAddAdminQuery() => """ - INSERT INTO sa_admins (player_steamid, player_name, immunity, ends, created, server_id) - VALUES (@playerSteamId, @playerName, @immunity, @ends, @created, @serverid); + INSERT INTO sa_admins (player_steamid, player_name, immunity, ends, created, `global`) + VALUES (@playerSteamId, @playerName, @immunity, @ends, @created, @isGlobal); SELECT last_insert_rowid(); """; + public string GetAddAdminServerQuery() => + "INSERT INTO sa_admins_servers (admin_id, server_id) VALUES (@adminId, @server_id);"; + public string GetGroupsQuery() => """ SELECT g.group_id, sg.name AS group_name, sg.immunity, f.flag diff --git a/CS2-SimpleAdmin/Managers/PermissionManager.cs b/CS2-SimpleAdmin/Managers/PermissionManager.cs index c9f2b417..ce8c3909 100644 --- a/CS2-SimpleAdmin/Managers/PermissionManager.cs +++ b/CS2-SimpleAdmin/Managers/PermissionManager.cs @@ -530,7 +530,7 @@ public async Task AddAdminBySteamId(string playerSteamId, string playerName, Lis immunity, ends = futureTime, created = now, - serverid = globalAdmin ? null : CS2_SimpleAdmin.ServerId + isGlobal = globalAdmin ? 1 : 0 }); // Insert flags into sa_admins_flags table @@ -563,6 +563,18 @@ public async Task AddAdminBySteamId(string playerSteamId, string playerName, Lis }); } + // Global admins apply to every server and need no per-server link row. + // Server-specific admins are linked to the current server. + if (!globalAdmin) + { + var insertAdminServerSql = databaseProvider.GetAddAdminServerQuery(); + await connection.ExecuteAsync(insertAdminServerSql, new + { + adminId, + server_id = CS2_SimpleAdmin.ServerId + }); + } + await Server.NextWorldUpdateAsync(() => { CS2_SimpleAdmin.Instance.ReloadAdmins(null); From fd94ac8d2b1ede2021607ecb4f9c1195d006f782 Mon Sep 17 00:00:00 2001 From: Ravid-A Date: Sun, 31 May 2026 17:47:13 +0300 Subject: [PATCH 2/2] Clean up orphaned non-global admins on the expire timer --- CS2-SimpleAdmin/Database/IDatabaseProvider.cs | 1 + .../Database/MysqlDatabaseProvider.cs | 3 +++ .../Database/SqliteDatabaseProvider.cs | 3 +++ CS2-SimpleAdmin/Managers/PermissionManager.cs | 20 +++++++++++++++++++ CS2-SimpleAdmin/Managers/PlayerManager.cs | 3 ++- 5 files changed, 29 insertions(+), 1 deletion(-) diff --git a/CS2-SimpleAdmin/Database/IDatabaseProvider.cs b/CS2-SimpleAdmin/Database/IDatabaseProvider.cs index 2517ccc2..c8472454 100644 --- a/CS2-SimpleAdmin/Database/IDatabaseProvider.cs +++ b/CS2-SimpleAdmin/Database/IDatabaseProvider.cs @@ -30,6 +30,7 @@ public interface IDatabaseProvider string GetAddGroupServerQuery(); string GetDeleteGroupQuery(); string GetDeleteOldAdminsQuery(); + string GetDeleteOrphanedAdminsQuery(); // BanManager string GetAddBanQuery(); diff --git a/CS2-SimpleAdmin/Database/MysqlDatabaseProvider.cs b/CS2-SimpleAdmin/Database/MysqlDatabaseProvider.cs index de0bdeef..6edaa368 100644 --- a/CS2-SimpleAdmin/Database/MysqlDatabaseProvider.cs +++ b/CS2-SimpleAdmin/Database/MysqlDatabaseProvider.cs @@ -187,6 +187,9 @@ public string GetDeleteGroupQuery() => public string GetDeleteOldAdminsQuery() => "DELETE FROM sa_admins WHERE ends IS NOT NULL AND ends <= @CurrentTime;"; + + public string GetDeleteOrphanedAdminsQuery() => + "DELETE FROM sa_admins WHERE `global` = 0 AND id NOT IN (SELECT admin_id FROM sa_admins_servers);"; public string GetAddBanQuery() { diff --git a/CS2-SimpleAdmin/Database/SqliteDatabaseProvider.cs b/CS2-SimpleAdmin/Database/SqliteDatabaseProvider.cs index fd0bd47f..b2264518 100644 --- a/CS2-SimpleAdmin/Database/SqliteDatabaseProvider.cs +++ b/CS2-SimpleAdmin/Database/SqliteDatabaseProvider.cs @@ -240,6 +240,9 @@ public string GetDeleteGroupQuery() => public string GetDeleteOldAdminsQuery() => "DELETE FROM sa_admins WHERE ends IS NOT NULL AND ends <= @CurrentTime;"; + + public string GetDeleteOrphanedAdminsQuery() => + "DELETE FROM sa_admins WHERE `global` = 0 AND id NOT IN (SELECT admin_id FROM sa_admins_servers);"; public string GetAddMuteQuery(bool includePlayerName) => includePlayerName diff --git a/CS2-SimpleAdmin/Managers/PermissionManager.cs b/CS2-SimpleAdmin/Managers/PermissionManager.cs index ce8c3909..34a26ba3 100644 --- a/CS2-SimpleAdmin/Managers/PermissionManager.cs +++ b/CS2-SimpleAdmin/Managers/PermissionManager.cs @@ -677,4 +677,24 @@ public async Task DeleteOldAdmins() CS2_SimpleAdmin._logger?.LogCritical("Unable to remove expired admins"); } } + + /// + /// Deletes orphaned admins that are not global and have no server assignments left. + /// + public async Task DeleteOrphanedAdmins() + { + if (databaseProvider == null) return; + + try + { + await using var connection = await databaseProvider.CreateConnectionAsync(); + + var sql = databaseProvider.GetDeleteOrphanedAdminsQuery(); + await connection.ExecuteAsync(sql); + } + catch (Exception) + { + CS2_SimpleAdmin._logger?.LogCritical("Unable to remove orphaned admins"); + } + } } \ No newline at end of file diff --git a/CS2-SimpleAdmin/Managers/PlayerManager.cs b/CS2-SimpleAdmin/Managers/PlayerManager.cs index 2f08393e..c3132b1d 100644 --- a/CS2-SimpleAdmin/Managers/PlayerManager.cs +++ b/CS2-SimpleAdmin/Managers/PlayerManager.cs @@ -306,7 +306,8 @@ public void CheckPlayersTimer() pluginInstance.MuteManager.ExpireOldMutes(), pluginInstance.WarnManager.ExpireOldWarns(), pluginInstance.CacheManager?.RefreshCacheAsync() ?? Task.CompletedTask, - pluginInstance.PermissionManager.DeleteOldAdmins() + pluginInstance.PermissionManager.DeleteOldAdmins(), + pluginInstance.PermissionManager.DeleteOrphanedAdmins() }; await Task.WhenAll(expireTasks);