backend: 添加token验证功能,重构用户登录逻辑,优化消息发送与接收
This commit is contained in:
parent
adc339af8f
commit
04b32791e9
47
app.js
47
app.js
@ -5,8 +5,9 @@ import apiRouter from "./router/api.js"
|
||||
import { Server } from "socket.io";
|
||||
import { saveToDatabase } from "./utils/storage.js";
|
||||
import { onlineList, clientMap } from "./data.js";
|
||||
import { verifyToken } from "./utils/auth.js";
|
||||
|
||||
const PORT = process.env.PORT || 5691;
|
||||
const PORT = process.env.PORT || 1111;
|
||||
|
||||
|
||||
const app = express();
|
||||
@ -52,25 +53,37 @@ io.on("connection", (socket) => {
|
||||
const { opt, args } = JSON.parse(data);
|
||||
logger.trace(opt, args)
|
||||
if (opt == "signin") {
|
||||
if (!args || !args.username) {
|
||||
socket.emit("system", JSON.stringify(new Response(false, "username is empty.")))
|
||||
if (!args || !args.token) {
|
||||
socket.emit("system", JSON.stringify(new Response(false, "token is empty.")))
|
||||
return;
|
||||
}
|
||||
const { username } = args
|
||||
if (username.length > 20) {
|
||||
socket.emit("system", JSON.stringify(new Response(false, "username is too long.")))
|
||||
}
|
||||
if (onlineList.indexOf(username) != -1) {
|
||||
socket.emit("system", JSON.stringify(new Response(false, "username is already taken.")))
|
||||
const { token } = args
|
||||
// const username = token.trim();
|
||||
verifyToken(token)
|
||||
.then(res => {
|
||||
if (res.data.code != 200) {
|
||||
socket.emit("system", JSON.stringify(new Response(false, "token is invaild.")))
|
||||
username = res.data.UserName;
|
||||
uid = res.data.ID;
|
||||
return;
|
||||
}
|
||||
if (onlineList.indexOf(username) != -1) {
|
||||
socket.emit("system", JSON.stringify(new Response(false, "token is already taken.")))
|
||||
return;
|
||||
}
|
||||
socket.emit("system", JSON.stringify(new Response(true, "sign in successfully.")))
|
||||
socket.emit("username", username)
|
||||
socket.username = username;
|
||||
onlineList.push(username);
|
||||
clientMap.set(socket.id, username);
|
||||
io.emit("notice", JSON.stringify(new Response(true, `${socket.username} join the chat.`)))
|
||||
logger.debug(`socket(${socket.id}) -- ${username} signed in.`)
|
||||
})
|
||||
.catch(err => {
|
||||
logger.error(`socket(${socket.id}) -- ${err}.`)
|
||||
socket.emit("system", JSON.stringify(new Response(false, "token is invaild.")))
|
||||
return;
|
||||
}
|
||||
socket.username = username;
|
||||
onlineList.push(username);
|
||||
clientMap.set(socket.id, username);
|
||||
socket.emit("username", socket.username);
|
||||
socket.emit("system", JSON.stringify(new Response(true, `welcome, ${socket.username}`)))
|
||||
io.emit("notice", JSON.stringify(new Response(true, `${socket.username} join the chat.`)))
|
||||
logger.debug(`socket(${socket.id}) -- ${username} signed in.`)
|
||||
})
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
"author": "star",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"axios": "^1.13.2",
|
||||
"express": "^5.1.0",
|
||||
"log4js": "^6.9.1",
|
||||
"sequelize": "^6.37.7",
|
||||
|
||||
@ -4,267 +4,63 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Document</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'HarmonyOS Sans SC Medium';
|
||||
}
|
||||
.title {
|
||||
font-size: 24px;
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
.card {
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
#container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
#msg-list {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
#online-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
width: 800px;
|
||||
}
|
||||
|
||||
#sysmsg {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
#usrmsg > span {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
#online {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
gap: 5px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
#online > span {
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
background-color: #f9f9f9;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
padding: 8px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
button, input[type="button"] {
|
||||
padding: 8px 12px;
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
#container {
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
#online-list {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<title>Chat</title>
|
||||
<link rel="stylesheet" href="style/style.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="container">
|
||||
|
||||
<div id="msg-list">
|
||||
<div class="card">
|
||||
<div class="title">系统消息</div>
|
||||
<div id="sysmsg"></div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="title">用户消息</div>
|
||||
<div id="usrmsg"></div>
|
||||
<div class="card" style="flex: 1; display: flex; flex-direction: column;">
|
||||
<div class="title">消息</div>
|
||||
<div id="usrmsg" style="flex: 1;"></div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div style="display: flex;">
|
||||
<input type="text" id="message" style="width: 100%;" />
|
||||
<button onclick="sendMsg()">Submit</button>
|
||||
<div class="input-group">
|
||||
<input type="text" id="message" placeholder="输入消息..." />
|
||||
<button onclick="sendMsg()">发送</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="online-list">
|
||||
<div class="card">
|
||||
<div class="title">在线列表</div>
|
||||
<div style="color: gray;">在线人数:<span id="online-number">NaN</span></div>
|
||||
<div class="title">在线用户</div>
|
||||
<div style="color: var(--text-secondary); font-size: 13px; margin-bottom: 8px;">
|
||||
在线人数:<span id="online-number">0</span>
|
||||
</div>
|
||||
<div id="online"></div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="title">服务器选项</div>
|
||||
<div>连接状态: <span id="stat"></span></div>
|
||||
<button onclick="connect()">连接</button>
|
||||
<button onclick="disconnect()">断开</button>
|
||||
<div class="title">服务器状态</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<span id="stat" class="status-badge disconnected">
|
||||
<span class="status-dot"></span>
|
||||
<span id="stat-text">disconnected</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<button onclick="connect()">连接</button>
|
||||
<button onclick="disconnect()" style="background-color: var(--text-secondary);">断开</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="title">设置用户名</div>
|
||||
<div>当前用户名:<span id="current-username">未设置用户名</span></div>
|
||||
<input type="text" id="username" maxlength="20" />
|
||||
<button onclick="register()">提交</button>
|
||||
<div class="title">用户信息</div>
|
||||
<div id="user-info">
|
||||
<div class="username-display" id="current-username">未登录</div>
|
||||
<div class="login-prompt" id="login-prompt" style="display: none;">
|
||||
<p>请先登录</p>
|
||||
<button onclick="showLogin()">去登录</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding-top: 20px" id="control">
|
||||
<div>
|
||||
|
||||
</div>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<script src="https://cdn.socket.io/4.7.5/socket.io.min.js"></script>
|
||||
<script>
|
||||
window.onload = () => {
|
||||
fetchHistory()
|
||||
}
|
||||
const socket = io("ws://localhost:5691");
|
||||
socket.on("connect", () => {
|
||||
console.log("Connected");
|
||||
updateState(true)
|
||||
writeLogSystem("You are connected to server", true, "client")
|
||||
});
|
||||
|
||||
socket.on("system", data => {
|
||||
console.log("system", data)
|
||||
data = JSON.parse(data)
|
||||
writeLogSystem(data.msg, data.status, "system")
|
||||
})
|
||||
|
||||
socket.on("username", data => {
|
||||
updateUsername(data);
|
||||
})
|
||||
|
||||
socket.on("notice", data => {
|
||||
console.log("notice", data)
|
||||
data = JSON.parse(data)
|
||||
writeLogSystem(data.msg, data.status, "notice")
|
||||
})
|
||||
|
||||
socket.on("msg", data => {
|
||||
console.log("user", data)
|
||||
data = JSON.parse(data);
|
||||
let msg = ` ${data.msg}`
|
||||
writeLogUser(msg, data.sender)
|
||||
})
|
||||
|
||||
socket.on("disconnect", () => {
|
||||
updateState(false);
|
||||
console.log("Disconnected")
|
||||
writeLogSystem("You are disconnected from server", false, "client")
|
||||
updateUsername("未设置用户名")
|
||||
});
|
||||
|
||||
const connect = () => socket.connect()
|
||||
const disconnect = () => socket.disconnect();
|
||||
const register = () => socket.emit("system", JSON.stringify({ opt: "signin", args: { username: document.getElementById("username").value } }));
|
||||
const sendMsg = () => {
|
||||
socket.emit("msg", document.getElementById("message").value)
|
||||
document.getElementById("message").value = ""
|
||||
}
|
||||
|
||||
const updateState = (value) => {
|
||||
document.getElementById('stat').textContent = value ? "connected" : "disconnected";
|
||||
}
|
||||
|
||||
document.getElementById("message").addEventListener("keydown", (e) => {
|
||||
if (e.key === "Enter") {
|
||||
sendMsg();
|
||||
}
|
||||
});
|
||||
|
||||
const updateUsername = data => document.getElementById("current-username").textContent = data;
|
||||
|
||||
function fetchHistory() {
|
||||
fetch("/history")
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
res.data.reverse().forEach(element => {
|
||||
writeLogUser(element.message, element.sender)
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
}
|
||||
|
||||
function fetchOnline() {
|
||||
fetch("/online").then(res => res.json()).then(res => {
|
||||
const c = document.getElementById('online');
|
||||
// console.log(res)
|
||||
document.getElementById('online-number').textContent = res.online;
|
||||
c.innerHTML = "";
|
||||
res.user.forEach(element => {
|
||||
const u = document.createElement('span');
|
||||
u.textContent = element;
|
||||
c.append(u);
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
const onlineChecker = setInterval(() => {
|
||||
fetchOnline();
|
||||
}, 1000)
|
||||
|
||||
function writeLogSystem(msg, status = true, topic = "system") {
|
||||
const c = document.getElementById('sysmsg');
|
||||
const m = document.createElement('span');
|
||||
m.style.color = status ? 'green' : 'red'
|
||||
m.textContent = `[${new Date().toLocaleDateString()} ${new Date().toLocaleTimeString()}][${topic}] ${msg}`
|
||||
c.append(m);
|
||||
}
|
||||
|
||||
function writeLogUser(msg, sender) {
|
||||
const c = document.getElementById('usrmsg');
|
||||
const m = document.createElement('span');
|
||||
m.textContent = `[${new Date().toLocaleDateString()} ${new Date().toLocaleTimeString()}][${sender}] ${msg}`
|
||||
c.append(m);
|
||||
}
|
||||
|
||||
document.getElementById("username").addEventListener("keydown", (e) => {
|
||||
if (e.key === "Enter") {
|
||||
register();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<script src="script/main.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
145
static/script/main.js
Normal file
145
static/script/main.js
Normal file
@ -0,0 +1,145 @@
|
||||
window.onload = () => {
|
||||
fetchHistory()
|
||||
updateLoginState(false)
|
||||
}
|
||||
const socket = io("ws://localhost:5691");
|
||||
socket.on("connect", () => {
|
||||
console.log("Connected");
|
||||
updateState(true)
|
||||
writeLogSystem("已连接到服务器", true, "client")
|
||||
});
|
||||
|
||||
socket.on("system", data => {
|
||||
console.log("system", data)
|
||||
data = JSON.parse(data)
|
||||
writeLogSystem(data.msg, data.status, "system")
|
||||
})
|
||||
|
||||
socket.on("username", data => {
|
||||
updateUsername(data);
|
||||
})
|
||||
|
||||
socket.on("notice", data => {
|
||||
console.log("notice", data)
|
||||
data = JSON.parse(data)
|
||||
writeLogSystem(data.msg, data.status, "notice")
|
||||
})
|
||||
|
||||
socket.on("msg", data => {
|
||||
console.log("user", data)
|
||||
data = JSON.parse(data);
|
||||
let msg = ` ${data.msg}`
|
||||
writeLogUser(msg, data.sender)
|
||||
})
|
||||
|
||||
socket.on("disconnect", () => {
|
||||
updateState(false);
|
||||
console.log("Disconnected")
|
||||
writeLogSystem("已断开服务器连接", false, "client")
|
||||
updateLoginState(false)
|
||||
});
|
||||
|
||||
const connect = () => socket.connect()
|
||||
const disconnect = () => socket.disconnect();
|
||||
const register = () => socket.emit("system", JSON.stringify({ opt: "signin", args: { username: document.getElementById("username").value } }));
|
||||
const sendMsg = () => {
|
||||
socket.emit("msg", document.getElementById("message").value)
|
||||
document.getElementById("message").value = ""
|
||||
}
|
||||
|
||||
const updateState = (value) => {
|
||||
const statEl = document.getElementById('stat');
|
||||
const statText = document.getElementById('stat-text');
|
||||
if (value) {
|
||||
statEl.className = 'status-badge connected';
|
||||
statText.textContent = 'connected';
|
||||
} else {
|
||||
statEl.className = 'status-badge disconnected';
|
||||
statText.textContent = 'disconnected';
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById("message").addEventListener("keydown", (e) => {
|
||||
if (e.key === "Enter") {
|
||||
sendMsg();
|
||||
}
|
||||
});
|
||||
|
||||
const updateUsername = data => {
|
||||
document.getElementById("current-username").textContent = data;
|
||||
updateLoginState(true, data);
|
||||
}
|
||||
|
||||
const updateLoginState = (isLoggedIn, username = '') => {
|
||||
const usernameEl = document.getElementById("current-username");
|
||||
const loginPrompt = document.getElementById("login-prompt");
|
||||
|
||||
if (isLoggedIn) {
|
||||
usernameEl.textContent = username || '已登录';
|
||||
usernameEl.style.display = 'block';
|
||||
if (loginPrompt) loginPrompt.style.display = 'none';
|
||||
} else {
|
||||
usernameEl.textContent = '未登录';
|
||||
if (loginPrompt) loginPrompt.style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
const showLogin = () => {
|
||||
const username = prompt("请输入用户名:");
|
||||
if (username && username.trim()) {
|
||||
socket.emit("system", JSON.stringify({ opt: "signin", args: { username: username.trim() } }));
|
||||
}
|
||||
}
|
||||
|
||||
function fetchHistory() {
|
||||
fetch("/history")
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
res.data.reverse().forEach(element => {
|
||||
writeLogUser(element.message, element.sender)
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
}
|
||||
|
||||
function fetchOnline() {
|
||||
fetch("/online").then(res => res.json()).then(res => {
|
||||
const c = document.getElementById('online');
|
||||
document.getElementById('online-number').textContent = res.online;
|
||||
c.innerHTML = "";
|
||||
res.user.forEach(element => {
|
||||
const u = document.createElement('span');
|
||||
u.textContent = element;
|
||||
c.append(u);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const onlineChecker = setInterval(() => {
|
||||
fetchOnline();
|
||||
}, 1000)
|
||||
|
||||
function writeLogSystem(msg, status = true, topic = "system") {
|
||||
const c = document.getElementById('sysmsg');
|
||||
const m = document.createElement('span');
|
||||
m.style.color = status ? 'var(--success-color)' : 'var(--danger-color)'
|
||||
m.textContent = `[${new Date().toLocaleTimeString()}][${topic}] ${msg}`
|
||||
c.appendChild(m);
|
||||
c.scrollTop = c.scrollHeight;
|
||||
}
|
||||
|
||||
function writeLogUser(msg, sender) {
|
||||
const c = document.getElementById('usrmsg');
|
||||
const m = document.createElement('span');
|
||||
m.textContent = `[${new Date().toLocaleTimeString()}][${sender}] ${msg}`
|
||||
c.appendChild(m);
|
||||
c.scrollTop = c.scrollHeight;
|
||||
}
|
||||
|
||||
document.getElementById("username")?.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Enter") {
|
||||
register();
|
||||
}
|
||||
});
|
||||
251
static/style/style.css
Normal file
251
static/style/style.css
Normal file
@ -0,0 +1,251 @@
|
||||
:root {
|
||||
--primary-color: #3b82f6;
|
||||
--primary-hover: #2563eb;
|
||||
--bg-color: #f8fafc;
|
||||
--card-bg: #ffffff;
|
||||
--border-color: #e2e8f0;
|
||||
--text-primary: #1e293b;
|
||||
--text-secondary: #64748b;
|
||||
--success-color: #22c55e;
|
||||
--danger-color: #ef4444;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'HarmonyOS Sans SC Medium', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
background-color: var(--bg-color);
|
||||
color: var(--text-primary);
|
||||
line-height: 1.6;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.card {
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
||||
background-color: var(--card-bg);
|
||||
transition: box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
#container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 20px;
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
#msg-list {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
#online-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
width: 320px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
#sysmsg {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
#sysmsg::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
#sysmsg::-webkit-scrollbar-thumb {
|
||||
background-color: var(--border-color);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
#usrmsg {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
max-height: calc(100vh - 280px);
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
#usrmsg::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
#usrmsg::-webkit-scrollbar-thumb {
|
||||
background-color: var(--border-color);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
#usrmsg > span {
|
||||
padding: 10px 14px;
|
||||
background-color: var(--bg-color);
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
#online {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 8px;
|
||||
padding-top: 12px;
|
||||
}
|
||||
|
||||
#online > span {
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
padding: 8px 12px;
|
||||
text-align: center;
|
||||
font-size: 13px;
|
||||
background-color: var(--bg-color);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
#online > span:hover {
|
||||
border-color: var(--primary-color);
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
padding: 10px 14px;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
width: 100%;
|
||||
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
input[type="text"]:focus {
|
||||
border-color: var(--primary-color);
|
||||
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 10px 20px;
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: var(--primary-hover);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
button:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.input-group {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.input-group input {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.input-group button {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 4px 10px;
|
||||
border-radius: 20px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.status-badge.connected {
|
||||
background-color: rgba(34, 197, 94, 0.1);
|
||||
color: var(--success-color);
|
||||
}
|
||||
|
||||
.status-badge.disconnected {
|
||||
background-color: rgba(239, 68, 68, 0.1);
|
||||
color: var(--danger-color);
|
||||
}
|
||||
|
||||
.status-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background-color: currentColor;
|
||||
}
|
||||
|
||||
.username-display {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: var(--primary-color);
|
||||
padding: 12px 16px;
|
||||
background-color: rgba(59, 130, 246, 0.05);
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.login-prompt {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.login-prompt p {
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 900px) {
|
||||
#container {
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
#online-list {
|
||||
width: 100%;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
#online-list .card {
|
||||
flex: 1;
|
||||
min-width: 280px;
|
||||
}
|
||||
}
|
||||
15
utils/auth.js
Normal file
15
utils/auth.js
Normal file
@ -0,0 +1,15 @@
|
||||
import axios from "axios"
|
||||
|
||||
var apiUrl = "https://www.cloudy233.top:6699/bearer";
|
||||
|
||||
class Resp {
|
||||
constructor(success, message, data = null) {
|
||||
this.success = success
|
||||
this.message = message
|
||||
this.data = data
|
||||
}
|
||||
}
|
||||
|
||||
export function verifyToken(token) {
|
||||
return axios.get(apiUrl, {headers: {Authorization: "Bearer " + token}})
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user