'use strict' const http = require('http') const { session } = require('electron') // 大屏实例看板:复用服务端管理面板的登录态,不另造一套登录逻辑。 // 管理面板窗口与 Console 同属默认 session,登录后 scoreboard_session 就存在这里, // 主进程取同一张 Cookie 访问 GET /api/display/instances(记录员及以上可读)。 // Cookie 名与服务端 server/internal/auth/auth.go 的 CookieSession 一致。 const SESSION_COOKIE = 'scoreboard_session' const REQUEST_TIMEOUT_MS = 3000 function requestJSON({ host, port, path, cookie }) { return new Promise((resolve) => { const request = http.get( { host, port, path, timeout: REQUEST_TIMEOUT_MS, headers: { Cookie: cookie, Accept: 'application/json', Connection: 'close' } }, (response) => { const chunks = [] response.on('data', (chunk) => chunks.push(chunk)) response.on('end', () => { resolve({ status: response.statusCode, body: Buffer.concat(chunks).toString('utf8') }) }) // 连接中途出错时 end 不会触发,必须自己收尾,否则每次轮询都泄漏一个 pending Promise response.on('error', (err) => resolve({ error: err })) } ) request.on('timeout', () => request.destroy(new Error('请求超时'))) request.on('error', (err) => resolve({ error: err })) }) } async function readSessionCookie(url) { try { const cookies = await session.defaultSession.cookies.get({ url }) const hit = cookies.find((item) => item.name === SESSION_COOKIE) return hit ? `${hit.name}=${hit.value}` : null } catch { return null } } // 成功返回 { ok: true, instances };失败返回 { ok: false, reason, message }, // message 直接作为界面占位文案,reason 供调用方决定要不要记日志(避免失败时刷屏)。 async function fetchDisplayInstances({ host, port }) { const cookie = await readSessionCookie(`http://${host}:${port}/`) if (!cookie) { return { ok: false, reason: 'unauthenticated', message: '未登录,请先在服务端管理面板登录(记录员及以上)' } } const result = await requestJSON({ host, port, path: '/api/display/instances', cookie }) if (result.error) { if (result.error.code === 'ECONNREFUSED' || result.error.code === 'ECONNRESET') { return { ok: false, reason: 'offline', message: '服务端未运行,暂时无法读取大屏实例' } } return { ok: false, reason: 'error', message: `读取大屏实例失败:${result.error.message}` } } if (result.status === 401) { return { ok: false, reason: 'unauthenticated', message: '登录已失效,请重新登录服务端管理面板' } } if (result.status === 403) { return { ok: false, reason: 'forbidden', message: '当前账号权限不足(需要记录员及以上)' } } if (result.status === 404) { return { ok: false, reason: 'unsupported', message: '服务端版本较旧,未提供大屏实例接口' } } if (result.status !== 200) { return { ok: false, reason: 'error', message: `读取大屏实例失败:HTTP ${result.status}` } } try { const payload = JSON.parse(result.body) return { ok: true, instances: Array.isArray(payload.data) ? payload.data : [] } } catch (err) { return { ok: false, reason: 'error', message: `大屏实例响应无法解析:${err.message}` } } } module.exports = { fetchDisplayInstances }