Files
umbrix/update-server/api.php
Umbrix Developer 76a374950f feat: mobile-like window size and always-visible stats
- Changed window size to mobile phone format (400x800)
- Removed width condition for ActiveProxyFooter - now always visible
- Added run-umbrix.sh launch script with icon copying
- Stats cards now display on all screen sizes
2026-01-17 13:09:20 +03:00

194 lines
6.0 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* Umbrix Update Server API для Desktop Платформ
*
* Сервер обновлений для Windows, macOS и Linux версий приложения.
* Android использует Google Play Store для обновлений.
*
* Версия: 1.1
*
* Использование:
* GET /api/latest - получить последнюю версию для Desktop
* GET /api/latest?include_prerelease=true - включая бета-версии
*/
// === НАСТРОЙКИ ===
// Разрешенные домены для CORS (замените на свой домен)
$allowed_origins = [
'https://umbrix.net',
'https://www.umbrix.net',
'https://api.umbrix.net'
];
// Путь к файлу с информацией о версии
$version_file = __DIR__ . '/latest.json';
// Включить логирование запросов
$enable_logging = true;
$log_file = __DIR__ . '/logs/access.log';
// Ограничение запросов (Rate Limiting)
$rate_limit_enabled = true;
$max_requests_per_minute = 10;
// === КОНЕЦ НАСТРОЕК ===
// Установка заголовков
header('Content-Type: application/json; charset=utf-8');
header('X-Powered-By: Umbrix Update Server');
// CORS - разрешаем запросы с приложения
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
if (in_array($origin, $allowed_origins)) {
header("Access-Control-Allow-Origin: $origin");
} else {
header('Access-Control-Allow-Origin: *'); // Разрешаем все для начала
}
header('Access-Control-Allow-Methods: GET, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type');
// Обработка preflight запроса
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(200);
exit();
}
// Только GET запросы
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
sendError(405, 'Method Not Allowed');
}
// Rate Limiting
if ($rate_limit_enabled && !checkRateLimit($max_requests_per_minute)) {
sendError(429, 'Too Many Requests', 'Превышен лимит запросов. Попробуйте позже.');
}
// Логирование
if ($enable_logging) {
logRequest($log_file);
}
// Проверка существования файла с версией
if (!file_exists($version_file)) {
sendError(500, 'Internal Server Error', 'Файл с версией не найден');
}
// Чтение и парсинг JSON
$version_data = file_get_contents($version_file);
$version_info = json_decode($version_data, true);
if (json_last_error() !== JSON_ERROR_NONE) {
sendError(500, 'Internal Server Error', 'Ошибка парсинга JSON: ' . json_last_error_msg());
}
// Проверка обязательных полей
$required_fields = ['version', 'build_number', 'download_url', 'published_at'];
foreach ($required_fields as $field) {
if (!isset($version_info[$field])) {
sendError(500, 'Internal Server Error', "Отсутствует обязательное поле: $field");
}
}
// Фильтр пре-релизов
$include_prerelease = isset($_GET['include_prerelease']) && $_GET['include_prerelease'] === 'true';
if (!$include_prerelease && isset($version_info['is_prerelease']) && $version_info['is_prerelease'] === true) {
// Вернуть пустой ответ, если пре-релиз не запрошен
sendError(404, 'Not Found', 'Стабильная версия не найдена');
}
// Добавление дополнительной информации
$version_info['server_time'] = date('c');
$version_info['api_version'] = '1.0';
// Успешный ответ
http_response_code(200);
echo json_encode($version_info, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
exit();
// === ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ===
/**
* Отправка ошибки в формате JSON
*/
function sendError($code, $message, $details = null) {
http_response_code($code);
$error = [
'error' => $message,
'code' => $code,
'timestamp' => date('c')
];
if ($details) {
$error['details'] = $details;
}
echo json_encode($error, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
exit();
}
/**
* Проверка лимита запросов
*/
function checkRateLimit($max_requests) {
$ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
$cache_file = sys_get_temp_dir() . '/umbrix_ratelimit_' . md5($ip);
$current_time = time();
$window_start = $current_time - 60; // Окно в 1 минуту
$requests = [];
if (file_exists($cache_file)) {
$requests = json_decode(file_get_contents($cache_file), true) ?? [];
}
// Удаляем старые запросы
$requests = array_filter($requests, function($timestamp) use ($window_start) {
return $timestamp > $window_start;
});
// Проверяем лимит
if (count($requests) >= $max_requests) {
return false;
}
// Добавляем текущий запрос
$requests[] = $current_time;
file_put_contents($cache_file, json_encode($requests));
return true;
}
/**
* Логирование запроса
*/
function logRequest($log_file) {
$log_dir = dirname($log_file);
if (!is_dir($log_dir)) {
mkdir($log_dir, 0755, true);
}
$ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
$user_agent = $_SERVER['HTTP_USER_AGENT'] ?? 'unknown';
$request_uri = $_SERVER['REQUEST_URI'] ?? 'unknown';
$timestamp = date('Y-m-d H:i:s');
// Извлекаем версию приложения из User-Agent (если есть)
$app_version = 'unknown';
if (preg_match('/Umbrix\/([0-9.]+)/', $user_agent, $matches)) {
$app_version = $matches[1];
}
$log_entry = sprintf(
"[%s] IP: %s | App Version: %s | URI: %s | User-Agent: %s\n",
$timestamp,
$ip,
$app_version,
$request_uri,
$user_agent
);
file_put_contents($log_file, $log_entry, FILE_APPEND | LOCK_EX);
}
?>