feat: implement client-side room listing UI and server-side authentication blueprint

This commit is contained in:
Ben Miller 2026-06-23 12:22:06 -05:00
parent 9ccfbd1dc0
commit c24c4acaec
3 changed files with 72 additions and 1 deletions

View file

@ -69,3 +69,31 @@ def validate():
return jsonify({"error": "Invalid credentials"}), 403 return jsonify({"error": "Invalid credentials"}), 403
@auth_bp.route("/logout", methods=["POST"])
def logout():
data = request.get_json()
username = data.get("username")
key = data.get("key")
if not username or not key:
return jsonify({"error": "Missing credentials"}), 400
if verify_user_key(username, key):
# Remove from active sessions
user_sessions.pop(username, None)
# Remove from user_ips in memory and SQLite to free up the username immediately
user_ips.pop(username, None)
from models.database import db_delete_user_ip
db_delete_user_ip(username)
# Remove from all rooms
from models.state import room_users
for room in room_users:
room_users[room].pop(username, None)
return jsonify({"status": "logged_out"})
return jsonify({"error": "Invalid credentials"}), 403

View file

@ -25,8 +25,51 @@ document.addEventListener("DOMContentLoaded", async () => {
return; return;
} }
function escapeHtml(text) {
if (!text) return text;
return text
.replace(/&/g, "&")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
const userInfoEl = document.getElementById('user-info'); const userInfoEl = document.getElementById('user-info');
userInfoEl.textContent = `Logged in as: ${username}`; userInfoEl.style.display = 'flex';
userInfoEl.style.alignItems = 'center';
userInfoEl.style.justifyContent = 'flex-start';
userInfoEl.style.gap = '15px';
userInfoEl.style.marginBottom = '20px';
const logoutBtn = document.createElement('button');
logoutBtn.textContent = 'Logout';
logoutBtn.style.backgroundColor = '#d9534f';
logoutBtn.style.color = 'white';
logoutBtn.style.border = 'none';
logoutBtn.style.padding = '6px 12px';
logoutBtn.style.borderRadius = '4px';
logoutBtn.style.cursor = 'pointer';
logoutBtn.onclick = async () => {
if (confirm("Are you sure you want to logout? This will release your username immediately.")) {
try {
await fetch('/logout', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, key: userKey })
});
} catch (e) {
console.error("Logout request failed:", e);
}
localStorage.clear();
window.location.href = '/';
}
};
const userSpan = document.createElement('span');
userSpan.innerHTML = `Logged in as: <strong>${escapeHtml(username)}</strong>`;
userInfoEl.append(logoutBtn, userSpan);
document.querySelectorAll('.collapsible').forEach(heading => { document.querySelectorAll('.collapsible').forEach(heading => {
heading.addEventListener('click', () => { heading.addEventListener('click', () => {

Binary file not shown.