Variantes WordPress et proxy, banc d'essai
This commit is contained in:
parent
e422e2452e
commit
7fb4f34f5c
1 changed files with 181 additions and 0 deletions
181
build.py
Normal file
181
build.py
Normal file
|
|
@ -0,0 +1,181 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Pages d'erreur du portfolio (style yb2026), générées depuis un seul modèle.
|
||||||
|
- out/apache/<code>.html : servies par Apache (ErrorDocument), polices liées au thème
|
||||||
|
- out/npm/<code>.html : servies par NPM quand Apache est éteint (502/503/504), polices intégrées en base64
|
||||||
|
- out/template.php : même page en PHP pour wp_die() et wp-content/php-error.php
|
||||||
|
Relancer après modification : python3 ~/yb-erreurs/build.py && sudo ~/yb-erreurs/install.sh
|
||||||
|
"""
|
||||||
|
import base64, html, os
|
||||||
|
ROOT = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
FONTS = '/home/yass/wordpress/wp-content/themes/yb2026/assets/fonts'
|
||||||
|
|
||||||
|
CODES = {
|
||||||
|
400: ("Requête invalide", "Le navigateur a envoyé une requête que le serveur n'a pas su lire. Recharger la page suffit souvent."),
|
||||||
|
401: ("Authentification requise", "Cette zone demande des identifiants. Si tu n'en as pas, le reste du site est ouvert à tous."),
|
||||||
|
403: ("Accès interdit", "Cette zone est privée : le serveur refuse de l'afficher. Le reste du site, lui, est grand ouvert."),
|
||||||
|
404: ("Page introuvable", "Cette adresse ne mène nulle part. Elle a peut-être changé, ou il y a une faute de frappe."),
|
||||||
|
405: ("Méthode non autorisée", "Cette adresse n'accepte pas ce type de requête."),
|
||||||
|
408: ("Délai dépassé", "La requête a mis trop de temps à arriver. Vérifie ta connexion et réessaie."),
|
||||||
|
410: ("Page supprimée", "Ce contenu a été retiré définitivement."),
|
||||||
|
413: ("Envoi trop lourd", "Ce que tu as envoyé dépasse la taille autorisée par le serveur."),
|
||||||
|
414: ("Adresse trop longue", "L'adresse demandée est trop longue pour être traitée."),
|
||||||
|
429: ("Trop de requêtes", "Doucement : trop de demandes en peu de temps. Patiente un instant puis réessaie."),
|
||||||
|
500: ("Erreur interne", "Quelque chose a planté côté serveur. Ce n'est pas de ton fait, et c'est sûrement déjà en cours de correction."),
|
||||||
|
501: ("Non pris en charge", "Le serveur ne sait pas traiter cette demande."),
|
||||||
|
502: ("Le site ne répond pas", "Le serveur du site ne répond pas correctement pour le moment. Réessaie dans une minute."),
|
||||||
|
503: ("Maintenance en cours", "Le site redémarre ou est en maintenance. Il revient très vite, réessaie dans une minute."),
|
||||||
|
504: ("Délai dépassé", "Le serveur du site a mis trop de temps à répondre. Réessaie dans un instant."),
|
||||||
|
}
|
||||||
|
NPM_CODES = (502, 503, 504)
|
||||||
|
|
||||||
|
def font_css(inline):
|
||||||
|
faces = [("Archivo", "Archivo-100-900-latin.woff2", "100 900", "62% 125%"),
|
||||||
|
("JetBrains Mono", "JetBrainsMono-400-latin.woff2", "400 700", None)]
|
||||||
|
out = []
|
||||||
|
for fam, f, w, st in faces:
|
||||||
|
src = ("data:font/woff2;base64," + base64.b64encode(open(os.path.join(FONTS, f), 'rb').read()).decode()) if inline \
|
||||||
|
else "/wp-content/themes/yb2026/assets/fonts/" + f
|
||||||
|
out.append("@font-face{font-family:'%s';font-weight:%s;%sfont-display:swap;src:url(%s) format('woff2')}" % (fam, w, ("font-stretch:%s;" % st) if st else "", src))
|
||||||
|
return "\n".join(out)
|
||||||
|
|
||||||
|
CSS = r"""
|
||||||
|
:root{--bg:#0a0a0a;--ink:#f5f5f0;--lime:#d3fd50;--ink-rgb:245,245,240;--lime-rgb:211,253,80;--mut:rgba(245,245,240,.55);--b:rgba(245,245,240,.12);color-scheme:dark}
|
||||||
|
@media (prefers-color-scheme:light){:root:not([data-theme="dark"]){--bg:#f3f2ed;--ink:#111;--lime:#4a7300;--ink-rgb:17,17,17;--lime-rgb:74,115,0;--mut:rgba(17,17,17,.55);--b:rgba(17,17,17,.12);color-scheme:light}}
|
||||||
|
:root[data-theme="light"]{--bg:#f3f2ed;--ink:#111;--lime:#4a7300;--ink-rgb:17,17,17;--lime-rgb:74,115,0;--mut:rgba(17,17,17,.55);--b:rgba(17,17,17,.12);color-scheme:light}
|
||||||
|
*{box-sizing:border-box;margin:0;padding:0}
|
||||||
|
html,body{min-height:100%;background:var(--bg);overflow-x:clip}
|
||||||
|
body{color:var(--ink);font:400 16px/1.6 'Archivo',system-ui,sans-serif;display:flex;flex-direction:column;min-height:100vh;padding:0 clamp(20px,4vw,44px);overflow-x:hidden}
|
||||||
|
::selection{background:var(--lime);color:var(--bg)}
|
||||||
|
a{color:inherit;text-decoration:none}
|
||||||
|
.grain{position:fixed;inset:0;pointer-events:none;opacity:.05;z-index:5;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2'/%3E%3C/filter%3E%3Crect width='240' height='240' filter='url(%23n)'/%3E%3C/svg%3E")}
|
||||||
|
.top{display:flex;justify-content:space-between;align-items:center;padding:22px 0;border-bottom:1px solid var(--b);font:700 14px 'JetBrains Mono',monospace;letter-spacing:.05em}
|
||||||
|
.top b{color:var(--lime);font-weight:700}
|
||||||
|
.top span{font-weight:500;font-size:11.5px;letter-spacing:.18em;color:var(--mut)}
|
||||||
|
main{flex:1;display:flex;flex-direction:column;justify-content:center;gap:22px;padding:48px 0;position:relative;min-width:0}
|
||||||
|
.ring{position:absolute;right:-6vw;top:50%;width:min(56vw,620px);aspect-ratio:1;transform:translateY(-50%);border-radius:50%;pointer-events:none;
|
||||||
|
background:radial-gradient(circle,rgba(var(--lime-rgb),.10),transparent 62%);border:1px dashed rgba(var(--lime-rgb),.22);animation:spin 60s linear infinite}
|
||||||
|
.ring::after{content:'';position:absolute;inset:16%;border-radius:50%;border:1px solid rgba(var(--ink-rgb),.08)}
|
||||||
|
@keyframes spin{to{transform:translateY(-50%) rotate(360deg)}}
|
||||||
|
.kick{font:700 12px 'JetBrains Mono',monospace;letter-spacing:.22em;color:var(--lime)}
|
||||||
|
.code{font:900 clamp(110px,26vw,300px)/.82 'Archivo',sans-serif;font-stretch:118%;letter-spacing:-.05em;display:flex;gap:.04em;user-select:none}
|
||||||
|
.code span:nth-child(odd){color:var(--ink)}
|
||||||
|
.code span:nth-child(even){color:transparent;-webkit-text-stroke:2px var(--lime)}
|
||||||
|
h1{font:800 clamp(28px,4.4vw,52px)/1.05 'Archivo',sans-serif;letter-spacing:-.025em;text-wrap:balance;max-width:16ch}
|
||||||
|
p.msg{max-width:52ch;color:rgba(var(--ink-rgb),.78);font-size:17px}
|
||||||
|
.req{font:500 12.5px 'JetBrains Mono',monospace;color:var(--mut);display:flex;gap:10px;flex-wrap:wrap;align-items:center;max-width:100%}
|
||||||
|
.req code{font:inherit;color:var(--ink);background:rgba(var(--ink-rgb),.06);border:1px solid var(--b);padding:3px 8px;border-radius:6px;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||||
|
.req em{font-style:normal;color:var(--lime)}
|
||||||
|
.actions{display:flex;gap:12px;flex-wrap:wrap;margin-top:6px}
|
||||||
|
.btn{font:700 12.5px 'JetBrains Mono',monospace;letter-spacing:.14em;padding:14px 20px;border-radius:99px;border:1px solid var(--b);transition:transform .25s cubic-bezier(.3,1.5,.5,1),border-color .2s,color .2s,background .2s;cursor:pointer;background:none;color:var(--ink)}
|
||||||
|
.btn:hover{border-color:var(--lime);color:var(--lime);transform:translateY(-2px)}
|
||||||
|
.btn.main{background:var(--lime);border-color:var(--lime);color:var(--bg)}
|
||||||
|
.btn.main:hover{color:var(--bg)}
|
||||||
|
.btn:focus-visible{outline:2px solid var(--lime);outline-offset:3px}
|
||||||
|
footer{padding:20px 0 26px;border-top:1px solid var(--b);font:500 11px 'JetBrains Mono',monospace;letter-spacing:.16em;color:var(--mut);display:flex;justify-content:space-between;gap:12px;flex-wrap:wrap}
|
||||||
|
@media (max-width:640px){.ring{right:-30vw;width:90vw}.top span{display:none}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
def page(code, title, msg, inline_fonts, php=False):
|
||||||
|
digits = "".join("<span>%s</span>" % d for d in str(code))
|
||||||
|
e = (lambda s: s) if php else html.escape
|
||||||
|
head_title = "%s · %s" % (code, title) if not php else "<?php echo esc_html($yb_code . ' · ' . $yb_title); ?>"
|
||||||
|
t = title if not php else "<?php echo esc_html($yb_title); ?>"
|
||||||
|
m = e(msg) if not php else "<?php echo $yb_msg; ?>"
|
||||||
|
dg = digits if not php else "<?php foreach (str_split((string) $yb_code) as $d) echo '<span>' . esc_html($d) . '</span>'; ?>"
|
||||||
|
cd = str(code) if not php else "<?php echo esc_html($yb_code); ?>"
|
||||||
|
return """<!doctype html>
|
||||||
|
<html lang="fr">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="robots" content="noindex, nofollow">
|
||||||
|
<meta name="theme-color" content="#0a0a0a">
|
||||||
|
<title>%(head)s · Yasser Boulayoune</title>
|
||||||
|
<script>try{var t=localStorage.getItem('yb-theme');if(t==='light'||t==='dark')document.documentElement.setAttribute('data-theme',t)}catch(e){}</script>
|
||||||
|
<style>%(fonts)s%(css)s</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="grain" aria-hidden="true"></div>
|
||||||
|
<header class="top"><a href="/">YB<b>©</b>2026</a><span>YASSER BOULAYOUNE · PORTFOLIO</span></header>
|
||||||
|
<main>
|
||||||
|
<div class="ring" aria-hidden="true"></div>
|
||||||
|
<p class="kick">ERREUR %(cd)s</p>
|
||||||
|
<div class="code" aria-hidden="true">%(digits)s</div>
|
||||||
|
<h1>%(title)s</h1>
|
||||||
|
<p class="msg">%(msg)s</p>
|
||||||
|
<p class="req"><span>REQUÊTE</span><code id="yb-path">/</code><em>→ %(cd)s</em></p>
|
||||||
|
<div class="actions">
|
||||||
|
<a class="btn main" href="/">ACCUEIL →</a>
|
||||||
|
<button class="btn" type="button" id="yb-back">← RETOUR</button>
|
||||||
|
<a class="btn" href="/contact/">CONTACT</a>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
<footer><span>© 2026 YASSER BOULAYOUNE</span><span>RÉGION LYONNAISE</span></footer>
|
||||||
|
<script>
|
||||||
|
(function(){var p=document.getElementById('yb-path');if(p)p.textContent=location.pathname+location.search;
|
||||||
|
var b=document.getElementById('yb-back');if(b){if(history.length<2)b.hidden=true;b.addEventListener('click',function(){history.back()});}})();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
""" % {"head": head_title if php else html.escape(head_title), "fonts": font_css(inline_fonts), "css": CSS, "cd": cd, "digits": dg, "title": t if php else html.escape(t), "msg": m}
|
||||||
|
|
||||||
|
HUB_CSS = r"""
|
||||||
|
.hub{flex:1;padding:44px 0 60px;display:flex;flex-direction:column;gap:38px}
|
||||||
|
.hub h1{max-width:none}
|
||||||
|
.hub .lead{max-width:62ch;color:rgba(var(--ink-rgb),.75)}
|
||||||
|
.grp h2{font:700 11.5px 'JetBrains Mono',monospace;letter-spacing:.22em;color:var(--mut);padding-bottom:10px;border-bottom:1px solid var(--b);display:flex;gap:10px}
|
||||||
|
.grp h2 b{color:var(--lime)}
|
||||||
|
.grp p.note{margin-top:10px;font-size:14px;color:var(--mut);max-width:70ch}
|
||||||
|
.row{display:grid;grid-template-columns:96px 1fr auto;gap:18px;align-items:center;padding:16px 0;border-bottom:1px solid var(--b)}
|
||||||
|
.row .n{font:900 40px/1 'Archivo',sans-serif;font-stretch:115%;color:transparent;-webkit-text-stroke:1.5px var(--lime)}
|
||||||
|
.row .t{min-width:0}
|
||||||
|
.row .t strong{display:block;font:700 18px 'Archivo',sans-serif}
|
||||||
|
.row .t span{display:block;font:500 12px 'JetBrains Mono',monospace;color:var(--mut);margin-top:3px;overflow-wrap:anywhere}
|
||||||
|
.row form{margin:0}
|
||||||
|
.row .btn{white-space:nowrap}
|
||||||
|
@media (max-width:620px){.row{grid-template-columns:64px 1fr;}.row .n{font-size:30px}.row .go{grid-column:1/-1}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
def hub(inline_fonts):
|
||||||
|
real = [
|
||||||
|
(403, "Dossier privé (Apache)", "GET /wp-content/uploads/ · listage de dossier interdit", '<a class="btn main" href="/wp-content/uploads/">DÉCLENCHER →</a>'),
|
||||||
|
(403, "Fichier sensible (Apache)", "GET /wp-config.php · fichier protégé", '<a class="btn main" href="/wp-config.php">DÉCLENCHER →</a>'),
|
||||||
|
(403, "Dossier système (Apache)", "GET /wp-includes/", '<a class="btn main" href="/wp-includes/">DÉCLENCHER →</a>'),
|
||||||
|
(404, "Page introuvable (thème WordPress)", "GET /cette-page-nexiste-pas/ · c'est le 404 du thème, pas ces pages-ci", '<a class="btn main" href="/cette-page-nexiste-pas/">DÉCLENCHER →</a>'),
|
||||||
|
(403, "Refus WordPress (wp_die)", "POST /wp-comments-post.php · commentaire sur un article aux commentaires fermés",
|
||||||
|
'<form method="post" action="/wp-comments-post.php"><input type="hidden" name="comment_post_ID" value="375"><input type="hidden" name="comment" value="test"><input type="hidden" name="author" value="test"><input type="hidden" name="email" value="test@example.com"><button class="btn main" type="submit">DÉCLENCHER →</button></form>'),
|
||||||
|
]
|
||||||
|
prev = [(c, t, "Page Apache · /yb-erreur/%d.html" % c) for c, (t, m) in CODES.items()]
|
||||||
|
rows = lambda items: "".join('<div class="row"><span class="n">%s</span><div class="t"><strong>%s</strong><span>%s</span></div><div class="go">%s</div></div>' % i for i in items)
|
||||||
|
prev_rows = rows([(str(c), html.escape(t), html.escape(d), '<a class="btn" href="/yb-erreur/%d.html">APERÇU →</a>' % c) for c, t, d in prev] +
|
||||||
|
[("500", "Erreur critique (plantage PHP)", "Drop-in wp-content/php-error.php", '<a class="btn" href="/yb-erreur/critique.html">APERÇU →</a>')])
|
||||||
|
return """<!doctype html>
|
||||||
|
<html lang="fr"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="robots" content="noindex, nofollow"><meta name="theme-color" content="#0a0a0a">
|
||||||
|
<title>Pages d'erreur · test</title>
|
||||||
|
<script>try{var t=localStorage.getItem('yb-theme');if(t==='light'||t==='dark')document.documentElement.setAttribute('data-theme',t)}catch(e){}</script>
|
||||||
|
<style>%s%s%s</style></head>
|
||||||
|
<body><div class="grain" aria-hidden="true"></div>
|
||||||
|
<header class="top"><a href="/">YB<b>©</b>2026</a><span>BANC D'ESSAI · PAGES D'ERREUR</span></header>
|
||||||
|
<div class="hub">
|
||||||
|
<div><p class="kick">TERRAIN DE TEST</p><h1>Toutes les pages d'erreur</h1></div>
|
||||||
|
<p class="lead">Les liens du haut provoquent de vraies erreurs sur le site, avec le vrai code HTTP. Ceux du bas ouvrent chaque page en aperçu, y compris celles qu'on ne peut pas déclencher sans casser le site.</p>
|
||||||
|
<section class="grp"><h2><b>%02d</b> ERREURS RÉELLES</h2>%s</section>
|
||||||
|
<section class="grp"><h2><b>%02d</b> APERÇUS</h2>%s
|
||||||
|
<p class="note">Les 502, 503 et 504 s'affichent aussi quand le site est complètement éteint : c'est alors le proxy (NPM) qui les sert, avec le même design. Les aperçus renvoient un code 200, c'est normal.</p></section>
|
||||||
|
</div>
|
||||||
|
<footer><span>© 2026 YASSER BOULAYOUNE</span><span>PAGE NON INDEXÉE</span></footer>
|
||||||
|
</body></html>
|
||||||
|
""" % (font_css(inline_fonts), CSS, HUB_CSS, len(real), rows(real), len(prev) + 1, prev_rows)
|
||||||
|
|
||||||
|
os.makedirs(os.path.join(ROOT, "out/apache"), exist_ok=True)
|
||||||
|
os.makedirs(os.path.join(ROOT, "out/npm"), exist_ok=True)
|
||||||
|
for c, (t, m) in CODES.items():
|
||||||
|
open(os.path.join(ROOT, "out/apache/%d.html" % c), "w").write(page(c, t, m, False))
|
||||||
|
if c in NPM_CODES:
|
||||||
|
open(os.path.join(ROOT, "out/npm/%d.html" % c), "w").write(page(c, t, m, True))
|
||||||
|
open(os.path.join(ROOT, "out/apache/critique.html"), "w").write(page(500, "Erreur critique", "Le site a rencontré un problème technique. Ce n'est pas de ton fait et c'est sûrement déjà en cours de correction : réessaie dans quelques minutes.", False))
|
||||||
|
open(os.path.join(ROOT, "out/apache/index.html"), "w").write(hub(False))
|
||||||
|
open(os.path.join(ROOT, "out/template.php"), "w").write("<?php\n/* Généré par ~/yb-erreurs/build.py : ne pas modifier à la main. Variables : $yb_code, $yb_title, $yb_msg (HTML sûr). */\n?>" + page(0, "", "", False, php=True))
|
||||||
|
print("ok", len(CODES), "pages apache,", len(NPM_CODES), "pages npm, 1 modèle php")
|
||||||
Loading…
Add table
Add a link
Reference in a new issue