收紧输入渲染与后台配置权限

This commit is contained in:
2026-04-19 14:43:02 +08:00
parent ba6406ed68
commit 438241e878
12 changed files with 550 additions and 48 deletions
+45 -11
View File
@@ -556,19 +556,26 @@
'<div style="text-align:center;color:#bbb;padding:16px 0;font-size:11px;">暂无房间</div>';
return;
}
container.innerHTML = data.rooms.map(room => {
const isCurrent = room.id === _currentRoomId;
const roomRows = data.rooms.map(room => {
const roomId = Number.parseInt(room.id, 10);
if (!Number.isInteger(roomId)) {
return '';
}
const isCurrent = roomId === _currentRoomId;
const closed = !room.door_open;
const safeRoomName = escapeHtml(String(room.name ?? ''));
const safeOnlineCount = Math.max(Number.parseInt(room.online, 10) || 0, 0);
const bg = isCurrent ? '#ecf4ff' : '#fff';
const border = isCurrent ? '#aac5f0' : '#e0eaf5';
const nameColor = isCurrent ? '#336699' : (closed ? '#bbb' : '#444');
const badge = room.online > 0 ?
`<span style="background:#e8f5e9;color:#2e7d32;border-radius:8px;padding:0 5px;font-size:10px;font-weight:bold;white-space:nowrap;flex-shrink:0;">${room.online} 人</span>` :
const badge = safeOnlineCount > 0 ?
`<span style="background:#e8f5e9;color:#2e7d32;border-radius:8px;padding:0 5px;font-size:10px;font-weight:bold;white-space:nowrap;flex-shrink:0;">${safeOnlineCount} 人</span>` :
`<span style="background:#f5f5f5;color:#bbb;border-radius:8px;padding:0 5px;font-size:10px;white-space:nowrap;flex-shrink:0;">空</span>`;
const currentTag = isCurrent ?
`<span style="font-size:9px;color:#336699;opacity:.7;margin-left:3px;">当前</span>` :
'';
const clickHandler = isCurrent ? '' : `onclick="location.href='/room/${room.id}'"`;
const clickHandler = isCurrent ? '' : `onclick="location.href='/room/${roomId}'"`;
return `<div ${clickHandler}
style="display:flex;align-items:center;justify-content:space-between;
@@ -579,11 +586,14 @@
onmouseover="if(${!isCurrent}) this.style.background='#ddeeff';"
onmouseout="this.style.background='${bg}';">
<span style="color:${nameColor};font-size:11px;font-weight:${isCurrent?'bold':'normal'};overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;margin-right:4px;">
${room.name}${currentTag}
${safeRoomName}${currentTag}
</span>
${badge}
</div>`;
}).join('');
}).filter(Boolean).join('');
container.innerHTML = roomRows ||
'<div style="text-align:center;color:#bbb;padding:16px 0;font-size:11px;">暂无房间</div>';
})
.catch(() => {
container.innerHTML =
@@ -1030,7 +1040,7 @@
// 生成自然语序的动作串:情绪型=[人][着/地]对[目标][verb]:;动作型=[人][了][目标][verb]
const buildActionStr = (action, fromHtml, toHtml, verb = '说') => {
const info = actionTextMap[action];
if (!info) return `${fromHtml}对${toHtml}${action}${verb}`;
if (!info) return `${fromHtml}对${toHtml}${escapeHtml(String(action || ''))}${verb}`;
if (info.type === 'emotion') return `${fromHtml}${info.word}对${toHtml}${verb}`;
return `${fromHtml}${info.word}${toHtml}${verb}`;
};
@@ -1437,6 +1447,7 @@
.listen('ScreenCleared', (e) => {
console.log('收到全员清屏事件:', e);
const operator = e.operator;
const safeOperator = escapeHtml(String(operator || ''));
// 清除公聊窗口所有消息
const say1 = document.getElementById('chat-messages-container');
@@ -1462,7 +1473,7 @@
now.getMinutes().toString().padStart(2, '0') + ':' +
now.getSeconds().toString().padStart(2, '0');
sysDiv.innerHTML =
`<span style="color: #dc2626; font-weight: bold;">🧹 管理员 <b>${operator}</b> 已执行全员清屏</span><span class="msg-time">(${timeStr})</span>`;
`<span style="color: #dc2626; font-weight: bold;">🧹 管理员 <b>${safeOperator}</b> 已执行全员清屏</span><span class="msg-time">(${timeStr})</span>`;
if (say1) {
say1.appendChild(sysDiv);
say1.scrollTop = say1.scrollHeight;
@@ -1494,6 +1505,9 @@
const timeStr = now.getHours().toString().padStart(2, '0') + ':' +
now.getMinutes().toString().padStart(2, '0') + ':' +
now.getSeconds().toString().padStart(2, '0');
const safeVersion = e.safe_version ?? escapeHtml(String(e.version ?? ''));
const safeTitle = e.safe_title ?? escapeHtml(String(e.title ?? ''));
const safeUrl = escapeHtml(normalizeSafeChatUrl(e.url, '{{ route('changelog.index') }}'));
const sysDiv = document.createElement('div');
sysDiv.className = 'msg-line';
@@ -1501,8 +1515,8 @@
sysDiv.style.cssText =
'background: #fffbeb; border-left: 3px solid #d97706; border-radius: 4px; padding: 5px 10px; margin: 3px 0;';
sysDiv.innerHTML = `<span style="color: #b45309; font-weight: bold;">
📋 【版本更新】v${e.version} · ${e.title}
<a href="${e.url}" target="_blank" rel="noopener"
📋 【版本更新】v${safeVersion} · ${safeTitle}
<a href="${safeUrl}" target="_blank" rel="noopener"
style="color: #7c3aed; text-decoration: underline; margin-left: 8px; font-size: 0.85em;">
查看详情
</a>
@@ -2405,4 +2419,24 @@
div.textContent = text;
return div.innerHTML;
}
/**
* 规整广播携带的链接,只允许当前站点的 http(s) 地址进入 innerHTML。
*/
function normalizeSafeChatUrl(url, fallback) {
try {
const parsedUrl = new URL(url || fallback, window.location.origin);
if (!['http:', 'https:'].includes(parsedUrl.protocol)) {
return fallback;
}
if (parsedUrl.origin !== window.location.origin) {
return fallback;
}
return parsedUrl.toString();
} catch (error) {
return fallback;
}
}
</script>