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:
commit
d7ec32a458
92 changed files with 17476 additions and 0 deletions
229
public/js/components.js
Normal file
229
public/js/components.js
Normal 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">×</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();
|
||||
}
|
||||
})();
|
||||
Loading…
Reference in a new issue