|
20 | 20 | #include "config/configmanager.hpp" |
21 | 21 | #include "lib/di/container.hpp" |
22 | 22 | #include "lib/metrics/metrics.hpp" |
| 23 | +#include "utils/tools.hpp" |
23 | 24 |
|
24 | 25 | Database::~Database() { |
25 | 26 | if (handle != nullptr) { |
@@ -68,6 +69,89 @@ bool Database::connect(const std::string* host, const std::string* user, const s |
68 | 69 | return true; |
69 | 70 | } |
70 | 71 |
|
| 72 | +void Database::createDatabaseBackup(bool compress) const { |
| 73 | + if (!g_configManager().getBoolean(MYSQL_DB_BACKUP)) { |
| 74 | + return; |
| 75 | + } |
| 76 | + // Get current time for formatting |
| 77 | + auto now = std::chrono::system_clock::now(); |
| 78 | + std::time_t now_c = std::chrono::system_clock::to_time_t(now); |
| 79 | + std::string formattedDate = fmt::format("{:%Y-%m-%d}", fmt::localtime(now_c)); |
| 80 | + std::string formattedTime = fmt::format("{:%H-%M-%S}", fmt::localtime(now_c)); |
| 81 | + // Create a backup directory based on the current date |
| 82 | + std::string backupDir = fmt::format("database_backup/{}/", formattedDate); |
| 83 | + std::filesystem::create_directories(backupDir); |
| 84 | + std::string backupFileName = fmt::format("{}backup_{}.sql", backupDir, formattedTime); |
| 85 | + // Create a temporary configuration file for MySQL credentials |
| 86 | + std::string tempConfigFile = "database_backup.cnf"; |
| 87 | + std::ofstream configFile(tempConfigFile); |
| 88 | + if (configFile.is_open()) { |
| 89 | + configFile << "[client]\n"; |
| 90 | + configFile << "user=" << g_configManager().getString(MYSQL_USER) << "\n"; |
| 91 | + configFile << "password=" << g_configManager().getString(MYSQL_PASS) << "\n"; |
| 92 | + configFile << "host=" << g_configManager().getString(MYSQL_HOST) << "\n"; |
| 93 | + configFile << "port=" << g_configManager().getNumber(SQL_PORT) << "\n"; |
| 94 | + configFile.close(); |
| 95 | + } else { |
| 96 | + g_logger().error("Failed to create temporary MySQL configuration file."); |
| 97 | + return; |
| 98 | + } |
| 99 | + // Execute mysqldump command to create backup file |
| 100 | + std::string command = fmt::format( |
| 101 | + "mysqldump --defaults-extra-file={} {} > {}", |
| 102 | + tempConfigFile, g_configManager().getString(MYSQL_DB), backupFileName |
| 103 | + ); |
| 104 | + int result = std::system(command.c_str()); |
| 105 | + std::filesystem::remove(tempConfigFile); |
| 106 | + if (result != 0) { |
| 107 | + g_logger().error("Failed to create database backup using mysqldump."); |
| 108 | + return; |
| 109 | + } |
| 110 | + // Compress the backup file if requested |
| 111 | + std::string compressedFileName; |
| 112 | + compressedFileName = backupFileName + ".gz"; |
| 113 | + gzFile gzFile = gzopen(compressedFileName.c_str(), "wb9"); |
| 114 | + if (!gzFile) { |
| 115 | + g_logger().error("Failed to open gzip file for compression."); |
| 116 | + return; |
| 117 | + } |
| 118 | + std::ifstream backupFile(backupFileName, std::ios::binary); |
| 119 | + if (!backupFile.is_open()) { |
| 120 | + g_logger().error("Failed to open backup file for compression: {}", backupFileName); |
| 121 | + gzclose(gzFile); |
| 122 | + return; |
| 123 | + } |
| 124 | + std::string buffer(8192, '\0'); |
| 125 | + while (backupFile.read(&buffer[0], buffer.size()) || backupFile.gcount() > 0) { |
| 126 | + gzwrite(gzFile, buffer.data(), backupFile.gcount()); |
| 127 | + } |
| 128 | + backupFile.close(); |
| 129 | + gzclose(gzFile); |
| 130 | + std::filesystem::remove(backupFileName); |
| 131 | + g_logger().info("Database backup successfully compressed to: {}", compressedFileName); |
| 132 | + // Delete backups older than 7 days |
| 133 | + auto nowTime = std::chrono::system_clock::now(); |
| 134 | + auto sevenDaysAgo = nowTime - std::chrono::hours(7 * 24); // 7 days in hours |
| 135 | + for (const auto &entry : std::filesystem::directory_iterator("database_backup")) { |
| 136 | + if (entry.is_directory()) { |
| 137 | + try { |
| 138 | + for (const auto &file : std::filesystem::directory_iterator(entry)) { |
| 139 | + if (file.path().extension() == ".gz") { |
| 140 | + auto fileTime = std::filesystem::last_write_time(file); |
| 141 | + auto fileTimeSystemClock = std::chrono::clock_cast<std::chrono::system_clock>(fileTime); |
| 142 | + if (fileTimeSystemClock < sevenDaysAgo) { |
| 143 | + std::filesystem::remove(file); |
| 144 | + g_logger().info("Deleted old backup file: {}", file.path().string()); |
| 145 | + } |
| 146 | + } |
| 147 | + } |
| 148 | + } catch (const std::filesystem::filesystem_error &e) { |
| 149 | + g_logger().error("Failed to check or delete files in backup directory: {}. Error: {}", entry.path().string(), e.what()); |
| 150 | + } |
| 151 | + } |
| 152 | + } |
| 153 | +} |
| 154 | + |
71 | 155 | bool Database::beginTransaction() { |
72 | 156 | if (!executeQuery("BEGIN")) { |
73 | 157 | return false; |
|
0 commit comments