From b453b80c36c035535279933d0a816000b8cc4799 Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Thu, 7 Nov 2024 00:36:54 -0300 Subject: [PATCH] feat: create database backup --- .gitignore | 3 +++ config.lua.dist | 1 + src/canary_server.cpp | 2 ++ src/config/config_enums.hpp | 1 + src/config/configmanager.cpp | 1 + src/database/database.cpp | 48 ++++++++++++++++++++++++++++++++++++ src/database/database.hpp | 1 + 7 files changed, 57 insertions(+) diff --git a/.gitignore b/.gitignore index a73a8c3022c..4bd08dd8908 100644 --- a/.gitignore +++ b/.gitignore @@ -395,5 +395,8 @@ canary.old # VCPKG vcpkg_installed +# DB Backups +database_backup + # CLION cmake-build-* diff --git a/config.lua.dist b/config.lua.dist index 7d0360d9360..1ed6c59f367 100644 --- a/config.lua.dist +++ b/config.lua.dist @@ -399,6 +399,7 @@ mysqlHost = "127.0.0.1" mysqlUser = "root" mysqlPass = "root" mysqlDatabase = "otservbr-global" +mysqlDatabaseBackup = false mysqlPort = 3306 mysqlSock = "" passwordType = "sha1" diff --git a/src/canary_server.cpp b/src/canary_server.cpp index 687fa51b6cd..2db6e033f03 100644 --- a/src/canary_server.cpp +++ b/src/canary_server.cpp @@ -316,6 +316,8 @@ void CanaryServer::initializeDatabase() { )); } + g_database().createDatabaseBackup(); + DatabaseManager::updateDatabase(); if (g_configManager().getBoolean(OPTIMIZE_DATABASE) diff --git a/src/config/config_enums.hpp b/src/config/config_enums.hpp index 559045fdb9b..7e417c2dfb5 100644 --- a/src/config/config_enums.hpp +++ b/src/config/config_enums.hpp @@ -165,6 +165,7 @@ enum ConfigKey_t : uint16_t { MONTH_KILLS_TO_RED, MULTIPLIER_ATTACKONFIST, MYSQL_DB, + MYSQL_DB_BACKUP, MYSQL_HOST, MYSQL_PASS, MYSQL_SOCK, diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp index 1c00df76c2f..d52096f123d 100644 --- a/src/config/configmanager.cpp +++ b/src/config/configmanager.cpp @@ -71,6 +71,7 @@ bool ConfigManager::load() { loadStringConfig(L, MAP_DOWNLOAD_URL, "mapDownloadUrl", ""); loadStringConfig(L, MAP_NAME, "mapName", "canary"); loadStringConfig(L, MYSQL_DB, "mysqlDatabase", "canary"); + loadBoolConfig(L, MYSQL_DB_BACKUP, "mysqlDatabaseBackup", false); loadStringConfig(L, MYSQL_HOST, "mysqlHost", "127.0.0.1"); loadStringConfig(L, MYSQL_PASS, "mysqlPass", ""); loadStringConfig(L, MYSQL_SOCK, "mysqlSock", ""); diff --git a/src/database/database.cpp b/src/database/database.cpp index fcb7371708d..8ce5a3fa939 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -12,6 +12,7 @@ #include "config/configmanager.hpp" #include "lib/di/container.hpp" #include "lib/metrics/metrics.hpp" +#include "utils/tools.hpp" Database::~Database() { if (handle != nullptr) { @@ -60,6 +61,53 @@ bool Database::connect(const std::string* host, const std::string* user, const s return true; } +void Database::createDatabaseBackup() const { + if (!g_configManager().getBoolean(MYSQL_DB_BACKUP)) { + return; + } + + std::time_t now = getTimeNow(); + std::string formattedTime = fmt::format("{:%Y-%m-%d_%H-%M-%S}", fmt::localtime(now)); + + if (formattedTime.empty()) { + g_logger().error("Failed to format time for database backup."); + return; + } + + std::string backupDir = "database_backup/"; + std::filesystem::create_directories(backupDir); + std::string backupFileName = fmt::format("{}/backup_{}.sql", backupDir, formattedTime); + + std::string tempConfigFile = "database_backup.cnf"; + std::ofstream configFile(tempConfigFile); + if (configFile.is_open()) { + configFile << "[client]\n"; + configFile << "user=" << g_configManager().getString(MYSQL_USER) << "\n"; + configFile << "password=" << g_configManager().getString(MYSQL_PASS) << "\n"; + configFile << "host=" << g_configManager().getString(MYSQL_HOST) << "\n"; + configFile << "port=" << g_configManager().getNumber(SQL_PORT) << "\n"; + configFile.close(); + } else { + g_logger().error("Failed to create temporary MySQL configuration file."); + return; + } + + std::string command = fmt::format( + "mysqldump --defaults-extra-file={} {} > {}", + tempConfigFile, g_configManager().getString(MYSQL_DB), backupFileName + ); + + int result = std::system(command.c_str()); + + std::filesystem::remove(tempConfigFile); + + if (result != 0) { + g_logger().error("Failed to create database backup using mysqldump."); + } else { + g_logger().info("Database backup successfully created at: {}", backupFileName); + } +} + bool Database::beginTransaction() { if (!executeQuery("BEGIN")) { return false; diff --git a/src/database/database.hpp b/src/database/database.hpp index 69c47d324ad..1aa2943aa3c 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -36,6 +36,7 @@ class Database { bool connect(); bool connect(const std::string* host, const std::string* user, const std::string* password, const std::string* database, uint32_t port, const std::string* sock); + void createDatabaseBackup() const; bool retryQuery(std::string_view query, int retries); bool executeQuery(std::string_view query);