server.mjs 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243
  1. // 业务平台后端示例(无依赖):提供构建好的前端,并用 Key 为浏览器换取短期令牌。
  2. // Key 只存在于后端环境变量中,浏览器拿到的是 1 小时有效的浏览器令牌:
  3. // AGENT_API_KEY → /v1(单个助手的对话),AGENT_BOT_KEY → /bot/v1(机器人工作组),至少设置一个。
  4. // 真实平台应先校验自己的登录态(只给已登录用户签发令牌)。
  5. import fs from 'node:fs'
  6. import http from 'node:http'
  7. import path from 'node:path'
  8. import { fileURLToPath } from 'node:url'
  9. const AGENT_SERVER_URL = process.env.AGENT_SERVER_URL ?? 'http://127.0.0.1:8787'
  10. const AGENT_API_KEY = process.env.AGENT_API_KEY
  11. const AGENT_BOT_KEY = process.env.AGENT_BOT_KEY
  12. const PORT = Number(process.env.PORT ?? 5180)
  13. const dist = path.join(path.dirname(fileURLToPath(import.meta.url)), 'dist')
  14. if (!AGENT_API_KEY && !AGENT_BOT_KEY) throw new Error('请设置环境变量 AGENT_API_KEY 和 / 或 AGENT_BOT_KEY')
  15. const types = { '.html': 'text/html; charset=utf-8', '.js': 'text/javascript', '.css': 'text/css', '.svg': 'image/svg+xml' }
  16. const tokenEndpoints = {
  17. '/api/agent-token': [AGENT_API_KEY, '/v1/browser-tokens'],
  18. '/api/bot-token': [AGENT_BOT_KEY, '/bot/v1/browser-tokens'],
  19. }
  20. http.createServer(async (req, res) => {
  21. const endpoint = req.method === 'POST' && tokenEndpoints[req.url]
  22. if (endpoint && endpoint[0]) {
  23. // 在这里校验平台自己的登录态
  24. const r = await fetch(`${AGENT_SERVER_URL}${endpoint[1]}`, {
  25. method: 'POST',
  26. headers: { authorization: `Bearer ${endpoint[0]}`, 'content-type': 'application/json' },
  27. body: JSON.stringify({ ttlSeconds: 3600 }),
  28. })
  29. res.writeHead(r.status, { 'content-type': 'application/json' })
  30. return res.end(await r.text())
  31. }
  32. if (req.url === '/api/config') {
  33. res.writeHead(200, { 'content-type': 'application/json' })
  34. return res.end(JSON.stringify({ agentServerUrl: process.env.AGENT_SERVER_PUBLIC_URL ?? AGENT_SERVER_URL, chat: !!AGENT_API_KEY, teams: !!AGENT_BOT_KEY }))
  35. }
  36. const file = path.join(dist, path.normalize(decodeURIComponent(req.url.split('?')[0])).replace(/^(\.\.[/\\])+/, ''))
  37. const target = fs.existsSync(file) && fs.statSync(file).isFile() ? file : path.join(dist, 'index.html')
  38. res.writeHead(200, { 'content-type': types[path.extname(target)] ?? 'application/octet-stream' })
  39. fs.createReadStream(target).pipe(res)
  40. }).listen(PORT, '127.0.0.1', () => console.log(`示例平台:http://127.0.0.1:${PORT}(AgentServer:${AGENT_SERVER_URL})`))