init
This commit is contained in:
107
web/templates/alerts.html
Normal file
107
web/templates/alerts.html
Normal file
@@ -0,0 +1,107 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Alerts - A/D Infrastructure Control{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<h1>Security Alerts <i class="bi bi-arrow-clockwise refresh-btn" id="refreshAlerts"></i></h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5>Send Test Alert</h5>
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" id="testMessage" placeholder="Enter test message">
|
||||
<button class="btn btn-primary" onclick="sendTestAlert()">
|
||||
<i class="bi bi-send"></i> Send to Telegram
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5>Alert History</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="list-group" id="alertsList">
|
||||
<div class="text-center py-3">Loading...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script>
|
||||
function loadAlerts() {
|
||||
$.get('/api/alerts?limit=100', function (data) {
|
||||
if (data && data.length > 0) {
|
||||
let html = '';
|
||||
data.forEach(alert => {
|
||||
let badgeClass = alert.severity === 'critical' ? 'danger' : 'warning';
|
||||
let notifiedBadge = alert.notified
|
||||
? '<span class="badge bg-success">Notified</span>'
|
||||
: '<span class="badge bg-secondary">Pending</span>';
|
||||
|
||||
html += `
|
||||
<div class="list-group-item">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div class="flex-grow-1">
|
||||
<div class="mb-2">
|
||||
<span class="badge bg-${badgeClass}">${alert.severity.toUpperCase()}</span>
|
||||
${notifiedBadge}
|
||||
<span class="badge bg-info">${alert.alert_type}</span>
|
||||
<small class="text-muted ms-2">${new Date(alert.created_at).toLocaleString()}</small>
|
||||
</div>
|
||||
<p class="mb-0">${alert.message}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
$('#alertsList').html(html);
|
||||
} else {
|
||||
$('#alertsList').html('<div class="text-center py-3 text-muted">No alerts</div>');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function sendTestAlert() {
|
||||
const message = $('#testMessage').val();
|
||||
if (!message) {
|
||||
alert('Please enter a message');
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: '/api/telegram/send',
|
||||
method: 'POST',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify({ message: message }),
|
||||
success: function (data) {
|
||||
alert('Message sent to Telegram!');
|
||||
$('#testMessage').val('');
|
||||
},
|
||||
error: function (xhr) {
|
||||
alert(`Failed to send message: ${xhr.responseJSON?.detail || 'Unknown error'}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
loadAlerts();
|
||||
$('#refreshAlerts').click(loadAlerts);
|
||||
setInterval(loadAlerts, 10000); // Auto-refresh every 10 seconds
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
169
web/templates/attacks.html
Normal file
169
web/templates/attacks.html
Normal file
@@ -0,0 +1,169 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Attacks - A/D Infrastructure Control{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<h1>Attacks Monitor <i class="bi bi-arrow-clockwise refresh-btn" id="refreshAttacks"></i></h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-12">
|
||||
<div class="btn-group" role="group">
|
||||
<button type="button" class="btn btn-outline-primary active" id="filterAll">All Attacks</button>
|
||||
<button type="button" class="btn btn-outline-success" id="filterOur">Our Attacks</button>
|
||||
<button type="button" class="btn btn-outline-danger" id="filterAgainstUs">Attacks to Us</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5>Attacks by Service</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Service</th>
|
||||
<th>Total</th>
|
||||
<th>Our Attacks</th>
|
||||
<th>Against Us</th>
|
||||
<th>Points Gained</th>
|
||||
<th>Points Lost</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="serviceStatsTable">
|
||||
<tr>
|
||||
<td colspan="6" class="text-center">Loading...</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5>Recent Attacks</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Time</th>
|
||||
<th>Attacker</th>
|
||||
<th>Victim</th>
|
||||
<th>Service</th>
|
||||
<th>Points</th>
|
||||
<th>Type</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="attacksTable">
|
||||
<tr>
|
||||
<td colspan="6" class="text-center">Loading...</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script>
|
||||
let currentFilter = 'all';
|
||||
|
||||
function loadServiceStats() {
|
||||
$.get('/api/attacks/by-service', function (data) {
|
||||
if (data && data.length > 0) {
|
||||
let html = '';
|
||||
data.forEach(stat => {
|
||||
html += `
|
||||
<tr>
|
||||
<td><strong>${stat.service_name}</strong></td>
|
||||
<td>${stat.total_attacks}</td>
|
||||
<td><span class="text-success">${stat.our_attacks}</span></td>
|
||||
<td><span class="text-danger">${stat.attacks_to_us}</span></td>
|
||||
<td><span class="text-success">+${stat.points_gained.toFixed(2)}</span></td>
|
||||
<td><span class="text-danger">-${stat.points_lost.toFixed(2)}</span></td>
|
||||
</tr>
|
||||
`;
|
||||
});
|
||||
$('#serviceStatsTable').html(html);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loadAttacks() {
|
||||
let url = '/api/attacks?limit=100';
|
||||
if (currentFilter === 'our') {
|
||||
url += '&our_attacks=true';
|
||||
} else if (currentFilter === 'against') {
|
||||
url += '&attacks_to_us=true';
|
||||
}
|
||||
|
||||
$.get(url, function (data) {
|
||||
if (data && data.length > 0) {
|
||||
let html = '';
|
||||
data.forEach(attack => {
|
||||
let typeLabel = '';
|
||||
if (attack.is_our_attack) {
|
||||
typeLabel = '<span class="badge bg-success">Our Attack</span>';
|
||||
} else if (attack.is_attack_to_us) {
|
||||
typeLabel = '<span class="badge bg-danger">Against Us</span>';
|
||||
}
|
||||
|
||||
html += `
|
||||
<tr>
|
||||
<td>${new Date(attack.timestamp).toLocaleString()}</td>
|
||||
<td>Team ${attack.attacker_team_id}</td>
|
||||
<td>Team ${attack.victim_team_id}</td>
|
||||
<td>${attack.service_name}</td>
|
||||
<td>${attack.points ? attack.points.toFixed(2) : '-'}</td>
|
||||
<td>${typeLabel}</td>
|
||||
</tr>
|
||||
`;
|
||||
});
|
||||
$('#attacksTable').html(html);
|
||||
} else {
|
||||
$('#attacksTable').html('<tr><td colspan="6" class="text-center text-muted">No attacks</td></tr>');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function setFilter(filter) {
|
||||
currentFilter = filter;
|
||||
$('.btn-group button').removeClass('active');
|
||||
$(`#filter${filter.charAt(0).toUpperCase() + filter.slice(1)}`).addClass('active');
|
||||
loadAttacks();
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
loadServiceStats();
|
||||
loadAttacks();
|
||||
$('#refreshAttacks').click(function () {
|
||||
loadServiceStats();
|
||||
loadAttacks();
|
||||
});
|
||||
|
||||
$('#filterAll').click(() => setFilter('all'));
|
||||
$('#filterOur').click(() => setFilter('our'));
|
||||
$('#filterAgainstUs').click(() => setFilter('against'));
|
||||
|
||||
setInterval(loadAttacks, 5000); // Auto-refresh every 5 seconds
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
92
web/templates/base.html
Normal file
92
web/templates/base.html
Normal file
@@ -0,0 +1,92 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}A/D Infrastructure Control{% endblock %}</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css">
|
||||
<style>
|
||||
body {
|
||||
padding-top: 60px;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
border-left: 4px solid #007bff;
|
||||
}
|
||||
|
||||
.stat-card.danger {
|
||||
border-left-color: #dc3545;
|
||||
}
|
||||
|
||||
.stat-card.success {
|
||||
border-left-color: #28a745;
|
||||
}
|
||||
|
||||
.stat-card.warning {
|
||||
border-left-color: #ffc107;
|
||||
}
|
||||
|
||||
.service-running {
|
||||
color: #28a745;
|
||||
}
|
||||
|
||||
.service-stopped {
|
||||
color: #dc3545;
|
||||
}
|
||||
|
||||
.refresh-btn {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
{% block extra_css %}{% endblock %}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark fixed-top">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="/">
|
||||
<i class="bi bi-shield-check"></i> A/D Control
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/"><i class="bi bi-speedometer2"></i> Dashboard</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/services"><i class="bi bi-hdd-stack"></i> Services</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/attacks"><i class="bi bi-bullseye"></i> Attacks</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/alerts"><i class="bi bi-exclamation-triangle"></i> Alerts</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/logout"><i class="bi bi-box-arrow-right"></i> Logout</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container-fluid">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
|
||||
{% block extra_js %}{% endblock %}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
137
web/templates/index.html
Normal file
137
web/templates/index.html
Normal file
@@ -0,0 +1,137 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Dashboard - A/D Infrastructure Control{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<h1>Dashboard <i class="bi bi-arrow-clockwise refresh-btn" id="refreshDashboard"></i></h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-3">
|
||||
<div class="card stat-card">
|
||||
<div class="card-body">
|
||||
<h6 class="text-muted">Total Services</h6>
|
||||
<h2 id="totalServices">-</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card stat-card success">
|
||||
<div class="card-body">
|
||||
<h6 class="text-muted">Our Attacks</h6>
|
||||
<h2 id="ourAttacks">-</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card stat-card danger">
|
||||
<div class="card-body">
|
||||
<h6 class="text-muted">Attacks to Us</h6>
|
||||
<h2 id="attacksToUs">-</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card stat-card warning">
|
||||
<div class="card-body">
|
||||
<h6 class="text-muted">Critical Alerts</h6>
|
||||
<h2 id="criticalAlerts">-</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h5>Services Status</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="servicesStatus" class="list-group">
|
||||
<div class="text-center py-3">Loading...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h5>Recent Alerts</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="recentAlerts" class="list-group">
|
||||
<div class="text-center py-3">Loading...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script>
|
||||
function loadDashboard() {
|
||||
$.get('/api/dashboard', function (data) {
|
||||
// Update stats
|
||||
$('#totalServices').text(data.services ? data.services.length : 0);
|
||||
$('#ourAttacks').text(data.scoreboard?.attacks_by_us || 0);
|
||||
$('#attacksToUs').text(data.scoreboard?.attacks_to_us || 0);
|
||||
$('#criticalAlerts').text(data.scoreboard?.critical_alerts_5min || 0);
|
||||
|
||||
// Update services status
|
||||
if (data.services && data.services.length > 0) {
|
||||
let html = '';
|
||||
data.services.forEach(service => {
|
||||
let statusClass = service.status === 'running' ? 'service-running' : 'service-stopped';
|
||||
let icon = service.status === 'running' ? 'check-circle-fill' : 'x-circle-fill';
|
||||
html += `
|
||||
<div class="list-group-item">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span>${service.name}</span>
|
||||
<span class="${statusClass}">
|
||||
<i class="bi bi-${icon}"></i> ${service.status}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
$('#servicesStatus').html(html);
|
||||
}
|
||||
});
|
||||
|
||||
// Load recent alerts
|
||||
$.get('/api/alerts?limit=5', function (data) {
|
||||
if (data && data.length > 0) {
|
||||
let html = '';
|
||||
data.forEach(alert => {
|
||||
let badgeClass = alert.severity === 'critical' ? 'danger' : 'warning';
|
||||
html += `
|
||||
<div class="list-group-item">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div>
|
||||
<span class="badge bg-${badgeClass}">${alert.severity}</span>
|
||||
<small class="text-muted ms-2">${new Date(alert.created_at).toLocaleString()}</small>
|
||||
<p class="mb-0 mt-1">${alert.message}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
$('#recentAlerts').html(html);
|
||||
} else {
|
||||
$('#recentAlerts').html('<div class="text-center py-3 text-muted">No alerts</div>');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
loadDashboard();
|
||||
$('#refreshDashboard').click(loadDashboard);
|
||||
setInterval(loadDashboard, 10000); // Auto-refresh every 10 seconds
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
47
web/templates/login.html
Normal file
47
web/templates/login.html
Normal file
@@ -0,0 +1,47 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Login - A/D Infrastructure Control</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.login-card {
|
||||
max-width: 400px;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="login-card">
|
||||
<div class="card shadow-lg">
|
||||
<div class="card-body p-5">
|
||||
<h2 class="text-center mb-4">
|
||||
<i class="bi bi-shield-check"></i> A/D Control
|
||||
</h2>
|
||||
{% if error %}
|
||||
<div class="alert alert-danger">{{ error }}</div>
|
||||
{% endif %}
|
||||
<form method="POST">
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">Password</label>
|
||||
<input type="password" class="form-control" id="password" name="password" required autofocus>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary w-100">Login</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
130
web/templates/services.html
Normal file
130
web/templates/services.html
Normal file
@@ -0,0 +1,130 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Services - A/D Infrastructure Control{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<h1>Services <i class="bi bi-arrow-clockwise refresh-btn" id="refreshServices"></i></h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Path</th>
|
||||
<th>Status</th>
|
||||
<th>Last Updated</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="servicesTable">
|
||||
<tr>
|
||||
<td colspan="5" class="text-center">Loading...</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Logs Modal -->
|
||||
<div class="modal fade" id="logsModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Service Logs</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<pre id="logsContent"
|
||||
style="max-height: 500px; overflow-y: auto; background: #f8f9fa; padding: 15px;"></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script>
|
||||
function loadServices() {
|
||||
$.get('/api/services', function (data) {
|
||||
if (data && data.length > 0) {
|
||||
let html = '';
|
||||
data.forEach(service => {
|
||||
let statusBadge = service.status === 'running'
|
||||
? '<span class="badge bg-success">Running</span>'
|
||||
: '<span class="badge bg-secondary">Stopped</span>';
|
||||
|
||||
html += `
|
||||
<tr>
|
||||
<td><strong>${service.name}</strong></td>
|
||||
<td><code>${service.path}</code></td>
|
||||
<td>${statusBadge}</td>
|
||||
<td>${new Date(service.last_updated).toLocaleString()}</td>
|
||||
<td>
|
||||
<div class="btn-group btn-group-sm">
|
||||
<button class="btn btn-success" onclick="serviceAction(${service.id}, 'start')">
|
||||
<i class="bi bi-play-fill"></i>
|
||||
</button>
|
||||
<button class="btn btn-warning" onclick="serviceAction(${service.id}, 'restart')">
|
||||
<i class="bi bi-arrow-clockwise"></i>
|
||||
</button>
|
||||
<button class="btn btn-danger" onclick="serviceAction(${service.id}, 'stop')">
|
||||
<i class="bi bi-stop-fill"></i>
|
||||
</button>
|
||||
<button class="btn btn-info" onclick="viewLogs(${service.id})">
|
||||
<i class="bi bi-file-text"></i>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
});
|
||||
$('#servicesTable').html(html);
|
||||
} else {
|
||||
$('#servicesTable').html('<tr><td colspan="5" class="text-center text-muted">No services registered</td></tr>');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function serviceAction(serviceId, action) {
|
||||
if (!confirm(`Are you sure you want to ${action} this service?`)) return;
|
||||
|
||||
$.ajax({
|
||||
url: `/api/services/${serviceId}/action`,
|
||||
method: 'POST',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify({ action: action }),
|
||||
success: function (data) {
|
||||
alert(`Service ${action} successful!`);
|
||||
loadServices();
|
||||
},
|
||||
error: function (xhr) {
|
||||
alert(`Failed to ${action} service: ${xhr.responseJSON?.detail || 'Unknown error'}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function viewLogs(serviceId) {
|
||||
$.get(`/api/services/${serviceId}/logs?lines=200`, function (data) {
|
||||
$('#logsContent').text(data.logs || 'No logs available');
|
||||
new bootstrap.Modal(document.getElementById('logsModal')).show();
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
loadServices();
|
||||
$('#refreshServices').click(loadServices);
|
||||
setInterval(loadServices, 15000); // Auto-refresh every 15 seconds
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user