From 86e3f699896b1c3afe0135195d8ab8ab9b16dc98 Mon Sep 17 00:00:00 2001
From: Dawid Bepierszcz <41084667+daffyyyy@users.noreply.github.com>
Date: Fri, 17 Oct 2025 01:52:43 +0200
Subject: [PATCH] Fixed migrations
---
CS2-SimpleAdmin/Database/Migration.cs | 105 ++++++++++++++++++++++++++
1 file changed, 105 insertions(+)
create mode 100644 CS2-SimpleAdmin/Database/Migration.cs
diff --git a/CS2-SimpleAdmin/Database/Migration.cs b/CS2-SimpleAdmin/Database/Migration.cs
new file mode 100644
index 0000000..1f368be
--- /dev/null
+++ b/CS2-SimpleAdmin/Database/Migration.cs
@@ -0,0 +1,105 @@
+using System.Data.Common;
+using Microsoft.Extensions.Logging;
+
+namespace CS2_SimpleAdmin.Database;
+
+public class Migration(string migrationsPath)
+{
+ ///
+ /// Executes all migration scripts found in the configured migrations path that have not been applied yet.
+ /// Creates a migration tracking table if it does not exist.
+ /// Applies migration scripts in filename order and logs successes or failures.
+ ///
+ public async Task ExecuteMigrationsAsync()
+ {
+ if (CS2_SimpleAdmin.DatabaseProvider == null) return;
+ var files = Directory.GetFiles(migrationsPath, "*.sql").OrderBy(f => f).ToList();
+ if (files.Count == 0) return;
+
+ await using var connection = await CS2_SimpleAdmin.DatabaseProvider.CreateConnectionAsync();
+ await using (var cmd = connection.CreateCommand())
+ {
+ if (migrationsPath.Contains("sqlite", StringComparison.CurrentCultureIgnoreCase))
+ {
+ cmd.CommandText = """
+ CREATE TABLE IF NOT EXISTS sa_migrations (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ version TEXT NOT NULL
+ );
+
+ """;
+ }
+ else
+ {
+ cmd.CommandText = """
+ CREATE TABLE IF NOT EXISTS sa_migrations (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ version VARCHAR(128) NOT NULL
+ );
+ """;
+ }
+
+ await cmd.ExecuteNonQueryAsync();
+ }
+
+ var lastAppliedVersion = await GetLastAppliedVersionAsync(connection);
+
+ foreach (var file in files)
+ {
+ var version = Path.GetFileNameWithoutExtension(file);
+ if (string.Compare(version, lastAppliedVersion, StringComparison.OrdinalIgnoreCase) <= 0)
+ continue;
+
+ try
+ {
+ var sqlScript = await File.ReadAllTextAsync(file);
+
+ await using (var cmdMigration = connection.CreateCommand())
+ {
+ cmdMigration.CommandText = sqlScript;
+ await cmdMigration.ExecuteNonQueryAsync();
+ }
+
+ await UpdateLastAppliedVersionAsync(connection, version);
+
+ CS2_SimpleAdmin._logger?.LogInformation($"Migration \"{version}\" successfully applied.");
+ }
+ catch (Exception ex)
+ {
+ CS2_SimpleAdmin._logger?.LogError(ex, $"Error applying migration \"{version}\".");
+ break;
+ }
+ }
+ }
+
+ ///
+ /// Retrieves the version string of the last applied migration from the database.
+ ///
+ /// The open database connection.
+ /// The version string of the last applied migration, or empty string if none.
+ private static async Task GetLastAppliedVersionAsync(DbConnection connection)
+ {
+ await using var cmd = connection.CreateCommand();
+ cmd.CommandText = "SELECT version FROM sa_migrations ORDER BY id DESC LIMIT 1;";
+ var result = await cmd.ExecuteScalarAsync();
+ return result?.ToString() ?? string.Empty;
+ }
+
+ ///
+ /// Inserts a record tracking the successful application of a migration version.
+ ///
+ /// The open database connection.
+ /// The version string of the migration applied.
+ private static async Task UpdateLastAppliedVersionAsync(DbConnection connection, string version)
+ {
+ await using var cmd = connection.CreateCommand();
+ cmd.CommandText = "INSERT INTO sa_migrations (version) VALUES (@Version);";
+
+ var param = cmd.CreateParameter();
+ param.ParameterName = "@Version";
+ param.Value = version;
+ cmd.Parameters.Add(param);
+
+ await cmd.ExecuteNonQueryAsync();
+ }
+}