diff --git a/frontend/src/tools/devtools.jsx b/frontend/src/tools/devtools.jsx index 515b5b6..852ecee 100644 --- a/frontend/src/tools/devtools.jsx +++ b/frontend/src/tools/devtools.jsx @@ -786,6 +786,140 @@ function DatetimeRechner({ mobile }) { ); } +// ─────────────────────────────────────────────────────────────── Key-Gen ── + +const KEY_TYPES = [ + { id: 'laravel', label: 'Laravel APP_KEY', desc: 'base64, 32 Byte', gen: () => 'base64:' + btoa(String.fromCharCode(...crypto.getRandomValues(new Uint8Array(32)))) }, + { id: 'jwt_hs256', label: 'JWT Secret (HS256)', desc: '64 Byte hex', gen: () => hex(64) }, + { id: 'jwt_hs512', label: 'JWT Secret (HS512)', desc: '128 Byte hex', gen: () => hex(128) }, + { id: 'aes256', label: 'AES-256 Key', desc: '32 Byte base64', gen: () => b64(32) }, + { id: 'aes128', label: 'AES-128 Key', desc: '16 Byte base64', gen: () => b64(16) }, + { id: 'secret32', label: 'Generic Secret (32B)', desc: '32 Byte hex', gen: () => hex(32) }, + { id: 'secret64', label: 'Generic Secret (64B)', desc: '64 Byte hex', gen: () => hex(64) }, + { id: 'passwd', label: 'Passwort (stark)', desc: '20 Zeichen, alle Typen', gen: () => genPasswd(20) }, + { id: 'passwd16', label: 'Passwort (kurz)', desc: '16 Zeichen, alle Typen', gen: () => genPasswd(16) }, + { id: 'uuid', label: 'UUID v4', desc: 'zufällige UUID', gen: () => crypto.randomUUID() }, + { id: 'nanoid', label: 'NanoID (21)', desc: 'URL-safe, 21 Zeichen', gen: () => nanoid(21) }, + { id: 'nanoid32', label: 'NanoID (32)', desc: 'URL-safe, 32 Zeichen', gen: () => nanoid(32) }, + { id: 'docker_pw', label: 'DB-Passwort (Docker)', desc: '24 Zeichen, alnum+sonder',gen: () => genPasswd(24) }, + { id: 'hex16', label: 'Hex Token (16B)', desc: '32 Hex-Zeichen', gen: () => hex(16) }, + { id: 'hex32', label: 'Hex Token (32B)', desc: '64 Hex-Zeichen', gen: () => hex(32) }, + { id: 'b64_32', label: 'Base64 Token (32B)', desc: '32 Byte base64', gen: () => b64(32) }, + { id: 'b64_64', label: 'Base64 Token (64B)', desc: '64 Byte base64', gen: () => b64(64) }, + { id: 'b64url_32', label: 'Base64url Token (32B)', desc: '32 Byte base64url', gen: () => b64url(32) }, +]; + +function hex(bytes) { + return Array.from(crypto.getRandomValues(new Uint8Array(bytes))).map(b=>b.toString(16).padStart(2,'0')).join(''); +} +function b64(bytes) { + return btoa(String.fromCharCode(...crypto.getRandomValues(new Uint8Array(bytes)))); +} +function b64url(bytes) { + return b64(bytes).replace(/\+/g,'-').replace(/\//g,'_').replace(/=+$/,''); +} +function nanoid(len) { + const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'; + const rnd = crypto.getRandomValues(new Uint8Array(len)); + return Array.from(rnd).map(b => chars[b % 64]).join(''); +} +function genPasswd(len) { + const sets = ['ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz','0123456789','!@#$%^&*()-_=+[]{}']; + const all = sets.join(''); + const rnd = crypto.getRandomValues(new Uint8Array(len + 8)); + let result = sets.map(s => s[rnd[sets.indexOf(s)] % s.length]); // guarantee 1 from each set + let idx = 4; + while (result.length < len) { result.push(all[rnd[idx++ % rnd.length] % all.length]); } + // shuffle + for (let i = result.length - 1; i > 0; i--) { + const j = rnd[(i * 3) % rnd.length] % (i + 1); + [result[i], result[j]] = [result[j], result[i]]; + } + return result.join(''); +} + +function KeyGen({ toast }) { + const [keys, setKeys] = useState({}); + const [copied, setCopied] = useState(null); + + function generate(id) { + const kt = KEY_TYPES.find(k => k.id === id); + if (!kt) return; + setKeys(p => ({ ...p, [id]: kt.gen() })); + } + + function generateAll() { + const all = {}; + KEY_TYPES.forEach(kt => { all[kt.id] = kt.gen(); }); + setKeys(all); + } + + function clearAll() { setKeys({}); } + + async function copyKey(id, val) { + try { + await navigator.clipboard.writeText(val); + setCopied(id); + setTimeout(() => setCopied(null), 1800); + } catch { toast('Kopieren fehlgeschlagen'); } + } + + return ( +
+ {keys[kt.id]}
+
+
+ {copied===kt.id ? '✓' : '⎘'}
+
+