chore: checkpoint initial — Zektyc dataset, avant humanisation

- site vitrine + fingerprint test + zmap + z-panel
- remplacement duckdns -> riricdev.tail5ea5cd.ts.net (canonical/og/alternates/docs)
- suppression du token DuckDNS (plus utilisé)
This commit is contained in:
riricdev 2026-09-06 13:03:25 +02:00
commit 8e7ec41f18
104 changed files with 19256 additions and 0 deletions

107
src/docs.ts Normal file
View file

@ -0,0 +1,107 @@
import { marked } from "marked"
import { readFileSync } from "node:fs"
import { join, normalize } from "node:path"
const SITE_URL = "https://riricdev.tail5ea5cd.ts.net"
// Layout de la page de documentation (navigable, sections).
function layout(title: string, description: string, body: string, sidebar: string): string {
return `<!doctype html>
<html lang="fr">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="${description}" />
<meta name="robots" content="index, follow" />
<title>${title} · Zektyc</title>
<link rel="canonical" href="${SITE_URL}/docs/maxspeed" />
<meta name="theme-color" content="#070a0f" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="Zektyc" />
<meta property="og:title" content="${title}" />
<meta property="og:description" content="${description}" />
<meta property="og:url" content="${SITE_URL}/docs/maxspeed" />
<meta property="og:image" content="${SITE_URL}/og-image.png" />
<meta property="og:locale" content="fr_FR" />
<meta name="twitter:card" content="summary_large_image" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="shortcut icon" href="/favicon.ico" />
<link rel="me" href="https://piaille.fr/@zektyc" />
<link rel="stylesheet" href="/css/style.css?v=7" />
<style>
.docs-wrap { display: flex; gap: 32px; align-items: flex-start; max-width: 1180px; margin: 0 auto; padding: 40px 24px; }
.docs-side { flex: 0 0 240px; position: sticky; top: 24px; font-size: 14px; }
.docs-side h3 { margin: 0 0 10px; font-size: 12px; text-transform: uppercase; letter-spacing: 0.08em; opacity: 0.6; }
.docs-side ul { list-style: none; margin: 0; padding: 0; }
.docs-side li { margin: 4px 0; }
.docs-side a { color: inherit; text-decoration: none; opacity: 0.85; }
.docs-side a:hover { opacity: 1; text-decoration: underline; }
.docs-side a.active { opacity: 1; font-weight: 600; }
.docs-body { flex: 1 1 auto; min-width: 0; line-height: 1.7; }
.docs-body h1 { font-size: 1.9em; border-bottom: 1px solid #222; padding-bottom: 12px; }
.docs-body h2 { font-size: 1.35em; margin-top: 2.2em; border-bottom: 1px solid #1e1e1e; padding-bottom: 6px; }
.docs-body h3 { font-size: 1.1em; margin-top: 1.6em; }
.docs-body pre { background: #0d1117; border: 1px solid #222; border-radius: 8px; padding: 14px; overflow: auto; font-size: 13px; }
.docs-body code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.docs-body :not(pre) > code { background: #161b22; padding: 2px 5px; border-radius: 4px; font-size: 0.9em; }
.docs-body table { border-collapse: collapse; width: 100%; margin: 16px 0; font-size: 14px; }
.docs-body th, .docs-body td { border: 1px solid #24262b; padding: 8px 12px; text-align: left; }
.docs-body th { background: #0d1117; }
.docs-body blockquote { border-left: 3px solid #333; margin: 16px 0; padding: 4px 16px; opacity: 0.85; }
@media (max-width: 760px) { .docs-wrap { flex-direction: column; } .docs-side { position: static; flex: auto; } }
</style>
<script src="/js/theme.js" defer></script>
<script src="/js/i18n.js" defer></script>
<script>document.addEventListener("click",function(e){var t=e.target.closest?e.target.closest(".site-nav"):null;if(t){var n=document.getElementById("nav-toggle");if(n)n.checked=false}})</script>
</head>
<body>
<header class="site-header">
<a class="skip-link" href="#main">Aller au contenu</a>
<a class="brand" href="/"><span class="brand-mark" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 16" role="presentation" focusable="false"><path d="M5 4h22L5 12h22" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></svg></span><span class="brand-name">Zektyc</span></a>
<input type="checkbox" id="nav-toggle" class="nav-toggle-input" />
<label for="nav-toggle" class="nav-burger" aria-label="Menu">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M4 6h16M4 12h16M4 18h16"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg>
</label>
<nav class="site-nav" aria-label="Navigation">
<div class="lang-switch" role="group" aria-label="Langue / Language">
<button type="button" data-lang="fr" class="lang-btn active"><strong>FR</strong></button>
<button type="button" data-lang="en" class="lang-btn">EN</button>
</div>
<button type="button" class="theme-btn" aria-label="Theme">
<svg class="icon-moon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>
<svg class="icon-sun" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg>
</button>
</nav>
</header>
<main id="main" class="docs-wrap">
<nav class="docs-side" aria-label="Documentation">
<h3>API</h3>
<ul>
<li><a href="/docs/maxspeed" class="active">Maxspeed (vitesses)</a></li>
</ul>
</nav>
<article class="docs-body">
${body}
</article>
</main>
<footer class="site-footer"></footer>
</body>
</html>`
}
const DOCS_DIR = normalize(join(import.meta.dir))
export function docsPage(service: string): string | null {
if (service !== "maxspeed") return null
const md = readFileSync(join(DOCS_DIR, "maxspeed-doc.md"), "utf8")
const body = marked.parse(md) as string
return layout(
"API Maxspeed",
"API publique des limitations de vitesse (maxspeed) en Europe, depuis OpenStreetMap. Endpoints point & route, exemples, performance.",
body,
"",
)
}