Files
2026-07-23 08:58:13 +08:00

65 lines
2.4 KiB
PHP

<?php
declare(strict_types=1);
if (PHP_SAPI !== 'cli') {
http_response_code(404);
exit;
}
$config = require dirname(__DIR__) . '/config.php';
if (empty($config['installed']) || ($config['db']['driver'] ?? '') !== 'mysql') {
fwrite(STDERR, "系统尚未安装或未使用 MySQL。\n");
exit(1);
}
$targetDirectory = $argv[1] ?? '/var/backups/tanwan';
if (!str_starts_with($targetDirectory, '/') || in_array(rtrim($targetDirectory, '/'), ['', '/', dirname(__DIR__)], true)) {
fwrite(STDERR, "备份目录必须是 Web 根目录之外的绝对路径。\n");
exit(1);
}
$webRoot = realpath(dirname(__DIR__));
$resolvedParent = realpath(dirname($targetDirectory));
if ($webRoot !== false && $resolvedParent !== false && str_starts_with($resolvedParent . '/', $webRoot . '/')) {
fwrite(STDERR, "禁止将备份写入 Web 根目录。\n");
exit(1);
}
if (!is_dir($targetDirectory) && !mkdir($targetDirectory, 0700, true) && !is_dir($targetDirectory)) {
fwrite(STDERR, "无法创建备份目录。\n");
exit(1);
}
chmod($targetDirectory, 0700);
$db = $config['db'];
$filename = rtrim($targetDirectory, '/') . '/tanwan-' . date('Ymd-His') . '.sql';
$command = ['/usr/bin/mariadb-dump', '--single-transaction', '--quick', '--skip-lock-tables', '--default-character-set=utf8mb4'];
if (in_array((string) $db['host'], ['localhost', 'localhost.localdomain'], true)) {
$command[] = '--protocol=socket';
} else {
$command[] = '--host=' . $db['host'];
$command[] = '--port=' . $db['port'];
}
$command[] = '--user=' . $db['username'];
$command[] = (string) $db['database'];
$descriptors = [0 => ['file', '/dev/null', 'rb'], 1 => ['file', $filename, 'wb'], 2 => ['pipe', 'w']];
$environment = array_merge(getenv(), ['MYSQL_PWD' => (string) $db['password']]);
$process = proc_open($command, $descriptors, $pipes, null, $environment, ['bypass_shell' => true]);
if (!is_resource($process)) {
fwrite(STDERR, "无法启动数据库备份。\n");
exit(1);
}
$errors = stream_get_contents($pipes[2]);
fclose($pipes[2]);
$exitCode = proc_close($process);
if ($exitCode !== 0) {
@unlink($filename);
fwrite(STDERR, "备份失败:" . trim($errors) . "\n");
exit(1);
}
chmod($filename, 0600);
$cutoff = time() - 14 * 86400;
foreach (glob(rtrim($targetDirectory, '/') . '/tanwan-*.sql') ?: [] as $oldBackup) {
if (is_file($oldBackup) && filemtime($oldBackup) < $cutoff) unlink($oldBackup);
}
fwrite(STDOUT, $filename . "\n");