feat: implement client-side room listing UI and server-side authentication blueprint
This commit is contained in:
parent
9ccfbd1dc0
commit
c24c4acaec
3 changed files with 72 additions and 1 deletions
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,51 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function escapeHtml(text) {
|
||||||
|
if (!text) return text;
|
||||||
|
return text
|
||||||
|
.replace(/&/g, "&")
|
||||||
|
.replace(/</g, "<")
|
||||||
|
.replace(/>/g, ">")
|
||||||
|
.replace(/"/g, """)
|
||||||
|
.replace(/'/g, "'");
|
||||||
|
}
|
||||||
|
|
||||||
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.
Loading…
Reference in a new issue