feat: add app-level authentication with login, session cookies, and admin panel
Replace Nginx basic auth with proper user accounts: - Session cookies via itsdangerous (30-day expiry, httponly, secure) - Password hashing with bcrypt via passlib - Login page with AC-themed UI - Admin page for user management (CRUD) - AuthMiddleware exempts plugin WS and browser WS endpoints - Issues/comments author auto-populated from session - Sidebar shows logged-in username, admin link, and logout - Seed users: erik (admin), alex, lundberg - SECRET_KEY env var for cookie signing
This commit is contained in:
parent
fac5063878
commit
b09169ade2
9 changed files with 878 additions and 60 deletions
|
|
@ -4125,6 +4125,26 @@ fetch('/api-version').then(r => r.json()).then(d => {
|
|||
if (el) el.textContent = 'v' + d.version;
|
||||
}).catch(() => {});
|
||||
|
||||
// ─── Current User Info ──────────────────────────────────────────
|
||||
let _currentUser = null;
|
||||
|
||||
fetch('/me').then(r => {
|
||||
if (!r.ok) throw new Error('not authenticated');
|
||||
return r.json();
|
||||
}).then(data => {
|
||||
_currentUser = data;
|
||||
const userInfo = document.getElementById('userInfo');
|
||||
const nameEl = document.getElementById('currentUsername');
|
||||
const adminLink = document.getElementById('adminLink');
|
||||
if (userInfo && nameEl) {
|
||||
nameEl.textContent = data.username;
|
||||
userInfo.style.display = 'flex';
|
||||
}
|
||||
if (adminLink && data.is_admin) {
|
||||
adminLink.style.display = 'inline';
|
||||
}
|
||||
}).catch(() => {});
|
||||
|
||||
// ─── Issues Board ───────────────────────────────────────────────
|
||||
|
||||
const ISSUE_CATEGORIES = {
|
||||
|
|
@ -4165,7 +4185,6 @@ function showIssuesWindow() {
|
|||
form.className = 'issues-form';
|
||||
form.innerHTML = `
|
||||
<div style="display:flex;gap:4px;margin-bottom:4px;">
|
||||
<input type="text" id="issueAuthor" placeholder="Your name..." style="width:120px;padding:3px 6px;font-size:0.8rem;border:1px solid #555;background:#2a2a2a;color:#ddd;">
|
||||
<input type="text" id="issueTitle" placeholder="Issue title..." style="flex:1;padding:3px 6px;font-size:0.8rem;border:1px solid #555;background:#2a2a2a;color:#ddd;">
|
||||
<select id="issueCategory" style="padding:3px;font-size:0.75rem;border:1px solid #555;background:#2a2a2a;color:#ddd;">
|
||||
<option value="plugin">Plugin</option>
|
||||
|
|
@ -4182,24 +4201,17 @@ function showIssuesWindow() {
|
|||
`;
|
||||
content.appendChild(form);
|
||||
|
||||
// Remember author name in localStorage
|
||||
const authorInput = form.querySelector('#issueAuthor');
|
||||
authorInput.value = localStorage.getItem('issueAuthorName') || '';
|
||||
|
||||
// Add button handler
|
||||
form.querySelector('#issueAddBtn').addEventListener('click', async () => {
|
||||
const author = document.getElementById('issueAuthor').value.trim() || 'Anonymous';
|
||||
const title = document.getElementById('issueTitle').value.trim();
|
||||
const desc = document.getElementById('issueDescription').value.trim();
|
||||
const cat = document.getElementById('issueCategory').value;
|
||||
if (!title) return;
|
||||
|
||||
localStorage.setItem('issueAuthorName', author);
|
||||
|
||||
await fetch('/issues', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ title, description: desc, category: cat, author })
|
||||
body: JSON.stringify({ title, description: desc, category: cat })
|
||||
});
|
||||
document.getElementById('issueTitle').value = '';
|
||||
document.getElementById('issueDescription').value = '';
|
||||
|
|
@ -4406,11 +4418,10 @@ function showCommentsSection(row, issue, win) {
|
|||
addDiv.querySelector('.comment-add-btn').addEventListener('click', async () => {
|
||||
const text = addDiv.querySelector('.comment-text-input').value.trim();
|
||||
if (!text) return;
|
||||
const author = localStorage.getItem('issueAuthorName') || 'Anonymous';
|
||||
await fetch(`/issues/${issue.id}/comments`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ text, author })
|
||||
body: JSON.stringify({ text })
|
||||
});
|
||||
refreshIssuesList(win);
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue