gsdf
This commit is contained in:
+212
-7
@@ -14,7 +14,9 @@
|
||||
<!-- Nav links: desktop always-on, mobile hidden until toggled -->
|
||||
<div class="nav-collapse" id="navbarContent">
|
||||
<div class="nav-links">
|
||||
@if(!session.is_anon)
|
||||
<a id="nav-upload-link" style="cursor:pointer;"><i class="fa-solid fa-angle-up"></i> {{ t('nav.upload') }}</a>
|
||||
@endif
|
||||
@if(meme_creator)
|
||||
<a id="nav-meme-link" href="/meme"><i class="fa-regular fa-image"></i> {{ t('nav.meme') }}</a>
|
||||
@endif
|
||||
@@ -42,7 +44,9 @@
|
||||
<!-- Avatar dropdown -->
|
||||
<div class="nav-user-dropdown">
|
||||
<button class="nav-user-btn nav-avatar-btn" id="nav-user-toggle">
|
||||
@if(session.avatar_file)
|
||||
@if(session.is_anon)
|
||||
<span class="nav-avatar-icon" style="font-size: 1.2em; margin-right: 6px; display: inline-flex; align-items: center; cursor: pointer;" onclick="event.stopPropagation(); const u = '{{ session.login.toLowerCase() }}'; if(typeof window.loadPageAjax==='function'){ window.loadPageAjax('/user/' + u, true); } else { window.location.href='/user/' + u; }" title="{{ session.login }}"><i class="fa-solid fa-user-secret"></i></span>
|
||||
@elseif(session.avatar_file)
|
||||
<img class="nav-avatar-img" src="/a/{{ session.avatar_file }}" onerror="this.src='/a/default.png'" onclick="event.stopPropagation(); if(typeof window.loadPageAjax==='function'){ window.loadPageAjax('/user/{!! session.user.toLowerCase() !!}', true); } else { window.location.href='/user/{!! session.user.toLowerCase() !!}'; }" title="{{ session.user }}">
|
||||
@elseif(session.avatar && session.avatar > 0)
|
||||
<img class="nav-avatar-img" src="/t/{{ session.avatar }}.webp" onerror="this.src='/a/default.png'" onclick="event.stopPropagation(); if(typeof window.loadPageAjax==='function'){ window.loadPageAjax('/user/{!! session.user.toLowerCase() !!}', true); } else { window.location.href='/user/{!! session.user.toLowerCase() !!}'; }" title="{{ session.user }}">
|
||||
@@ -53,11 +57,18 @@
|
||||
<span class="nav-avatar-caret">▾</span>
|
||||
</button>
|
||||
<div class="nav-user-menu" id="nav-user-menu">
|
||||
@if(session.is_anon)
|
||||
<a href="/user/{{ session.login.toLowerCase() }}">{{ t('nav.profile') }}</a>
|
||||
@else
|
||||
<a href="/user/{!! session.user.toLowerCase() !!}">{{ t('nav.profile') }}</a>
|
||||
@if(userhalls_enabled)
|
||||
<a href="/user/{!! session.user.toLowerCase() !!}/halls">{{ t('nav.my_halls') }}</a>
|
||||
@endif
|
||||
<a href="/user/{{ session.user.toLowerCase() }}/favs" class="mobile-only">{{ t('nav.favs') }}</a>
|
||||
@endif
|
||||
@if(enable_anonymous_access && session.is_anon)
|
||||
<a href="#" id="nav-user-anon-identity-btn" onclick="event.preventDefault(); if(window.f0ckAnonSSH) { window.f0ckAnonSSH.openModal(); } else { const m = document.getElementById('anon-ssh-modal'); if (m) m.style.display='flex'; }"><i class="fa-solid fa-key"></i> Key Management</a>
|
||||
@endif
|
||||
<a href="/user/{{ (session.is_anon && session.login ? session.login : session.user).toLowerCase() }}/favs" class="mobile-only">{{ t('nav.favs') }}</a>
|
||||
<a href="/settings" class="mobile-only">{{ t('nav.settings') }}</a>
|
||||
<div class="nav-user-divider"></div>
|
||||
<a href="/logout" class="mobile-only">{{ t('nav.logout') }}</a>
|
||||
@@ -91,10 +102,10 @@
|
||||
</div>
|
||||
|
||||
<!-- Quick Favs -->
|
||||
<a href="/user/{{ session.user.toLowerCase() }}/favs" title="Favorites" class="desktop-only"><i class="fa-solid fa-heart"></i></a>
|
||||
<a href="/user/{{ (session.is_anon && session.login ? session.login : session.user).toLowerCase() }}/favs" title="Favorites" class="desktop-only"><i class="fa-solid fa-heart"></i></a>
|
||||
|
||||
<!-- DM -->
|
||||
@if(private_messages)
|
||||
@if(private_messages && !session.is_anon)
|
||||
<div id="nav-dm" class="nav-item-rel">
|
||||
<a href="/messages" id="nav-dm-btn" title="Direct Messages">
|
||||
<i class="fa-solid fa-envelope"></i>
|
||||
@@ -261,13 +272,32 @@
|
||||
|
||||
<div class="nav-right-group">
|
||||
<div class="nav-user-dropdown">
|
||||
<button class="nav-user-btn" id="nav-visitor-toggle"><i class="fa-solid fa-user-secret"></i> {{ t('nav.guest') }} <span class="nav-avatar-caret">▾</span></button>
|
||||
<div class="nav-user-menu" id="nav-visitor-menu">
|
||||
<button class="nav-user-btn" id="nav-visitor-toggle">
|
||||
<i id="nav-visitor-icon" class="fa-solid @if(enable_anonymous_access && session && session.is_anon) fa-user-secret @else fa-user @endif"></i>
|
||||
<span id="nav-anon-label">@if(enable_anonymous_access && session && session.is_anon)anonymous@else guest @endif</span>
|
||||
<span class="nav-avatar-caret">▾</span>
|
||||
</button>
|
||||
<div class="nav-user-menu" id="nav-visitor-menu" style="min-width: max-content; width: max-content; white-space: nowrap;">
|
||||
@if(enable_anonymous_access && session && session.is_anon)
|
||||
<a href="/favs" id="nav-guest-favs-link" class="mobile-only">{{ t('nav.favs') }}</a>
|
||||
@endif
|
||||
@if(enable_anonymous_access)
|
||||
<a href="#" id="nav-login-anon-btn" @if(session && session.is_anon) style="display:none;" @endif><i class="fa-solid fa-user-secret"></i> Login as anonymous</a>
|
||||
<a href="#" id="nav-anon-identity-btn" @if(!session || !session.is_anon) style="display:none;" @endif onclick="event.preventDefault(); if(window.f0ckAnonSSH) { window.f0ckAnonSSH.openModal(); } else { const m = document.getElementById('anon-ssh-modal'); if (m) m.style.display='flex'; }"><i class="fa-solid fa-key"></i> Key Management</a>
|
||||
<a href="/settings" id="nav-anon-settings-btn" @if(!session || !session.is_anon) style="display:none;" @endif><i class="fa-solid fa-gear"></i> Settings</a>
|
||||
@endif
|
||||
<a href="#" id="nav-login-btn">{{ t('nav.login') }}</a>
|
||||
<a href="#" id="nav-register-btn">{{ t('nav.register') }}</a>
|
||||
<div class="nav-user-divider"></div>
|
||||
@if(enable_anonymous_access)
|
||||
<div class="nav-user-divider" id="nav-anon-divider" @if(!session || !session.is_anon) style="display:none;" @endif></div>
|
||||
<a href="#" id="nav-anon-logout-btn" @if(!session || !session.is_anon) style="display:none;" @endif><i class="fa-solid fa-right-from-bracket"></i> Logout (Guest)</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@if(enable_anonymous_access && session && session.is_anon)
|
||||
<!-- Quick Favs for Anon -->
|
||||
<a href="/favs" id="nav-guest-favs" title="{{ t('nav.favs') }}" class="desktop-only"><i class="fa-solid fa-heart"></i></a>
|
||||
@endif
|
||||
<button class="navbar-toggler" type="button" id="nav-toggler"
|
||||
onclick="document.getElementById('navbarContent').classList.toggle('show'); this.classList.toggle('is-open')">
|
||||
<span></span><span></span><span></span>
|
||||
@@ -309,6 +339,13 @@
|
||||
{{ t('auth.no_account') }} <a href="#" id="login-to-register" style="color: var(--accent); text-decoration: underline;">{{ t('auth.register_now') }}</a>
|
||||
</p>
|
||||
@endif
|
||||
@if(enable_anonymous_access)
|
||||
<div style="margin-top: 15px; border-top: 1px solid rgba(255,255,255,0.1); padding-top: 12px; text-align: center;">
|
||||
<button type="button" id="modal-login-as-anon-btn" class="btn btn-sm" style="background: rgba(255,255,255,0.08); border: 1px solid rgba(255,255,255,0.2); color: #ddd; border-radius: 4px; padding: 7px 14px; font-size: 0.88em; cursor: pointer; width: 100%; transition: background 0.2s;">
|
||||
<i class="fa-solid fa-user-secret"></i> Login as anonymous
|
||||
</button>
|
||||
</div>
|
||||
@endif
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -346,6 +383,174 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if(enable_anonymous_access)
|
||||
<!-- Anonymous OpenSSH Ed25519 Identity Modal -->
|
||||
<div id="anon-ssh-modal" style="display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.75); backdrop-filter: blur(4px); z-index: 99999; align-items: center; justify-content: center;">
|
||||
<div class="login-modal-content" style="max-width: 540px; width: 92vw; max-height: 90vh; overflow-y: auto; text-align: left; padding: 25px; border: 1px solid rgba(255,255,255,0.15); border-radius: 8px; background: var(--bg-primary, #111); box-shadow: 0 10px 40px rgba(0,0,0,0.8);">
|
||||
<button id="anon-ssh-modal-close" style="position: absolute; top: 15px; right: 15px; background: none; border: none; color: var(--text-muted, #aaa); font-size: 1.4em; cursor: pointer; line-height: 1;">×</button>
|
||||
|
||||
<div style="display: flex; align-items: center; gap: 10px; margin-bottom: 8px;">
|
||||
<i class="fa-solid fa-key" style="color: var(--accent, #0096ff); font-size: 1.3em;"></i>
|
||||
<h3 style="margin: 0; font-size: 1.25em; color: var(--text-color, #fff);">Anonymous SSH Identity</h3>
|
||||
</div>
|
||||
<p style="margin: 0 0 15px 0; font-size: 0.85em; color: var(--text-muted, #aaa); line-height: 1.4;">
|
||||
Your browser holds an <strong>OpenSSH Ed25519</strong> private key. Your comments and favorites belong to this key without needing a password.
|
||||
</p>
|
||||
|
||||
<!-- Tabs -->
|
||||
<div style="display: flex; gap: 8px; margin-bottom: 15px; border-bottom: 1px solid rgba(255,255,255,0.1); padding-bottom: 8px;">
|
||||
<button type="button" id="anon-tab-btn-identity" class="btn btn-sm btn-primary" style="font-size: 0.85em; padding: 5px 12px;">My Identity</button>
|
||||
<button type="button" id="anon-tab-btn-import" class="btn btn-sm btn-secondary" style="font-size: 0.85em; padding: 5px 12px;">Import / Supply Key</button>
|
||||
</div>
|
||||
|
||||
<!-- Tab 1: Current Identity -->
|
||||
<div id="anon-tab-identity">
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; font-size: 0.8em; text-transform: uppercase; letter-spacing: 0.5px; color: var(--text-muted, #888); margin-bottom: 4px;">Fingerprint</label>
|
||||
<div style="display: flex; align-items: center; gap: 8px; background: rgba(0,0,0,0.4); padding: 8px 12px; border-radius: 4px; border: 1px solid rgba(255,255,255,0.08);">
|
||||
<code id="anon-ssh-fp-display" style="font-family: monospace; font-size: 0.88em; color: var(--accent, #00d2ff); word-break: break-all; flex: 1;">Generating...</code>
|
||||
<button type="button" id="anon-copy-fp-btn" title="Copy Fingerprint" class="btn btn-sm" style="background: transparent; border: none; color: #aaa; cursor: pointer; padding: 4px;"><i class="fa-solid fa-copy"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="display: block; font-size: 0.8em; text-transform: uppercase; letter-spacing: 0.5px; color: var(--text-muted, #888); margin-bottom: 4px;">OpenSSH Public Key (<code>id_ed25519.pub</code>)</label>
|
||||
<textarea id="anon-ssh-pub-display" readonly rows="2" style="width: 100%; box-sizing: border-box; font-family: monospace; font-size: 0.82em; background: rgba(0,0,0,0.4); color: #ddd; border: 1px solid rgba(255,255,255,0.08); border-radius: 4px; padding: 8px; resize: none;"></textarea>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 8px;">
|
||||
<button type="button" id="anon-copy-pub-btn" class="btn btn-sm btn-secondary" style="font-size: 0.85em; padding: 6px 12px;"><i class="fa-solid fa-copy"></i> Copy Public Key</button>
|
||||
<button type="button" id="anon-dl-priv-btn" class="btn btn-sm btn-primary" style="font-size: 0.85em; padding: 6px 12px;"><i class="fa-solid fa-download"></i> Download id_ed25519</button>
|
||||
<button type="button" id="anon-dl-pub-btn" class="btn btn-sm btn-secondary" style="font-size: 0.85em; padding: 6px 12px;"><i class="fa-solid fa-download"></i> Download id_ed25519.pub</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tab 2: Import Key -->
|
||||
<div id="anon-tab-import" style="display: none;">
|
||||
<p style="font-size: 0.85em; color: var(--text-muted, #aaa); margin-top: 0; margin-bottom: 10px;">
|
||||
Paste your existing <code>id_ed25519</code> OpenSSH private key or raw 32-byte seed to restore your anonymous identity on this browser.
|
||||
</p>
|
||||
<textarea id="anon-import-key-input" rows="4" placeholder="-----BEGIN OPENSSH PRIVATE KEY----- ... -----END OPENSSH PRIVATE KEY-----" style="width: 100%; box-sizing: border-box; font-family: monospace; font-size: 0.82em; background: rgba(0,0,0,0.4); color: #ddd; border: 1px solid rgba(255,255,255,0.08); border-radius: 4px; padding: 8px; resize: vertical; margin-bottom: 10px;"></textarea>
|
||||
|
||||
<input type="file" id="anon-import-file-elem" style="display: none;" />
|
||||
|
||||
<div style="display: flex; gap: 8px; align-items: center;">
|
||||
<button type="button" id="anon-upload-key-btn" class="btn btn-sm btn-secondary" style="font-size: 0.85em; padding: 6px 12px;"><i class="fa-solid fa-upload"></i> Upload File</button>
|
||||
<button type="button" id="anon-submit-import-btn" class="btn btn-sm btn-primary" style="font-size: 0.85em; padding: 6px 16px;"><i class="fa-solid fa-check"></i> Activate Key</button>
|
||||
</div>
|
||||
<div id="anon-import-status" style="margin-top: 10px; font-size: 0.85em; display: none;"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function(){
|
||||
// Tab switching
|
||||
const tabBtnId = document.getElementById('anon-tab-btn-identity');
|
||||
const tabBtnImp = document.getElementById('anon-tab-btn-import');
|
||||
const tabId = document.getElementById('anon-tab-identity');
|
||||
const tabImp = document.getElementById('anon-tab-import');
|
||||
if(tabBtnId && tabBtnImp && tabId && tabImp){
|
||||
tabBtnId.addEventListener('click', function(){
|
||||
tabId.style.display = 'block';
|
||||
tabImp.style.display = 'none';
|
||||
tabBtnId.classList.remove('btn-secondary'); tabBtnId.classList.add('btn-primary');
|
||||
tabBtnImp.classList.remove('btn-primary'); tabBtnImp.classList.add('btn-secondary');
|
||||
});
|
||||
tabBtnImp.addEventListener('click', function(){
|
||||
tabId.style.display = 'none';
|
||||
tabImp.style.display = 'block';
|
||||
tabBtnImp.classList.remove('btn-secondary'); tabBtnImp.classList.add('btn-primary');
|
||||
tabBtnId.classList.remove('btn-primary'); tabBtnId.classList.add('btn-secondary');
|
||||
});
|
||||
}
|
||||
|
||||
// Copy buttons
|
||||
const copyPubBtn = document.getElementById('anon-copy-pub-btn');
|
||||
if(copyPubBtn){
|
||||
copyPubBtn.addEventListener('click', function(){
|
||||
if(window.f0ckAnonSSH) window.f0ckAnonSSH.copyPublicKey();
|
||||
});
|
||||
}
|
||||
const copyFpBtn = document.getElementById('anon-copy-fp-btn');
|
||||
if(copyFpBtn){
|
||||
copyFpBtn.addEventListener('click', function(){
|
||||
const fp = document.getElementById('anon-ssh-fp-display')?.textContent;
|
||||
if(fp && navigator.clipboard){
|
||||
navigator.clipboard.writeText(fp).then(function(){
|
||||
if(typeof window.showToastNotification === 'function') window.showToastNotification('Fingerprint copied!');
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Download buttons
|
||||
const dlPriv = document.getElementById('anon-dl-priv-btn');
|
||||
if(dlPriv){
|
||||
dlPriv.addEventListener('click', function(){
|
||||
if(window.f0ckAnonSSH) window.f0ckAnonSSH.downloadPrivateKey();
|
||||
});
|
||||
}
|
||||
const dlPub = document.getElementById('anon-dl-pub-btn');
|
||||
if(dlPub){
|
||||
dlPub.addEventListener('click', function(){
|
||||
if(window.f0ckAnonSSH) window.f0ckAnonSSH.downloadPublicKey();
|
||||
});
|
||||
}
|
||||
|
||||
// File upload trigger
|
||||
const uploadBtn = document.getElementById('anon-upload-key-btn');
|
||||
const fileElem = document.getElementById('anon-import-file-elem');
|
||||
const inputElem = document.getElementById('anon-import-key-input');
|
||||
if(uploadBtn && fileElem){
|
||||
uploadBtn.addEventListener('click', function(){ fileElem.click(); });
|
||||
fileElem.addEventListener('change', function(){
|
||||
if(fileElem.files && fileElem.files[0]){
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(e){
|
||||
if(inputElem) inputElem.value = e.target.result;
|
||||
};
|
||||
reader.readAsText(fileElem.files[0]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Submit import
|
||||
const submitImpBtn = document.getElementById('anon-submit-import-btn');
|
||||
const statusEl = document.getElementById('anon-import-status');
|
||||
if(submitImpBtn){
|
||||
submitImpBtn.addEventListener('click', async function(){
|
||||
const keyVal = inputElem?.value;
|
||||
if(!keyVal || !keyVal.trim()){
|
||||
if(statusEl){ statusEl.style.display = 'block'; statusEl.style.color = '#ff4444'; statusEl.textContent = 'Please paste a key or select a file'; }
|
||||
return;
|
||||
}
|
||||
try {
|
||||
submitImpBtn.disabled = true;
|
||||
submitImpBtn.textContent = 'Importing...';
|
||||
await window.f0ckAnonSSH.importKey(keyVal);
|
||||
if(statusEl){
|
||||
statusEl.style.display = 'block';
|
||||
statusEl.style.color = '#00C851';
|
||||
statusEl.textContent = 'Key activated successfully! Reloading...';
|
||||
}
|
||||
setTimeout(function(){ window.location.reload(); }, 800);
|
||||
} catch(err){
|
||||
submitImpBtn.disabled = false;
|
||||
submitImpBtn.innerHTML = '<i class="fa-solid fa-check"></i> Activate Key';
|
||||
if(statusEl){
|
||||
statusEl.style.display = 'block';
|
||||
statusEl.style.color = '#ff4444';
|
||||
statusEl.textContent = 'Import error: ' + (err.message || err);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
@endif
|
||||
|
||||
@if(!private_society || session)
|
||||
<!-- Shortcuts Modal -->
|
||||
<div id="shortcuts-modal" style="display: none;">
|
||||
|
||||
Reference in New Issue
Block a user