131 lines
5.1 KiB
Twig
131 lines
5.1 KiB
Twig
{% extends 'base.html.twig' %}
|
|
{% block title %}Servers - ServerControl{% endblock %}
|
|
|
|
{% block body %}
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<h1 class="h3">Servers</h1>
|
|
{% if is_granted('ROLE_ADMIN') %}
|
|
<a href="{{ path('app_server_new') }}" class="btn btn-primary">
|
|
<i class="bi bi-plus-lg me-1"></i>Add Server
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="card mb-4">
|
|
<div class="card-body">
|
|
<form method="get" class="row g-3">
|
|
<div class="col-md-6">
|
|
<input type="text" class="form-control" name="search" placeholder="Search by name or IP..."
|
|
value="{{ search ?? '' }}">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<select class="form-select" name="status">
|
|
<option value="">All statuses</option>
|
|
<option value="online" {% if status == 'online' %}selected{% endif %}>Online</option>
|
|
<option value="offline" {% if status == 'offline' %}selected{% endif %}>Offline</option>
|
|
<option value="unknown" {% if status == 'unknown' %}selected{% endif %}>Unknown</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<button class="btn btn-outline-secondary w-100">Filter</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
{% if servers|length == 0 %}
|
|
<div class="alert alert-info">Aucun serveur trouver.</div>
|
|
{% else %}
|
|
<div class="table-responsive card">
|
|
<table class="table table-hover mb-0">
|
|
<thead class="table-dark">
|
|
<tr>
|
|
<th>Name</th>
|
|
<th>IP Address</th>
|
|
<th>SSH Port</th>
|
|
<th>Status</th>
|
|
<th>Owner</th>
|
|
<th>Added</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for server in servers %}
|
|
<tr>
|
|
<td><strong>{{ server.name }}</strong></td>
|
|
<td><code>{{ server.ipAddress }}</code></td>
|
|
<td>{{ server.sshPort }}</td>
|
|
<td>
|
|
<span class="status-{{ server.lastStatus }}" id="status-{{ server.id }}">
|
|
<i class="bi bi-circle-fill me-1" style="font-size:.5rem;"></i>
|
|
{{ server.lastStatus|capitalize }}
|
|
</span>
|
|
</td>
|
|
<td>{{ server.owner.pseudo }}</td>
|
|
<td>{{ server.createdAt|date('d/m/Y') }}</td>
|
|
<td>
|
|
<a href="{{ path('app_server_show', {id: server.id}) }}" class="btn btn-sm btn-outline-primary">
|
|
<i class="bi bi-eye"></i>
|
|
</a>
|
|
{% if is_granted('ROLE_ADMIN') %}
|
|
<a href="{{ path('app_server_edit', {id: server.id}) }}" class="btn btn-sm btn-outline-secondary">
|
|
<i class="bi bi-pencil"></i>
|
|
</a>
|
|
<button class="btn btn-sm btn-outline-danger" data-bs-toggle="modal"
|
|
data-bs-target="#deleteModal{{ server.id }}">
|
|
<i class="bi bi-trash"></i>
|
|
</button>
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="mt-3 d-flex justify-content-center">
|
|
{{ knp_pagination_render(servers) }}
|
|
</div>
|
|
|
|
{% for server in servers %}
|
|
<div class="modal fade" id="deleteModal{{ server.id }}" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Delete Server</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
Êtes-vous sûre de vouloir suprimmer <strong>{{ server.name }}</strong> ?
|
|
Tout les services et journaux associé seront supprimés.
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<form method="post" action="{{ path('app_server_delete', {id: server.id}) }}">
|
|
<input type="hidden" name="_token" value="{{ csrf_token('delete_server_' ~ server.id) }}">
|
|
<button class="btn btn-danger">Delete</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
{% endif %}
|
|
{% endblock %}
|
|
|
|
{% block javascripts %}
|
|
<script>
|
|
function refreshStatuses() {
|
|
document.querySelectorAll('[id^="status-"]').forEach(el => {
|
|
const id = el.id.replace('status-', '');
|
|
fetch(`/server/${id}/status`)
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
el.className = `status-${data.status}`;
|
|
el.innerHTML = `<i class="bi bi-circle-fill me-1" style="font-size:.5rem;"></i>${data.status.charAt(0).toUpperCase() + data.status.slice(1)}`;
|
|
}).catch(() => {});
|
|
});
|
|
}
|
|
setInterval(refreshStatuses, 30000);
|
|
</script>
|
|
{% endblock %}
|