v1.9.1: Admin UI - Section Reseau Tailscale (statut, IP, hostname, logs)

- admin.html: nouveau card 'Reseau Tailscale' avec statut connecte/deconnecte,
  IP tailnet, hostname, serveur Headscale et bouton Actualiser
- admin.html: bloc deroulant pour consulter les 50 dernieres lignes du log
  bootstrap (logs/tailscale_bootstrap.log)
- launcher.php: nouvelles actions get_tailscale_info (status + IP + hostname
  via sudo tailscale ip/status) et get_tailscale_log (tail -n 50)

Complete la v1.9.0 avec la visibilite UI necessaire pour valider/diagnostiquer
la connexion Tailscale sur chaque capteur sans avoir a passer en SSH.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
PaulVua
2026-05-19 12:11:03 +02:00
parent b008b486ae
commit 654c05f548
4 changed files with 192 additions and 1 deletions

View File

@@ -339,6 +339,45 @@
</div>
<!-- TAILSCALE SECTION -->
<div class="row mb-3">
<div class="col-lg-8 col-12">
<h4 class="mt-4">Réseau Tailscale</h4>
<div id="tailscale-card" class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<span class="fw-bold">État de la connexion au tailnet AirCarto</span>
<button type="button" class="btn btn-sm btn-outline-primary" onclick="refreshTailscaleInfo()">
<svg width="14" height="14" fill="currentColor" class="bi bi-arrow-clockwise" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2v1z"/>
<path d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466z"/>
</svg>
Actualiser
</button>
</div>
<div class="card-body">
<dl class="row mb-2">
<dt class="col-sm-3 text-muted">Statut</dt>
<dd class="col-sm-9"><span id="tailscaleStatus" class="badge bg-secondary">Chargement…</span></dd>
<dt class="col-sm-3 text-muted">IP tailnet</dt>
<dd class="col-sm-9"><code id="tailscaleIp"></code></dd>
<dt class="col-sm-3 text-muted">Hostname</dt>
<dd class="col-sm-9"><code id="tailscaleHostname"></code></dd>
<dt class="col-sm-3 text-muted">Serveur</dt>
<dd class="col-sm-9"><code id="tailscaleLoginServer"></code></dd>
</dl>
<div id="tailscaleMessage" class="alert alert-warning small mb-2" style="display: none;"></div>
<details>
<summary class="text-muted small" style="cursor: pointer;">Logs bootstrap (cliquer pour ouvrir)</summary>
<pre id="tailscaleLog" class="mb-0 mt-2" style="max-height: 250px; overflow-y: auto; font-size: 0.8rem; background-color: #f8f9fa; padding: 0.75rem; border-radius: 0.375rem;">Chargement…</pre>
</details>
</div>
</div>
</div>
</div>
<!-- SYSTEMD SERVICES SECTION -->
<div class="row mb-3">
<div class="col-lg-8 col-12">
@@ -702,6 +741,9 @@ window.onload = function() {
// Load firmware version
loadFirmwareVersion();
// Load Tailscale connection info
refreshTailscaleInfo();
} //end window.onload
@@ -2225,6 +2267,73 @@ function loadFirmwareVersion() {
});
}
function refreshTailscaleInfo() {
$.ajax({
url: 'launcher.php?type=get_tailscale_info',
dataType: 'json',
method: 'GET',
cache: false,
success: function(response) {
const statusBadge = document.getElementById('tailscaleStatus');
const ipEl = document.getElementById('tailscaleIp');
const hostEl = document.getElementById('tailscaleHostname');
const serverEl = document.getElementById('tailscaleLoginServer');
const msgEl = document.getElementById('tailscaleMessage');
serverEl.textContent = response.login_server || '—';
if (!response.installed) {
statusBadge.textContent = 'Non installé';
statusBadge.className = 'badge bg-secondary';
ipEl.textContent = '—';
hostEl.textContent = '—';
msgEl.style.display = 'block';
msgEl.textContent = response.message || 'Tailscale non installé.';
} else if (response.connected) {
statusBadge.textContent = '✓ Connecté';
statusBadge.className = 'badge bg-success';
ipEl.textContent = response.ip || '—';
hostEl.textContent = response.hostname || '—';
msgEl.style.display = 'none';
} else {
statusBadge.textContent = '✗ Déconnecté';
statusBadge.className = 'badge bg-danger';
ipEl.textContent = '—';
hostEl.textContent = '—';
msgEl.style.display = 'block';
msgEl.textContent = "Tailscale est installé mais n'est pas connecté au tailnet. Vérifier le log bootstrap ci-dessous ou relancer un Update firmware.";
}
refreshTailscaleLog();
},
error: function() {
const statusBadge = document.getElementById('tailscaleStatus');
statusBadge.textContent = 'Erreur';
statusBadge.className = 'badge bg-warning';
}
});
}
function refreshTailscaleLog() {
$.ajax({
url: 'launcher.php?type=get_tailscale_log',
dataType: 'json',
method: 'GET',
cache: false,
success: function(response) {
const logEl = document.getElementById('tailscaleLog');
if (response.success && response.log) {
logEl.textContent = response.log;
} else {
logEl.textContent = response.message || '(log vide)';
}
},
error: function() {
document.getElementById('tailscaleLog').textContent = '(erreur de chargement du log)';
}
});
}
function showChangelogModal() {
const modal = new bootstrap.Modal(document.getElementById('changelogModal'));
modal.show();