feat: initial VPS monitoring platform

- FastAPI backend with auth, server CRUD, metrics collection
- Web dashboard with dark theme, server cards, SSH terminal (xterm.js)
- Telegram bot with inline keyboards, server management, reboot
- Telegram Mini App for mobile monitoring
- Background monitoring via asyncssh (CPU, RAM, Disk, Network, Uptime)
- Alert system with configurable thresholds
- WebSocket SSH terminal in browser
- One-command install script
- Systemd service configuration

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Андрей Бобырев
2026-05-20 05:47:28 +03:00
commit e6e88314d5
23 changed files with 2507 additions and 0 deletions

View File

@@ -0,0 +1,56 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>VPS Monitoring — Вход</title>
<link rel="stylesheet" href="/static/css/style.css">
</head>
<body class="login-page">
<div class="login-container">
<div class="login-card">
<div class="login-header">
<h1>🖥 VPS Monitoring</h1>
<p>Панель мониторинга серверов</p>
</div>
<form id="loginForm">
<div class="form-group">
<label for="username">Логин</label>
<input type="text" id="username" name="username" required autofocus>
</div>
<div class="form-group">
<label for="password">Пароль</label>
<input type="password" id="password" name="password" required>
</div>
<button type="submit" class="btn-primary">Войти</button>
<div id="error" class="error-msg" style="display:none"></div>
</form>
</div>
</div>
<script>
document.getElementById('loginForm').addEventListener('submit', async (e) => {
e.preventDefault();
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;
const errorEl = document.getElementById('error');
try {
const resp = await fetch('/api/login', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({username, password})
});
if (resp.ok) {
window.location.href = '/';
} else {
errorEl.textContent = 'Неверный логин или пароль';
errorEl.style.display = 'block';
}
} catch (err) {
errorEl.textContent = 'Ошибка подключения';
errorEl.style.display = 'block';
}
});
</script>
</body>
</html>