alotta good shit
This commit is contained in:
+92
-28
@@ -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() + ' · 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,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"'); }
|
||||
|
||||
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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user