657 lines
53 KiB
HTML
657 lines
53 KiB
HTML
<!-- file: crates/ksp-app-config-desk/frontend/main.html -->
|
|
<!-- version: 18 -->
|
|
<!DOCTYPE html>
|
|
<html lang="fr">
|
|
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta content="width=device-width, initial-scale=1.0" name="viewport">
|
|
<link rel="icon" href="imgs/logo.png">
|
|
<link rel="stylesheet" href="sass/main.scss">
|
|
<title>Config Desk — Vue d'ensemble</title>
|
|
</head>
|
|
|
|
<body>
|
|
<header class="app-header">
|
|
<nav class="navbar h-100 py-0 bg-light text-dark">
|
|
<div class="container-fluid px-4 flex-nowrap">
|
|
<div class="navbar-brand d-flex align-items-center me-4 flex-nowrap">
|
|
<img alt="KSP" src="imgs/logo.png" class="app-logo">
|
|
<span class="ps-2 fs-4 fw-semibold text-primary text-nowrap">Config Desk</span>
|
|
<span class="mx-2 fs-5 text-body-secondary" aria-hidden="true">—</span>
|
|
<span id="headerViewTitle" class="fs-5 text-body text-nowrap">Vue d'ensemble</span>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
</header>
|
|
|
|
<main class="app-main container-fluid p-0">
|
|
<div class="row g-0 h-100 flex-nowrap">
|
|
<aside class="col-auto app-sidebar border-end bg-body-tertiary">
|
|
<div class="app-sidebar-scroll h-100" data-simplebar>
|
|
<nav class="nav nav-pills flex-column gap-1 p-3" aria-label="Navigation Config Desk">
|
|
<button class="nav-link text-start active" type="button" data-view="overview"><i class="fa-solid fa-gauge-high me-2" aria-hidden="true"></i>Vue d'ensemble</button>
|
|
<button class="nav-link text-start" type="button" data-view="documents"><i class="fa-solid fa-file-code me-2" aria-hidden="true"></i>Documents</button>
|
|
<button class="nav-link text-start" type="button" data-view="profiles"><i class="fa-solid fa-layer-group me-2" aria-hidden="true"></i>Profils</button>
|
|
<button class="nav-link text-start" type="button" data-view="environment"><i class="fa-solid fa-terminal me-2" aria-hidden="true"></i>Environnement / .env</button>
|
|
<button class="nav-link text-start" type="button" data-view="logging"><i class="fa-solid fa-list-ul me-2" aria-hidden="true"></i>Logging</button>
|
|
</nav>
|
|
</div>
|
|
</aside>
|
|
<section class="col app-content app-scrollable h-100" data-simplebar>
|
|
<div class="container-fluid px-4 py-4">
|
|
<div class="card app-shell-card mx-auto shadow-sm border-0">
|
|
<div class="card-body p-4">
|
|
<h1 id="viewTitle" class="h3 mb-2">Vue d'ensemble</h1>
|
|
<p id="viewDescription" class="text-body-secondary mb-4">État sûr du bootstrap Config Desk et du runtime Logging actuellement actif.</p>
|
|
|
|
<section id="overviewPanel" aria-label="Vue d'ensemble">
|
|
<dl class="row mb-4 app-overview-list">
|
|
<dt class="col-sm-5">Version application</dt>
|
|
<dd id="overviewVersion" class="col-sm-7 text-sm-end">—</dd>
|
|
<dt class="col-sm-5">Descripteurs Config enregistrés</dt>
|
|
<dd id="overviewDocuments" class="col-sm-7 text-sm-end">—</dd>
|
|
<dt class="col-sm-5">Profil Logging actif</dt>
|
|
<dd id="overviewLoggingProfile" class="col-sm-7 text-sm-end">—</dd>
|
|
<dt class="col-sm-5">Génération Logging</dt>
|
|
<dd id="overviewLoggingGeneration" class="col-sm-7 text-sm-end">—</dd>
|
|
<dt class="col-sm-5 mb-0">Fallback Logging actif</dt>
|
|
<dd id="overviewLoggingFallback" class="col-sm-7 text-sm-end mb-0">—</dd>
|
|
</dl>
|
|
</section>
|
|
|
|
|
|
<section id="documentsPanel" hidden aria-label="Documents Config">
|
|
<div class="d-flex flex-wrap gap-2 align-items-center justify-content-between mb-3">
|
|
<div>
|
|
<h2 class="h5 mb-1">Documents enregistrés</h2>
|
|
<p class="text-body-secondary small mb-0">Le registre, la validation et la persistence restent exclusivement sous contrôle de ksp-config-lib.</p>
|
|
</div>
|
|
<button id="refreshDocuments" class="btn btn-outline-primary btn-sm" type="button">
|
|
<i class="fa-solid fa-rotate me-1" aria-hidden="true"></i>
|
|
Rafraîchir
|
|
</button>
|
|
</div>
|
|
|
|
<div class="table-responsive mb-4">
|
|
<table id="configDocumentsTable" class="table table-striped table-hover align-middle w-100">
|
|
<thead>
|
|
<tr>
|
|
<th scope="col">file_id</th>
|
|
<th scope="col">Fichier</th>
|
|
<th scope="col">Schema</th>
|
|
<th scope="col">État</th>
|
|
<th scope="col">Étape</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<section id="documentDetailPanel" hidden class="border rounded p-3 mb-4 bg-body-tertiary" aria-label="Détail du document Config">
|
|
<div class="d-flex flex-wrap gap-2 justify-content-between align-items-start mb-3">
|
|
<div>
|
|
<h2 class="h5 mb-1">Détail du document</h2>
|
|
<span id="documentDetailStatus" class="badge text-bg-secondary">—</span>
|
|
</div>
|
|
<div class="d-flex gap-2">
|
|
<button id="openSpecializedEditor" class="btn btn-outline-primary btn-sm" type="button" hidden>Ouvrir l'éditeur spécialisé</button>
|
|
<button id="reloadDocumentSource" class="btn btn-outline-secondary btn-sm" type="button">Recharger</button>
|
|
<button id="saveDocumentSource" class="btn btn-primary btn-sm" type="button">Valider et sauvegarder</button>
|
|
</div>
|
|
</div>
|
|
<dl class="row small mb-3 app-document-metadata">
|
|
<dt class="col-lg-3">file_id</dt>
|
|
<dd id="documentDetailFileId" class="col-lg-9">—</dd>
|
|
<dt class="col-lg-3">Fichier mappé</dt>
|
|
<dd id="documentDetailFilename" class="col-lg-9">—</dd>
|
|
<dt class="col-lg-3">Schema</dt>
|
|
<dd id="documentDetailSchema" class="col-lg-9">—</dd>
|
|
<dt class="col-lg-3">Path résolu</dt>
|
|
<dd id="documentDetailPath" class="col-lg-9">—</dd>
|
|
<dt class="col-lg-3">Étape diagnostic</dt>
|
|
<dd id="documentDetailStage" class="col-lg-9">—</dd>
|
|
<dt class="col-lg-3 mb-0">Diagnostic</dt>
|
|
<dd id="documentDetailDiagnostic" class="col-lg-9 mb-0">—</dd>
|
|
</dl>
|
|
<label class="form-label fw-semibold" for="documentSourceEditor">Source brut Config</label>
|
|
<textarea id="documentSourceEditor" class="form-control app-document-source" rows="18" spellcheck="false" autocomplete="off"></textarea>
|
|
<div class="form-text">La sauvegarde est refusée tant que le candidat échoue au parsing JSON, au schema ou aux invariants sémantiques backend.</div>
|
|
</section>
|
|
|
|
<div id="documentsStatus" class="alert alert-primary mb-0" role="status" aria-live="polite">Chargement de l'inventaire Config...</div>
|
|
</section>
|
|
|
|
|
|
<section id="profilesPanel" hidden aria-label="Profils Config">
|
|
<div class="d-flex flex-wrap gap-3 align-items-end justify-content-between mb-3">
|
|
<div class="flex-grow-1">
|
|
<h2 class="h5 mb-2">Inspection des profils</h2>
|
|
<p class="text-body-secondary small mb-3">La sélection, la fusion global/profil et la résolution environnementale restent sous contrôle de ksp-config-lib.</p>
|
|
<div class="row g-2">
|
|
<div class="col-xl-6">
|
|
<label class="form-label" for="profileDocumentSelect">Document Config</label>
|
|
<select id="profileDocumentSelect" class="form-select" disabled></select>
|
|
</div>
|
|
<div class="col-xl-6">
|
|
<label class="form-label" for="profileIdSelect">Profil inspecté</label>
|
|
<select id="profileIdSelect" class="form-select" disabled></select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="d-flex flex-wrap gap-2">
|
|
<button id="inspectDefaultProfile" class="btn btn-outline-secondary btn-sm" type="button">Profil par défaut</button>
|
|
<button id="reloadProfileDetail" class="btn btn-outline-secondary btn-sm" type="button">Recharger</button>
|
|
<button id="refreshProfiles" class="btn btn-outline-primary btn-sm" type="button">
|
|
<i class="fa-solid fa-rotate me-1" aria-hidden="true"></i>
|
|
Rafraîchir
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<section id="profileDetailPanel" hidden class="border rounded p-3 mb-4 bg-body-tertiary" aria-label="Détail du profil Config">
|
|
<dl class="row small mb-4 app-profile-metadata">
|
|
<dt class="col-lg-3">file_id</dt>
|
|
<dd id="profileDetailFileId" class="col-lg-9">—</dd>
|
|
<dt class="col-lg-3">Path résolu</dt>
|
|
<dd id="profileDetailPath" class="col-lg-9">—</dd>
|
|
<dt class="col-lg-3">default_profile</dt>
|
|
<dd id="profileDetailDefault" class="col-lg-9">—</dd>
|
|
<dt class="col-lg-3">Profil sélectionné</dt>
|
|
<dd id="profileDetailSelected" class="col-lg-9">—</dd>
|
|
<dt class="col-lg-3">Source de sélection</dt>
|
|
<dd id="profileDetailSelectionSource" class="col-lg-9">—</dd>
|
|
<dt class="col-lg-3 mb-0">Sensibilité effective</dt>
|
|
<dd id="profileDetailSensitivity" class="col-lg-9 mb-0">—</dd>
|
|
</dl>
|
|
|
|
<div class="row g-3 mb-4">
|
|
<div class="col-xxl-4">
|
|
<h3 class="h6">Globals source</h3>
|
|
<pre id="profileGlobalsJson" class="app-profile-json mb-0"></pre>
|
|
</div>
|
|
<div class="col-xxl-4">
|
|
<h3 class="h6">Profil source</h3>
|
|
<pre id="profileSourceJson" class="app-profile-json mb-0"></pre>
|
|
</div>
|
|
<div class="col-xxl-4">
|
|
<h3 class="h6">Effective sûre</h3>
|
|
<pre id="profileEffectiveJson" class="app-profile-json mb-0"></pre>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row g-3">
|
|
<div class="col-xl-5">
|
|
<h3 class="h6">Provenance top-level</h3>
|
|
<div class="table-responsive">
|
|
<table id="profileOriginsTable" class="table table-sm table-striped align-middle mb-0">
|
|
<thead>
|
|
<tr><th scope="col">Clé</th><th scope="col">Origine</th></tr>
|
|
</thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<div class="col-xl-7">
|
|
<h3 class="h6">Provenance environnement</h3>
|
|
<div class="table-responsive">
|
|
<table id="profileEnvironmentProvenanceTable" class="table table-sm table-striped align-middle mb-0">
|
|
<thead>
|
|
<tr><th scope="col">JSON Pointer</th><th scope="col">Variable</th><th scope="col">Source</th><th scope="col">Sensibilité</th></tr>
|
|
</thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="form-text mt-3">L'effective affichée utilise exclusivement la représentation sûre/redacted de Config ; aucune valeur réelle Secret n'est projetée.</div>
|
|
</section>
|
|
|
|
<div id="profilesStatus" class="alert alert-primary mb-0" role="status" aria-live="polite">Chargement des profils Config...</div>
|
|
</section>
|
|
|
|
<section id="environmentPanel" hidden aria-label="Environnement Config">
|
|
<div class="d-flex flex-wrap gap-2 align-items-center justify-content-between mb-3">
|
|
<div>
|
|
<h2 class="h5 mb-1">Rapport Environnement / .env</h2>
|
|
<p class="text-body-secondary small mb-0">Valeurs desired/effective sûres, provenance externe, shadowing et mutations persistées exclusivement par ksp-config-lib.</p>
|
|
</div>
|
|
<button id="refreshEnvironment" class="btn btn-outline-primary btn-sm" type="button">
|
|
<i class="fa-solid fa-rotate me-1" aria-hidden="true"></i>
|
|
Rafraîchir
|
|
</button>
|
|
</div>
|
|
|
|
<div class="alert alert-light border small" role="note">
|
|
Priorité effective : <code>process > .env > fallback</code>. Une mutation ne modifie jamais l'environnement process déjà hérité. Les valeurs Secret restent redacted dans le rapport.
|
|
</div>
|
|
|
|
<div class="table-responsive mb-4">
|
|
<table id="environmentReportTable" class="table table-striped table-hover align-middle w-100">
|
|
<thead>
|
|
<tr>
|
|
<th scope="col">Variable</th>
|
|
<th scope="col">Namespace</th>
|
|
<th scope="col">Sensibilité</th>
|
|
<th scope="col">Desired .env (safe)</th>
|
|
<th scope="col">Effective (safe)</th>
|
|
<th scope="col">Source</th>
|
|
<th scope="col">Shadowed</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div id="environmentStatus" class="alert alert-primary mb-4" role="status" aria-live="polite">Chargement du rapport environnement...</div>
|
|
|
|
<section class="border rounded p-3 bg-body-tertiary" aria-label="Management .env">
|
|
<div class="d-flex flex-wrap gap-2 align-items-center justify-content-between mb-3">
|
|
<div>
|
|
<h2 class="h5 mb-1">Management / test .env</h2>
|
|
<p class="text-body-secondary small mb-0">Cliquer une ligne du rapport sélectionne son nom uniquement. Aucune ancienne valeur réelle n'est préchargée dans le formulaire.</p>
|
|
</div>
|
|
<button id="clearEnvironmentEditor" class="btn btn-outline-secondary btn-sm" type="button">Vider le formulaire</button>
|
|
</div>
|
|
|
|
<div class="row g-3 align-items-end mb-3">
|
|
<div class="col-xl-5">
|
|
<label class="form-label" for="environmentVariableName">Variable KSP/KSPB</label>
|
|
<input id="environmentVariableName" class="form-control font-monospace" type="text" list="environmentVariableSuggestions" autocomplete="off" placeholder="KSP_... ou KSPB_...">
|
|
<datalist id="environmentVariableSuggestions"></datalist>
|
|
</div>
|
|
<div class="col-xl-5">
|
|
<label class="form-label" for="environmentVariableValue">Nouvelle valeur</label>
|
|
<input id="environmentVariableValue" class="form-control font-monospace" type="text" autocomplete="new-password" spellcheck="false">
|
|
<div id="environmentEditorSensitivity" class="form-text">La valeur saisie n'est jamais préchargée depuis .env.</div>
|
|
</div>
|
|
<div class="col-xl-2 d-grid gap-2">
|
|
<button id="setEnvironmentValue" class="btn btn-primary btn-sm" type="button">Créer / Modifier</button>
|
|
<button id="removeEnvironmentValue" class="btn btn-outline-danger btn-sm" type="button">Supprimer</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="d-flex flex-wrap gap-2 align-items-center mb-3">
|
|
<span class="small text-body-secondary me-1">Reveal Secret privilégié :</span>
|
|
<button id="revealEnvironmentEffective" class="btn btn-outline-warning btn-sm" type="button" disabled>Révéler effective</button>
|
|
<button id="revealEnvironmentDotenv" class="btn btn-outline-warning btn-sm" type="button" disabled>Révéler .env</button>
|
|
<span class="form-text mb-0">Disponible uniquement pour KSP_SECRET_* / KSPB_SECRET_* et jamais préchargé automatiquement.</span>
|
|
</div>
|
|
|
|
<div id="environmentMutationStatus" class="alert alert-secondary mb-3" role="status" aria-live="polite">
|
|
Aucune mutation effectuée pendant ce lancement.
|
|
</div>
|
|
|
|
<div id="environmentMutationResult" hidden>
|
|
<h3 class="h6">Dernier résultat ConfigEnvironmentChangeReport</h3>
|
|
<dl class="row small mb-0 app-profile-metadata">
|
|
<dt class="col-md-4">Opération</dt>
|
|
<dd id="environmentMutationOperation" class="col-md-8">—</dd>
|
|
<dt class="col-md-4">Variable</dt>
|
|
<dd id="environmentMutationVariable" class="col-md-8 font-monospace">—</dd>
|
|
<dt class="col-md-4">source_changed</dt>
|
|
<dd id="environmentMutationSourceChanged" class="col-md-8 font-monospace">—</dd>
|
|
<dt class="col-md-4">effective_changed</dt>
|
|
<dd id="environmentMutationEffectiveChanged" class="col-md-8 font-monospace">—</dd>
|
|
<dt class="col-md-4">shadowed_by_process</dt>
|
|
<dd id="environmentMutationShadowed" class="col-md-8 font-monospace">—</dd>
|
|
<dt class="col-md-4 mb-0">reload_required</dt>
|
|
<dd id="environmentMutationReload" class="col-md-8 mb-0 font-monospace">—</dd>
|
|
</dl>
|
|
<div class="form-text mt-2">Le rapport est rechargé après chaque mutation. Un consumer qui conserve son propre snapshot doit respecter <code>reload_required</code>.</div>
|
|
</div>
|
|
</section>
|
|
</section>
|
|
|
|
<section id="loggingPanel" hidden aria-label="Logging Config">
|
|
<div class="d-flex flex-wrap gap-2 align-items-center justify-content-between mb-3">
|
|
<div>
|
|
<h2 class="h5 mb-1">Logging editor — mutations/persistence/hot reload</h2>
|
|
<p class="text-body-secondary small mb-0">Brouillon typé de <code>std.logging.json</code>, validé et persisté atomiquement par <code>ksp-config-lib</code>. <strong>Sauvegarder et appliquer</strong> hot-reload le runtime KSP sans redémarrage ; <strong>Recharger le document</strong> ne touche qu'au brouillon.</p>
|
|
</div>
|
|
<div class="d-flex gap-2">
|
|
<button id="refreshLoggingDocument" class="btn btn-outline-primary btn-sm" type="button">
|
|
<i class="fa-solid fa-rotate me-1" aria-hidden="true"></i>
|
|
Recharger le document
|
|
</button>
|
|
<button id="saveLoggingDocument" class="btn btn-primary btn-sm" type="button" disabled>
|
|
<i class="fa-solid fa-floppy-disk me-1" aria-hidden="true"></i>
|
|
Sauvegarder et appliquer
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<dl class="row small mb-3 app-profile-metadata">
|
|
<dt class="col-lg-3">file_id</dt>
|
|
<dd id="loggingFileId" class="col-lg-9 font-monospace">—</dd>
|
|
<dt class="col-lg-3">Path résolu</dt>
|
|
<dd id="loggingPath" class="col-lg-9 font-monospace text-break">—</dd>
|
|
<dt class="col-lg-3 mb-0">format_version</dt>
|
|
<dd id="loggingFormatVersion" class="col-lg-9 mb-0 font-monospace">—</dd>
|
|
</dl>
|
|
|
|
<section class="border rounded p-3 mb-4 bg-body-tertiary" aria-label="Runtime Logging actif">
|
|
<div class="d-flex flex-wrap gap-2 align-items-start justify-content-between mb-3">
|
|
<div>
|
|
<h3 class="h6 mb-1">Runtime actif</h3>
|
|
<p class="text-body-secondary small mb-0">Le profil runtime peut être appliqué explicitement sans modifier <code>default_profile</code>. L'identité de lancement reste stable pendant tous les hot reloads du processus.</p>
|
|
</div>
|
|
<button id="refreshLoggingRuntime" class="btn btn-outline-secondary btn-sm" type="button">Rafraîchir runtime</button>
|
|
</div>
|
|
<dl class="row small mb-3 app-profile-metadata">
|
|
<dt class="col-xl-3">Profil actif</dt><dd id="loggingRuntimeActiveProfile" class="col-xl-3 font-monospace">—</dd>
|
|
<dt class="col-xl-3">Source sélection</dt><dd id="loggingRuntimeSelectionSource" class="col-xl-3 font-monospace">—</dd>
|
|
<dt class="col-xl-3">Génération</dt><dd id="loggingRuntimeGeneration" class="col-xl-3 font-monospace">—</dd>
|
|
<dt class="col-xl-3">Console</dt><dd id="loggingRuntimeConsole" class="col-xl-3">—</dd>
|
|
<dt class="col-xl-3">Fallback</dt><dd id="loggingRuntimeFallback" class="col-xl-3">—</dd>
|
|
<dt class="col-xl-3">Dropped lines</dt><dd id="loggingRuntimeDropped" class="col-xl-3 font-monospace">—</dd>
|
|
<dt class="col-xl-3">Application</dt><dd id="loggingRuntimeApplicationId" class="col-xl-3 font-monospace">—</dd>
|
|
<dt class="col-xl-3">Launch timestamp</dt><dd id="loggingRuntimeLaunchTimestamp" class="col-xl-3 font-monospace">—</dd>
|
|
</dl>
|
|
<div class="row g-2 align-items-end mb-3">
|
|
<div class="col-xl-8">
|
|
<label class="form-label" for="loggingRuntimeProfileSelect">Profil persisté à appliquer explicitement</label>
|
|
<select id="loggingRuntimeProfileSelect" class="form-select" disabled></select>
|
|
<div class="form-text">Disponible uniquement lorsque le brouillon est synchronisé avec la source persistée.</div>
|
|
</div>
|
|
<div class="col-xl-4">
|
|
<button id="applyLoggingRuntimeProfile" class="btn btn-outline-primary w-100" type="button" disabled>Appliquer ce profil au runtime</button>
|
|
</div>
|
|
</div>
|
|
<div class="table-responsive">
|
|
<table id="loggingRuntimeFilesTable" class="table table-sm align-middle mb-0 w-100">
|
|
<thead><tr><th>output_id</th><th>Répertoire</th><th>Préfixe fichier effectif</th><th>Rotation</th></tr></thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="border rounded p-3 mb-4" aria-label="Test Logging">
|
|
<div class="d-flex flex-wrap gap-2 align-items-start justify-content-between mb-3">
|
|
<div>
|
|
<h3 class="h6 mb-1">Test Logging — routing contrôlé</h3>
|
|
<p class="text-body-secondary small mb-0">Émet des événements volontairement journalisés pour vérifier niveaux, targets, domains, sinks et hot reload. Ne colle jamais de Secret dans le message de test.</p>
|
|
</div>
|
|
</div>
|
|
<div class="row g-3 mb-3">
|
|
<div class="col-xl-6">
|
|
<label class="form-label" for="loggingTestMessage">Message</label>
|
|
<input id="loggingTestMessage" class="form-control" type="text" maxlength="500" autocomplete="off" value="Config Desk Logging routing test">
|
|
</div>
|
|
<div class="col-md-3 col-xl-2">
|
|
<label class="form-label" for="loggingTestLevel">Niveau</label>
|
|
<select id="loggingTestLevel" class="form-select">
|
|
<option value="trace">trace</option><option value="debug">debug</option><option value="info" selected>info</option><option value="warn">warn</option><option value="error">error</option><option value="all">tous</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-5 col-xl-4">
|
|
<label class="form-label" for="loggingTestTarget">Target backend contrôlé</label>
|
|
<select id="loggingTestTarget" class="form-select font-monospace">
|
|
<option value="app">ksp-app-config-desk</option>
|
|
<option value="logging_test" selected>ksp-app-config-desk.logging-test</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label" for="loggingTestDomainMode">Domain</label>
|
|
<select id="loggingTestDomainMode" class="form-select">
|
|
<option value="absent">absent</option>
|
|
<option value="known" selected>connu</option>
|
|
<option value="custom">personnalisé</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label" for="loggingTestKnownDomain">Domain connu</label>
|
|
<select id="loggingTestKnownDomain" class="form-select font-monospace">
|
|
<option value="config.logging_test">config.logging_test</option>
|
|
<option value="config.logging_runtime">config.logging_runtime</option>
|
|
<option value="frontend">frontend</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-5">
|
|
<label class="form-label" for="loggingTestCustomDomain">Domain personnalisé</label>
|
|
<input id="loggingTestCustomDomain" class="form-control font-monospace" type="text" maxlength="80" autocomplete="off" placeholder="custom.routing" disabled>
|
|
</div>
|
|
</div>
|
|
<div class="d-flex flex-wrap gap-2 mb-3">
|
|
<button id="emitLoggingTestBackend" class="btn btn-outline-primary" type="button">
|
|
<i class="fa-solid fa-terminal me-1" aria-hidden="true"></i>
|
|
Log backend
|
|
</button>
|
|
<button id="emitLoggingTestFrontend" class="btn btn-outline-secondary" type="button">
|
|
<i class="fa-solid fa-code me-1" aria-hidden="true"></i>
|
|
Log via bridge frontend
|
|
</button>
|
|
</div>
|
|
<div class="form-text mb-3">Le test backend respecte le target/domain choisis. Le bridge frontend conserve volontairement son contrat fixe <code>target=ksp-app-config-desk.frontend.main</code>, <code>domain=frontend</code> et réutilise uniquement le message/niveau.</div>
|
|
<div id="loggingTestStatus" class="alert alert-secondary mb-0" role="status" aria-live="polite">Aucun événement de test émis pendant ce lancement.</div>
|
|
</section>
|
|
|
|
<section class="border rounded p-3 mb-4 bg-body-tertiary" aria-label="Globals Logging">
|
|
<h3 class="h6 mb-3">Globals</h3>
|
|
<div class="row g-3">
|
|
<div class="col-xl-7">
|
|
<label class="form-label" for="loggingLogsDirectoryInput">logs_directory</label>
|
|
<input id="loggingLogsDirectoryInput" class="form-control font-monospace" type="text" autocomplete="off" disabled>
|
|
</div>
|
|
<div class="col-xl-5">
|
|
<label class="form-label" for="loggingDefaultProfileSelect">default_profile</label>
|
|
<select id="loggingDefaultProfileSelect" class="form-select" disabled></select>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="border rounded p-3 mb-4" aria-label="Gestion des profils Logging">
|
|
<div class="row g-3 align-items-end">
|
|
<div class="col-xl-4">
|
|
<label class="form-label" for="loggingProfileSelect">Profil édité</label>
|
|
<select id="loggingProfileSelect" class="form-select" disabled></select>
|
|
</div>
|
|
<div class="col-xl-4">
|
|
<label class="form-label" for="loggingProfileIdInput">ID cible create/clone/rename</label>
|
|
<input id="loggingProfileIdInput" class="form-control font-monospace" type="text" autocomplete="off" placeholder="local_test" disabled>
|
|
</div>
|
|
<div class="col-xl-4">
|
|
<div class="d-flex flex-wrap gap-2">
|
|
<button id="createLoggingProfile" class="btn btn-outline-primary btn-sm" type="button" disabled>Créer</button>
|
|
<button id="cloneLoggingProfile" class="btn btn-outline-primary btn-sm" type="button" disabled>Cloner</button>
|
|
<button id="renameLoggingProfile" class="btn btn-outline-secondary btn-sm" type="button" disabled>Renommer</button>
|
|
<button id="deleteLoggingProfile" class="btn btn-outline-danger btn-sm" type="button" disabled>Supprimer</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<div class="row g-3 mb-4">
|
|
<div class="col-xl-6">
|
|
<label class="form-label" for="loggingProfileDefaultFilter">default_filter</label>
|
|
<select id="loggingProfileDefaultFilter" class="form-select" disabled>
|
|
<option value="off">off</option><option value="error">error</option><option value="warn">warn</option><option value="info">info</option><option value="debug">debug</option><option value="trace">trace</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-xl-6">
|
|
<label class="form-label" for="loggingProfileSpanEvents">span_events</label>
|
|
<select id="loggingProfileSpanEvents" class="form-select" disabled>
|
|
<option value="off">off</option><option value="new_and_close">new_and_close</option><option value="full">full</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<section class="border rounded p-3 mb-4 bg-body-tertiary" aria-label="Console Logging">
|
|
<h3 class="h6 mb-3">Console</h3>
|
|
<div class="row g-3">
|
|
<div class="col-md-2 form-check ms-2">
|
|
<input id="loggingConsoleEnabled" class="form-check-input" type="checkbox" disabled>
|
|
<label class="form-check-label" for="loggingConsoleEnabled">Enabled</label>
|
|
</div>
|
|
<div class="col-md-2 form-check ms-2">
|
|
<input id="loggingConsoleAnsi" class="form-check-input" type="checkbox" disabled>
|
|
<label class="form-check-label" for="loggingConsoleAnsi">ANSI</label>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label" for="loggingConsoleOutput">Output</label>
|
|
<select id="loggingConsoleOutput" class="form-select" disabled><option value="stdout">stdout</option><option value="stderr">stderr</option></select>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label" for="loggingConsoleFormat">Format</label>
|
|
<select id="loggingConsoleFormat" class="form-select" disabled><option value="human">human</option><option value="compact">compact</option><option value="pretty">pretty</option><option value="json">json</option></select>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label" for="loggingConsoleLevel">Level</label>
|
|
<select id="loggingConsoleLevel" class="form-select" disabled><option value="off">off</option><option value="error">error</option><option value="warn">warn</option><option value="info">info</option><option value="debug">debug</option><option value="trace">trace</option></select>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label" for="loggingConsoleTargets">Targets</label>
|
|
<input id="loggingConsoleTargets" class="form-control font-monospace" type="text" autocomplete="off" disabled>
|
|
</div>
|
|
<div class="col-md-5">
|
|
<label class="form-label" for="loggingConsoleDomains">Domains</label>
|
|
<input id="loggingConsoleDomains" class="form-control font-monospace" type="text" autocomplete="off" disabled>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="mb-4" aria-label="Fichiers Logging">
|
|
<div class="d-flex align-items-center justify-content-between gap-2 mb-2">
|
|
<div>
|
|
<h3 class="h6 mb-0">Fichiers</h3>
|
|
<div class="form-text">0, 1 ou plusieurs sinks persistants. Les suppressions restent dans le brouillon jusqu'à Sauvegarder et appliquer.</div>
|
|
</div>
|
|
<button id="addLoggingFile" class="btn btn-outline-primary btn-sm" type="button" disabled>Ajouter un fichier</button>
|
|
</div>
|
|
<div class="table-responsive">
|
|
<table id="loggingFilesTable" class="table table-sm align-middle w-100">
|
|
<thead><tr><th>output_id</th><th>On</th><th>Path</th><th>Rotation</th><th>Format</th><th>Level</th><th>Targets</th><th>Domains</th><th></th></tr></thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="mb-4" aria-label="Target filters Logging">
|
|
<div class="d-flex align-items-center justify-content-between gap-2 mb-2">
|
|
<h3 class="h6 mb-0">Target filters globaux</h3>
|
|
<button id="addLoggingTargetFilter" class="btn btn-outline-primary btn-sm" type="button" disabled>Ajouter un target filter</button>
|
|
</div>
|
|
<div class="table-responsive">
|
|
<table id="loggingTargetFiltersTable" class="table table-sm align-middle w-100">
|
|
<thead><tr><th>target_prefix</th><th>Level</th><th></th></tr></thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
</div>
|
|
</section>
|
|
|
|
<div class="alert alert-warning small py-2 mb-2" role="note">
|
|
<strong>Runtime actif :</strong> <strong>Sauvegarder et appliquer</strong> persiste le candidat puis applique son <code>default_profile</code>. <strong>Appliquer ce profil au runtime</strong> sélectionne un profil persisté sans modifier le document. En cas d'échec, l'ancien runtime et sa génération restent actifs. <strong>Recharger le document</strong> resynchronise uniquement le brouillon.
|
|
</div>
|
|
<div id="loggingSaveReport" class="small text-body-secondary mb-2">Aucune sauvegarde/application effectuée pendant ce lancement.</div>
|
|
<div id="loggingStatus" class="alert alert-primary mb-0" role="status" aria-live="polite">Chargement du document Logging typé...</div>
|
|
</section>
|
|
|
|
<div class="alert alert-primary mb-0" role="status">
|
|
<i class="fa-solid fa-desktop me-2" aria-hidden="true"></i>
|
|
<span id="shellStatus" class="app-shell-status">Initialisation du frontend...</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</main>
|
|
|
|
<div id="loggingDiscardDraftModal" class="modal fade" tabindex="-1" aria-labelledby="loggingDiscardDraftTitle" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h2 id="loggingDiscardDraftTitle" class="modal-title fs-5">Abandonner le brouillon Logging</h2>
|
|
<button class="btn-close" type="button" data-bs-dismiss="modal" aria-label="Annuler"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<p class="mb-0">Des modifications non sauvegardées existent. Recharger le document depuis la source persistée les abandonnera. Cette action ne recharge pas le runtime Logging.</p>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn btn-outline-secondary" type="button" data-bs-dismiss="modal">Annuler</button>
|
|
<button id="loggingDiscardDraftConfirm" class="btn btn-danger" type="button">Abandonner et recharger le document</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="loggingProfileDeleteModal" class="modal fade" tabindex="-1" aria-labelledby="loggingProfileDeleteTitle" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h2 id="loggingProfileDeleteTitle" class="modal-title fs-5">Supprimer le profil du brouillon</h2>
|
|
<button class="btn-close" type="button" data-bs-dismiss="modal" aria-label="Annuler"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<p class="mb-2">Retirer le profil <code id="loggingProfileDeleteName" class="font-monospace"></code> du brouillon Logging ?</p>
|
|
<p class="text-body-secondary small mb-0">Le fichier n'est pas modifié tant que Sauvegarder et appliquer n'est pas déclenché.</p>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn btn-outline-secondary" type="button" data-bs-dismiss="modal">Annuler</button>
|
|
<button id="loggingProfileDeleteConfirm" class="btn btn-danger" type="button">Retirer du brouillon</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="environmentRemoveConfirmModal" class="modal fade" tabindex="-1" aria-labelledby="environmentRemoveConfirmTitle" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h2 id="environmentRemoveConfirmTitle" class="modal-title fs-5">Confirmer la suppression .env</h2>
|
|
<button class="btn-close" type="button" data-bs-dismiss="modal" aria-label="Annuler"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<p class="mb-2">Supprimer l'entrée <code id="environmentRemoveConfirmVariable" class="font-monospace"></code> de <code>.env</code> ?</p>
|
|
<p class="text-body-secondary small mb-0">L'environnement process déjà hérité ne sera pas modifié.</p>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn btn-outline-secondary" type="button" data-bs-dismiss="modal">Annuler</button>
|
|
<button id="environmentRemoveConfirmAction" class="btn btn-danger" type="button">Supprimer</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="secretRevealModal" class="modal fade" tabindex="-1" aria-labelledby="secretRevealModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h2 id="secretRevealModalLabel" class="modal-title fs-5">Reveal Secret privilégié</h2>
|
|
<button class="btn-close" type="button" data-bs-dismiss="modal" aria-label="Fermer"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div id="secretRevealConfirmPanel">
|
|
<p class="mb-2">Variable : <code id="secretRevealVariable" class="font-monospace"></code></p>
|
|
<p class="mb-3">Source demandée : <code id="secretRevealSource"></code></p>
|
|
<div class="alert alert-warning small mb-3" role="note">
|
|
Cette action expose temporairement la valeur réelle dans la WebView. Elle n'est ni journalisée, ni ajoutée à AppState, ni persistée par le frontend.
|
|
</div>
|
|
</div>
|
|
<div id="secretRevealResultPanel" hidden>
|
|
<label class="form-label fw-semibold" for="secretRevealValue">Valeur réelle transitoire</label>
|
|
<div class="input-group">
|
|
<input id="secretRevealValue" class="form-control font-monospace" type="password" readonly autocomplete="off" spellcheck="false">
|
|
<button id="secretRevealToggleVisibility" class="btn btn-outline-secondary" type="button">Afficher</button>
|
|
</div>
|
|
<div id="secretRevealValueAbsent" class="alert alert-secondary small mb-0" hidden>Aucune valeur n'existe dans cette source.</div>
|
|
</div>
|
|
<p id="secretRevealStatus" class="text-body-secondary small mt-3 mb-0" aria-live="polite"></p>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn btn-outline-secondary" type="button" data-bs-dismiss="modal">Fermer</button>
|
|
<button id="secretRevealConfirmAction" class="btn btn-warning" type="button">Révéler</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<footer class="app-footer bg-dark text-light">
|
|
<div class="container h-100 d-flex align-items-center justify-content-center">
|
|
<small>© 2026 SASEDEV</small>
|
|
</div>
|
|
</footer>
|
|
<script type="module" src="ts/main.ts" defer></script>
|
|
</body>
|
|
|
|
</html>
|