增加用户管理

This commit is contained in:
wtz
2026-02-20 23:39:49 +08:00
parent a272dad5f1
commit af3b805dbf
18 changed files with 1493 additions and 53 deletions

View File

@@ -1,4 +1,6 @@
let ws = null, playerId = '', roomId = '', state = null, selected = [];
let token = '', userId = '', username = '', nickname = '';
let loginCaptchaId = '', regCaptchaId = '';
const $ = id => document.getElementById(id);
function show(id) {
@@ -13,13 +15,191 @@ function chat(name, msg, sys) {
$('chatMsgs').scrollTop = 1e6;
}
function authHeaders() {
return {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token
};
}
async function getCaptcha(type) {
try {
const res = await fetch('/api/auth/captcha');
const d = await res.json();
if (d.code === 0) {
if (type === 'login') {
loginCaptchaId = d.data.captchaId;
$('loginCaptchaImg').src = d.data.image;
} else {
regCaptchaId = d.data.captchaId;
$('regCaptchaImg').src = d.data.image;
}
}
} catch (e) {
console.error('Failed to get captcha:', e);
}
}
async function login() {
const usernameVal = $('loginUsername').value.trim();
const passwordVal = $('loginPassword').value;
const captchaVal = $('loginCaptcha').value.trim().toUpperCase();
if (!usernameVal || !passwordVal || !captchaVal) {
alert('请填写完整信息');
return;
}
try {
const res = await fetch('/api/auth/login', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
username: usernameVal,
password: passwordVal,
captcha: captchaVal,
captchaId: loginCaptchaId
})
});
const d = await res.json();
if (d.code === 0) {
token = d.data.token;
userId = d.data.userId;
username = d.data.username;
nickname = d.data.nickname;
localStorage.setItem('token', token);
localStorage.setItem('userId', userId);
localStorage.setItem('username', username);
localStorage.setItem('nickname', nickname);
showLobby();
} else {
alert(d.message);
getCaptcha('login');
}
} catch (e) {
alert('登录失败: ' + e.message);
getCaptcha('login');
}
}
async function register() {
const usernameVal = $('regUsername').value.trim();
const passwordVal = $('regPassword').value;
const nicknameVal = $('regNickname').value.trim();
const captchaVal = $('regCaptcha').value.trim().toUpperCase();
if (!usernameVal || !passwordVal || !nicknameVal || !captchaVal) {
alert('请填写完整信息');
return;
}
if (usernameVal.length < 3 || usernameVal.length > 20) {
alert('用户名需要3-20个字符');
return;
}
if (passwordVal.length < 6) {
alert('密码至少6位');
return;
}
try {
const res = await fetch('/api/auth/register', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
username: usernameVal,
password: passwordVal,
nickname: nicknameVal,
captcha: captchaVal,
captchaId: regCaptchaId
})
});
const d = await res.json();
if (d.code === 0) {
token = d.data.token;
userId = d.data.userId;
username = d.data.username;
nickname = d.data.nickname;
localStorage.setItem('token', token);
localStorage.setItem('userId', userId);
localStorage.setItem('username', username);
localStorage.setItem('nickname', nickname);
showLobby();
} else {
alert(d.message);
getCaptcha('register');
}
} catch (e) {
alert('注册失败: ' + e.message);
getCaptcha('register');
}
}
async function logout() {
try {
await fetch('/api/auth/logout', {
method: 'POST',
headers: authHeaders()
});
} catch (e) {}
token = '';
userId = '';
username = '';
nickname = '';
localStorage.removeItem('token');
localStorage.removeItem('userId');
localStorage.removeItem('username');
localStorage.removeItem('nickname');
show('auth');
getCaptcha('login');
}
async function validateToken() {
if (!token) return false;
try {
const res = await fetch('/api/auth/validate', {
headers: authHeaders()
});
const d = await res.json();
if (d.code === 0) {
userId = d.data.userId;
username = d.data.username;
nickname = d.data.nickname;
return true;
}
} catch (e) {}
return false;
}
async function checkCurrentRoom() {
try {
const res = await fetch('/api/rooms/current', {
headers: authHeaders()
});
const d = await res.json();
if (d.code === 0 && d.data && d.data.roomId) {
roomId = d.data.roomId;
playerId = d.data.playerId;
return true;
}
} catch (e) {}
return false;
}
async function showLobby() {
$('welcomeUser').textContent = '欢迎, ' + nickname;
$('playerName').value = nickname;
show('lobby');
}
async function create() {
const name = $('playerName').value.trim();
if (!name) { alert('请输入昵称'); return; }
try {
const res = await fetch('/api/rooms', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
headers: authHeaders(),
body: JSON.stringify({playerName: name, maxPlayers: +$('maxPlayers').value})
});
const d = await res.json();
@@ -43,7 +223,7 @@ async function join() {
try {
const res = await fetch('/api/rooms/' + rid + '/join', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
headers: authHeaders(),
body: JSON.stringify({playerName: name})
});
const d = await res.json();
@@ -59,6 +239,21 @@ async function join() {
}
}
async function leaveRoom() {
try {
await fetch('/api/rooms/leave', {
method: 'POST',
headers: authHeaders()
});
} catch (e) {}
if (ws) ws.close();
show('lobby');
roomId = '';
playerId = '';
state = null;
selected = [];
}
function connect() {
const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
ws = new WebSocket(proto + '//' + location.host + '/api/ws?roomId=' + roomId + '&playerId=' + playerId);
@@ -68,6 +263,7 @@ function connect() {
if (msg.type === 'state') render(msg.data);
else if (msg.type === 'gameOver') showGameOver(msg.data);
else if (msg.type === 'chat') chat(msg.data.playerName, msg.data.message);
else if (msg.type === 'leave') chat('', '有玩家离开房间', true);
else if (msg.type === 'error') chat('', msg.data, true);
};
ws.onerror = function() { chat('', '连接错误', true); };
@@ -308,9 +504,7 @@ function toggleReady() {
}
function leave() {
if (ws) ws.close();
show('lobby');
roomId = ''; playerId = ''; state = null; selected = [];
leaveRoom();
}
function showGameOver(d) {
@@ -329,11 +523,31 @@ function again() {
send('ready', {ready: false});
}
document.addEventListener('DOMContentLoaded', function() {
document.addEventListener('DOMContentLoaded', async function() {
$('loginTab').onclick = function() {
$('loginTab').classList.add('active');
$('registerTab').classList.remove('active');
$('loginForm').classList.remove('hidden');
$('registerForm').classList.add('hidden');
getCaptcha('login');
};
$('registerTab').onclick = function() {
$('registerTab').classList.add('active');
$('loginTab').classList.remove('active');
$('registerForm').classList.remove('hidden');
$('loginForm').classList.add('hidden');
getCaptcha('register');
};
$('loginCaptchaImg').onclick = function() { getCaptcha('login'); };
$('regCaptchaImg').onclick = function() { getCaptcha('register'); };
$('loginBtn').onclick = login;
$('registerBtn').onclick = register;
$('logoutBtn').onclick = logout;
$('createBtn').onclick = create;
$('joinBtn').onclick = join;
$('readyBtn').onclick = toggleReady;
$('leaveBtn').onclick = leave;
$('leaveBtn').onclick = leaveRoom;
$('playBtn').onclick = play;
$('passBtn').onclick = pass;
$('againBtn').onclick = again;
@@ -343,4 +557,29 @@ document.addEventListener('DOMContentLoaded', function() {
};
$('chatInput').onkeypress = function(e) { if (e.key === 'Enter') $('chatBtn').click(); };
$('roomIdInput').onkeypress = function(e) { if (e.key === 'Enter') join(); };
token = localStorage.getItem('token') || '';
userId = localStorage.getItem('userId') || '';
username = localStorage.getItem('username') || '';
nickname = localStorage.getItem('nickname') || '';
if (token) {
var valid = await validateToken();
if (valid) {
var inRoom = await checkCurrentRoom();
if (inRoom) {
connect();
return;
}
showLobby();
return;
}
localStorage.removeItem('token');
localStorage.removeItem('userId');
localStorage.removeItem('username');
localStorage.removeItem('nickname');
}
show('auth');
getCaptcha('login');
});