Bruno Charest 88742892d0
Some checks failed
Tests / Backend Tests (Python) (3.10) (push) Has been cancelled
Tests / Backend Tests (Python) (3.11) (push) Has been cancelled
Tests / Backend Tests (Python) (3.12) (push) Has been cancelled
Tests / Frontend Tests (JS) (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / All Tests Passed (push) Has been cancelled
refactorisation pour correction de sécurité
2026-03-03 08:29:52 -05:00

326 lines
15 KiB
HTML

<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ safe_title }}</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Segoe UI', system-ui, sans-serif;
background: #0f0f1a;
color: #e5e7eb;
height: 100vh;
display: flex;
flex-direction: column;
}
.terminal-header {
background: #1a1a2e;
padding: 0.5rem 1rem;
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 1px solid #374151;
flex-shrink: 0;
}
.terminal-header .host-info {
display: flex;
align-items: center;
gap: 0.75rem;
}
.terminal-header .host-name { font-weight: 600; color: #fff; }
.terminal-header .host-ip { color: #9ca3af; font-size: 0.875rem; }
.terminal-header .status-badge {
padding: 0.25rem 0.75rem;
border-radius: 9999px;
font-size: 0.75rem;
font-weight: 500;
}
.terminal-header .status-badge.online {
background: rgba(34, 197, 94, 0.2);
color: #4ade80;
}
.terminal-header .session-timer {
display: flex;
align-items: center;
gap: 0.5rem;
color: #9ca3af;
font-size: 0.875rem;
}
.terminal-header .session-timer.warning { color: #fbbf24; }
.terminal-header .session-timer.critical { color: #ef4444; }
.terminal-header .actions { display: flex; gap: 0.5rem; }
.terminal-header .btn {
padding: 0.375rem 0.75rem;
border-radius: 0.375rem;
font-size: 0.875rem;
border: none;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.15s;
}
.terminal-header .btn-secondary { background: #374151; color: #e5e7eb; }
.terminal-header .btn-secondary:hover { background: #4b5563; }
.terminal-header .btn-secondary.active { background: #4f46e5; color: #fff; }
.terminal-header .btn-danger { background: #dc2626; color: #fff; }
.terminal-header .btn-danger:hover { background: #b91c1c; }
.terminal-container { flex: 1; position: relative; overflow: hidden; }
.terminal-container iframe { width: 100%; height: 100%; border: none; background: #000; }
.terminal-loading {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
background: #0f0f1a;
}
.terminal-loading .spinner {
width: 48px; height: 48px;
border: 3px solid #374151;
border-top-color: #3b82f6;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
.terminal-loading .loading-text { margin-top: 1rem; color: #9ca3af; }
.pwa-hint {
background: #1e3a5f;
color: #93c5fd;
padding: 0.5rem 1rem;
font-size: 0.75rem;
display: flex;
align-items: center;
justify-content: space-between;
}
.pwa-hint button {
background: none; border: none; color: #60a5fa; cursor: pointer; font-size: 0.75rem;
}
body.embed #pwaHint { display: none !important; }
body.embed .terminal-header { display: none !important; }
.hidden { display: none !important; }
/* History Panel */
.terminal-history-panel {
position: absolute; top: 0; left: 0; right: 0;
max-height: 0; overflow: hidden;
background: #1e1e2e; border-bottom: 1px solid #374151;
display: flex; flex-direction: column; z-index: 50;
box-shadow: 0 5px 25px rgba(0,0,0,0.5);
opacity: 0;
transition: max-height 0.3s ease, opacity 0.2s ease;
}
.terminal-history-panel.open { max-height: 350px; opacity: 1; }
.terminal-history-header {
padding: 0.75rem; background: #151520; border-bottom: 1px solid #374151; flex-shrink: 0;
}
.terminal-history-search {
position: relative; display: flex; align-items: center;
background: #0f0f1a; border: 1px solid #374151;
border-radius: 0.375rem; padding: 0.5rem 0.75rem; transition: border-color 0.15s;
}
.terminal-history-search:focus-within { border-color: #7c3aed; }
.terminal-history-search i { color: #6b7280; font-size: 0.875rem; margin-right: 0.5rem; }
.terminal-history-search input {
flex: 1; background: transparent; border: none; color: #e5e7eb;
font-size: 0.875rem; outline: none;
}
.terminal-history-search input::placeholder { color: #6b7280; }
.terminal-history-clear-search {
background: none; border: none; color: #6b7280; cursor: pointer;
padding: 0.25rem; font-size: 0.75rem; transition: color 0.15s;
}
.terminal-history-clear-search:hover { color: #e5e7eb; }
.terminal-history-filters {
display: flex; align-items: center; gap: 0.75rem; margin-top: 0.5rem;
}
.terminal-history-filter-select {
background: #0f0f1a; border: 1px solid #374151; border-radius: 0.375rem;
color: #e5e7eb; padding: 0.375rem 0.5rem; font-size: 0.75rem;
cursor: pointer; outline: none;
}
.terminal-history-filter-select:hover,
.terminal-history-filter-select:focus { border-color: #7c3aed; }
.terminal-history-scope {
display: flex; align-items: center; gap: 0.375rem;
font-size: 0.75rem; color: #9ca3af; cursor: pointer;
}
.terminal-history-scope input {
width: 0.875rem; height: 0.875rem; accent-color: #7c3aed; cursor: pointer;
}
.terminal-history-list {
flex: 1; overflow-y: auto; padding: 0.5rem;
scrollbar-width: thin; scrollbar-color: #374151 transparent;
}
.terminal-history-list::-webkit-scrollbar { width: 6px; }
.terminal-history-list::-webkit-scrollbar-track { background: transparent; }
.terminal-history-list::-webkit-scrollbar-thumb { background: #374151; border-radius: 3px; }
.terminal-history-item {
display: flex; align-items: center; padding: 0.5rem 0.75rem;
border-radius: 0.375rem; cursor: pointer; transition: background 0.15s;
gap: 0.75rem; border: 1px solid transparent;
}
.terminal-history-item:hover { background: rgba(124, 58, 237, 0.1); }
.terminal-history-item.selected {
background: rgba(124, 58, 237, 0.2); border-color: rgba(124, 58, 237, 0.4);
}
.terminal-history-cmd { flex: 1; min-width: 0; }
.terminal-history-cmd code {
font-family: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace;
font-size: 0.8125rem; color: #a5b4fc;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: block;
}
.terminal-history-cmd code mark {
background: rgba(251, 191, 36, 0.3); color: #fbbf24;
padding: 0 0.125rem; border-radius: 2px;
}
.terminal-history-meta { display: flex; align-items: center; gap: 0.5rem; flex-shrink: 0; }
.terminal-history-time { font-size: 0.6875rem; color: #6b7280; }
.terminal-history-count {
font-size: 0.625rem; color: #7c3aed;
background: rgba(124, 58, 237, 0.2); padding: 0.125rem 0.375rem;
border-radius: 9999px; font-weight: 500;
}
.terminal-history-actions-inline {
display: flex; gap: 0.25rem; opacity: 0; transition: opacity 0.15s;
}
.terminal-history-item:hover .terminal-history-actions-inline,
.terminal-history-item.selected .terminal-history-actions-inline { opacity: 1; }
.terminal-history-action {
background: rgba(55, 65, 81, 0.5); border: none; color: #9ca3af;
padding: 0.375rem; border-radius: 0.25rem; cursor: pointer;
display: flex; align-items: center; justify-content: center;
font-size: 0.75rem; transition: background 0.15s, color 0.15s, transform 0.1s;
}
.terminal-history-action:hover {
background: rgba(124, 58, 237, 0.3); color: #fff; transform: scale(1.1);
}
.terminal-history-action-execute:hover {
background: rgba(34, 197, 94, 0.3); color: #4ade80;
}
.terminal-history-empty {
display: flex; flex-direction: column; align-items: center;
justify-content: center; padding: 2rem; color: #6b7280; gap: 0.5rem; text-align: center;
}
.terminal-history-empty i { font-size: 2rem; opacity: 0.5; }
.terminal-history-loading {
display: flex; align-items: center; justify-content: center;
padding: 2rem; color: #9ca3af; gap: 0.5rem;
}
.terminal-history-footer {
padding: 0.5rem 1rem; background: #151520;
border-top: 1px solid #374151; flex-shrink: 0;
}
.terminal-history-hint {
font-size: 0.6875rem; color: #6b7280;
display: flex; align-items: center; gap: 0.25rem; flex-wrap: wrap;
}
.terminal-history-hint kbd {
background: #374151; color: #e5e7eb; padding: 0.125rem 0.375rem;
border-radius: 0.25rem; font-family: inherit; font-size: 0.625rem; border: 1px solid #4b5563;
}
</style>
</head>
<body class="{{ 'embed' if embed_mode else '' }}">
<div class="pwa-hint" id="pwaHint">
<span>💡 Pour une expérience sans barre d'outils : installez en PWA ou utilisez <code>chrome --app=URL</code></span>
<button onclick="dismissPwaHint()">✕ Fermer</button>
</div>
<div class="terminal-header">
<div class="host-info">
<span class="host-name">{{ safe_host_name }}</span>
<span class="host-ip">{{ safe_host_ip }}</span>
<span class="status-badge online">Connecté</span>
</div>
<div class="session-timer" id="sessionTimer">
<i class="fas fa-clock"></i>
<span id="timerDisplay">{{ timer_display }}</span>
</div>
<div class="actions">
<button class="btn btn-secondary" onclick="toggleHistory()" id="btnHistory" title="Historique (Ctrl+R)">
<i class="fas fa-history"></i> Historique
</button>
<button class="btn btn-secondary" onclick="copySSHCommand()" title="Copier la commande SSH">
<i class="fas fa-copy"></i> SSH
</button>
<button class="btn btn-secondary" onclick="reconnect()" title="Reconnecter">
<i class="fas fa-redo"></i> Reconnecter
</button>
<button class="btn btn-secondary" onclick="goToDashboard()" title="Retour au Dashboard">
<i class="fas fa-arrow-left"></i> Dashboard
</button>
<button class="btn btn-danger" onclick="closeSession()" title="Fermer la session">
<i class="fas fa-times"></i> Fermer
</button>
</div>
</div>
<div class="terminal-container">
<div class="terminal-loading" id="terminalLoading">
<div style="text-align: center;">
<div class="spinner"></div>
<div class="loading-text">Connexion au terminal...</div>
<div style="margin-top:0.75rem; font-size:0.85rem; color:#9ca3af;">
session={{ session_id_short }}… · ttyd_port={{ ttyd_port }} · Debug: Ctrl+Shift+D
</div>
</div>
</div>
<noscript>
<div style="position:absolute; top:14px; right:14px; z-index:9999; background:rgba(239,68,68,0.18); border:1px solid rgba(239,68,68,0.6); color:#fff; padding:10px 12px; border-radius:10px; font-size:12px; max-width:520px;">
JavaScript est désactivé: le terminal web ne peut pas se charger.
</div>
</noscript>
{% if debug_panel_html %}
{{ debug_panel_html | safe }}
{% endif %}
<iframe
id="terminalFrame"
src="about:blank"
allow="clipboard-read; clipboard-write; clipboard-write-text"
></iframe>
<div class="terminal-history-panel" id="terminalHistoryPanel" style="display: none;">
<div class="terminal-history-header">
<div class="terminal-history-search">
<i class="fas fa-search"></i>
<input type="text"
id="terminalHistorySearch"
placeholder="Rechercher... (Ctrl+R)"
oninput="searchHistory(this.value)"
onkeydown="handleHistoryKeydown(event)"
autocomplete="off">
<button class="terminal-history-clear-search" onclick="clearHistorySearch()" title="Effacer">
<i class="fas fa-times"></i>
</button>
</div>
<div class="terminal-history-filters">
<select id="terminalHistoryTimeFilter" onchange="setHistoryTimeFilter(this.value)" class="terminal-history-filter-select">
<option value="all">Tout</option>
<option value="today">Aujourd'hui</option>
<option value="week">7 jours</option>
<option value="month">30 jours</option>
</select>
<label class="terminal-history-scope">
<input type="checkbox" id="terminalHistoryAllHosts" onchange="toggleHistoryScope()">
<span>Tous hôtes</span>
</label>
</div>
</div>
<div class="terminal-history-list" id="terminalHistoryList">
<div class="terminal-history-loading">
<i class="fas fa-spinner fa-spin"></i> Chargement...
</div>
</div>
<div class="terminal-history-footer">
<span class="terminal-history-hint">
<kbd></kbd><kbd></kbd> naviguer · <kbd>Enter</kbd> insérer · <kbd>Esc</kbd> fermer
</span>
</div>
</div>
</div>
{{ script_block | safe }}
</body>
</html>