seize-infra-doc/scripts/intranet/index.html.template
Hedi Kalboussi 1e90a85359 Documentation initiale infra Seize — passation J7
- 6 docs racine (README, HANDOVER, ARCHITECTURE, DECISIONS, INSTALL, OPERATIONS)
- 9 docs thématiques dans docs/
- 10 templates docker-compose dans configs/
- 7 scripts Python/Shell d'import Furious vers Zitadel
- 2 matrices RBAC

Etat infra au 5 juin 2026 :
- 88 users Furious importes dans Zitadel
- 8 roles RBAC crees et attribues
- Intranet filtre les cartes selon role (Approche A)
- 7 outils SSO operationnels
2026-06-05 17:49:43 +00:00

240 lines
8.3 KiB
Plaintext

<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Seize Consulting — Intranet</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&family=Inter+Tight:wght@400;500;600;700&display=swap" rel="stylesheet">
<!--
============================================================================
TEMPLATE INTRANET SEIZE CONSULTING
============================================================================
Ce fichier est un template. Le vrai fichier en production est sur le serveur
sur ~/docker/intranet/index.html.
Points clés :
- __USER_EMAIL__ est un placeholder substitué par nginx (sub_filter)
- Chaque carte service-card a un attribut data-app="<nom>" pour le RBAC
- Le JavaScript en bas fait le filtrage selon le rôle
============================================================================
-->
<style>
:root {
--c-bg-deep: #071C1C;
--c-surface: rgba(8, 57, 52, 0.6);
--c-surface-2: rgba(169, 212, 211, 0.04);
--c-accent: #008A81;
--c-accent-bright: #00C9B7;
--c-mint: #A9D4D3;
--c-lime: #F3FBBA;
--c-cream: #FBFBF4;
--c-text-dim: rgba(251, 251, 244, 0.55);
--c-text-muted: rgba(251, 251, 244, 0.4);
--c-border: rgba(169, 212, 211, 0.12);
--c-border-bright: rgba(169, 212, 211, 0.3);
--font-display: 'Space Grotesk', sans-serif;
--font-body: 'Inter Tight', sans-serif;
--font-mono: 'JetBrains Mono', monospace;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body {
background: var(--c-bg-deep);
color: var(--c-cream);
font-family: var(--font-body);
min-height: 100vh;
overflow-x: hidden;
}
/* ⚠️ Les styles complets (orbs, animations, etc.) sont dans le fichier réel.
Ici on garde le minimum pour ne pas surcharger le template. */
.container {
position: relative; z-index: 10;
max-width: 1400px; margin: 0 auto; padding: 60px 40px;
}
.brand {
display: flex; align-items: center; gap: 16px; margin-bottom: 60px;
}
.brand-logo img { height: 50px; }
.brand-title {
font-family: var(--font-display); font-size: 28px; font-weight: 700;
}
.intro {
margin-bottom: 50px;
}
.intro h1 {
font-family: var(--font-display); font-size: 48px; font-weight: 700;
margin-bottom: 12px;
}
.services-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 24px;
}
.service-card {
background: var(--c-surface);
border: 1px solid var(--c-border);
border-radius: 16px; padding: 24px;
display: flex; flex-direction: column; gap: 12px;
text-decoration: none; color: inherit;
transition: all 0.3s ease;
}
.service-card:hover {
border-color: var(--c-border-bright);
transform: translateY(-4px);
}
.card-title {
font-family: var(--font-display); font-size: 22px; font-weight: 600;
}
.card-desc {
color: var(--c-text-dim); font-size: 14px; line-height: 1.5;
}
.card-meta {
display: flex; gap: 8px; flex-wrap: wrap; font-family: var(--font-mono); font-size: 11px;
}
.card-url { color: var(--c-mint); }
.card-tag {
background: var(--c-surface-2); padding: 4px 10px; border-radius: 12px;
color: var(--c-accent-bright);
}
</style>
</head>
<body>
<div class="container">
<div class="brand">
<div class="brand-logo"><img src="/logo.png" alt="Seize"></div>
<div class="brand-title">Seize Consulting</div>
</div>
<div class="intro">
<h1>Tableau de bord</h1>
<p>Accédez à vos outils internes</p>
</div>
<div class="services-grid">
<!-- ⚠️ Chaque carte a un attribut data-app="<nom>" pour le RBAC -->
<!-- Le JavaScript en bas du fichier filtre ces cartes selon le rôle -->
<!-- PORTAINER -->
<a class="service-card" data-app="portainer" href="https://portainer.seizeconsulting.com" target="_blank" rel="noopener">
<div class="card-title">Portainer</div>
<div class="card-desc">Console de gestion des conteneurs Docker.</div>
<div class="card-meta">
<span class="card-url">portainer.seizeconsulting.com</span>
<span class="card-tag">Infra</span>
</div>
</a>
<!-- UPTIME KUMA / STATUS -->
<a class="service-card" data-app="status" href="https://status.seizeconsulting.com" target="_blank" rel="noopener">
<div class="card-title">Status</div>
<div class="card-desc">Surveillance de la disponibilité des services.</div>
<div class="card-meta">
<span class="card-url">status.seizeconsulting.com</span>
<span class="card-tag">Monitoring</span>
</div>
</a>
<!-- N8N -->
<a class="service-card" data-app="n8n" href="https://n8n.seizeconsulting.com" target="_blank" rel="noopener">
<div class="card-title">n8n</div>
<div class="card-desc">Plateforme d'automatisation de workflows.</div>
<div class="card-meta">
<span class="card-url">n8n.seizeconsulting.com</span>
<span class="card-tag">Automation</span>
</div>
</a>
<!-- GITEA -->
<a class="service-card" data-app="gitea" href="https://git.seizeconsulting.com" target="_blank" rel="noopener">
<div class="card-title">Gitea</div>
<div class="card-desc">Dépôts Git et gestion collaborative du code.</div>
<div class="card-meta">
<span class="card-url">git.seizeconsulting.com</span>
<span class="card-tag">Code</span>
</div>
</a>
<!-- CODE SERVER -->
<a class="service-card" data-app="code" href="https://code.seizeconsulting.com" target="_blank" rel="noopener">
<div class="card-title">Code Server</div>
<div class="card-desc">Environnement VS Code dans le navigateur.</div>
<div class="card-meta">
<span class="card-url">code.seizeconsulting.com</span>
<span class="card-tag">IDE</span>
</div>
</a>
<!-- TRAEFIK -->
<a class="service-card" data-app="traefik" href="https://traefik.seizeconsulting.com" target="_blank" rel="noopener">
<div class="card-title">Traefik</div>
<div class="card-desc">Reverse proxy et routage du trafic.</div>
<div class="card-meta">
<span class="card-url">traefik.seizeconsulting.com</span>
<span class="card-tag">Réseau</span>
</div>
</a>
</div>
<footer>
<div>SEIZE CONSULTING · © 2026</div>
<div>Hébergé en France · Made with care</div>
</footer>
</div>
<script>
/* ============================================================================
RBAC INTRANET — Filtrage des cartes selon rôle Zitadel
============================================================================
- __USER_EMAIL__ est remplacé par nginx (sub_filter) par la valeur du
header X-Forwarded-Email (envoyé par OAuth2-Proxy après auth Zitadel)
- On fetch /roles-mapping.json (généré par generate-roles-mapping.py)
- Pour chaque carte service-card[data-app], on cache si rôle pas autorisé
============================================================================ */
(async function() {
const USER_EMAIL = "__USER_EMAIL__"; // ← Substitué par nginx sub_filter
/* Matrice RBAC : qui voit quelle carte */
const ACCESS_MATRIX = {
"portainer": ["direction", "manager"],
"status": ["direction"],
"n8n": ["direction", "manager", "expert"],
"gitea": ["direction", "manager", "expert", "consultant_senior", "consultant_junior", "alternant", "stagiaire", "support"],
"code": ["direction", "manager", "expert", "consultant_senior", "consultant_junior", "alternant", "stagiaire", "support"],
"traefik": ["direction"]
};
let userRole = null;
try {
const response = await fetch("/roles-mapping.json", { cache: "no-store" });
if (response.ok) {
const mapping = await response.json();
userRole = mapping[USER_EMAIL.toLowerCase()] || null;
}
} catch (e) {
console.error("[RBAC] Erreur chargement mapping:", e);
}
console.log("[RBAC] User:", USER_EMAIL, "| Rôle:", userRole);
/* Filtrer les cartes selon le rôle */
document.querySelectorAll(".service-card[data-app]").forEach(card => {
const app = card.dataset.app;
const allowedRoles = ACCESS_MATRIX[app] || [];
if (!userRole || !allowedRoles.includes(userRole)) {
card.style.display = "none";
}
});
})();
</script>
</body>
</html>