From 1b7fdc4a5337a69819fc6bff86ac9c406a1e0c1a Mon Sep 17 00:00:00 2001 From: Michal Buzik Date: Thu, 17 Oct 2024 21:43:36 +0200 Subject: [PATCH] init commit --- .gitignore | 5 + tbfDBBackup.sln | 16 + tbfDBBackup/BackupCombination.cs | 15 + tbfDBBackup/Configuration.cs | 117 ++++++++ tbfDBBackup/Program.cs | 276 ++++++++++++++++++ tbfDBBackup/config/configuration.json | 55 ++++ tbfDBBackup/config/configuration_backup.json | 55 ++++ tbfDBBackup/configuration/Backup.cs | 15 + tbfDBBackup/configuration/DbConnection.cs | 40 +++ .../configuration/IStandartBehaviour.cs | 7 + tbfDBBackup/configuration/PathWrapper.cs | 13 + .../configuration/StandardBehaviour.cs | 7 + tbfDBBackup/configuration/Update.cs | 16 + tbfDBBackup/tbfDBBackup.csproj | 14 + 14 files changed, 651 insertions(+) create mode 100644 .gitignore create mode 100644 tbfDBBackup.sln create mode 100644 tbfDBBackup/BackupCombination.cs create mode 100644 tbfDBBackup/Configuration.cs create mode 100644 tbfDBBackup/Program.cs create mode 100644 tbfDBBackup/config/configuration.json create mode 100644 tbfDBBackup/config/configuration_backup.json create mode 100644 tbfDBBackup/configuration/Backup.cs create mode 100644 tbfDBBackup/configuration/DbConnection.cs create mode 100644 tbfDBBackup/configuration/IStandartBehaviour.cs create mode 100644 tbfDBBackup/configuration/PathWrapper.cs create mode 100644 tbfDBBackup/configuration/StandardBehaviour.cs create mode 100644 tbfDBBackup/configuration/Update.cs create mode 100644 tbfDBBackup/tbfDBBackup.csproj diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..add57be --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +bin/ +obj/ +/packages/ +riderModule.iml +/_ReSharper.Caches/ \ No newline at end of file diff --git a/tbfDBBackup.sln b/tbfDBBackup.sln new file mode 100644 index 0000000..ec928bf --- /dev/null +++ b/tbfDBBackup.sln @@ -0,0 +1,16 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "tbfDBBackup", "tbfDBBackup\tbfDBBackup.csproj", "{9F4F004F-8E31-42B3-BE6D-EA9453EADC7F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9F4F004F-8E31-42B3-BE6D-EA9453EADC7F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9F4F004F-8E31-42B3-BE6D-EA9453EADC7F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9F4F004F-8E31-42B3-BE6D-EA9453EADC7F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9F4F004F-8E31-42B3-BE6D-EA9453EADC7F}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/tbfDBBackup/BackupCombination.cs b/tbfDBBackup/BackupCombination.cs new file mode 100644 index 0000000..ce0b417 --- /dev/null +++ b/tbfDBBackup/BackupCombination.cs @@ -0,0 +1,15 @@ +using tbfDBBackup.configuration; + +namespace tbfDBBackup; + +public struct BackupCombination +{ + public PathWrapper Path { get; init; } + public string DatabaseName { get; set; } + public string BackupFilePath { get; set; } + public string UserName { get; set; } + public string Password { get; set; } + public string CommandPath { get; set; } + + public string ServerName { get; set; } +} \ No newline at end of file diff --git a/tbfDBBackup/Configuration.cs b/tbfDBBackup/Configuration.cs new file mode 100644 index 0000000..4347c71 --- /dev/null +++ b/tbfDBBackup/Configuration.cs @@ -0,0 +1,117 @@ +using System.Text.Json; +using tbfDBBackup.configuration; + +namespace tbfDBBackup; + +public class Configuration +{ + public const string ConfigDir = "config"; + public const string FileName = "configuration.json"; + + public string? PathToDumpMysql { get; set; } + public Boolean? WaitOnEndCmdOpen { get; set; } + public Boolean? PrintConfigurationEnabled { get; set; } + + public List? DbConnectionStrings { get; set; } + public List? UpdateString { get; set; } + public List? BackupDB { get; set; } + + + public static Configuration DeserializeFromJson(string jsonString) + { + return JsonSerializer.Deserialize(jsonString); + } + + // Serialize method for saving + public string SerializeToJson() + { + return JsonSerializer.Serialize(this, new JsonSerializerOptions { WriteIndented = true }); + } + + public void PrintConfiguration() + { + if (DbConnectionStrings != null) + { + Console.WriteLine("-- DbConnectionStrings:"); + foreach (var dbConnection in DbConnectionStrings) + { + Console.WriteLine(dbConnection.ToString()); + } + } + + if (UpdateString != null) + { + Console.WriteLine("-- UpdateString:"); + foreach (var update in UpdateString) + { + Console.WriteLine(update.ToString()); + } + } + + if (BackupDB != null) + { + Console.WriteLine("-- BackupResults:"); + foreach (var backup in BackupDB) + { + Console.WriteLine(backup.ToString()); + } + } + + } + + public List GetBackupCombinations() + { + List combinations = new List(); + + if (DbConnectionStrings != null) + { + foreach (DbConnection dbConnection in DbConnectionStrings) + { + if (!(dbConnection.Enabled ?? true)) + { + continue; + } + + combinations.AddRange(GetBackupCombinations(BackupDB, dbConnection)); + } + } + + return combinations; + } + + private List GetBackupCombinations(List? backups, DbConnection dbConnection) + { + List combinations = new List(); + + if (backups != null) + { + foreach (Backup backup in backups) + { + if (!(backup.Enabled??true) || backup.DbConnectionStringsName != dbConnection.Name ) + { + continue; + } + + foreach (PathWrapper path in backup.StoreDirPaths) + { + if ((path.Enabled ?? true)) + { + combinations.Add(new BackupCombination + { + Path = path, + DatabaseName = dbConnection.DatabaseName, + ServerName = dbConnection.getServer(), + Password = dbConnection.getPassword(), + UserName = dbConnection.getUserName(), + CommandPath = PathToDumpMysql ?? "", + BackupFilePath = path.Path + }); + } + } + + } + } + + return combinations; + } +} \ No newline at end of file diff --git a/tbfDBBackup/Program.cs b/tbfDBBackup/Program.cs new file mode 100644 index 0000000..16d6789 --- /dev/null +++ b/tbfDBBackup/Program.cs @@ -0,0 +1,276 @@ +// See https://aka.ms/new-console-template for more information + +using System.Diagnostics; +using MySqlConnector; +using tbfDBBackup; +using tbfDBBackup.configuration; + + +Console.WriteLine("---- TBF DB Backup process started -----"); +//Read configuration +Configuration? configuration = GetConfiguration(); + +if (configuration == null) +{ + Console.WriteLine("**** Configuration no found!! ***"); + return -1; +} + +//update part +if (configuration.UpdateString != null){ + try + { + UpdateDb(configuration); + } + catch (Exception e) + { + Console.WriteLine("------ MySql Data uprade FAILED!! *** -----"); + Console.WriteLine(e); + } +} + +//database backup part +List backupCombinations = configuration.GetBackupCombinations(); +foreach (BackupCombination combination in backupCombinations) +{ + try + { + BackupDatabase(combination); + } + catch (Exception e) + { + Console.WriteLine($"------ MySql backup of database: {combination.DatabaseName} FAILED!! *** -----"); + Console.WriteLine(e); + } +} + +//Update one time config + +//store configuration +SaveConfiguration(configuration); + +//final info +Console.WriteLine("Program finished correctly!!"); + +//wait to end +if (configuration != null && (configuration.WaitOnEndCmdOpen ?? false)) +{ + Console.ReadLine(); +} + +//program END +return 0; +////////////////////////////////////////////////////////////////////////////// + +Configuration? GetConfiguration() +{ + + + try + { + Console.WriteLine($"---- program started path: {Environment.CurrentDirectory}"); + string fileNamePath = ConfigurationFileNamePath(); + Console.WriteLine($"- full config file path: {fileNamePath}"); + + + string jsonConfiguration = ""; + using (StreamReader reader = new StreamReader(fileNamePath)) + { + jsonConfiguration = reader.ReadToEnd(); + } + + Configuration? configurationDeserialized = Configuration.DeserializeFromJson(jsonConfiguration); + if (configurationDeserialized != null && (configurationDeserialized.PrintConfigurationEnabled ?? false)) + { + configurationDeserialized.PrintConfiguration(); + } + + return configurationDeserialized; + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } +} + +void SaveConfiguration(Configuration configuration) +{ + try + { + // Serialize the configuration back to JSON + string jsonString = configuration.SerializeToJson(); + + string fileNamePath = ConfigurationFileNamePath(); + Console.WriteLine($"- full config file path: {fileNamePath}"); + + // Write the JSON string to the file + using (StreamWriter writer = new StreamWriter(fileNamePath)) + { + writer.Write(jsonString); + } + + Console.WriteLine("Configuration saved successfully."); + } + catch (Exception e) + { + Console.WriteLine($"Error saving configuration: {e}"); + throw; + } +} + +void BackupDatabase(BackupCombination backupCombination) +{ + Console.WriteLine($"------ MySql backup of database: {backupCombination.DatabaseName} started -----"); + DateTime currentTime = DateTime.Now; // Get current date and time + + // Format the date and time as yyyyMMdd_HHmmss (e.g., 20241015_142530) + string time = currentTime.ToString("yyyyMMdd_HHmmss"); + + // Create the file name by appending the formatted time to the database name + string fileName = backupCombination.DatabaseName.Trim() + .Replace('\\', '_') + .Replace(' ', '_') + .ToLower() + "_backup_" + time + ".sql"; + + string filePathName = Path.Combine(backupCombination.BackupFilePath, fileName); + // Set up the mysqldump command + string dumpCommand = + $"mysqldump --user={backupCombination.UserName} --password={backupCombination.Password} --host={backupCombination.ServerName} {backupCombination.DatabaseName} --result-file=\"{filePathName}\" --routines --events"; + + // Check if the directory exists, and create it if not + if (!Directory.Exists(backupCombination.BackupFilePath)) + { + Directory.CreateDirectory(backupCombination.BackupFilePath); + } + + // Execute the mysqldump command + ProcessStartInfo processInfo = new ProcessStartInfo + { + FileName = "cmd.exe", + RedirectStandardInput = true, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true + }; + + Process process = new Process + { + StartInfo = processInfo + }; + + process.Start(); + + using (var writer = process.StandardInput) + { + if (writer.BaseStream.CanWrite) + { + if (backupCombination.CommandPath.Length == 0) + { + writer.WriteLine(dumpCommand); + } + else + { + // Split the dumpCommand into the executable and the arguments + string[] + parts = dumpCommand.Split(' ', 2); // Split into two parts: the command and the rest (arguments) + + // Combine the commandPath with the executable (first part of dumpCommand) + string fullCommand = Path.Combine(backupCombination.CommandPath, parts[0]) + " " + parts[1]; + + // Execute the command with the full path + writer.WriteLine(fullCommand); + } + } + } + + process.WaitForExit(); + + string result = process.StandardOutput.ReadToEnd(); + string error = process.StandardError.ReadToEnd(); + + if (!string.IsNullOrEmpty(error)) + { + Console.WriteLine($"Error: {error}"); + } + else + { + Console.WriteLine($"Backup created successfully at {filePathName}"); + } + + process.Close(); + + //Deactivate if only one time possible run + + if ((backupCombination.Path.IsOneTimeOpaque ?? false) && (backupCombination.Path.Enabled??false)) + { + backupCombination.Path.Enabled = false; + } +} + +void UpdateDb(Configuration dbConfiguration) +{ + if (dbConfiguration.DbConnectionStrings != null) + foreach (DbConnection dbConnection in dbConfiguration.DbConnectionStrings) + { + if (!(dbConnection.Enabled ?? true)) + { + continue; + } + + if (dbConfiguration.UpdateString != null) + foreach (Update update in dbConfiguration.UpdateString) + { + if (!(update.Enabled ?? true) || dbConnection.DatabaseName != update.DatabaseName) + { + continue; + } + + try + { + Console.WriteLine($"------ MySql Data upgrade name: {update.Name} starting -----"); + string connectionString = + dbConnection.Connection.Replace("{DatabaseName}", dbConnection.DatabaseName); + MySqlConnection connection = new MySqlConnection(connectionString); + connection.Open(); + + + MySqlCommand command = + new MySqlCommand(update.Query.Replace("{DatabaseName}", update.DatabaseName), connection); + int affectedItems = command.ExecuteNonQuery(); + + Console.WriteLine($"Affected items: {affectedItems}"); + + connection.Close(); + + if (update.IsOneTimeOpaque ?? false) + { + update.Enabled = false; + } + Console.WriteLine($"------ MySql Data upgrade name: {update.Name} finished! -----"); + } + catch (Exception e) + { + Console.WriteLine($"------ MySql Data upgrade name: {update.Name} ERROR!"); + Console.WriteLine(e); + } + } + } +} + +string ConfigurationFileNamePath() +{ + string s; +#if DEBUG + Console.WriteLine("-- DEV version of THE PROJECT -- "); + string projectPath = Directory.GetParent(Directory.GetCurrentDirectory()).Parent.Parent.FullName; + + s = Path.Combine(projectPath, Configuration.ConfigDir, Configuration.FileName); +#else + // For release mode, use an absolute path or the application base directory + Console.WriteLine("-- PROD version of THE PROJECT -- "); + fileNamePath = Path.Combine(Environment.CurrentDirectory, Configuration.ConfigDir, Configuration.FileName); +#endif + return s; +} \ No newline at end of file diff --git a/tbfDBBackup/config/configuration.json b/tbfDBBackup/config/configuration.json new file mode 100644 index 0000000..0212f99 --- /dev/null +++ b/tbfDBBackup/config/configuration.json @@ -0,0 +1,55 @@ +{ + "PathToDumpMysql": "C:\\xampp\\mysql\\bin", + "WaitOnEndCmdOpen": false, + "PrintConfigurationEnabled": false, + "DbConnectionStrings": [ + { + "Enabled": false, + "Name": "Settings DB", + "Connection": "SERVER=localhost; DATABASE={DatabaseName}; UID=root; PASSWORD=; CHARSET=utf8;", + "DatabaseName": "wrcswindon298" + }, + { + "Enabled": true, + "Name": "Results DB", + "Connection": "SERVER=localhost; DATABASE={DatabaseName}; UID=root; PASSWORD=; CHARSET=utf8;", + "DatabaseName": "wrcswindon298-r" + } + ], + "UpdateString": [ + { + "Enabled": false, + "Name": "Alter table Water Meter", + "Query": "ALTER TABLE `{DatabaseName}`.`WaterMeter` ADD COLUMN `RadioAddress` BOOLEAN;", + "DatabaseName": "wrcswindon298-r" + } + ], + "BackupDB": [ + { + "Enabled": true, + "DbConnectionStringsName": "Results DB", + "StoreDirPaths": [ + { + "Path": "C:\\TBF\\" + }, + { + "Path": "C:\\TBF\\storeString", + "Enable": false + } + ] + }, + { + "Enabled": false, + "DbConnectionStringsName": "Settings DB", + "StoreDirPaths": [ + { + "Path": "C:\\TBF\\" + }, + { + "Path": "C:\\TBF\\storeString", + "Enable": true + } + ] + } + ] +} \ No newline at end of file diff --git a/tbfDBBackup/config/configuration_backup.json b/tbfDBBackup/config/configuration_backup.json new file mode 100644 index 0000000..0212f99 --- /dev/null +++ b/tbfDBBackup/config/configuration_backup.json @@ -0,0 +1,55 @@ +{ + "PathToDumpMysql": "C:\\xampp\\mysql\\bin", + "WaitOnEndCmdOpen": false, + "PrintConfigurationEnabled": false, + "DbConnectionStrings": [ + { + "Enabled": false, + "Name": "Settings DB", + "Connection": "SERVER=localhost; DATABASE={DatabaseName}; UID=root; PASSWORD=; CHARSET=utf8;", + "DatabaseName": "wrcswindon298" + }, + { + "Enabled": true, + "Name": "Results DB", + "Connection": "SERVER=localhost; DATABASE={DatabaseName}; UID=root; PASSWORD=; CHARSET=utf8;", + "DatabaseName": "wrcswindon298-r" + } + ], + "UpdateString": [ + { + "Enabled": false, + "Name": "Alter table Water Meter", + "Query": "ALTER TABLE `{DatabaseName}`.`WaterMeter` ADD COLUMN `RadioAddress` BOOLEAN;", + "DatabaseName": "wrcswindon298-r" + } + ], + "BackupDB": [ + { + "Enabled": true, + "DbConnectionStringsName": "Results DB", + "StoreDirPaths": [ + { + "Path": "C:\\TBF\\" + }, + { + "Path": "C:\\TBF\\storeString", + "Enable": false + } + ] + }, + { + "Enabled": false, + "DbConnectionStringsName": "Settings DB", + "StoreDirPaths": [ + { + "Path": "C:\\TBF\\" + }, + { + "Path": "C:\\TBF\\storeString", + "Enable": true + } + ] + } + ] +} \ No newline at end of file diff --git a/tbfDBBackup/configuration/Backup.cs b/tbfDBBackup/configuration/Backup.cs new file mode 100644 index 0000000..e3363a9 --- /dev/null +++ b/tbfDBBackup/configuration/Backup.cs @@ -0,0 +1,15 @@ +using System.Text.Json; + +namespace tbfDBBackup.configuration; + +public class Backup : StandardBehaviour +{ + + public string DbConnectionStringsName { get; set; } + public List StoreDirPaths { get; set; } + + public override string ToString() + { + return JsonSerializer.Serialize(this, new JsonSerializerOptions { WriteIndented = true }); + } +} diff --git a/tbfDBBackup/configuration/DbConnection.cs b/tbfDBBackup/configuration/DbConnection.cs new file mode 100644 index 0000000..24dc743 --- /dev/null +++ b/tbfDBBackup/configuration/DbConnection.cs @@ -0,0 +1,40 @@ +using System.Text.Json; +using System.Text.RegularExpressions; + +namespace tbfDBBackup.configuration; + +public class DbConnection : StandardBehaviour +{ + + public string Name { get; set; } + public string Connection { get; set; } + public string DatabaseName { get; set; } + + + public string getServer() + { + return ExtractValueUsingRegex(Connection, @"SERVER=([^;]+)"); + } + + public string getPassword() + { + return ExtractValueUsingRegex(Connection, @"PASSWORD=([^;]+)"); + } + + public string getUserName() + { + return ExtractValueUsingRegex(Connection, @"UID=([^;]+)"); + } + + // Helper function to extract value using regex + public static string ExtractValueUsingRegex(string input, string pattern) + { + Match match = Regex.Match(input, pattern); + return match.Success ? match.Groups[1].Value : null; + } + + public override string ToString() + { + return JsonSerializer.Serialize(this, new JsonSerializerOptions { WriteIndented = true }); + } +} \ No newline at end of file diff --git a/tbfDBBackup/configuration/IStandartBehaviour.cs b/tbfDBBackup/configuration/IStandartBehaviour.cs new file mode 100644 index 0000000..61d1e2e --- /dev/null +++ b/tbfDBBackup/configuration/IStandartBehaviour.cs @@ -0,0 +1,7 @@ +namespace tbfDBBackup.configuration; + +public interface IStandartBehaviour +{ + bool IsEnabled(); + bool IsOneTimeOpaque(); +} \ No newline at end of file diff --git a/tbfDBBackup/configuration/PathWrapper.cs b/tbfDBBackup/configuration/PathWrapper.cs new file mode 100644 index 0000000..73f6d23 --- /dev/null +++ b/tbfDBBackup/configuration/PathWrapper.cs @@ -0,0 +1,13 @@ +using System.Text.Json; + +namespace tbfDBBackup.configuration; + +public class PathWrapper : StandardBehaviour +{ + public string Path { get; set; } + + public override string ToString() + { + return JsonSerializer.Serialize(this, new JsonSerializerOptions { WriteIndented = true }); + } +} \ No newline at end of file diff --git a/tbfDBBackup/configuration/StandardBehaviour.cs b/tbfDBBackup/configuration/StandardBehaviour.cs new file mode 100644 index 0000000..87446fd --- /dev/null +++ b/tbfDBBackup/configuration/StandardBehaviour.cs @@ -0,0 +1,7 @@ +namespace tbfDBBackup.configuration; + +public class StandardBehaviour +{ + public Boolean? Enabled { get; set; } + public Boolean? IsOneTimeOpaque { get; set; } +} \ No newline at end of file diff --git a/tbfDBBackup/configuration/Update.cs b/tbfDBBackup/configuration/Update.cs new file mode 100644 index 0000000..e246546 --- /dev/null +++ b/tbfDBBackup/configuration/Update.cs @@ -0,0 +1,16 @@ +using System.Text.Json; + +namespace tbfDBBackup.configuration; + +public class Update : StandardBehaviour +{ + + public string Name { get; set; } + public string Query { get; set; } + public string DatabaseName { get; set; } + + public override string ToString() + { + return JsonSerializer.Serialize(this, new JsonSerializerOptions { WriteIndented = true }); + } +} \ No newline at end of file diff --git a/tbfDBBackup/tbfDBBackup.csproj b/tbfDBBackup/tbfDBBackup.csproj new file mode 100644 index 0000000..03b2351 --- /dev/null +++ b/tbfDBBackup/tbfDBBackup.csproj @@ -0,0 +1,14 @@ + + + + Exe + net8.0 + enable + enable + + + + + + +