xss,删掉头像

This commit is contained in:
shuaiplus committed 2025-05-05 18:36:01 +08:00
1 parent 1d9fc7c467
commit 7f99fc5a5b
2 files changed
+57 -155

No files matched your search

+29 -59
View File
@@ -1,15 +1,5 @@
import config from './config.js';
// 头像尺寸常量
const AVATAR_SIZE_SIDEBAR = 44;
const AVATAR_SIZE_MEMBER = 38;
const AVATAR_SIZE_MSG = 32;
// 统一头像渲染工具函数
function renderAvatar(name, size) {
return getSvgAvatar(name, size);
}
// 多房间状态管理
let roomsData = [];
let activeRoomIndex = -1;
@@ -24,8 +14,6 @@ function switchRoom(index) {
// 同步更新sidebar用户名和头像
const sidebarUsername = document.getElementById('sidebar-username');
if (sidebarUsername) sidebarUsername.textContent = rd.myName;
const avatarEl = document.getElementById('sidebar-user-avatar');
if (avatarEl) avatarEl.innerHTML = renderAvatar(rd.myName, AVATAR_SIZE_SIDEBAR);
renderRooms(index);
renderMainHeader();
renderUserList();
@@ -58,13 +46,6 @@ function stringToColor(str) {
return `hsl(${h}, 70%, 75%)`;
}
// 生成 Dicebear Micah 风格头像,背景色与用户名相关
function getSvgAvatar(text, size = 42) {
const seed = encodeURIComponent(text || 'User');
const bg = stringToColor(text || 'User');
return `<img src="https://api.dicebear.com/9.x/micah/svg?seed=${seed}&size=${size}&baseColor=ac6651,f9c9b6" alt="avatar" width="${size}" height="${size}" style="border-radius:50%;background:${bg};object-fit:cover;" />`;
}
// 获取用户名首字母(前2位大写)
function getInitials(name) {
if (!name) return '?';
@@ -92,13 +73,13 @@ function renderUserList() {
function createUserItem(user, isMe) {
let div = document.createElement('div');
div.className = 'member' + (isMe ? ' me' : '');
const safeUsername = escapeHTML(user.username);
div.innerHTML = `
<span class="avatar">${renderAvatar(user.username, AVATAR_SIZE_MEMBER)}</span>
<span class="avatar"></span>
<div class="member-info">
<div class="member-name">${escapeHTML(user.username)}${isMe ? ' (我)' : ''}</div>
<div class="member-name">${safeUsername}${isMe ? ' (我)' : ''}</div>
</div>
`;
// 不可点击
return div;
}
@@ -108,7 +89,9 @@ function handleClientList(idx, list, selfId) {
if (!rd) return;
rd.userList = list;
rd.userMap = {};
list.forEach(u => { rd.userMap[u.clientId] = u; });
list.forEach(u => {
rd.userMap[u.clientId] = u;
});
rd.myId = selfId;
if (activeRoomIndex === idx) renderUserList();
}
@@ -154,7 +137,9 @@ function addMsg(text, isHistory = false) {
div.className = 'bubble me';
const now = new Date();
const time = now.getHours().toString().padStart(2, '0') + ':' + now.getMinutes().toString().padStart(2, '0');
div.innerHTML = `<span class="bubble-content">${text}</span><span class="bubble-meta">${time}</span>`;
// 先escape再处理换行
const safeText = escapeHTML(text).replace(/\n/g, '<br>');
div.innerHTML = `<span class="bubble-content">${safeText}</span><span class="bubble-meta">${time}</span>`;
chatArea.appendChild(div);
chatArea.scrollTop = chatArea.scrollHeight;
}
@@ -166,12 +151,15 @@ function addOtherMsg(msg, name = 'Anonymous', avatar = '', isHistory = false) {
if (!chatArea) return;
const bubbleWrap = document.createElement('div');
bubbleWrap.className = 'bubble-other-wrap';
// 先escape再处理换行
const safeMsg = escapeHTML(msg).replace(/\n/g, '<br>');
const safeName = escapeHTML(name);
bubbleWrap.innerHTML = `
<span class="bubble-other-avatar">${renderAvatar(avatar || name, AVATAR_SIZE_MSG)}</span>
<span class="avatar"></span>
<div class="bubble-other-main">
<div class="bubble other">
<div class="bubble-other-name">${name}</div>
<span class="bubble-content">${msg.replace(/\n/g, '<br>')}</span>
<div class="bubble-other-name">${safeName}</div>
<span class="bubble-content">${safeMsg}</span>
<span class="bubble-meta">${(new Date()).getHours().toString().padStart(2, '0')}:${(new Date()).getMinutes().toString().padStart(2, '0')}</span>
</div>
</div>
@@ -187,9 +175,10 @@ function renderRooms(activeId = 0) {
roomsData.forEach((rd, i) => {
const div = document.createElement('div');
div.className = 'room' + (i === activeId ? ' active' : '');
const safeRoom = escapeHTML(rd.room);
div.innerHTML = `
<div class="info">
<div class="title">#${escapeHTML(rd.room)}</div>
<div class="title">#${safeRoom}</div>
</div>
`;
div.onclick = () => switchRoom(i);
@@ -219,8 +208,6 @@ function joinRoom(username, room, password, modal = null) {
// 更新侧边栏
const sidebarUsername = document.getElementById('sidebar-username');
if (sidebarUsername) sidebarUsername.textContent = username;
const avatarEl = document.getElementById('sidebar-user-avatar');
if (avatarEl) avatarEl.innerHTML = renderAvatar(username, AVATAR_SIZE_SIDEBAR);
// 隐藏登录界面或关闭modal
if (modal) modal.remove();
else document.getElementById('login-container').style.display = 'none';
@@ -297,9 +284,10 @@ function renderMainHeader() {
if (rd && !rd.userList.some(u => u.clientId === rd.myId)) {
onlineCount += 1;
}
const safeRoomName = escapeHTML(roomName);
document.getElementById("main-header").innerHTML = `
<div style="display: flex; align-items: center;">
<div class="group-title" style="font-size: 1.22em; font-weight: bold;">#${escapeHTML(roomName)}</div>
<div class="group-title" style="font-size: 1.22em; font-weight: bold;">#${safeRoomName}</div>
<span style="margin-left:10px;font-size:13px;color:#888;">${onlineCount} members</span>
</div>
<div class="main-header-actions">
@@ -395,7 +383,7 @@ function setupMoreBtnMenu() {
}
}
function unbindMouseMove() {
if (isMouseMoveBound) {
if (!isMouseMoveBound) {
window.removeEventListener('mousemove', onMouseMove);
isMouseMoveBound = false;
}
@@ -435,26 +423,7 @@ function setupMoreBtnMenu() {
if (e.target.dataset.action === 'share') {
alert('分享功能待实现');
} else if (e.target.dataset.action === 'exit') {
// 退出当前房间
if (activeRoomIndex >= 0 && roomsData[activeRoomIndex]) {
// 优先彻底销毁连接
const chatInst = roomsData[activeRoomIndex].chat;
if (chatInst && typeof chatInst.destruct === 'function') {
chatInst.destruct();
} else if (chatInst && typeof chatInst.disconnect === 'function') {
chatInst.disconnect();
}
roomsData[activeRoomIndex].chat = null;
// 移除房间
roomsData.splice(activeRoomIndex, 1);
if (roomsData.length > 0) {
// 切换到第一个房间
switchRoom(0);
} else {
// 没有房间了,回到登录
location.reload();
}
}
// 退出功能待实现
}
closeMenu();
}
@@ -475,6 +444,7 @@ function setupMoreBtnMenu() {
});
}
// 页面初始化
window.addEventListener('DOMContentLoaded', () => {
const loginContainer = document.getElementById('login-container');
const chatContainer = document.getElementById('chat-container');
@@ -507,11 +477,11 @@ window.addEventListener('DOMContentLoaded', () => {
}
});
}
});
// 初始化
renderRooms(activeRoomIndex);
renderMainHeader();
renderUserList();
setupTabs();
renderChatArea();
// 初始化
renderRooms(activeRoomIndex);
renderMainHeader();
renderUserList();
setupTabs();
renderChatArea();
});
+28 -96
View File
@@ -20,21 +20,6 @@ body {
background: #fff;
border-bottom: 1px solid #e0e0e0;
}
.sidebar-user .avatar {
width: 44px;
height: 44px;
border-radius: 50%;
margin-right: 12px;
background: #30a8f7;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-weight: bold;
font-size: 22px;
user-select: none;
overflow: hidden;
}
.sidebar-username {
font-weight: 600;
font-size: 17px;
@@ -103,23 +88,6 @@ body {
box-shadow: 0 2px 8px #b3d8f7a0;
}
.room .avatar {
width: 42px;
height: 42px;
border-radius: 50%;
margin-right: 12px;
display: flex;
align-items: center;
justify-content: center;
background: #30a8f7;
color: #fff;
font-weight: bold;
font-size: 18px;
user-select: none;
overflow: hidden;
flex-shrink: 0;
}
.room .info {
flex: 1;
}
@@ -173,22 +141,6 @@ body {
box-shadow: 0 2px 8px #e0e0e040;
}
.main-header .avatar {
width: 40px;
height: 40px;
border-radius: 50%;
background: #30a8f7;
margin-right: 12px;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-weight: bold;
font-size: 20px;
user-select: none;
overflow: hidden;
}
.main-header .group-title {
font-weight: bold;
font-size: 18px;
@@ -308,21 +260,6 @@ body {
align-items: flex-end;
margin-bottom: 12px;
}
.bubble-other-avatar {
width: 32px;
height: 32px;
border-radius: 50%;
background: #30a8f7;
color: #fff;
font-weight: bold;
font-size: 16px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 8px;
flex-shrink: 0;
user-select: none;
}
.bubble-other-main {
display: flex;
flex-direction: column;
@@ -657,22 +594,6 @@ body {
justify-content: space-around;
}
.rightbar-group .avatar {
width: 44px;
height: 44px;
border-radius: 50%;
background: #30a8f7;
margin-right: 12px;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-weight: bold;
font-size: 22px;
user-select: none;
overflow: hidden;
}
.rightbar .group-info .title {
font-size: 18px;
font-weight: 600;
@@ -735,23 +656,6 @@ body {
box-shadow: 0 2px 8px #b3d8f7a0;
}
.member .avatar {
width: 38px;
height: 38px;
border-radius: 50%;
margin-right: 14px;
background: #30a8f7;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-weight: bold;
font-size: 18px;
user-select: none;
overflow: hidden;
flex-shrink: 0;
}
.member .member-info {
flex: 1;
}
@@ -900,4 +804,32 @@ body {
}
.login-modal-close:hover {
color: #30a8f7;
}
/* 统一所有头像样式为.avatar */
.avatar {
width: 42px !important;
height: 42px !important;
border-radius: 50% !important;
background: #30a8f7 !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
color: #fff !important;
font-weight: bold !important;
font-size: 20px !important;
user-select: none !important;
overflow: hidden !important;
flex-shrink: 0 !important;
object-fit: cover !important;
margin-right: 12px !important;
box-sizing: border-box !important;
}
.avatar img {
width: 100% !important;
height: 100% !important;
border-radius: 50% !important;
object-fit: cover !important;
display: block !important;
}