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 d7ec32a458
92 changed files with 17476 additions and 0 deletions

229
public/js/components.js Normal file
View file

@ -0,0 +1,229 @@
(function () {
var SVG = '<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>';
var BRAND = '<span class="brand-mark" aria-hidden="true">' + SVG + '</span><span class="brand-name">Zektyc</span>';
var SUN = '<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>';
var MOON = '<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>';
var THEME_BTN = '<button type="button" class="theme-btn" aria-label="Theme">' + MOON + SUN + '</button>';
function path() { return location.pathname; }
function lang() { return /^\/en(\/|$)/.test(path()) ? 'en' : 'fr'; }
function isHome() { var p = path(); return p === '/' || p === '/index.html' || p === '/en/' || p === '/en/index.html'; }
function isFpTest() { return /\/fingerprint-test/.test(path()); }
function isSocial() { return /\/social/.test(path()); }
function frLink() {
var p = path();
if (p === '/en/') return '/';
var m = p.match(/^(\/[^/]+)\/en\/?$/);
if (m) return m[1] + '/';
return p;
}
function enLink() {
var p = path();
if (p === '/' || p === '/index.html') return '/en/';
if (/\/en\/?$/.test(p)) return p;
return p.replace(/\/?$/, '/en/');
}
function buildHeader() {
var h = lang();
var skip = h === 'fr' ? 'Aller au contenu' : 'Skip to content';
var html = '<a class="skip-link" href="#main">' + skip + '</a>';
html += '<a class="brand" href="/">' + BRAND + '</a>';
html += '<input type="checkbox" id="nav-toggle" class="nav-toggle-input" />';
html += '<label for="nav-toggle" class="nav-burger" aria-label="Menu">';
html += '<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>';
html += '<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>';
html += '</label>';
if (isSocial()) {
html += '<nav id="nav" class="site-nav" aria-label="Navigation">';
html += '<div class="lang-switch" role="group" aria-label="Langue / Language">';
html += '<button type="button" data-lang="fr" class="lang-btn' + (h === 'fr' ? ' active' : '') + '">' + (h === 'fr' ? '<strong>FR</strong>' : 'FR') + '</button>';
html += '<button type="button" data-lang="en" class="lang-btn' + (h === 'en' ? ' active' : '') + '">' + (h === 'en' ? '<strong>EN</strong>' : 'EN') + '</button>';
html += '</div>';
html += THEME_BTN;
html += '</nav>';
} else {
html += '<nav class="site-nav" aria-label="Navigation">';
html += '<a href="/blog" data-lang-path="/blog" data-i18n="nav.blog">Blog</a>';
html += '<a href="/projets" data-lang-path="/projets" data-i18n="nav.projects">Projets</a>';
html += '<a href="/equipe" data-lang-path="/equipe" data-i18n="nav.team">\u00c9quipe</a>';
if (isHome()) {
html += '<a href="#principes" data-i18n="nav.principes">Nos principes</a>';
html += '<a href="#faq" data-i18n="nav.faq">FAQ</a>';
html += '<a href="#contact" data-i18n="nav.contact">Contact</a>';
}
html += '<div class="lang-switch" role="group" aria-label="Langue / Language">';
html += '<button type="button" data-lang="fr" class="lang-btn' + (h === 'fr' ? ' active' : '') + '">' + (h === 'fr' ? '<strong>FR</strong>' : 'FR') + '</button>';
html += '<button type="button" data-lang="en" class="lang-btn' + (h === 'en' ? ' active' : '') + '">' + (h === 'en' ? '<strong>EN</strong>' : 'EN') + '</button>';
html += '</div>';
html += THEME_BTN;
html += '</nav>';
}
return '<header class="site-header">' + html + '</header>';
}
function buildFooter() {
var h = lang();
var tos = h === 'fr' ? "Conditions d\u2019utilisation" : 'Terms of Service';
var priv = h === 'fr' ? 'Confidentialit\u00e9' : 'Privacy';
var note = h === 'fr' ? 'Ce site ne collecte rien. Aucun cookie, aucun tracker.' : 'This site collects nothing. No cookies, no trackers.';
var tosHref = h === 'fr' ? '/tos' : '/en/tos';
var privHref = h === 'fr' ? '/privacy-policy' : '/en/privacy-policy';
var html = '';
if (isSocial()) {
var backText = h === 'fr' ? 'Retour au site' : 'Back to site';
html += '<p><a href="/" class="footer-back">' + backText + ' \u2190</a></p>';
}
html += '<p>Zektyc \u00b7 France</p>';
html += '<p class="footer-note">' + note + '</p>';
html += '<nav class="footer-nav" aria-label="Legal">';
html += '<a rel="me" href="https://piaille.fr/@zektyc"><img src="/icons/mastodon.svg" alt="" width="14" height="14" class="footer-icon" /> Mastodon</a>';
html += '<span class="footer-sep" aria-hidden="true">\u00b7</span>';
html += '<a href="' + tosHref + '">' + tos + '</a>';
html += '<span class="footer-sep" aria-hidden="true">\u00b7</span>';
html += '<a href="' + privHref + '">' + priv + '</a>';
html += '<span class="footer-sep" aria-hidden="true">\u00b7</span>';
html += '<a href="/docs/maxspeed">API</a>';
html += '<span class="footer-sep" aria-hidden="true">\u00b7</span>';
html += '<a href="/.well-known/security.txt">security.txt</a>';
html += '<span class="footer-sep" aria-hidden="true">\u00b7</span>';
html += '<a href="http://l5cbkqmok4rnkhbtpltmtlf3pa5i5rfw23z2mtj5tugpo3quta3b5hqd.onion">Tor</a>';
html += '</nav>';
return '<footer class="site-footer">' + html + '</footer>';
}
function inject() {
var hs = document.getElementById('h');
var fs = document.getElementById('f');
if (hs) hs.outerHTML = buildHeader();
if (fs) fs.outerHTML = buildFooter();
var nav = document.getElementById('nav-toggle');
if (nav) {
document.addEventListener('click', function (e) {
if (e.target.closest && e.target.closest('.site-nav')) nav.checked = false;
});
}
}
function isTorBrowser() {
var dbg = {
hostname: location.hostname,
userAgent: navigator.userAgent,
hasTorSensor: !!window.TorSensor,
uaMatch: /TorBrowser|Tor\/|Tor Project/i.test(navigator.userAgent),
timezoneOffset: new Date().getTimezoneOffset(),
gmt: (new Date().getTimezoneOffset() === 0),
screenW: window.screen && window.screen.width,
screenH: window.screen && window.screen.height,
innerW: window.innerWidth,
innerH: window.innerHeight,
letterboxed: !!(window.screen &&
window.screen.width === window.innerWidth &&
window.screen.height === window.innerHeight),
pluginCount: (navigator.plugins && navigator.plugins.length) || 0,
noPlugins: !navigator.plugins || navigator.plugins.length === 0,
vendor: navigator.vendor
};
if (isOnion()) {
dbg.result = 'onion:' + location.hostname;
console.log('[TorToast] on .onion, skip', dbg);
return true;
}
if (dbg.hasTorSensor) {
dbg.result = 'TorSensor';
console.log('[TorToast] detected via TorSensor', dbg);
return true;
}
if (dbg.uaMatch) {
dbg.result = 'userAgent';
console.log('[TorToast] detected via userAgent', dbg);
return true;
}
if (dbg.gmt && dbg.letterboxed) {
dbg.result = 'gmt+letterbox';
console.log('[TorToast] detected via gmt+letterbox', dbg);
return true;
}
if (dbg.gmt && dbg.letterboxed && dbg.noPlugins) {
dbg.result = 'gmt+letterbox+noPlugins';
console.log('[TorToast] detected via gmt+letterbox+noPlugins', dbg);
return true;
}
if (dbg.vendor === '' && dbg.gmt && dbg.noPlugins) {
dbg.result = 'vendorEmpty+gmt+noPlugins';
console.log('[TorToast] detected via vendorEmpty+gmt+noPlugins', dbg);
return true;
}
if (dbg.gmt && dbg.letterboxed && dbg.vendor === '') {
dbg.result = 'gmt+letterbox+vendorEmpty';
console.log('[TorToast] detected via gmt+letterbox+vendorEmpty', dbg);
return true;
}
dbg.result = 'NOT tor';
console.log('[TorToast] not detected as Tor', dbg);
return false;
}
function isOnion() {
return location.hostname.indexOf('.onion') !== -1;
}
function showTorToast() {
if (!isTorBrowser() || isOnion()) return;
var onionBase = 'http://l5cbkqmok4rnkhbtpltmtlf3pa5i5rfw23z2mtj5tugpo3quta3b5hqd.onion';
var path = location.pathname + location.search + location.hash;
var onionUrl = onionBase + path;
var toast = document.createElement('div');
toast.className = 'tor-toast';
toast.setAttribute('role', 'alert');
var h = lang();
var msg = h === 'fr'
? 'Vous utilisez Tor. Acc\u00e9dez \u00e0 la version .onion pour plus de s\u00e9curit\u00e9.'
: 'You are using Tor. Access the .onion version for better security.';
toast.innerHTML =
'<p>' + msg + '</p>' +
'<a href="' + onionUrl + '" class="tor-toast-btn">' + (h === 'fr' ? 'Ouvrir en .onion' : 'Open .onion') + '</a>' +
'<button type="button" class="tor-toast-close" aria-label="Fermer">&times;</button>';
document.body.appendChild(toast);
toast.querySelector('.tor-toast-close').addEventListener('click', function () {
toast.remove();
});
setTimeout(function () { toast.remove(); }, 30000);
}
function boot() {
inject();
showTorToast();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', boot);
} else {
boot();
}
})();