alotta good shit

This commit is contained in:
2026-09-19 06:20:37 +02:00
parent 74f7884525
commit 1477d56658
45 changed files with 4363 additions and 2069 deletions
+92 -28
View File
@@ -356,6 +356,7 @@
</fieldset>
<!-- 4. Comments & Discussion -->
@if(enable_comments)
<fieldset style="border: 1px solid var(--nav-border-color); padding: 12px 16px; border-radius: 6px; margin-bottom: 20px; background: rgba(0,0,0,0.06);">
<legend style="width: auto; padding: 0 8px; font-size: 1.05em; font-weight: bold;"><i class="fa-solid fa-comments"></i> Comments & Discussion</legend>
@@ -390,6 +391,7 @@
<small class="text-muted" style="margin-left: 25px;">{{ t('settings.embed_yt_hint') }}</small>
</div>
</fieldset>
@endif
<!-- 5. Content Preferences & Blurring -->
<fieldset style="border: 1px solid var(--nav-border-color); padding: 12px 16px; border-radius: 6px; margin-bottom: 20px; background: rgba(0,0,0,0.06);">
@@ -528,30 +530,20 @@
</div>
@if(session.is_anon)
<!-- ═══════════════════════════════ ANONYMOUS SSH IDENTITY ═══════════════════════════════ -->
<h2 id="anon-ssh"><i class="fa-solid fa-key"></i> Anonymous SSH Identity</h2>
<!-- ═══════════════════════════════ ANONYMOUS PASSKEY IDENTITY ═══════════════════════════════ -->
<h2 id="anon-passkey"><i class="fa-solid fa-fingerprint"></i> Anonymous Passkey Identity</h2>
<div class="account-settings-wrapper" style="background: rgba(0,0,0,0.1); padding: 20px; border-radius: 6px; border: 1px solid var(--nav-border-color); margin-bottom: 30px;">
<p style="color: var(--text-muted); margin-bottom: 16px;">
You are currently browsing anonymously using an OpenSSH Ed25519 keypair. Your favorites and comments are tied to this cryptographic identity without requiring a username, password, or email.
You are browsing anonymously. Your identity is protected by a <strong>passkey</strong> stored in your OS or password manager. No private key ever touches this browser's storage.
</p>
<div style="margin-bottom: 16px;">
<label style="font-weight: bold; color: var(--text-muted); display: block; margin-bottom: 6px;">OpenSSH SHA256 Fingerprint</label>
<code id="settings-anon-fp" style="background: rgba(0,0,0,0.3); padding: 6px 12px; border-radius: 4px; display: inline-block; color: #5bc0be; font-family: monospace;">{{ session.fingerprint || 'Loading...' }}</code>
</div>
<div style="margin-bottom: 20px;">
<label style="font-weight: bold; color: var(--text-muted); display: block; margin-bottom: 6px;">OpenSSH Public Key</label>
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
<input type="text" id="settings-anon-pubkey" readonly class="input" style="flex: 1; min-width: 250px; font-family: monospace; font-size: 0.85em;" value="" placeholder="Loading public key...">
<button type="button" class="button" onclick="if(window.f0ckAnonSSH) window.f0ckAnonSSH.copyPublicKey();"><i class="fa-solid fa-copy"></i> Copy</button>
</div>
<label style="font-weight: bold; color: var(--text-muted); display: block; margin-bottom: 6px;">Short Fingerprint</label>
<code id="settings-anon-fp" style="background: rgba(0,0,0,0.3); padding: 6px 12px; border-radius: 4px; display: inline-block; color: #5bc0be; font-family: monospace;">{{ session.fingerprint ? session.fingerprint.slice(7, 15) : 'Loading...' }}</code>
</div>
<div style="display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 20px;">
<button type="button" class="button button-primary" onclick="if(window.f0ckAnonSSH) window.f0ckAnonSSH.downloadPrivateKey();"><i class="fa-solid fa-download"></i> Download id_ed25519</button>
<button type="button" class="button" onclick="if(window.f0ckAnonSSH) window.f0ckAnonSSH.downloadPublicKey();"><i class="fa-solid fa-download"></i> Download id_ed25519.pub</button>
<button type="button" class="button" onclick="const m = document.getElementById('anon-ssh-modal'); if(m) m.style.display='flex';"><i class="fa-solid fa-key"></i> Import / Supply Key</button>
<button type="button" class="button button-primary" onclick="if(window.f0ckAnonPasskey) { window.f0ckAnonPasskey.openModal(); } else { const m = document.getElementById('anon-passkey-modal'); if(m) m.style.display='flex'; }"><i class="fa-solid fa-fingerprint"></i> Manage Passkey</button>
</div>
<div style="padding: 12px 16px; background: rgba(255,255,255,0.03); border-radius: 6px; border: 1px solid rgba(255,255,255,0.08); font-size: 0.88em; color: var(--text-muted);">
@@ -559,20 +551,92 @@
<strong>Want full features?</strong> Registered accounts can upload items, customize avatars, create API keys, and invite friends. You can <a href="#" onclick="event.preventDefault(); const m=document.getElementById('register-modal'); if(m) m.style.display='flex';" style="color: var(--accent); text-decoration: underline;">register an account</a> at any time.
</div>
</div>
@endif
@if(!session.is_anon && session)
<!-- ═══════════════════════════════ PASSKEYS ═══════════════════════════════ -->
<h2 id="passkeys"><i class="fa-solid fa-fingerprint"></i> Passkeys</h2>
<div class="account-settings-wrapper" style="background: rgba(0,0,0,0.1); padding: 20px; border-radius: 6px; border: 1px solid var(--nav-border-color); margin-bottom: 30px;">
<p style="color: var(--text-muted); margin-bottom: 16px;">
Passkeys let you sign in without a password using your OS, Bitwarden, or any compatible password manager. They are phishing-resistant and device-bound.
</p>
<div id="settings-passkey-list" style="margin-bottom: 16px;">
<p style="color: var(--text-muted); font-size: 0.9em;">Loading...</p>
</div>
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
<button type="button" class="button button-primary" id="settings-passkey-add-btn" onclick="settingsPasskeyAdd()">
<i class="fa-solid fa-plus"></i> Add a passkey
</button>
</div>
<div id="settings-passkey-status" style="margin-top: 10px; font-size: 0.88em; display: none;"></div>
</div>
<script>
(function() {
const syncKey = () => {
if (window.f0ckAnonSSH) {
const pk = document.getElementById('settings-anon-pubkey');
if (pk && window.f0ckAnonSSH.pubkey) pk.value = window.f0ckAnonSSH.pubkey;
const fp = document.getElementById('settings-anon-fp');
if (fp && window.f0ckAnonSSH.fingerprint) fp.textContent = window.f0ckAnonSSH.fingerprint;
(function() {
async function loadPasskeys() {
const container = document.getElementById('settings-passkey-list');
if (!container) return;
try {
const res = await fetch('/api/v2/settings/passkeys');
const data = await res.json();
const passkeys = data.passkeys || [];
if (passkeys.length === 0) {
container.innerHTML = '<p style="color: var(--text-muted); font-size: 0.9em;">No passkeys registered yet.</p>';
return;
}
};
syncKey();
window.addEventListener('f0ck:anon_session_ready', syncKey);
document.addEventListener('DOMContentLoaded', syncKey);
})();
container.innerHTML = passkeys.map(function(pk) {
return '<div style="display: flex; align-items: center; gap: 10px; padding: 10px 12px; background: rgba(0,0,0,0.25); border-radius: 5px; border: 1px solid rgba(255,255,255,0.07); margin-bottom: 8px;">' +
'<i class="fa-solid fa-key" style="color: var(--accent); font-size: 1.1em; flex-shrink: 0;"></i>' +
'<div style="flex: 1; min-width: 0;">' +
'<div style="font-weight: 600; font-size: 0.9em;">' + escHtml(pk.name || 'Passkey') + '</div>' +
'<div style="font-size: 0.78em; color: var(--text-muted);">Added ' + new Date(pk.created_at).toLocaleDateString() + ' &nbsp;·&nbsp; Last used ' + new Date(pk.last_used).toLocaleDateString() + '</div>' +
'</div>' +
'<button class="button button-danger" style="font-size: 0.8em; padding: 4px 10px;" onclick="settingsPasskeyDelete(' + JSON.stringify(pk.credential_id) + ')"><i class="fa-solid fa-trash"></i></button>' +
'</div>';
}).join('');
} catch (e) {
container.innerHTML = '<p style="color: #e06c75; font-size: 0.9em;">Failed to load passkeys.</p>';
}
}
function escHtml(s) { return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;'); }
window.settingsPasskeyAdd = async function() {
var status = document.getElementById('settings-passkey-status');
var btn = document.getElementById('settings-passkey-add-btn');
if (!window.f0ckPasskeyManager) { alert('Passkey manager not loaded.'); return; }
if (!window.PublicKeyCredential) { alert('Passkeys are not supported in this browser.'); return; }
var name = prompt('Name this passkey (e.g. "Bitwarden", "iPhone"):', 'Passkey') || 'Passkey';
if (btn) btn.disabled = true;
if (status) status.style.display = 'none';
try {
await window.f0ckPasskeyManager.addPasskey(name);
if (status) { status.style.display = 'block'; status.style.color = '#98c379'; status.textContent = 'Passkey added!'; }
await loadPasskeys();
} catch (err) {
if (err.name !== 'NotAllowedError') {
if (status) { status.style.display = 'block'; status.style.color = '#e06c75'; status.textContent = 'Error: ' + err.message; }
}
} finally {
if (btn) btn.disabled = false;
}
};
window.settingsPasskeyDelete = async function(credentialId) {
if (!confirm('Remove this passkey? You will no longer be able to use it to sign in.')) return;
var status = document.getElementById('settings-passkey-status');
try {
await window.f0ckPasskeyManager.deletePasskey(credentialId);
if (status) { status.style.display = 'block'; status.style.color = '#98c379'; status.textContent = 'Passkey removed.'; }
await loadPasskeys();
} catch (err) {
if (status) { status.style.display = 'block'; status.style.color = '#e06c75'; status.textContent = 'Error: ' + err.message; }
}
};
loadPasskeys();
})();
</script>
@endif