getMessage());
}
$allowedAdminIps = array_values(array_filter(array_map(static fn($ip): string => trim((string)$ip), (array)($secrets['allowed_admin_ips'] ?? [])), static fn(string $ip): bool => $ip !== ''));
$clientIp = (string)($_SERVER['REMOTE_ADDR'] ?? '');
if ($allowedAdminIps !== [] && !in_array($clientIp, $allowedAdminIps, true)) {
http_response_code(403);
exit('Forbidden');
}
$basicUser = (string)($secrets['basic_auth_user'] ?? '');
$basicPass = (string)($secrets['basic_auth_pass'] ?? '');
if ($basicUser !== '' || $basicPass !== '') {
$authUser = (string)($_SERVER['PHP_AUTH_USER'] ?? '');
$authPass = (string)($_SERVER['PHP_AUTH_PW'] ?? '');
if (!hash_equals($basicUser, $authUser) || !hash_equals($basicPass, $authPass)) {
header('WWW-Authenticate: Basic realm="Admin"');
http_response_code(401);
exit('Unauthorized');
}
}
$tokenExpected = (string)($secrets['admin_token'] ?? '');
$tokenIncoming = (string)($_REQUEST['token'] ?? '');
if ($tokenExpected === '' || !hash_equals($tokenExpected, $tokenIncoming)) {
http_response_code(403);
exit('Forbidden');
}
$deployConfig = (array)($config['deploy'] ?? []);
$deployBranch = trim((string)($deployConfig['branch'] ?? 'main'));
if ($deployBranch === '') {
$deployBranch = 'main';
}
$deployScript = trim((string)($deployConfig['script'] ?? (__DIR__ . '/scripts/deploy.sh')));
if ($deployScript !== '' && !str_starts_with($deployScript, '/')) {
$deployScript = __DIR__ . '/' . ltrim($deployScript, '/');
}
$deployPhpBin = trim((string)($deployConfig['php_bin'] ?? 'php'));
if ($deployPhpBin === '') {
$deployPhpBin = 'php';
}
$requestAction = (string)($_REQUEST['action'] ?? '');
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
adminHandleGetAction($requestAction);
}
$message = '';
$errors = [];
$deployStatus = null;
$deployOutput = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = (string)($_POST['action'] ?? '');
$isAjax = (string)($_POST['ajax'] ?? '') === '1'
|| strtolower((string)($_SERVER['HTTP_X_REQUESTED_WITH'] ?? '')) === 'xmlhttprequest';
try {
$result = adminHandlePostAction($action, $isAjax, __DIR__, [
'branch' => $deployBranch,
'script' => $deployScript,
'php_bin' => $deployPhpBin,
]);
$message = (string)($result['message'] ?? '');
$deployStatus = $result['deploy_status'] ?? null;
$deployOutput = (string)($result['deploy_output'] ?? '');
if (isset($result['errors']) && is_array($result['errors']) && $result['errors'] !== []) {
$errors = array_merge($errors, $result['errors']);
}
} catch (Throwable $e) {
if ($isAjax) {
adminJsonResponse(['ok' => false, 'message' => $e->getMessage()], 400);
}
$errors[] = $e->getMessage();
}
}
$sections = sectionsAll();
$activeSectionId = (int)($_GET['section_id'] ?? ($_POST['section_id'] ?? ($sections[0]['id'] ?? 0)));
$activeSection = $activeSectionId > 0 ? sectionById($activeSectionId) : null;
if (!$activeSection && $sections !== []) {
$activeSectionId = (int)$sections[0]['id'];
$activeSection = sectionById($activeSectionId);
}
$photos = $activeSectionId > 0 ? photosBySection($activeSectionId) : [];
$commenters = commentersAll();
$welcomeText = settingGet('welcome_text', 'Добро пожаловать в галерею. Выберите раздел слева, чтобы посмотреть фотографии.');
$watermarkText = settingGet('watermark_text', 'photo.andr33v.ru');
$watermarkBrightness = max(5, min(100, (int)settingGet('watermark_brightness', '35')));
$watermarkAngle = max(-75, min(75, (int)settingGet('watermark_angle', '-28')));
$adminMode = (string)($_GET['mode'] ?? 'photos');
if ($adminMode === 'media') {
$adminMode = 'photos';
}
if (!in_array($adminMode, ['sections', 'photos', 'topics', 'commenters', 'comments', 'welcome'], true)) {
$adminMode = 'photos';
}
$previewVersion = (string)time();
$commentPhotoQuery = trim((string)($_GET['comment_photo'] ?? ($_POST['comment_photo'] ?? '')));
$commentUserQuery = trim((string)($_GET['comment_user'] ?? ($_POST['comment_user'] ?? '')));
$filteredComments = commentsSearch($commentPhotoQuery, $commentUserQuery, 200);
$photoCommentCounts = commentCountsByPhotoIds(array_map(static fn(array $p): int => (int)$p['id'], $photos));
$topics = [];
$topicRoots = [];
$photoTopicsMap = [];
$topicTree = [];
$topicsError = '';
try {
$topics = topicsAllForSelect();
foreach ($topics as $topic) {
if ((int)$topic['level'] === 0) {
$topicRoots[] = $topic;
}
}
$topicTree = buildTopicTree($topics);
$photoTopicsMap = photoTopicsMapByPhotoIds(array_map(static fn(array $p): int => (int)$p['id'], $photos));
} catch (Throwable $e) {
$topicsError = 'Тематики недоступны. Запусти миграции: php scripts/migrate.php';
}
function h(string $v): string { return htmlspecialchars($v, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); }
function assetUrl(string $path): string { $f=__DIR__ . '/' . ltrim($path,'/'); $v=is_file($f)?(string)filemtime($f):(string)time(); return $path . '?v=' . rawurlencode($v); }
?>
Админка
Админка
= h($message) ?>
= h($e) ?>
Настройки
Обновление проекта
Ветка: = h($deployBranch) ?>
= h($deployStateMessage) ?>
Локально: = h((string)($deployStatus['local_ref'] ?? '-')) ?> · origin/= h($deployBranch) ?>: = h((string)($deployStatus['remote_ref'] ?? '-')) ?> · behind: = (int)($deployStatus['behind'] ?? 0) ?> · ahead: = (int)($deployStatus['ahead'] ?? 0) ?>
= h($deployOutput) ?>
Список разделов
Разделов пока нет.
Фото: = (int)$section['photos_count'] ?>
Создать тематику
= h($topicsError) ?>
Список тематик
= h($topicsError) ?>
Тематик пока нет.
Уровень 1
Уровень 2 · внутри «= h((string)$root['name']) ?>»
Загрузка фото “до” в выбранный раздел
0): ?>
После загрузки имя (code_name) заполняется автоматически из имени файла — затем можно отредактировать.
Сначала выбери раздел слева.
Фото в разделе
= h($topicsError) ?>
| До | После | Поля | Действия |
|
|
= h((string)$topic['full_name']) ?>
|
|
Пользователи комментариев
Комментарии
Комментарии не найдены.
| Фото | Пользователь | Комментарий | Дата | |
| = h((string)$c['code_name']) ?> |
= h((string)($c['display_name'] ?? '—')) ?> |
= h((string)$c['comment_text']) ?> |
= h((string)$c['created_at']) ?> |
|