File size: 3.42Kb
<?php
/**
* CMS: LaiCMS (v1.0 Edition 2026)
* File: admin/save_handler.php
* Description: Высоконадёжный AJAX-обработчик с защитой ядра.
*/
header('Content-Type: application/json');
require_once '../system/db.php';
require_once '../system/functions.php';
// 1. Усиленная проверка доступа
if (!isAdmin()) {
http_response_code(403);
exit(json_encode(['status' => 'error', 'message' => 'Доступ заблокирован']));
}
$input = json_decode(file_get_contents('php://input'), true);
// 2. Валидация CSRF и входных данных
if (empty($input['csrf_token']) || !check_csrf($input['csrf_token'])) {
exit(json_encode(['status' => 'error', 'message' => 'Ошибка безопасности (CSRF)']));
}
$base_dir = dirname(__DIR__);
$allowed_files = [
'style.css' => $base_dir . '/theme/style.css',
'app.js' => $base_dir . '/theme/app.js',
'footer.php' => $base_dir . '/system/footer.php',
'header.php' => $base_dir . '/system/header.php'
];
$file_key = $input['file_key'] ?? '';
$new_content = $input['content'] ?? '';
if (!array_key_exists($file_key, $allowed_files)) {
exit(json_encode(['status' => 'error', 'message' => 'Файл не входит в белый список']));
}
$path = $allowed_files[$file_key];
// 3. ПРОВЕРКА СИНТАКСИСА (Для PHP файлов)
if (pathinfo($path, PATHINFO_EXTENSION) === 'php') {
$tmp_check = tempnam(sys_get_temp_dir(), 'lincms_check');
file_put_contents($tmp_check, $new_content);
// Выполняем PHP Lint (проверка на ошибки)
exec("php -l " . escapeshellarg($tmp_check), $output, $return_var);
unlink($tmp_check);
if ($return_var !== 0) {
exit(json_encode([
'status' => 'error',
'message' => 'Ошибка в коде: ' . ($output[0] ?? 'Синтаксическая ошибка PHP. Сохранение отменено во избежание поломки сайта.')
]));
}
}
// 4. АТОМАРНОЕ СОХРАНЕНИЕ С БЭКАПОМ
try {
// Создаем бэкап с меткой времени
if (file_exists($path)) {
copy($path, $path . '.v' . time() . '.bak');
// Очистка старых бэкапов (оставляем только последние 3)
$backups = glob($path . '.*.bak');
if (count($backups) > 3) {
array_map('unlink', array_slice($backups, 0, count($backups) - 3));
}
}
// Запись через временный файл (Atomic Write)
$tmp_file = $path . '.tmp';
if (file_put_contents($tmp_file, $new_content) === false) {
throw new Exception("Ошибка записи во временный буфер.");
}
if (!rename($tmp_file, $path)) {
throw new Exception("Не удалось обновить основной файл. Проверьте права CHMOD.");
}
// Логируем успешное действие
write_log("Файл $file_key успешно обновлен", "success");
echo json_encode([
'status' => 'success',
'time' => date('H:i:s'),
'size' => strlen($new_content) . ' bytes'
]);
} catch (Exception $e) {
if (isset($tmp_file) && file_exists($tmp_file)) unlink($tmp_file);
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
}