Modules: Dashboard, Serveurs, Campagnes, Planning, Specifiques, Settings, Users Stack: FastAPI + Jinja2 + HTMX + Alpine.js + TailwindCSS + PostgreSQL Features: Qualys sync, prereqs auto, planning annuel, server specifics, role-based access Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
97 lines
5.6 KiB
HTML
97 lines
5.6 KiB
HTML
{% extends 'base.html' %}
|
|
{% block title %}Serveurs specifiques{% endblock %}
|
|
{% block content %}
|
|
<div class="flex justify-between items-center mb-4">
|
|
<h2 class="text-xl font-bold text-cyber-accent">Serveurs specifiques <span class="text-sm text-gray-500">({{ entries|length }})</span></h2>
|
|
</div>
|
|
|
|
{% set msg = request.query_params.get('msg') %}
|
|
{% if msg %}
|
|
<div class="mb-4 p-2 rounded text-sm {% if msg in ('not_found','exists') %}bg-red-900/30 text-cyber-red{% else %}bg-green-900/30 text-cyber-green{% endif %}">
|
|
{% if msg == 'saved' %}Specificites sauvegardees.{% elif msg == 'added' %}Serveur ajoute.{% elif msg == 'not_found' %}Hostname non trouve en base.{% elif msg == 'exists' %}Ce serveur a deja des specificites.{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Filtres -->
|
|
<div class="flex gap-3 mb-4 items-center flex-wrap">
|
|
<a href="/specifics" class="btn-sm {% if not filter_type %}bg-cyber-accent text-black{% else %}bg-cyber-border text-gray-300{% endif %}">Tous ({{ entries|length }})</a>
|
|
{% for t in types_in_db %}
|
|
<a href="/specifics?app_type={{ t }}" class="btn-sm {% if filter_type == t %}bg-cyber-accent text-black{% else %}bg-cyber-border text-gray-300{% endif %}">{{ t }}</a>
|
|
{% endfor %}
|
|
<form method="GET" action="/specifics" class="flex gap-1 ml-auto">
|
|
<input type="text" name="search" value="{{ filter_search or '' }}" placeholder="Hostname..." class="text-xs py-1 px-2 w-36">
|
|
<button type="submit" class="btn-sm bg-cyber-border text-cyber-accent">Chercher</button>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- Table -->
|
|
<div class="card overflow-x-auto">
|
|
<table class="w-full table-cyber">
|
|
<thead><tr>
|
|
<th class="text-left p-2">Hostname</th>
|
|
<th class="p-2">Type</th>
|
|
<th class="p-2">Domaine</th>
|
|
<th class="p-2">Env</th>
|
|
<th class="p-2">Flags</th>
|
|
<th class="p-2">Ordre</th>
|
|
<th class="p-2">Auto-restart</th>
|
|
<th class="text-left p-2">Note</th>
|
|
<th class="p-2">Actions</th>
|
|
</tr></thead>
|
|
<tbody>
|
|
{% for e in entries %}
|
|
<tr id="row-{{ e.id }}">
|
|
<td class="p-2 font-mono text-sm text-cyber-accent">{{ e.hostname }}</td>
|
|
<td class="p-2 text-center"><span class="badge badge-blue">{{ e.app_type or '-' }}</span></td>
|
|
<td class="p-2 text-center text-xs">{{ e.domaine or '-' }}</td>
|
|
<td class="p-2 text-center"><span class="badge {% if e.environnement == 'Production' %}badge-green{% else %}badge-yellow{% endif %}">{{ (e.environnement or '-')[:6] }}</span></td>
|
|
<td class="p-2 text-center">
|
|
<div class="flex gap-0.5 justify-center flex-wrap">
|
|
{% if e.kernel_update_blocked %}<span class="badge badge-red" title="Kernel bloque">K</span>{% endif %}
|
|
{% if e.is_cluster %}<span class="badge badge-blue" title="Cluster">C</span>{% endif %}
|
|
{% if e.is_db %}<span class="badge badge-yellow" title="BDD">DB</span>{% endif %}
|
|
{% if e.is_middleware %}<span class="badge badge-gray" title="Middleware">MW</span>{% endif %}
|
|
{% if e.sentinel_disable_required %}<span class="badge badge-red" title="SentinelOne">S1</span>{% endif %}
|
|
{% if e.ip_forwarding_required %}<span class="badge badge-yellow" title="IP Forward">IP</span>{% endif %}
|
|
{% if e.rolling_update %}<span class="badge badge-blue" title="Rolling update">RU</span>{% endif %}
|
|
{% if e.needs_manual_step %}<span class="badge badge-yellow" title="Etape manuelle">M</span>{% endif %}
|
|
{% if e.extra_excludes %}<span class="badge badge-gray" title="Excludes: {{ e.extra_excludes }}">EX</span>{% endif %}
|
|
</div>
|
|
</td>
|
|
<td class="p-2 text-center text-xs">{% if e.reboot_order %}#{{ e.reboot_order }}{% if e.patch_order_group %} ({{ e.patch_order_group }}){% endif %}{% else %}-{% endif %}</td>
|
|
<td class="p-2 text-center"><span class="badge {% if e.auto_restart %}badge-green{% else %}badge-red{% endif %}">{{ 'Oui' if e.auto_restart else 'Non' }}</span></td>
|
|
<td class="p-2 text-xs text-gray-400" style="max-width:300px">{{ (e.note or '')[:80] }}{% if e.note and e.note|length > 80 %}...{% endif %}</td>
|
|
<td class="p-2 text-center">
|
|
<button class="btn-sm bg-cyber-border text-cyber-accent"
|
|
hx-get="/specifics/{{ e.id }}/edit" hx-target="#edit-panel" hx-swap="innerHTML"
|
|
onclick="document.getElementById('edit-panel').style.display='block'; window.scrollTo({top:0,behavior:'smooth'})">Edit</button>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- Panel edition -->
|
|
<div id="edit-panel" class="card mt-4 p-5" style="display:none"></div>
|
|
|
|
<!-- Ajouter -->
|
|
<div class="card p-4 mt-4">
|
|
<h4 class="text-sm font-bold text-cyber-accent mb-3">Ajouter un serveur specifique</h4>
|
|
<form method="POST" action="/specifics/add" class="flex gap-3 items-end">
|
|
<div>
|
|
<label class="text-xs text-gray-500">Hostname</label>
|
|
<input type="text" name="hostname" required placeholder="vpinfaweb1" class="text-xs py-1 px-2 w-44">
|
|
</div>
|
|
<div>
|
|
<label class="text-xs text-gray-500">Type application</label>
|
|
<select name="app_type" class="text-xs py-1 px-2">
|
|
<option value="">-</option>
|
|
{% for t in app_types %}<option value="{{ t }}">{{ t }}</option>{% endfor %}
|
|
</select>
|
|
</div>
|
|
<button type="submit" class="btn-primary px-4 py-1 text-sm">Ajouter</button>
|
|
</form>
|
|
</div>
|
|
{% endblock %}
|