feat: Add offline monitoring and default settings
- Add offline_timeout and notify_on_offline fields to servers table - Create default_settings table for configurable defaults - Create offline_alerts table for tracking offline events - Add sendOfflineNotification/sendOnlineNotification methods - Create check_offline.php CRON script for periodic checks - Add admin page for default settings management - Add offline settings to server edit form - Apply default values when creating new servers Usage: Add to CRON - * * * * * php /var/www/mon/public/check_offline.php
This commit is contained in:
parent
6d3542232a
commit
d03aff714f
|
|
@ -0,0 +1,40 @@
|
||||||
|
-- 009_add_offline_settings.sql
|
||||||
|
-- Добавляет настройки для уведомлений при offline и дефолтные параметры
|
||||||
|
|
||||||
|
-- Поля для offline уведомлений в таблице servers
|
||||||
|
ALTER TABLE servers ADD COLUMN offline_timeout INT DEFAULT 300 COMMENT 'Таймаут в секундах до считания сервера offline (0 = выключено)';
|
||||||
|
ALTER TABLE servers ADD COLUMN notify_on_offline TINYINT(1) DEFAULT 1 COMMENT 'Уведомлять при offline';
|
||||||
|
ALTER TABLE servers ADD COLUMN last_offline_alert_at TIMESTAMP NULL COMMENT 'Время последнего алерта offline (анти-спам)';
|
||||||
|
|
||||||
|
-- Таблица дефолтных параметров
|
||||||
|
CREATE TABLE IF NOT EXISTS default_settings (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
setting_key VARCHAR(100) UNIQUE NOT NULL,
|
||||||
|
setting_value TEXT,
|
||||||
|
description TEXT,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- Вставляем дефолтные значения
|
||||||
|
INSERT INTO default_settings (setting_key, setting_value, description) VALUES
|
||||||
|
('offline_check_interval', '60', 'Интервал проверки offline в секундах'),
|
||||||
|
('default_offline_timeout', '300', 'Дефолтный таймаут offline в секундах'),
|
||||||
|
('default_warning_threshold', '70', 'Дефолтный warning threshold (%)'),
|
||||||
|
('default_critical_threshold', '90', 'Дефолтный critical threshold (%)'),
|
||||||
|
('default_duration', '0', 'Дефолтная длительность превышения порога в минутах')
|
||||||
|
ON DUPLICATE KEY UPDATE description = VALUES(description);
|
||||||
|
|
||||||
|
-- Таблица для алертов offline (отдельная от метрик)
|
||||||
|
CREATE TABLE IF NOT EXISTS offline_alerts (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
server_id INT NOT NULL,
|
||||||
|
triggered_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
resolved TINYINT(1) DEFAULT 0,
|
||||||
|
resolved_at TIMESTAMP NULL,
|
||||||
|
notified_at TIMESTAMP NULL,
|
||||||
|
FOREIGN KEY (server_id) REFERENCES servers(id) ON DELETE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- Индекс для быстрого поиска активных offline алертов
|
||||||
|
CREATE INDEX idx_offline_alerts_active ON offline_alerts (server_id, resolved);
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
<?php
|
||||||
|
// public/check_offline.php
|
||||||
|
// CRON скрипт для проверки offline серверов и отправки уведомлений
|
||||||
|
// Запуск: */1 * * * * php /var/www/mon/public/check_offline.php
|
||||||
|
|
||||||
|
date_default_timezone_set("Asia/Irkutsk");
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../vendor/autoload.php';
|
||||||
|
|
||||||
|
use Config\DatabaseConfig;
|
||||||
|
use App\Services\NotificationService;
|
||||||
|
|
||||||
|
echo "[" . date('Y-m-d H:i:s') . "] Checking offline servers...\n";
|
||||||
|
|
||||||
|
$pdo = DatabaseConfig::getInstance();
|
||||||
|
$notificationService = new NotificationService();
|
||||||
|
|
||||||
|
$stmt = $pdo->query("
|
||||||
|
SELECT
|
||||||
|
s.id,
|
||||||
|
s.name,
|
||||||
|
s.offline_timeout,
|
||||||
|
s.notify_on_offline,
|
||||||
|
s.last_offline_alert_at,
|
||||||
|
s.last_metrics_at,
|
||||||
|
TIMESTAMPDIFF(SECOND, s.last_metrics_at, NOW()) as seconds_since_update,
|
||||||
|
(SELECT COUNT(*) FROM offline_alerts oa WHERE oa.server_id = s.id AND oa.resolved = 0) as active_offline_alerts
|
||||||
|
FROM servers s
|
||||||
|
WHERE s.offline_timeout > 0
|
||||||
|
");
|
||||||
|
|
||||||
|
$servers = $stmt->fetchAll();
|
||||||
|
|
||||||
|
foreach ($servers as $server) {
|
||||||
|
$timeout = (int)$server['offline_timeout'];
|
||||||
|
$secondsSinceUpdate = (int)$server['seconds_since_update'];
|
||||||
|
$isOffline = $secondsSinceUpdate > $timeout;
|
||||||
|
$activeAlerts = (int)$server['active_offline_alerts'];
|
||||||
|
|
||||||
|
if ($isOffline) {
|
||||||
|
if ($activeAlerts === 0) {
|
||||||
|
$stmtInsert = $pdo->prepare("
|
||||||
|
INSERT INTO offline_alerts (server_id, triggered_at, resolved, notified_at)
|
||||||
|
VALUES (:server_id, NOW(), 0, NOW())
|
||||||
|
");
|
||||||
|
$stmtInsert->execute([':server_id' => $server['id']]);
|
||||||
|
|
||||||
|
if ($server['notify_on_offline']) {
|
||||||
|
$notificationService->sendOfflineNotification($server['name'], $secondsSinceUpdate);
|
||||||
|
echo "[OFFLINE] Server '{$server['name']}' is offline (no metrics for {$secondsSinceUpdate}s)\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmtUpdate = $pdo->prepare("
|
||||||
|
UPDATE servers SET last_offline_alert_at = NOW() WHERE id = :id
|
||||||
|
");
|
||||||
|
$stmtUpdate->execute([':id' => $server['id']]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($activeAlerts > 0) {
|
||||||
|
$stmtResolve = $pdo->prepare("
|
||||||
|
UPDATE offline_alerts
|
||||||
|
SET resolved = 1, resolved_at = NOW()
|
||||||
|
WHERE server_id = :server_id AND resolved = 0
|
||||||
|
");
|
||||||
|
$stmtResolve->execute([':server_id' => $server['id']]);
|
||||||
|
|
||||||
|
$notificationService->sendOnlineNotification($server['name']);
|
||||||
|
echo "[ONLINE] Server '{$server['name']}' is back online\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "[" . date('Y-m-d H:i:s') . "] Offline check completed. Processed {$stmt->rowCount()} servers.\n";
|
||||||
|
|
@ -210,6 +210,8 @@ $adminGroup = $app->group('/admin', function ($group) use ($adminController) {
|
||||||
$group->get('/notifications', [$adminController, 'notificationSettings']);
|
$group->get('/notifications', [$adminController, 'notificationSettings']);
|
||||||
$group->post("/notifications/save", [$adminController, "saveNotificationSettings"]);
|
$group->post("/notifications/save", [$adminController, "saveNotificationSettings"]);
|
||||||
$group->get("/notifications/test", [$adminController, "testNotification"]);
|
$group->get("/notifications/test", [$adminController, "testNotification"]);
|
||||||
|
$group->get('/defaults', [$adminController, 'defaultSettings']);
|
||||||
|
$group->post("/defaults/save", [$adminController, "saveDefaultSettings"]);
|
||||||
})->add($csrfMiddleware)->add(AuthMiddleware::class);
|
})->add($csrfMiddleware)->add(AuthMiddleware::class);
|
||||||
|
|
||||||
// API route for agents (public, no auth middleware, no csrf)
|
// API route for agents (public, no auth middleware, no csrf)
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ namespace App\Controllers;
|
||||||
|
|
||||||
use App\Models\Model;
|
use App\Models\Model;
|
||||||
use App\Services\NotificationService;
|
use App\Services\NotificationService;
|
||||||
|
use PDO;
|
||||||
use Psr\Http\Message\ResponseInterface as Response;
|
use Psr\Http\Message\ResponseInterface as Response;
|
||||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||||
use Slim\Views\Twig;
|
use Slim\Views\Twig;
|
||||||
|
|
@ -267,4 +268,60 @@ class AdminController extends Model
|
||||||
|
|
||||||
return $response->withHeader('Location', '/admin/notifications')->withStatus(302);
|
return $response->withHeader('Location', '/admin/notifications')->withStatus(302);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== ДЕФОЛТНЫЕ ПАРАМЕТРЫ ====================
|
||||||
|
|
||||||
|
public function defaultSettings(Request $request, Response $response, $args)
|
||||||
|
{
|
||||||
|
if ($_SESSION['role'] !== 'admin') {
|
||||||
|
return $response->withHeader('Location', '/')->withStatus(302);
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $this->pdo->query("SELECT * FROM default_settings ORDER BY id");
|
||||||
|
$settings = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
|
||||||
|
|
||||||
|
return $this->twig->render($response, 'admin/defaults.twig', [
|
||||||
|
'title' => 'Дефолтные параметры',
|
||||||
|
'settings' => $settings
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function saveDefaultSettings(Request $request, Response $response, $args)
|
||||||
|
{
|
||||||
|
if ($_SESSION['role'] !== 'admin') {
|
||||||
|
return $response->withHeader('Location', '/')->withStatus(302);
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = $request->getParsedBody();
|
||||||
|
|
||||||
|
$settingsToUpdate = [
|
||||||
|
'offline_check_interval' => (int)($data['offline_check_interval'] ?? 60),
|
||||||
|
'default_offline_timeout' => (int)($data['default_offline_timeout'] ?? 300),
|
||||||
|
'default_warning_threshold' => (float)($data['default_warning_threshold'] ?? 70),
|
||||||
|
'default_critical_threshold' => (float)($data['default_critical_threshold'] ?? 90),
|
||||||
|
'default_duration' => (int)($data['default_duration'] ?? 0),
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($settingsToUpdate as $key => $value) {
|
||||||
|
$stmt = $this->pdo->prepare("
|
||||||
|
INSERT INTO default_settings (setting_key, setting_value)
|
||||||
|
VALUES (:key, :value)
|
||||||
|
ON DUPLICATE KEY UPDATE setting_value = :value2
|
||||||
|
");
|
||||||
|
$stmt->execute([':key' => $key, ':value' => $value, ':value2' => $value]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$_SESSION['flash_message'] = 'Дефолтные параметры сохранены';
|
||||||
|
$_SESSION['flash_type'] = 'success';
|
||||||
|
|
||||||
|
return $response->withHeader('Location', '/admin/defaults')->withStatus(302);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDefaultSetting($key, $default = null)
|
||||||
|
{
|
||||||
|
$stmt = $this->pdo->prepare("SELECT setting_value FROM default_settings WHERE setting_key = :key");
|
||||||
|
$stmt->execute([':key' => $key]);
|
||||||
|
$result = $stmt->fetch();
|
||||||
|
return $result ? $result['setting_value'] : $default;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,23 +57,33 @@ class ServerController extends Model
|
||||||
{
|
{
|
||||||
$params = $request->getParsedBody();
|
$params = $request->getParsedBody();
|
||||||
|
|
||||||
|
// Получаем дефолтные значения
|
||||||
|
$stmtDefault = $this->pdo->query("SELECT setting_key, setting_value FROM default_settings");
|
||||||
|
$defaults = [];
|
||||||
|
while ($row = $stmtDefault->fetch()) {
|
||||||
|
$defaults[$row['setting_key']] = $row['setting_value'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$defaultOfflineTimeout = (int)($defaults['default_offline_timeout'] ?? 300);
|
||||||
|
|
||||||
// Генерируем уникальный токен
|
// Генерируем уникальный токен
|
||||||
$token = bin2hex(random_bytes(16)); // 32-символьный токен
|
$token = bin2hex(random_bytes(16));
|
||||||
|
|
||||||
$this->pdo->beginTransaction();
|
$this->pdo->beginTransaction();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Сохраняем сервер
|
// Сохраняем сервер с дефолтными значениями
|
||||||
$stmt = $this->pdo->prepare("
|
$stmt = $this->pdo->prepare("
|
||||||
INSERT INTO servers (name, address, group_id, description)
|
INSERT INTO servers (name, address, group_id, description, offline_timeout, notify_on_offline)
|
||||||
VALUES (:name, :address, :group_id, :description)
|
VALUES (:name, :address, :group_id, :description, :offline_timeout, 1)
|
||||||
");
|
");
|
||||||
|
|
||||||
$result = $stmt->execute([
|
$result = $stmt->execute([
|
||||||
':name' => $params['name'],
|
':name' => $params['name'],
|
||||||
':address' => $params['address'] ?? '',
|
':address' => $params['address'] ?? '',
|
||||||
':group_id' => $params['group_id'] ?? null,
|
':group_id' => $params['group_id'] ?? null,
|
||||||
':description' => $params['description'] ?? ''
|
':description' => $params['description'] ?? '',
|
||||||
|
':offline_timeout' => $defaultOfflineTimeout
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$serverId = $this->pdo->lastInsertId();
|
$serverId = $this->pdo->lastInsertId();
|
||||||
|
|
@ -110,7 +120,6 @@ class ServerController extends Model
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$this->pdo->rollback();
|
$this->pdo->rollback();
|
||||||
|
|
||||||
// TODO: Обработка ошибки
|
|
||||||
return $response->withHeader('Location', '/servers/create')->withStatus(302);
|
return $response->withHeader('Location', '/servers/create')->withStatus(302);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -153,7 +162,12 @@ class ServerController extends Model
|
||||||
|
|
||||||
$stmt = $this->pdo->prepare("
|
$stmt = $this->pdo->prepare("
|
||||||
UPDATE servers
|
UPDATE servers
|
||||||
SET name = :name, address = :address, group_id = :group_id, description = :description
|
SET name = :name,
|
||||||
|
address = :address,
|
||||||
|
group_id = :group_id,
|
||||||
|
description = :description,
|
||||||
|
offline_timeout = :offline_timeout,
|
||||||
|
notify_on_offline = :notify_on_offline
|
||||||
WHERE id = :id
|
WHERE id = :id
|
||||||
");
|
");
|
||||||
|
|
||||||
|
|
@ -162,13 +176,14 @@ class ServerController extends Model
|
||||||
':name' => $params['name'],
|
':name' => $params['name'],
|
||||||
':address' => $params['address'] ?? '',
|
':address' => $params['address'] ?? '',
|
||||||
':group_id' => $params['group_id'] ?? null,
|
':group_id' => $params['group_id'] ?? null,
|
||||||
':description' => $params['description'] ?? ''
|
':description' => $params['description'] ?? '',
|
||||||
|
':offline_timeout' => (int)($params['offline_timeout'] ?? 300),
|
||||||
|
':notify_on_offline' => isset($params['notify_on_offline']) ? 1 : 0
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($result) {
|
if ($result) {
|
||||||
return $response->withHeader('Location', '/servers')->withStatus(302);
|
return $response->withHeader('Location', '/servers')->withStatus(302);
|
||||||
} else {
|
} else {
|
||||||
// TODO: Обработка ошибки
|
|
||||||
return $response->withHeader('Location', '/servers/' . $id . '/edit')->withStatus(302);
|
return $response->withHeader('Location', '/servers/' . $id . '/edit')->withStatus(302);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,36 @@ class NotificationService
|
||||||
$this->sendNotification($subject, $message);
|
$this->sendNotification($subject, $message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Отправить уведомление о недоступности сервера (offline)
|
||||||
|
*/
|
||||||
|
public function sendOfflineNotification($serverName, $secondsSinceUpdate)
|
||||||
|
{
|
||||||
|
$minutes = round($secondsSinceUpdate / 60);
|
||||||
|
$subject = "🛑 Сервер недоступен: {$serverName}";
|
||||||
|
$message = "🖥 Сервер: {$serverName}\n";
|
||||||
|
$message .= "📊 Статус: OFFLINE\n";
|
||||||
|
$message .= "⏱️ Последние метрики: {$minutes} мин. назад\n";
|
||||||
|
$message .= "🕒 Время: " . date('d.m.Y H:i:s') . "\n";
|
||||||
|
$message .= "🔔 Требуется проверка!";
|
||||||
|
|
||||||
|
$this->sendNotification($subject, $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Отправить уведомление о восстановлении сервера (online)
|
||||||
|
*/
|
||||||
|
public function sendOnlineNotification($serverName)
|
||||||
|
{
|
||||||
|
$subject = "✅ Сервер восстановлен: {$serverName}";
|
||||||
|
$message = "🖥 Сервер: {$serverName}\n";
|
||||||
|
$message .= "📊 Статус: ONLINE\n";
|
||||||
|
$message .= "🕒 Время: " . date('d.m.Y H:i:s') . "\n";
|
||||||
|
$message .= "🟢 Метрики снова поступают";
|
||||||
|
|
||||||
|
$this->sendNotification($subject, $message);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Отправить уведомление о сервисе (остановка/запуск)
|
* Отправить уведомление о сервисе (остановка/запуск)
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,137 @@
|
||||||
|
{% extends "layout.twig" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row mb-4">
|
||||||
|
<div class="col-12">
|
||||||
|
<h2><i class="fas fa-sliders-h"></i> Дефолтные параметры</h2>
|
||||||
|
<p class="text-muted">Эти параметры будут использоваться по умолчанию для новых серверов и метрик</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if session.flash_message is defined and session.flash_message %}
|
||||||
|
<div class="alert alert-{{ session.flash_type == 'error' ? 'danger' : 'success' }} alert-dismissible fade show">
|
||||||
|
{{ session.flash_message|nl2br }}
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<form method="post" action="/admin/defaults/save">
|
||||||
|
<input type="hidden" name="csrf_name" value="{{ csrf_name }}">
|
||||||
|
<input type="hidden" name="csrf_value" value="{{ csrf_value }}">
|
||||||
|
|
||||||
|
<!-- Offline настройки -->
|
||||||
|
<div class="row mb-4">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="card h-100">
|
||||||
|
<div class="card-header bg-dark text-white">
|
||||||
|
<h5 class="mb-0"><i class="fas fa-wifi"></i> Мониторинг недоступности (Offline)</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="offline_check_interval" class="form-label">
|
||||||
|
<i class="fas fa-clock"></i> Интервал проверки offline
|
||||||
|
</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="number" class="form-control" id="offline_check_interval" name="offline_check_interval"
|
||||||
|
value="{{ settings.offline_check_interval|default(60) }}" min="30" max="3600">
|
||||||
|
<span class="input-group-text">секунд</span>
|
||||||
|
</div>
|
||||||
|
<div class="form-text">CRON будет проверять серверы каждые N секунд (минимум 30)</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="default_offline_timeout" class="form-label">
|
||||||
|
<i class="fas fa-hourglass-half"></i> Дефолтный таймаут offline
|
||||||
|
</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="number" class="form-control" id="default_offline_timeout" name="default_offline_timeout"
|
||||||
|
value="{{ settings.default_offline_timeout|default(300) }}" min="60" max="86400">
|
||||||
|
<span class="input-group-text">секунд</span>
|
||||||
|
</div>
|
||||||
|
<div class="form-text">Сервер считается offline если метрики не приходили N секунд (по умолчанию 300 = 5 мин)</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="card h-100">
|
||||||
|
<div class="card-header bg-warning text-dark">
|
||||||
|
<h5 class="mb-0"><i class="fas fa-chart-line"></i> Дефолтные пороги метрик</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="default_warning_threshold" class="form-label">
|
||||||
|
<i class="fas fa-exclamation-triangle text-warning"></i> Warning threshold
|
||||||
|
</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="number" class="form-control" id="default_warning_threshold" name="default_warning_threshold"
|
||||||
|
value="{{ settings.default_warning_threshold|default(70) }}" min="0" max="100" step="0.1">
|
||||||
|
<span class="input-group-text">%</span>
|
||||||
|
</div>
|
||||||
|
<div class="form-text">При превышении этого значения будет Warning</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="default_critical_threshold" class="form-label">
|
||||||
|
<i class="fas fa-radiation text-danger"></i> Critical threshold
|
||||||
|
</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="number" class="form-control" id="default_critical_threshold" name="default_critical_threshold"
|
||||||
|
value="{{ settings.default_critical_threshold|default(90) }}" min="0" max="100" step="0.1">
|
||||||
|
<span class="input-group-text">%</span>
|
||||||
|
</div>
|
||||||
|
<div class="form-text">При превышении этого значения будет Critical</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="default_duration" class="form-label">
|
||||||
|
<i class="fas fa-hourglass-start"></i> Длительность превышения
|
||||||
|
</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="number" class="form-control" id="default_duration" name="default_duration"
|
||||||
|
value="{{ settings.default_duration|default(0) }}" min="0" max="60">
|
||||||
|
<span class="input-group-text">минут</span>
|
||||||
|
</div>
|
||||||
|
<div class="form-text">Алерт отправится только если порог превышен N минут (0 = сразу)</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Информация о CRON -->
|
||||||
|
<div class="row mb-4">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="card border-primary">
|
||||||
|
<div class="card-header bg-primary text-white">
|
||||||
|
<h5 class="mb-0"><i class="fas fa-terminal"></i> Настройка CRON</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<p>Для автоматической проверки offline серверов добавьте в CRON:</p>
|
||||||
|
<div class="bg-dark text-white p-3 rounded mb-3">
|
||||||
|
<code>* * * * * php {{ app_path|default('/var/www/mon/public') }}/check_offline.php</code>
|
||||||
|
</div>
|
||||||
|
<p class="mb-0">
|
||||||
|
<i class="fas fa-info-circle text-info"></i>
|
||||||
|
Это запустит проверку каждую минуту. Интервал в настройках определяет как часто проверяется каждый сервер.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Кнопки -->
|
||||||
|
<div class="row mb-4">
|
||||||
|
<div class="col-12">
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
<i class="fas fa-save"></i> Сохранить настройки
|
||||||
|
</button>
|
||||||
|
<a href="/admin" class="btn btn-outline-secondary">
|
||||||
|
<i class="fas fa-arrow-left"></i> Назад в админку
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
@ -52,6 +52,8 @@
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li><a class="dropdown-item" href="/admin/users"><i class="fas fa-users"></i> Пользователи</a></li>
|
<li><a class="dropdown-item" href="/admin/users"><i class="fas fa-users"></i> Пользователи</a></li>
|
||||||
<li><a class="dropdown-item" href="/admin/notifications"><i class="fas fa-bell"></i> Уведомления</a></li>
|
<li><a class="dropdown-item" href="/admin/notifications"><i class="fas fa-bell"></i> Уведомления</a></li>
|
||||||
|
<li><hr class="dropdown-divider"></li>
|
||||||
|
<li><a class="dropdown-item" href="/admin/defaults"><i class="fas fa-sliders-h"></i> Дефолтные параметры</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,43 @@
|
||||||
<textarea class="form-control" id="description" name="description" rows="3">{{ server.description|default('') }}</textarea>
|
<textarea class="form-control" id="description" name="description" rows="3">{{ server.description|default('') }}</textarea>
|
||||||
<small class="form-text text-muted">Дополнительная информация о сервере</small>
|
<small class="form-text text-muted">Дополнительная информация о сервере</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<h5 class="mb-3"><i class="fas fa-wifi"></i> Настройки мониторинга недоступности</h5>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="offline_timeout" class="form-label">Таймаут offline</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="number" class="form-control" id="offline_timeout" name="offline_timeout"
|
||||||
|
value="{{ server.offline_timeout|default(300) }}" min="0" max="86400">
|
||||||
|
<span class="input-group-text">секунд</span>
|
||||||
|
</div>
|
||||||
|
<small class="form-text text-muted">Сервер будет считаться offline если метрики не приходили N секунд (0 = выключено)</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">Уведомления</label>
|
||||||
|
<div class="form-check form-switch mt-2">
|
||||||
|
<input class="form-check-input" type="checkbox" name="notify_on_offline" id="notify_on_offline"
|
||||||
|
value="1" {% if server.notify_on_offline|default(1) %}checked{% endif %}>
|
||||||
|
<label class="form-check-label" for="notify_on_offline">
|
||||||
|
Уведомлять при offline
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if server.last_offline_alert_at %}
|
||||||
|
<div class="alert alert-info small">
|
||||||
|
<i class="fas fa-clock"></i> Последний offline алерт: {{ server.last_offline_alert_at|date('d.m.Y H:i:s') }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
|
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
|
||||||
<a href="/servers" class="btn btn-secondary me-md-2">
|
<a href="/servers" class="btn btn-secondary me-md-2">
|
||||||
<i class="fas fa-arrow-left"></i> Назад
|
<i class="fas fa-arrow-left"></i> Назад
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue