94b4218ba5
- dossier : 3.2 remontee des demandes depuis l'app (formulaire -> issue Gitea) - dossier : CI/CD reecrit (PHPStan, couverture, SonarQube, deploiement SSH) + diagramme + annexes - slides : mention du formulaire in-app + legende CD SSH Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
249 lines
16 KiB
JavaScript
249 lines
16 KiB
JavaScript
// ══════════════════════════════════════════════════════════════
|
|
// Soutenance — BLOC 3 (INFCDAAL3)
|
|
// « Déployer et sécuriser les applications informatiques »
|
|
// Style strictement calqué sur presentation.js (thème CESIZen).
|
|
// Génération : node presentation_bloc3.js
|
|
// ══════════════════════════════════════════════════════════════
|
|
const pptxgen = require("pptxgenjs");
|
|
|
|
const pres = new pptxgen();
|
|
pres.layout = "LAYOUT_16x9";
|
|
pres.author = "Sam DEPARDIEU";
|
|
pres.title = "CESIZen — Soutenance BLOC 3 (Déploiement & Sécurité)";
|
|
|
|
const C = {
|
|
darkBg: "0D1B2A", lightBg: "F7F7F2", teal: "00BF63", yellow: "FFDE59",
|
|
dark: "2D2D2D", navy: "1B2838", white: "FFFFFF", gray: "6B7280",
|
|
border: "E5E7EB",
|
|
};
|
|
|
|
// ─── Helpers (identiques à presentation.js) ─────────────────
|
|
const topBar = (s) => s.addShape(pres.shapes.RECTANGLE, { x: 0, y: 0, w: 10, h: 0.09, fill: { color: C.teal }, line: { color: C.teal } });
|
|
const vLine = (s, x, y, h, color) => s.addShape(pres.shapes.RECTANGLE, { x, y, w: 0.07, h, fill: { color: color || C.teal }, line: { color: color || C.teal } });
|
|
const hLine = (s, x, y, w, color) => s.addShape(pres.shapes.RECTANGLE, { x, y, w, h: 0.02, fill: { color: color || C.border }, line: { color: color || C.border } });
|
|
|
|
function header(s, num, section, title) {
|
|
topBar(s);
|
|
s.addText(String(num).padStart(2, "0"), { x: 9.0, y: 0.11, w: 0.82, h: 0.38, fontSize: 14, color: C.teal, fontFace: "Calibri", bold: true, align: "right", margin: 0 });
|
|
s.addText(section.toUpperCase(), { x: 0.65, y: 0.13, w: 7, h: 0.26, fontSize: 9, color: C.gray, fontFace: "Calibri", charSpacing: 2, margin: 0 });
|
|
s.addShape(pres.shapes.RECTANGLE, { x: 0.65, y: 0.44, w: 0.55, h: 0.07, fill: { color: C.yellow }, line: { color: C.yellow } });
|
|
s.addText(title, { x: 0.65, y: 0.52, w: 8.7, h: 0.7, fontSize: 26, color: C.darkBg, fontFace: "Calibri", bold: false, margin: 0 });
|
|
hLine(s, 0.65, 1.25, 8.8);
|
|
}
|
|
|
|
// Carte (bloc encadré avec titre + lignes)
|
|
function card(s, x, y, w, h, accent, title, lines) {
|
|
s.addShape(pres.shapes.RECTANGLE, { x, y, w, h, fill: { color: C.white }, line: { color: C.border, width: 1 } });
|
|
vLine(s, x, y, h, accent);
|
|
s.addText(title, { x: x + 0.2, y: y + 0.14, w: w - 0.35, h: 0.35, fontSize: 13.5, bold: true, color: C.darkBg, fontFace: "Calibri", margin: 0 });
|
|
s.addText(lines.map((t) => ({ text: t, options: { bullet: { code: "2022", indent: 12 }, color: C.dark, fontSize: 11, paraSpaceAfter: 4 } })),
|
|
{ x: x + 0.22, y: y + 0.52, w: w - 0.4, h: h - 0.62, fontFace: "Calibri", valign: "top", margin: 0 });
|
|
}
|
|
|
|
// ─── SLIDE 01 — Couverture ──────────────────────────────────
|
|
{
|
|
const s = pres.addSlide();
|
|
s.background = { color: C.darkBg };
|
|
topBar(s);
|
|
s.addShape(pres.shapes.RECTANGLE, { x: 0.7, y: 0.72, w: 0.55, h: 0.09, fill: { color: C.yellow }, line: { color: C.yellow } });
|
|
s.addText("CESIZEN", { x: 0.7, y: 0.83, w: 8.5, h: 1.25, fontSize: 54, color: C.white, fontFace: "Calibri", margin: 0 });
|
|
s.addText("Déploiement & Sécurisation de l'application", { x: 0.7, y: 2.05, w: 8.5, h: 0.5, fontSize: 18, color: C.teal, fontFace: "Calibri", margin: 0 });
|
|
hLine(s, 0.7, 2.78, 5.9, "374151");
|
|
s.addText("Soutenance CDA CESI — BLOC 3 · Déployer et sécuriser les applications", { x: 0.7, y: 2.9, w: 5.9, h: 0.38, fontSize: 12, color: "D1D5DB", fontFace: "Calibri", margin: 0 });
|
|
s.addText("Réf. : INFCDAAL3", { x: 0.7, y: 3.28, w: 5, h: 0.34, fontSize: 11, color: C.gray, fontFace: "Calibri", margin: 0 });
|
|
s.addText("Sam DEPARDIEU", { x: 0.7, y: 3.72, w: 5, h: 0.38, fontSize: 13, color: C.yellow, fontFace: "Calibri", margin: 0 });
|
|
s.addText("Juillet 2026", { x: 0.7, y: 4.12, w: 4, h: 0.35, fontSize: 12, color: C.gray, fontFace: "Calibri", margin: 0 });
|
|
|
|
// Panel droit teal
|
|
s.addShape(pres.shapes.RECTANGLE, { x: 6.95, y: 0.09, w: 3.05, h: 5.535, fill: { color: C.teal }, line: { color: C.teal } });
|
|
s.addText("Au programme", { x: 7.1, y: 0.28, w: 2.75, h: 0.44, fontSize: 13, bold: true, color: C.white, fontFace: "Calibri", margin: 0 });
|
|
hLine(s, 7.1, 0.74, 2.7, "00995A");
|
|
["Gestion de version", "Évolutions & maintenance", "Plan de déploiement", "CI/CD automatisée", "🔒 Sécurité & RGPD"].forEach((label, i) => {
|
|
const hl = label.startsWith("🔒");
|
|
vLine(s, 7.1, 0.95 + i * 0.82, 0.6, hl ? C.yellow : C.white);
|
|
s.addText(label, { x: 7.28, y: 1.03 + i * 0.82, w: 2.55, h: 0.44, fontSize: 12, color: hl ? C.yellow : C.white, fontFace: "Calibri", margin: 0 });
|
|
});
|
|
}
|
|
|
|
// ─── SLIDE 02 — Sommaire ────────────────────────────────────
|
|
{
|
|
const s = pres.addSlide();
|
|
s.background = { color: C.lightBg };
|
|
topBar(s);
|
|
s.addShape(pres.shapes.RECTANGLE, { x: 0.65, y: 0.37, w: 0.55, h: 0.09, fill: { color: C.yellow }, line: { color: C.yellow } });
|
|
s.addText("SOMMAIRE", { x: 0.65, y: 0.47, w: 8, h: 0.88, fontSize: 34, color: C.darkBg, fontFace: "Calibri", margin: 0 });
|
|
hLine(s, 0.65, 1.38, 8.8);
|
|
["Contexte & objectifs du bloc", "Gestion des versions (Gitea)", "Gestion des évolutions & maintenance",
|
|
"Plan de déploiement & environnements", "Intégration & déploiement continus (CI/CD)",
|
|
"Plan de sécurisation & RGPD", "Bilan & livrables"].forEach((item, i) => {
|
|
s.addText(String(i + 1).padStart(2, "0"), { x: 0.65, y: 1.6 + i * 0.5, w: 0.65, h: 0.38, fontSize: 16, color: C.teal, fontFace: "Calibri", margin: 0 });
|
|
s.addText(item, { x: 1.38, y: 1.63 + i * 0.5, w: 5.2, h: 0.36, fontSize: 13.5, color: C.dark, fontFace: "Calibri", margin: 0 });
|
|
});
|
|
// panel droit
|
|
s.addShape(pres.shapes.RECTANGLE, { x: 6.95, y: 0.09, w: 3.05, h: 5.535, fill: { color: C.teal }, line: { color: C.teal } });
|
|
s.addText("Objectif", { x: 7.1, y: 0.35, w: 2.75, h: 0.4, fontSize: 13, bold: true, color: C.white, fontFace: "Calibri", margin: 0 });
|
|
hLine(s, 7.1, 0.78, 2.7, "00995A");
|
|
s.addText("Déployer et sécuriser une application déjà développée, dans un cadre professionnel et conforme au RGPD.",
|
|
{ x: 7.1, y: 0.95, w: 2.75, h: 1.4, fontSize: 12.5, color: C.white, fontFace: "Calibri", valign: "top", margin: 0 });
|
|
}
|
|
|
|
// ─── SLIDE 03 — Contexte & objectifs ────────────────────────
|
|
{
|
|
const s = pres.addSlide();
|
|
s.background = { color: C.lightBg };
|
|
header(s, 3, "Introduction", "Contexte & objectifs du bloc");
|
|
s.addText("L'application CESIZen (Web, Mobile, Wear OS) a été conçue et développée au BLOC 2. L'enjeu de ce bloc est de la mettre en service de façon fiable, automatisée et sécurisée.",
|
|
{ x: 0.65, y: 1.4, w: 8.8, h: 0.7, fontSize: 13, color: C.dark, fontFace: "Calibri", valign: "top", margin: 0 });
|
|
card(s, 0.65, 2.25, 4.35, 2.9, C.teal, "4 livrables attendus", [
|
|
"Outil de gestion de version", "Outil de gestion des évolutions",
|
|
"Plan de déploiement (CI/CD)", "Plan de sécurisation (RGPD, chiffrement)"]);
|
|
card(s, 5.15, 2.25, 4.3, 2.9, C.yellow, "Contexte du projet", [
|
|
"Projet (RE)Sources Relationnelles", "Données personnelles sensibles",
|
|
"Exigences ministérielles & RGPD", "Architecture MVC (Laravel 12)"]);
|
|
}
|
|
|
|
// ─── SLIDE 04 — Gestion de version ──────────────────────────
|
|
{
|
|
const s = pres.addSlide();
|
|
s.background = { color: C.lightBg };
|
|
header(s, 4, "Maintenance", "Gestion des versions : Git + Gitea");
|
|
card(s, 0.65, 1.45, 4.35, 3.7, C.teal, "Gitea auto-hébergé", [
|
|
"gitea.sam-coffre.duckdns.org", "Souveraineté totale des données",
|
|
"Très léger (~200 Mo RAM)", "CI/CD native intégrée",
|
|
"Open source & gratuit"]);
|
|
card(s, 5.15, 1.45, 4.3, 3.7, C.yellow, "Organisation Git", [
|
|
"Monorepo : WEB / mobile / Wear", "main : production",
|
|
"develop : intégration", "feature/* : via Pull Request",
|
|
"Conventional Commits + CHANGELOG"]);
|
|
s.addText("Pourquoi Gitea plutôt que GitHub/GitLab ? → maîtrise de l'hébergement des données, indispensable pour un projet ministériel.",
|
|
{ x: 0.65, y: 5.05, w: 8.8, h: 0.4, fontSize: 11, italic: true, color: C.gray, fontFace: "Calibri", margin: 0 });
|
|
}
|
|
|
|
// ─── SLIDE 05 — Évolutions & maintenance ────────────────────
|
|
{
|
|
const s = pres.addSlide();
|
|
s.background = { color: C.lightBg };
|
|
header(s, 5, "Maintenance", "Gestion des évolutions & incidents");
|
|
card(s, 0.65, 1.45, 4.35, 2.5, C.teal, "Gitea Issues + formulaire in-app", [
|
|
"🐞 Rapport d'incident (correctif)", "✨ Demande d'évolution (évolutif)",
|
|
"Formulaire du site → ticket Gitea (API)", "Champs structurés & priorité"]);
|
|
card(s, 5.15, 1.45, 4.3, 2.5, C.yellow, "Pilotage", [
|
|
"Labels + tableau Kanban", "Jalons = versions (SemVer)",
|
|
"Pull Request obligatoire"]);
|
|
// Flux
|
|
s.addText("Cycle de vie d'une demande", { x: 0.65, y: 4.15, w: 8.8, h: 0.3, fontSize: 12, bold: true, color: C.darkBg, fontFace: "Calibri", margin: 0 });
|
|
const steps = ["Nouveau", "Priorisé", "En cours", "En revue", "Déployé"];
|
|
steps.forEach((st, i) => {
|
|
const x = 0.65 + i * 1.78;
|
|
s.addShape(pres.shapes.RECTANGLE, { x, y: 4.5, w: 1.55, h: 0.55, fill: { color: i === 4 ? C.teal : C.navy }, line: { color: C.navy } });
|
|
s.addText(st, { x, y: 4.5, w: 1.55, h: 0.55, fontSize: 11, color: C.white, align: "center", valign: "middle", fontFace: "Calibri", margin: 0 });
|
|
if (i < 4) s.addText("→", { x: x + 1.5, y: 4.5, w: 0.32, h: 0.55, fontSize: 16, color: C.teal, align: "center", valign: "middle", margin: 0 });
|
|
});
|
|
}
|
|
|
|
// ─── SLIDE 06 — Plan de déploiement (diagramme) ─────────────
|
|
{
|
|
const s = pres.addSlide();
|
|
s.background = { color: C.lightBg };
|
|
header(s, 6, "Déploiement", "Environnements & hébergement");
|
|
s.addText([
|
|
{ text: "4 environnements : ", options: { bold: true } },
|
|
{ text: "Développement → Test/CI → Préproduction → Production", options: {} },
|
|
], { x: 0.65, y: 1.4, w: 8.8, h: 0.4, fontSize: 12.5, color: C.dark, fontFace: "Calibri", margin: 0 });
|
|
s.addImage({ path: "docs/img/diag_archi.png", x: 0.65, y: 1.95, w: 8.8, h: 2.68 });
|
|
s.addText("Production : cesizen.sam-coffre.duckdns.org — conteneurisée & orchestrée via Portainer, TLS géré par Nginx Proxy Manager.",
|
|
{ x: 0.65, y: 4.8, w: 8.8, h: 0.4, fontSize: 11, italic: true, color: C.gray, fontFace: "Calibri", margin: 0 });
|
|
}
|
|
|
|
// ─── SLIDE 07 — CI/CD (diagramme) ───────────────────────────
|
|
{
|
|
const s = pres.addSlide();
|
|
s.background = { color: C.lightBg };
|
|
header(s, 7, "Déploiement", "CI/CD automatisée — Gitea Actions");
|
|
s.addImage({ path: "docs/img/diag_cicd.png", x: 0.65, y: 1.4, w: 8.8, h: 3.5 });
|
|
s.addText("Un push lance style, analyse statique, tests & couverture ; une fusion sur main déclenche le déploiement SSH (mise à jour, conteneurs, migrations).",
|
|
{ x: 0.65, y: 4.95, w: 8.8, h: 0.4, fontSize: 11, italic: true, color: C.gray, fontFace: "Calibri", margin: 0 });
|
|
}
|
|
|
|
// ─── SLIDE 08 — Sécurité (fond sombre) ──────────────────────
|
|
{
|
|
const s = pres.addSlide();
|
|
s.background = { color: C.darkBg };
|
|
topBar(s);
|
|
s.addText("08", { x: 9.0, y: 0.11, w: 0.82, h: 0.38, fontSize: 14, color: C.teal, fontFace: "Calibri", bold: true, align: "right", margin: 0 });
|
|
s.addText("SÉCURITÉ", { x: 0.65, y: 0.13, w: 7, h: 0.26, fontSize: 9, color: "9CA3AF", fontFace: "Calibri", charSpacing: 2, margin: 0 });
|
|
s.addShape(pres.shapes.RECTANGLE, { x: 0.65, y: 0.44, w: 0.55, h: 0.07, fill: { color: C.yellow }, line: { color: C.yellow } });
|
|
s.addText("Plan de sécurisation & RGPD", { x: 0.65, y: 0.52, w: 8.7, h: 0.7, fontSize: 26, color: C.white, fontFace: "Calibri", margin: 0 });
|
|
hLine(s, 0.65, 1.25, 8.8, "374151");
|
|
|
|
const sec = [
|
|
["Applicatif", ["Auth Sanctum + rôles (RBAC)", "CSRF, validation, en-têtes HTTP", "CSP / HSTS / X-Frame-Options"]],
|
|
["Chiffrement", ["HTTPS/TLS (Let's Encrypt)", "Mots de passe : bcrypt", "Sessions chiffrées"]],
|
|
["RGPD", ["Minimisation & consentement", "Droit à l'effacement", "Anonymisation des stats"]],
|
|
["Qualité", ["Pint + tests PHPUnit", "Audit des dépendances (CI)", "Revues via Pull Request"]],
|
|
];
|
|
sec.forEach(([title, lines], i) => {
|
|
const x = 0.65 + (i % 2) * 4.5;
|
|
const y = 1.5 + Math.floor(i / 2) * 1.95;
|
|
s.addShape(pres.shapes.RECTANGLE, { x, y, w: 4.2, h: 1.75, fill: { color: C.navy }, line: { color: "374151", width: 1 } });
|
|
vLine(s, x, y, 1.75, C.teal);
|
|
s.addText(title, { x: x + 0.2, y: y + 0.12, w: 3.8, h: 0.35, fontSize: 14, bold: true, color: C.yellow, fontFace: "Calibri", margin: 0 });
|
|
s.addText(lines.map((t) => ({ text: t, options: { bullet: { code: "2022", indent: 12 }, color: "E5E7EB", fontSize: 11, paraSpaceAfter: 3 } })),
|
|
{ x: x + 0.22, y: y + 0.5, w: 3.8, h: 1.15, fontFace: "Calibri", valign: "top", margin: 0 });
|
|
});
|
|
}
|
|
|
|
// ─── SLIDE 09 — Analyse des risques (OWASP) ─────────────────
|
|
{
|
|
const s = pres.addSlide();
|
|
s.background = { color: C.lightBg };
|
|
header(s, 9, "Sécurité", "Analyse des risques & traitement");
|
|
const rows = [
|
|
["Injection SQL", "ORM Eloquent / requêtes préparées"],
|
|
["XSS", "Échappement Blade + CSP"],
|
|
["CSRF", "Jeton CSRF Laravel"],
|
|
["Vol de session", "HTTPS forcé, cookies Secure, HSTS"],
|
|
["Dépendances vulnérables", "composer audit / npm audit (CI)"],
|
|
["Force brute", "Rate limiting"],
|
|
];
|
|
// en-tête tableau
|
|
s.addShape(pres.shapes.RECTANGLE, { x: 0.65, y: 1.45, w: 4.3, h: 0.5, fill: { color: C.navy }, line: { color: C.navy } });
|
|
s.addShape(pres.shapes.RECTANGLE, { x: 4.95, y: 1.45, w: 4.5, h: 0.5, fill: { color: C.navy }, line: { color: C.navy } });
|
|
s.addText("Risque", { x: 0.8, y: 1.45, w: 4.1, h: 0.5, fontSize: 12, bold: true, color: C.white, valign: "middle", fontFace: "Calibri", margin: 0 });
|
|
s.addText("Mesure de prévention", { x: 5.1, y: 1.45, w: 4.3, h: 0.5, fontSize: 12, bold: true, color: C.white, valign: "middle", fontFace: "Calibri", margin: 0 });
|
|
rows.forEach((r, i) => {
|
|
const y = 1.95 + i * 0.5;
|
|
const bg = i % 2 ? "FFFFFF" : "EFF5F1";
|
|
s.addShape(pres.shapes.RECTANGLE, { x: 0.65, y, w: 4.3, h: 0.5, fill: { color: bg }, line: { color: C.border } });
|
|
s.addShape(pres.shapes.RECTANGLE, { x: 4.95, y, w: 4.5, h: 0.5, fill: { color: bg }, line: { color: C.border } });
|
|
s.addText(r[0], { x: 0.8, y, w: 4.1, h: 0.5, fontSize: 11, color: C.dark, valign: "middle", fontFace: "Calibri", margin: 0 });
|
|
s.addText(r[1], { x: 5.1, y, w: 4.3, h: 0.5, fontSize: 11, color: C.dark, valign: "middle", fontFace: "Calibri", margin: 0 });
|
|
});
|
|
}
|
|
|
|
// ─── SLIDE 10 — Bilan & livrables ───────────────────────────
|
|
{
|
|
const s = pres.addSlide();
|
|
s.background = { color: C.darkBg };
|
|
topBar(s);
|
|
s.addShape(pres.shapes.RECTANGLE, { x: 0.7, y: 0.6, w: 0.55, h: 0.09, fill: { color: C.yellow }, line: { color: C.yellow } });
|
|
s.addText("Bilan & livrables", { x: 0.7, y: 0.72, w: 8.6, h: 0.9, fontSize: 34, color: C.white, fontFace: "Calibri", margin: 0 });
|
|
hLine(s, 0.7, 1.7, 8.6, "374151");
|
|
const items = [
|
|
["Gestion de version", "Gitea auto-hébergé + stratégie de branches"],
|
|
["Évolutions & maintenance", "Gitea Issues, Kanban, SemVer, CHANGELOG"],
|
|
["Déploiement automatisé", "CI/CD Gitea Actions → Registry → Portainer"],
|
|
["Sécurité", "Chiffrement, en-têtes, RGPD, audits, tests"],
|
|
];
|
|
items.forEach((it, i) => {
|
|
const y = 2.0 + i * 0.78;
|
|
vLine(s, 0.7, y, 0.6, C.teal);
|
|
s.addText(it[0], { x: 0.9, y: y - 0.02, w: 3.6, h: 0.4, fontSize: 14, bold: true, color: C.yellow, fontFace: "Calibri", margin: 0 });
|
|
s.addText(it[1], { x: 4.4, y: y - 0.02, w: 5.2, h: 0.4, fontSize: 12.5, color: "E5E7EB", fontFace: "Calibri", margin: 0 });
|
|
});
|
|
s.addText("Une base industrialisée, reproductible et sécurisée. Merci de votre attention — place aux questions.",
|
|
{ x: 0.7, y: 5.25, w: 8.8, h: 0.4, fontSize: 12, italic: true, color: C.teal, fontFace: "Calibri", margin: 0 });
|
|
}
|
|
|
|
pres.writeFile({ fileName: "CESIZen_Presentation_Bloc3.pptx" }).then((f) => console.log("Généré :", f));
|