fix: address code review feedback - timer leaks, CORS, validation, cache TTL

- Fix timer leak in fetchNotionProfile/loadDatabaseCache with finally block
- Remove HTTP origin from CORS whitelist (HTTPS-only)
- Validate devId against DEV_REGISTRY instead of regex
- Fix cache TTL check to use >= instead of >
- Fix chat.js count queries to use page_size 100 instead of 1

Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com>
Agent-Logs-Url: https://github.com/qinfendebingshuo/guanghulab/sessions/a260ce18-279a-4490-af90-a90a7b1f46cd
This commit is contained in:
copilot-swe-agent[bot] 2026-03-25 08:15:48 +00:00
parent df21bae395
commit 726b05bc0b
5 changed files with 15 additions and 17 deletions

View File

@ -55,7 +55,7 @@ router.post('/chat/context', async function(req, res) {
// 获取 Agent 注册表计数 // 获取 Agent 注册表计数
try { try {
if (dbConfig.agentRegistry) { if (dbConfig.agentRegistry) {
var agents = await notionService.queryDB(dbConfig.agentRegistry, null, null, 1); var agents = await notionService.queryDB(dbConfig.agentRegistry, null, null, 100);
context.agents_count = agents.results ? agents.results.length : 0; context.agents_count = agents.results ? agents.results.length : 0;
} }
} catch (_) {} } catch (_) {}
@ -66,7 +66,7 @@ router.post('/chat/context', async function(req, res) {
var syslogs = await notionService.queryDB(dbConfig.syslogInbox, { var syslogs = await notionService.queryDB(dbConfig.syslogInbox, {
property: 'DEV编号', property: 'DEV编号',
rich_text: { equals: devId } rich_text: { equals: devId }
}, null, 1); }, null, 100);
context.syslogs_count = syslogs.results ? syslogs.results.length : 0; context.syslogs_count = syslogs.results ? syslogs.results.length : 0;
} }
} catch (_) {} } catch (_) {}
@ -75,7 +75,7 @@ router.post('/chat/context', async function(req, res) {
try { try {
if (dbConfig.ticketBook) { if (dbConfig.ticketBook) {
var tickets = await notionService.queryDB(dbConfig.ticketBook, var tickets = await notionService.queryDB(dbConfig.ticketBook,
{ property: '状态', select: { does_not_equal: '已完成' } }, null, 1); { property: '状态', select: { does_not_equal: '已完成' } }, null, 100);
context.tickets_count = tickets.results ? tickets.results.length : 0; context.tickets_count = tickets.results ? tickets.results.length : 0;
} }
} catch (_) {} } catch (_) {}

View File

@ -28,15 +28,12 @@ var DEV_REGISTRY = {
router.get('/dev/:devId', async function(req, res) { router.get('/dev/:devId', async function(req, res) {
var devId = req.params.devId; var devId = req.params.devId;
// 验证 devId 格式 // 验证 devId
if (!/^DEV-\d{3}$/.test(devId)) { if (!DEV_REGISTRY[devId]) {
return res.status(400).json({ error: true, code: 'INVALID_DEV_ID', message: '无效的开发者编号格式' }); return res.status(404).json({ error: true, code: 'DEV_NOT_FOUND', message: '开发者不存在' });
} }
var devInfo = DEV_REGISTRY[devId]; var devInfo = DEV_REGISTRY[devId];
if (!devInfo) {
return res.status(404).json({ error: true, code: 'DEV_NOT_FOUND', message: '开发者不存在' });
}
var profile = { var profile = {
dev_id: devId, dev_id: devId,

View File

@ -18,7 +18,6 @@ const app = express();
app.use(cors({ app.use(cors({
origin: [ origin: [
'https://guanghulab.com', 'https://guanghulab.com',
'http://guanghulab.com',
'https://www.guanghulab.com', 'https://www.guanghulab.com',
'https://qinfendebingshuo.github.io' 'https://qinfendebingshuo.github.io'
], ],

View File

@ -14,7 +14,7 @@ var store = new Map();
function get(key) { function get(key) {
var item = store.get(key); var item = store.get(key);
if (!item) return null; if (!item) return null;
if (Date.now() - item.time > (item.ttl || DEFAULT_TTL)) { if (Date.now() - item.time >= (item.ttl || DEFAULT_TTL)) {
store.delete(key); store.delete(key);
return null; return null;
} }

View File

@ -1771,9 +1771,10 @@ async function fetchNotionProfile(devId) {
try { try {
const ctrl = new AbortController(); const ctrl = new AbortController();
const tid = setTimeout(function() { ctrl.abort(); }, 5000); const tid = setTimeout(function() { ctrl.abort(); }, 5000);
const res = await fetch(API_BASE + '/dev/' + encodeURIComponent(devId), { signal: ctrl.signal }); try {
clearTimeout(tid); const res = await fetch(API_BASE + '/dev/' + encodeURIComponent(devId), { signal: ctrl.signal });
if (res.ok) { const d = await res.json(); d._source = 'realtime'; return d; } if (res.ok) { const d = await res.json(); d._source = 'realtime'; return d; }
} finally { clearTimeout(tid); }
} catch (e) { console.debug('[通道B] profile fetch failed, 降级到通道A:', e.message); } } catch (e) { console.debug('[通道B] profile fetch failed, 降级到通道A:', e.message); }
} }
// 通道AGitHub 缓存) // 通道AGitHub 缓存)
@ -1791,9 +1792,10 @@ async function loadDatabaseCache(name) {
try { try {
const ctrl = new AbortController(); const ctrl = new AbortController();
const tid = setTimeout(function() { ctrl.abort(); }, 5000); const tid = setTimeout(function() { ctrl.abort(); }, 5000);
const res = await fetch(API_BASE + '/databases/' + encodeURIComponent(name), { signal: ctrl.signal }); try {
clearTimeout(tid); const res = await fetch(API_BASE + '/databases/' + encodeURIComponent(name), { signal: ctrl.signal });
if (res.ok) return await res.json(); if (res.ok) return await res.json();
} finally { clearTimeout(tid); }
} catch (e) { console.debug('[通道B] database ' + name + ' fetch failed:', e.message); } } catch (e) { console.debug('[通道B] database ' + name + ' fetch failed:', e.message); }
} }
// 通道AGitHub 缓存) // 通道AGitHub 缓存)