#include <iostream>
#include <string>
#include <vector>
#include <filesystem>
#include <chrono>
#include <iomanip>
#include <sstream>
#include <cstdlib>
#include <io.h>
#include <direct.h>
namespace fs = std::filesystem;
using namespace std::chrono;
// 执行外部命令(Windows)
int exec_cmd(const std::string& cmd) {
std::cout << "[CMD] " << cmd << std::endl;
return std::system(cmd.c_str());
}
// 获取当前本地时间字符串
std::string current_local_time_str(const char* fmt = "%Y-%m-%d %H:%M:%S") {
auto now = system_clock::now();
std::time_t tt = system_clock::to_time_t(now);
std::tm tm{};
localtime_s(&tm, &tt);
std::ostringstream oss;
oss << std::put_time(&tm, fmt);
return oss.str();
}
// 获取当前日期(%Y-%m-%d)
std::string get_date() {
return current_local_time_str("%Y-%m-%d");
}
// 获取星期几(0=Sunday, 6=Saturday)
int get_weekday() {
auto now = system_clock::now();
std::time_t tt = system_clock::to_time_t(now);
std::tm tm{};
localtime_s(&tm, &tt);
return tm.tm_wday;
}
// 获取 N 天前的日期(字符串)
std::string get_date_offset(int days) {
auto now = system_clock::now();
auto past = now - hours(24 * days);
std::time_t tt = system_clock::to_time_t(past);
std::tm tm{};
localtime_s(&tm, &tt);
std::ostringstream oss;
oss << std::put_time(&tm, "%Y-%m-%d");
return oss.str();
}
// 递归计算目录大小(字节)
uint64_t get_directory_size(const fs::path& dir) {
uint64_t size = 0;
for (const auto& entry : fs::recursive_directory_iterator(dir)) {
if (entry.is_regular_file()) {
size += entry.file_size();
}
}
return size;
}
// 将字节数转为人类可读格式(KB, MB, GB)
std::string human_readable_size(uint64_t bytes) {
const char* units[] = {"B", "KB", "MB", "GB", "TB"};
int unit = 0;
double size = static_cast<double>(bytes);
while (size >= 1024.0 && unit < 4) {
size /= 1024.0;
++unit;
}
std::ostringstream oss;
if (unit == 0) {
oss << static_cast<int>(size) << " " << units[unit];
} else {
oss << std::fixed << std::setprecision(1) << size << " " << units[unit];
}
return oss.str();
}
// ========== 备份逻辑 ==========
void do_backup() {
set_pg_env();
std::string BAK_BASE = "C:\\backup"; // Windows 路径
std::string DATE = get_date();
std::string YESTERDAY = get_date_offset(1);
int WEEK_DAY = get_weekday();
std::string BAK_DIR = BAK_BASE + "\\" + DATE + "-" + std::to_string(WEEK_DAY);
std::cout << std::endl;
std::string START_TIME = current_local_time_str();
std::cout << "############## Physical backup start at " << START_TIME << " ##############" << std::endl;
std::cout << std::endl;
// 确保备份根目录存在
fs::create_directories(BAK_BASE);
std::string TYPE;
int ret = -1;
// 构建 pg_basebackup 路径(可配置)
std::string PG_BASEBACKUP = "\"C:\\Program Files\\PostgreSQL\\15\\bin\\pg_basebackup.exe\"";
if (WEEK_DAY == 6) {
// Full backup on Saturday
std::string cmd = PG_BASEBACKUP + " -Fp -D \"" + BAK_DIR + "\" -v";
ret = exec_cmd(cmd);
TYPE = "FULL";
} else {
std::string INCRE_BASE;
if (WEEK_DAY == 0) {
INCRE_BASE = BAK_BASE + "\\" + YESTERDAY + "-6";
} else {
INCRE_BASE = BAK_BASE + "\\" + YESTERDAY + "-" + std::to_string(WEEK_DAY - 1);
}
std::string manifest = INCRE_BASE + "\\backup_manifest";
if (!fs::exists(manifest)) {
std::cerr << "ERROR: Incremental base manifest not found: " << manifest << std::endl;
return;
}
std::string cmd = PG_BASEBACKUP + " -Fp -D \"" + BAK_DIR + "\" -v -i \"" + manifest + "\"";
ret = exec_cmd(cmd);
TYPE = "INCR";
}
std::cout << std::endl;
std::string END_TIME = current_local_time_str();
std::cout << "############## Physical backup end at " << END_TIME << " ##############" << std::endl;
std::cout << std::endl;
if (ret == 0 && fs::exists(BAK_DIR)) {
uint64_t size_bytes = get_directory_size(BAK_DIR);
std::string SIZE = human_readable_size(size_bytes);
std::cout << "Backup completed successfully." << std::endl;
std::cout << "Backup is available at: " << BAK_DIR << "\\backup_label" << std::endl;
} else {
std::cerr << "Backup failed." << std::endl;
}
}
// ========== 清理逻辑 ==========
void do_cleanup() {
std::string BAK_BASE = "C:\\backup";
std::cout << std::endl;
std::string START_TIME = current_local_time_str();
std::cout << "############## clean up start at " << START_TIME << " ##############" << std::endl;
std::cout << std::endl;
auto now = file_time_type::clock::now();
auto cutoff = now - days(30); // 30天前
std::vector<fs::path> to_delete;
for (const auto& entry : fs::directory_iterator(BAK_BASE)) {
if (entry.is_directory()) {
auto last_write = entry.last_write_time();
if (last_write < cutoff) {
std::cout << "Will delete: " << entry.path().string() << std::endl;
to_delete.push_back(entry.path());
}
}
}
// 执行删除
for (const auto& dir : to_delete) {
try {
fs::remove_all(dir);
std::cout << "Deleted: " << dir.string() << std::endl;
} catch (const fs::filesystem_error& e) {
std::cerr << "Failed to delete " << dir << ": " << e.what() << std::endl;
}
}
std::cout << std::endl;
std::string END_TIME = current_local_time_str();
std::cout << "############## clean up end at " << END_TIME << " ##############" << std::endl;
std::cout << std::endl;
}
// ========== 主函数 ==========
int main(int argc, char* argv[]) {
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " [backup|cleanup]" << std::endl;
return 1;
}
std::string mode = argv[1];
if (mode == "backup") {
do_backup();
} else if (mode == "cleanup") {
do_cleanup();
} else {
std::cerr << "Unknown mode: " << mode << std::endl;
return 1;
}
return 0;
}