788 lines
26 KiB
HTML
Executable File
788 lines
26 KiB
HTML
Executable File
#!/usr/local/bin/php -nc/usr/local/directadmin/plugins/borg-restore/php.ini
|
|
<?php
|
|
|
|
function h(string $value): string
|
|
{
|
|
return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
|
|
}
|
|
|
|
function parse_sh_value(string $value): string
|
|
{
|
|
$value = trim($value);
|
|
if ($value === '') {
|
|
return '';
|
|
}
|
|
if ($value[0] === "'" && substr($value, -1) === "'") {
|
|
return substr($value, 1, -1);
|
|
}
|
|
if ($value[0] === '"' && substr($value, -1) === '"') {
|
|
return stripcslashes(substr($value, 1, -1));
|
|
}
|
|
$value = preg_replace('/\s+#.*$/', '', $value);
|
|
return trim((string)$value);
|
|
}
|
|
|
|
function parse_bool(string $value, bool $default = false): bool
|
|
{
|
|
$normalized = strtolower(trim($value));
|
|
if ($normalized === '') {
|
|
return $default;
|
|
}
|
|
if (in_array($normalized, ['1', 'true', 'yes', 'y', 'on'], true)) {
|
|
return true;
|
|
}
|
|
if (in_array($normalized, ['0', 'false', 'no', 'n', 'off'], true)) {
|
|
return false;
|
|
}
|
|
return $default;
|
|
}
|
|
|
|
function read_params(): array
|
|
{
|
|
$params = [];
|
|
if (!empty($_GET)) {
|
|
$params = array_merge($params, $_GET);
|
|
}
|
|
if (!empty($_POST)) {
|
|
$params = array_merge($params, $_POST);
|
|
}
|
|
|
|
$get_raw = getenv('GET') ?: getenv('get') ?: '';
|
|
if ($get_raw !== '') {
|
|
parse_str($get_raw, $parsed);
|
|
$params = array_merge($parsed, $params);
|
|
}
|
|
|
|
$qs = $_SERVER['QUERY_STRING'] ?? '';
|
|
if ($qs !== '') {
|
|
parse_str($qs, $parsed);
|
|
$params = array_merge($parsed, $params);
|
|
}
|
|
|
|
return $params;
|
|
}
|
|
|
|
function load_settings(string $path): array
|
|
{
|
|
$settings = [
|
|
'BB_PATH' => '/opt/bb',
|
|
'BORG_VARIABLES_FILE' => '/opt/bb/bbvars.sh',
|
|
'ADMIN_MODE_ENABLE' => 'TRUE',
|
|
'RESULTS_PER_PAGE' => '10',
|
|
'DEBUG_MODE' => 'FALSE',
|
|
];
|
|
if (!is_readable($path)) {
|
|
return $settings;
|
|
}
|
|
$lines = file($path, FILE_IGNORE_NEW_LINES);
|
|
if ($lines === false) {
|
|
return $settings;
|
|
}
|
|
foreach ($lines as $line) {
|
|
$line = trim($line);
|
|
if ($line === '' || $line[0] === '#') {
|
|
continue;
|
|
}
|
|
if (preg_match('/^(BB_PATH|BORG_VARIABLES_FILE|ADMIN_MODE_ENABLE|RESULTS_PER_PAGE|DEBUG_MODE)=(.*)$/', $line, $m)) {
|
|
$settings[$m[1]] = parse_sh_value($m[2]);
|
|
}
|
|
}
|
|
return $settings;
|
|
}
|
|
|
|
function load_borg_vars(string $path): array
|
|
{
|
|
$vars = [
|
|
'BORG_PASSPHRASE' => null,
|
|
'REPO' => null,
|
|
];
|
|
if (!is_readable($path)) {
|
|
return $vars;
|
|
}
|
|
$lines = file($path, FILE_IGNORE_NEW_LINES);
|
|
if ($lines === false) {
|
|
return $vars;
|
|
}
|
|
foreach ($lines as $line) {
|
|
$line = trim($line);
|
|
if ($line === '' || $line[0] === '#') {
|
|
continue;
|
|
}
|
|
if (preg_match('/^(?:export\\s+)?(BORG_PASSPHRASE|REPO)=(.*)$/', $line, $m)) {
|
|
$vars[$m[1]] = parse_sh_value($m[2]);
|
|
}
|
|
}
|
|
return $vars;
|
|
}
|
|
|
|
function load_user_skin(string $user): string
|
|
{
|
|
$path = '/usr/local/directadmin/data/users/' . $user . '/user.conf';
|
|
if (!is_readable($path)) {
|
|
return 'enhanced';
|
|
}
|
|
$lines = file($path, FILE_IGNORE_NEW_LINES);
|
|
if ($lines === false) {
|
|
return 'enhanced';
|
|
}
|
|
foreach ($lines as $line) {
|
|
$line = trim($line);
|
|
if ($line === '' || $line[0] === '#') {
|
|
continue;
|
|
}
|
|
if (preg_match('/^skin=(.+)$/', $line, $m)) {
|
|
$skin = strtolower(trim($m[1]));
|
|
return $skin !== '' ? $skin : 'enhanced';
|
|
}
|
|
}
|
|
return 'enhanced';
|
|
}
|
|
|
|
function load_job_vars(string $path): array
|
|
{
|
|
$vars = [
|
|
'DA_USER' => '',
|
|
'REPONAME' => '',
|
|
'USERRESTOREPATH' => '',
|
|
'TARGETPATH' => '',
|
|
'RESTOREMODE' => '',
|
|
'RESTORETYPE' => '',
|
|
'START_TS' => '',
|
|
];
|
|
if (!is_readable($path)) {
|
|
return $vars;
|
|
}
|
|
$lines = file($path, FILE_IGNORE_NEW_LINES);
|
|
if ($lines === false) {
|
|
return $vars;
|
|
}
|
|
foreach ($lines as $line) {
|
|
$line = trim($line);
|
|
if ($line === '' || $line[0] === '#') {
|
|
continue;
|
|
}
|
|
if (preg_match('/^(DA_USER|REPONAME|USERRESTOREPATH|TARGETPATH|RESTOREMODE|RESTORETYPE|START_TS|USERLANG)=(.*)$/', $line, $m)) {
|
|
$vars[$m[1]] = parse_sh_value($m[2]);
|
|
}
|
|
}
|
|
return $vars;
|
|
}
|
|
|
|
function job_id_from_file(string $file): string
|
|
{
|
|
$base = basename($file);
|
|
if (substr($base, -3) === '.ok') {
|
|
$base = substr($base, 0, -3);
|
|
} elseif (substr($base, -5) === '.fail') {
|
|
$base = substr($base, 0, -5);
|
|
} elseif (substr($base, -7) === '.cancel') {
|
|
$base = substr($base, 0, -7);
|
|
}
|
|
if (substr($base, -4) === '.env') {
|
|
$base = substr($base, 0, -4);
|
|
}
|
|
return $base;
|
|
}
|
|
|
|
function find_job_file(string $job_base, string $file, string $job_id): string
|
|
{
|
|
$candidates = [
|
|
$job_base . '/processing/' . $file,
|
|
$job_base . '/pending/' . $file,
|
|
$job_base . '/done/' . $file,
|
|
];
|
|
if ($job_id !== '') {
|
|
$candidates[] = $job_base . '/processing/' . $job_id . '.env';
|
|
$candidates[] = $job_base . '/pending/' . $job_id . '.env';
|
|
$candidates[] = $job_base . '/done/' . $job_id . '.ok';
|
|
$candidates[] = $job_base . '/done/' . $job_id . '.fail';
|
|
$candidates[] = $job_base . '/done/' . $job_id . '.cancel';
|
|
$candidates[] = $job_base . '/done/' . $job_id . '.env.ok';
|
|
$candidates[] = $job_base . '/done/' . $job_id . '.env.fail';
|
|
$candidates[] = $job_base . '/done/' . $job_id . '.env.cancel';
|
|
}
|
|
foreach ($candidates as $path) {
|
|
if (is_file($path)) {
|
|
return $path;
|
|
}
|
|
}
|
|
return '';
|
|
}
|
|
|
|
function read_log_tail(string $path, int $max_bytes = 200000): string
|
|
{
|
|
if (!is_readable($path)) {
|
|
return '';
|
|
}
|
|
$size = filesize($path);
|
|
if ($size === false || $size <= $max_bytes) {
|
|
return (string)file_get_contents($path);
|
|
}
|
|
$fp = fopen($path, 'rb');
|
|
if ($fp === false) {
|
|
return '';
|
|
}
|
|
fseek($fp, -$max_bytes, SEEK_END);
|
|
$data = fread($fp, $max_bytes);
|
|
fclose($fp);
|
|
return (string)$data;
|
|
}
|
|
|
|
function normalize_log(string $text): string
|
|
{
|
|
$text = str_replace("\r", "\n", $text);
|
|
$lines = preg_split('/\n/', $text);
|
|
if (!is_array($lines)) {
|
|
return $text;
|
|
}
|
|
$filtered = [];
|
|
foreach ($lines as $line) {
|
|
$trimmed = trim($line);
|
|
if ($trimmed !== '' && stripos($trimmed, 'Permanently added') !== false && stripos($trimmed, 'known host') !== false) {
|
|
continue;
|
|
}
|
|
$filtered[] = $line;
|
|
}
|
|
return implode("\n", $filtered);
|
|
}
|
|
|
|
function last_non_empty_line(string $text): string
|
|
{
|
|
$lines = preg_split('/\r\n|\n|\r/', $text);
|
|
if (!is_array($lines)) {
|
|
return '';
|
|
}
|
|
for ($i = count($lines) - 1; $i >= 0; $i--) {
|
|
$line = trim((string)$lines[$i]);
|
|
if ($line !== '') {
|
|
return $line;
|
|
}
|
|
}
|
|
return '';
|
|
}
|
|
|
|
function is_calculating_line(string $line): bool
|
|
{
|
|
return (bool)preg_match('/calculat/i', $line);
|
|
}
|
|
|
|
function extract_progress(string $line): ?int
|
|
{
|
|
if (preg_match('/\b(\d{1,3})%\b/', $line, $m)) {
|
|
$p = (int)$m[1];
|
|
if ($p < 0) {
|
|
$p = 0;
|
|
} elseif ($p > 100) {
|
|
$p = 100;
|
|
}
|
|
return $p;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function get_job_status(string $job_id, string $job_base): string
|
|
{
|
|
if ($job_id === '') {
|
|
return 'unknown';
|
|
}
|
|
if (is_file($job_base . '/processing/' . $job_id . '.env')) {
|
|
return 'processing';
|
|
}
|
|
if (is_file($job_base . '/pending/' . $job_id . '.env')) {
|
|
return 'pending';
|
|
}
|
|
if (is_file($job_base . '/done/' . $job_id . '.ok') || is_file($job_base . '/done/' . $job_id . '.env.ok')) {
|
|
return 'done';
|
|
}
|
|
if (is_file($job_base . '/done/' . $job_id . '.fail') || is_file($job_base . '/done/' . $job_id . '.env.fail')) {
|
|
return 'failed';
|
|
}
|
|
if (is_file($job_base . '/done/' . $job_id . '.cancel') || is_file($job_base . '/done/' . $job_id . '.env.cancel')) {
|
|
return 'canceled';
|
|
}
|
|
return 'unknown';
|
|
}
|
|
|
|
function display_status(string $base_status, string $last_line): string
|
|
{
|
|
if ($base_status === 'processing' && $last_line !== '' && is_calculating_line($last_line)) {
|
|
return 'calculating';
|
|
}
|
|
return $base_status;
|
|
}
|
|
|
|
function normalize_admin_status(string $status): string
|
|
{
|
|
if (in_array($status, ['pending', 'processing', 'calculating'], true)) {
|
|
return 'processing';
|
|
}
|
|
if ($status === 'done') {
|
|
return 'done';
|
|
}
|
|
if (in_array($status, ['failed', 'canceled'], true)) {
|
|
return 'failed';
|
|
}
|
|
return 'failed';
|
|
}
|
|
|
|
function admin_status_label(string $status): string
|
|
{
|
|
if ($status === 'done') {
|
|
return 'Zakończone';
|
|
}
|
|
if ($status === 'processing') {
|
|
return 'W trakcie';
|
|
}
|
|
return 'Błąd';
|
|
}
|
|
|
|
function admin_status_sort(string $status): int
|
|
{
|
|
if ($status === 'processing') {
|
|
return 1;
|
|
}
|
|
if ($status === 'done') {
|
|
return 2;
|
|
}
|
|
return 3;
|
|
}
|
|
|
|
function restore_mode_label(string $mode): string
|
|
{
|
|
if ($mode === 'overwrite') {
|
|
return 'Nadpisz aktualne dane';
|
|
}
|
|
if ($mode === 'update') {
|
|
return 'Scalenie';
|
|
}
|
|
if ($mode === 'location') {
|
|
return 'Przywracanie do lokalizacji';
|
|
}
|
|
return '-';
|
|
}
|
|
|
|
function restore_type_label(string $type): string
|
|
{
|
|
if ($type === 'mail') {
|
|
return 'Zawartość skrzynki pocztowej';
|
|
}
|
|
if ($type === 'web') {
|
|
return 'Pliki www';
|
|
}
|
|
return '-';
|
|
}
|
|
|
|
function display_restore_path(string $value): string
|
|
{
|
|
$trimmed = trim($value);
|
|
if ($trimmed === '') {
|
|
return '-';
|
|
}
|
|
if ($trimmed[0] !== '/') {
|
|
$trimmed = '/' . $trimmed;
|
|
}
|
|
return $trimmed;
|
|
}
|
|
|
|
function log_missing_include_explanation(string $log_text, string $restore_path_label = ''): string
|
|
{
|
|
if (stripos($log_text, 'W przywracanej kopii zapasowej nie odnaleziono wskazanej ścieżki') !== false) {
|
|
return '';
|
|
}
|
|
if (stripos($log_text, 'The specified restore path was not found in the backup') !== false) {
|
|
return '';
|
|
}
|
|
if (preg_match("/Include pattern '.*' never matched\\./i", $log_text)) {
|
|
$suffix = '';
|
|
if ($restore_path_label !== '' && $restore_path_label !== '-') {
|
|
$suffix = ' (' . $restore_path_label . ')';
|
|
}
|
|
return 'W przywracanej kopii zapasowej nie odnaleziono wskazanej ścieżki' . $suffix . '. Prosimy spróbować przywrócić dane z innego archiwum, w którym te pliki mogły się znajdować.';
|
|
}
|
|
return '';
|
|
}
|
|
|
|
function collect_jobs(string $job_base, string $log_dir): array
|
|
{
|
|
$rows = [];
|
|
$sources = [
|
|
['dir' => $job_base . '/pending', 'pattern' => '*.env', 'status' => 'pending'],
|
|
['dir' => $job_base . '/processing', 'pattern' => '*.env', 'status' => 'processing'],
|
|
['dir' => $job_base . '/done', 'pattern' => '*.ok', 'status' => 'done'],
|
|
['dir' => $job_base . '/done', 'pattern' => '*.env.ok', 'status' => 'done'],
|
|
['dir' => $job_base . '/done', 'pattern' => '*.fail', 'status' => 'failed'],
|
|
['dir' => $job_base . '/done', 'pattern' => '*.env.fail', 'status' => 'failed'],
|
|
['dir' => $job_base . '/done', 'pattern' => '*.cancel', 'status' => 'canceled'],
|
|
['dir' => $job_base . '/done', 'pattern' => '*.env.cancel', 'status' => 'canceled'],
|
|
];
|
|
|
|
foreach ($sources as $source) {
|
|
foreach (glob($source['dir'] . '/' . $source['pattern']) ?: [] as $path) {
|
|
$file = basename($path);
|
|
if (isset($rows[$file])) {
|
|
continue;
|
|
}
|
|
$vars = load_job_vars($path);
|
|
$job_id = job_id_from_file($file);
|
|
if ($job_id === '') {
|
|
continue;
|
|
}
|
|
$log_path = $log_dir . '/' . $job_id . '.log';
|
|
$base_status = $source['status'];
|
|
if ($base_status === 'processing' && is_readable($log_path)) {
|
|
$tail = normalize_log(read_log_tail($log_path, 20000));
|
|
$last = last_non_empty_line($tail);
|
|
$base_status = display_status($base_status, $last);
|
|
}
|
|
$status = normalize_admin_status($base_status);
|
|
$start_ts = is_numeric($vars['START_TS'] ?? '') ? (int)$vars['START_TS'] : (int)(@filemtime($path) ?: 0);
|
|
if ($start_ts <= 0) {
|
|
$start_ts = time();
|
|
}
|
|
|
|
$rows[$file] = [
|
|
'file' => $file,
|
|
'job_id' => $job_id,
|
|
'user' => (string)($vars['DA_USER'] ?? ''),
|
|
'start_ts' => $start_ts,
|
|
'start_text' => date('Y-m-d H:i:s', $start_ts),
|
|
'type' => (string)($vars['RESTORETYPE'] ?? ''),
|
|
'type_label' => restore_type_label((string)($vars['RESTORETYPE'] ?? '')),
|
|
'mode' => (string)($vars['RESTOREMODE'] ?? ''),
|
|
'mode_label' => restore_mode_label((string)($vars['RESTOREMODE'] ?? '')),
|
|
'status' => $status,
|
|
'status_label' => admin_status_label($status),
|
|
'status_sort' => admin_status_sort($status),
|
|
];
|
|
}
|
|
}
|
|
|
|
return array_values($rows);
|
|
}
|
|
|
|
function compare_rows(array $a, array $b, string $sort, string $dir): int
|
|
{
|
|
$cmp = 0;
|
|
switch ($sort) {
|
|
case 'user':
|
|
$cmp = strcasecmp((string)$a['user'], (string)$b['user']);
|
|
break;
|
|
case 'type':
|
|
$cmp = strcasecmp((string)$a['type_label'], (string)$b['type_label']);
|
|
break;
|
|
case 'mode':
|
|
$cmp = strcasecmp((string)$a['mode_label'], (string)$b['mode_label']);
|
|
break;
|
|
case 'status':
|
|
$cmp = ((int)$a['status_sort']) <=> ((int)$b['status_sort']);
|
|
break;
|
|
case 'start':
|
|
default:
|
|
$cmp = ((int)$a['start_ts']) <=> ((int)$b['start_ts']);
|
|
break;
|
|
}
|
|
|
|
if ($cmp === 0) {
|
|
$cmp = ((int)$a['start_ts']) <=> ((int)$b['start_ts']);
|
|
}
|
|
if ($cmp === 0) {
|
|
$cmp = strcmp((string)$a['job_id'], (string)$b['job_id']);
|
|
}
|
|
|
|
return $dir === 'asc' ? $cmp : -$cmp;
|
|
}
|
|
|
|
function build_admin_url(array $base, array $override = []): string
|
|
{
|
|
$query = array_merge($base, $override);
|
|
foreach ($query as $key => $value) {
|
|
if ($value === null || $value === '') {
|
|
unset($query[$key]);
|
|
}
|
|
}
|
|
$qs = http_build_query($query);
|
|
return '/CMD_PLUGINS_ADMIN/borg-restore/index.html' . ($qs !== '' ? ('?' . $qs) : '');
|
|
}
|
|
|
|
$params = read_params();
|
|
$action = (string)($params['action'] ?? '');
|
|
|
|
$username = getenv('USERNAME');
|
|
if ($username === false || $username === '') {
|
|
$username = getenv('LOGNAME') ?: getenv('USER') ?: 'admin';
|
|
}
|
|
|
|
$plugin_dir = realpath(__DIR__ . '/..') ?: '/usr/local/directadmin/plugins/borg-restore';
|
|
$settings_path = $plugin_dir . '/plugin-settings.conf';
|
|
$settings = load_settings($settings_path);
|
|
|
|
$admin_mode_enabled = parse_bool((string)($settings['ADMIN_MODE_ENABLE'] ?? 'TRUE'), true);
|
|
$debug_mode = parse_bool((string)($settings['DEBUG_MODE'] ?? 'FALSE'), false);
|
|
$bb_path = trim((string)($settings['BB_PATH'] ?? '/opt/bb'));
|
|
$borg_vars_file = trim((string)($settings['BORG_VARIABLES_FILE'] ?? '/opt/bb/bbvars.sh'));
|
|
$bbvars_path = $borg_vars_file !== '' ? $borg_vars_file : $bb_path;
|
|
if ($bbvars_path !== '' && substr($bbvars_path, -3) !== '.sh') {
|
|
$bbvars_path = rtrim($bbvars_path, '/') . '/bbvars.sh';
|
|
}
|
|
$bbvars = load_borg_vars($bbvars_path);
|
|
$admin_missing_passphrase = $debug_mode && is_readable($bbvars_path) && trim((string)($bbvars['BORG_PASSPHRASE'] ?? '')) === '';
|
|
$results_per_page = (int)trim((string)($settings['RESULTS_PER_PAGE'] ?? '10'));
|
|
if ($results_per_page <= 0) {
|
|
$results_per_page = 10;
|
|
}
|
|
if ($results_per_page > 200) {
|
|
$results_per_page = 200;
|
|
}
|
|
|
|
$skin = load_user_skin($username);
|
|
$css_file = $skin === 'evolution' ? 'evolution.css' : 'enhanced.css';
|
|
$inline_css = '';
|
|
$css_path = $plugin_dir . '/user/' . $css_file;
|
|
if (is_readable($css_path)) {
|
|
$inline_css = (string)file_get_contents($css_path);
|
|
}
|
|
|
|
$job_base = '/usr/local/directadmin/plugins/borg-restore/data/jobs';
|
|
$log_dir = '/usr/local/directadmin/plugins/borg-restore/data/logs';
|
|
|
|
$allowed_sorts = ['user', 'start', 'type', 'mode', 'status'];
|
|
$sort = strtolower((string)($params['sort'] ?? 'start'));
|
|
if (!in_array($sort, $allowed_sorts, true)) {
|
|
$sort = 'start';
|
|
}
|
|
$dir = strtolower((string)($params['dir'] ?? 'desc')) === 'asc' ? 'asc' : 'desc';
|
|
$page = (int)($params['page'] ?? 1);
|
|
if ($page < 1) {
|
|
$page = 1;
|
|
}
|
|
|
|
$base_link_params = ['sort' => $sort, 'dir' => $dir, 'page' => $page];
|
|
$jobs = [];
|
|
$total = 0;
|
|
$total_pages = 1;
|
|
$page_start = 0;
|
|
$page_end = 0;
|
|
$page_jobs = [];
|
|
|
|
if ($admin_mode_enabled) {
|
|
$jobs = collect_jobs($job_base, $log_dir);
|
|
usort($jobs, static function (array $a, array $b) use ($sort, $dir): int {
|
|
return compare_rows($a, $b, $sort, $dir);
|
|
});
|
|
|
|
$total = count($jobs);
|
|
$total_pages = max(1, (int)ceil($total / $results_per_page));
|
|
if ($page > $total_pages) {
|
|
$page = $total_pages;
|
|
$base_link_params['page'] = $page;
|
|
}
|
|
$offset = ($page - 1) * $results_per_page;
|
|
$page_jobs = array_slice($jobs, $offset, $results_per_page);
|
|
if ($total > 0) {
|
|
$page_start = $offset + 1;
|
|
$page_end = min($offset + count($page_jobs), $total);
|
|
}
|
|
}
|
|
|
|
$view_log = null;
|
|
if ($admin_mode_enabled && $action === 'view_log') {
|
|
$file = basename((string)($params['job'] ?? ''));
|
|
if ($file !== '' && preg_match('/^[-A-Za-z0-9_.]+$/', $file)) {
|
|
$job_id = job_id_from_file($file);
|
|
$job_file_path = find_job_file($job_base, $file, $job_id);
|
|
if ($job_file_path !== '') {
|
|
$vars = load_job_vars($job_file_path);
|
|
$log_path = $log_dir . '/' . $job_id . '.log';
|
|
$log_message = is_readable($log_path) ? normalize_log((string)file_get_contents($log_path)) : 'Log nie istnieje.';
|
|
$restore_path_label = display_restore_path((string)($vars['USERRESTOREPATH'] ?? ''));
|
|
$explanation = log_missing_include_explanation($log_message, $restore_path_label);
|
|
if ($explanation !== '') {
|
|
$log_message = rtrim($log_message) . "\n\n" . $explanation;
|
|
}
|
|
|
|
$base_status = get_job_status($job_id, $job_base);
|
|
$last_line = last_non_empty_line($log_message);
|
|
$progress = extract_progress($last_line);
|
|
$display = display_status($base_status, $last_line);
|
|
$status = normalize_admin_status($display);
|
|
if ($status === 'done') {
|
|
$progress_text = '100%';
|
|
} elseif ($status === 'failed') {
|
|
$progress_text = '0%';
|
|
} else {
|
|
$progress_text = $progress !== null ? ($progress . '%') : ($last_line !== '' ? $last_line : '-');
|
|
}
|
|
|
|
$start_ts = is_numeric($vars['START_TS'] ?? '') ? (int)$vars['START_TS'] : (int)(@filemtime($job_file_path) ?: 0);
|
|
if ($start_ts <= 0) {
|
|
$start_ts = time();
|
|
}
|
|
|
|
$view_log = [
|
|
'job' => $file,
|
|
'user' => (string)($vars['DA_USER'] ?? '-'),
|
|
'start' => date('Y-m-d H:i:s', $start_ts),
|
|
'type' => restore_type_label((string)($vars['RESTORETYPE'] ?? '')),
|
|
'mode' => restore_mode_label((string)($vars['RESTOREMODE'] ?? '')),
|
|
'path' => $restore_path_label,
|
|
'target' => display_restore_path((string)($vars['TARGETPATH'] ?? '')),
|
|
'status' => admin_status_label($status),
|
|
'progress' => $progress_text,
|
|
'content' => $log_message,
|
|
];
|
|
} else {
|
|
$view_log = [
|
|
'error' => 'Nie znaleziono wskazanego zadania.',
|
|
];
|
|
}
|
|
} else {
|
|
$view_log = [
|
|
'error' => 'Nieprawidłowy identyfikator zadania.',
|
|
];
|
|
}
|
|
}
|
|
|
|
$sort_labels = [
|
|
'user' => 'Nazwa użytkownika',
|
|
'start' => 'Data rozpoczęcia',
|
|
'type' => 'Rodzaj przywracanych danych',
|
|
'mode' => 'Tryb przywracania',
|
|
'status' => 'Status',
|
|
];
|
|
|
|
$sort_arrow = static function (string $field) use ($sort, $dir): string {
|
|
if ($field !== $sort) {
|
|
return '';
|
|
}
|
|
return $dir === 'asc' ? ' ▲' : ' ▼';
|
|
};
|
|
|
|
$sort_link = static function (string $field) use ($sort, $dir, $base_link_params): string {
|
|
$next_dir = ($sort === $field && $dir === 'asc') ? 'desc' : 'asc';
|
|
return build_admin_url($base_link_params, ['sort' => $field, 'dir' => $next_dir, 'page' => 1]);
|
|
};
|
|
|
|
$view_back_url = build_admin_url($base_link_params, ['action' => null, 'job' => null]);
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<title>BorgBackup - Przywracanie plików (Admin)</title>
|
|
<?php if ($inline_css !== '') { ?>
|
|
<style><?php echo $inline_css; ?></style>
|
|
<?php } ?>
|
|
<style>
|
|
.br-admin-subtitle { margin: 0 0 8px; color: var(--br-muted); }
|
|
.br-sort-link { color: inherit; text-decoration: none; }
|
|
.br-sort-link:hover { text-decoration: underline; }
|
|
.br-col-log { width: 130px; text-align: center; }
|
|
.br-pagination {
|
|
margin-top: 12px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
gap: 10px;
|
|
}
|
|
.br-pagination .br-page-meta { color: var(--br-muted); font-size: 13px; }
|
|
.br-pagination .br-page-actions { display: flex; gap: 8px; }
|
|
.br-log-content { max-height: 58vh; overflow: auto; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="br-container">
|
|
<div class="br-header">
|
|
<div class="br-title">BorgBackup - Przywracanie plików (Admin)</div>
|
|
</div>
|
|
|
|
<?php if (!$admin_mode_enabled) { ?>
|
|
<div class="br-card">
|
|
<div class="br-alert br-alert-warn">Poziom administratora został wyłączony</div>
|
|
</div>
|
|
<?php } else { ?>
|
|
|
|
<?php if ($view_log !== null) { ?>
|
|
<div class="br-card">
|
|
<div class="br-actions" style="justify-content:flex-start; margin-bottom: 10px;">
|
|
<a class="br-btn br-btn-ghost" href="<?php echo h($view_back_url); ?>">Powrót do zestawienia</a>
|
|
</div>
|
|
<?php if (!empty($view_log['error'])) { ?>
|
|
<div class="br-alert br-alert-error"><?php echo h((string)$view_log['error']); ?></div>
|
|
<?php } else { ?>
|
|
<h3 class="m-0" style="margin-bottom:10px;">Log zadania</h3>
|
|
<div class="br-log-meta">
|
|
<div><strong>Nazwa użytkownika:</strong> <?php echo h((string)$view_log['user']); ?></div>
|
|
<div><strong>Data rozpoczęcia:</strong> <?php echo h((string)$view_log['start']); ?></div>
|
|
<div><strong>Rodzaj przywracanych danych:</strong> <?php echo h((string)$view_log['type']); ?></div>
|
|
<div><strong>Tryb przywracania:</strong> <?php echo h((string)$view_log['mode']); ?></div>
|
|
<div><strong>Ścieżka do przywrócenia:</strong> <?php echo h((string)$view_log['path']); ?></div>
|
|
<div><strong>Lokalizacja docelowa:</strong> <?php echo h((string)$view_log['target']); ?></div>
|
|
</div>
|
|
<div class="br-status-line">Status: <strong><?php echo h((string)$view_log['status']); ?></strong></div>
|
|
<div class="br-status-line">Postęp: <strong><?php echo h((string)$view_log['progress']); ?></strong></div>
|
|
<pre class="br-log-content" id="log-content"><?php echo h((string)$view_log['content']); ?></pre>
|
|
<?php } ?>
|
|
</div>
|
|
<?php } ?>
|
|
|
|
<div class="br-card">
|
|
<h3 style="margin-bottom:6px;">Zestawienie operacji przywracania</h3>
|
|
<p class="br-admin-subtitle">Wyniki na stronę: <?php echo h((string)$results_per_page); ?></p>
|
|
<?php if ($admin_missing_passphrase) { ?>
|
|
<div class="br-alert br-alert-warn">
|
|
Brak zmiennej <strong>BORG_PASSPHRASE</strong> w pliku <?php echo h($bbvars_path); ?>.
|
|
</div>
|
|
<?php } ?>
|
|
|
|
<?php if (empty($jobs)) { ?>
|
|
<p class="br-muted">Brak operacji do wyświetlenia.</p>
|
|
<?php } else { ?>
|
|
<table class="br-table" border="1" cellpadding="6" cellspacing="0">
|
|
<thead>
|
|
<tr>
|
|
<?php foreach ($sort_labels as $field => $label) { ?>
|
|
<th>
|
|
<a class="br-sort-link" href="<?php echo h($sort_link($field)); ?>">
|
|
<?php echo h($label . $sort_arrow($field)); ?>
|
|
</a>
|
|
</th>
|
|
<?php } ?>
|
|
<th class="br-col-log">Logi</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($page_jobs as $job) { ?>
|
|
<tr>
|
|
<td><?php echo h((string)$job['user']); ?></td>
|
|
<td><?php echo h((string)$job['start_text']); ?></td>
|
|
<td><?php echo h((string)$job['type_label']); ?></td>
|
|
<td><?php echo h((string)$job['mode_label']); ?></td>
|
|
<td><?php echo h((string)$job['status_label']); ?></td>
|
|
<td class="br-col-log">
|
|
<a class="br-btn br-btn-ghost br-btn-small" href="<?php echo h(build_admin_url($base_link_params, ['action' => 'view_log', 'job' => (string)$job['file']])); ?>">Pokaż logi</a>
|
|
</td>
|
|
</tr>
|
|
<?php } ?>
|
|
</tbody>
|
|
</table>
|
|
|
|
<div class="br-pagination">
|
|
<div class="br-page-meta">
|
|
<?php echo h('Strona ' . $page . ' z ' . $total_pages . ' (wyniki ' . $page_start . '-' . $page_end . ' z ' . $total . ')'); ?>
|
|
</div>
|
|
<div class="br-page-actions">
|
|
<?php if ($page > 1) { ?>
|
|
<a class="br-btn br-btn-ghost br-btn-small" href="<?php echo h(build_admin_url($base_link_params, ['page' => $page - 1])); ?>">Poprzednia</a>
|
|
<?php } ?>
|
|
<?php if ($page < $total_pages) { ?>
|
|
<a class="br-btn br-btn-ghost br-btn-small" href="<?php echo h(build_admin_url($base_link_params, ['page' => $page + 1])); ?>">Następna</a>
|
|
<?php } ?>
|
|
</div>
|
|
</div>
|
|
<?php } ?>
|
|
</div>
|
|
<?php } ?>
|
|
</div>
|
|
</body>
|
|
</html>
|