lepaoProxyLogDisplay.js 3.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. /**
  2. * 管理员列表:可读摘要 + Arco Tag 色号(与设计约定一致)。
  3. */
  4. function parseDetail(raw) {
  5. if (raw == null || raw === '') return {}
  6. if (typeof raw === 'object') return raw
  7. try {
  8. return JSON.parse(raw)
  9. } catch {
  10. return { _text: String(raw) }
  11. }
  12. }
  13. const EVENT_META = {
  14. fetch: { label: '提取 IP', color: 'green' },
  15. invalidate: { label: '作废缓存', color: 'orangered' },
  16. fallback_direct: { label: '回退直连', color: 'red' },
  17. config_change: { label: '配置变更', color: 'arcoblue' }
  18. }
  19. function summarizeLogRow(record) {
  20. const event = record.event
  21. const d = parseDetail(record.detail)
  22. const lines = []
  23. if (event === 'fetch') {
  24. if (d.request_id) lines.push(`请求 ID:${d.request_id}`)
  25. if (d.code) lines.push(`接口状态:${d.code}`)
  26. } else if (event === 'invalidate') {
  27. if (d.reason) lines.push(`原因:${d.reason}`)
  28. if (d.message) lines.push(`说明:${d.message}`)
  29. if (d.code) lines.push(`错误码:${d.code}`)
  30. if (d.status) lines.push(`HTTP:${d.status}`)
  31. } else if (event === 'fallback_direct') {
  32. if (d.reason) lines.push(`触发原因:${d.reason}`)
  33. if (d.reason === 'exhausted_proxy_then_direct') {
  34. lines.push('多轮提取与经代理 POST 均未成功,已改直连接口')
  35. } else if (d.message) {
  36. lines.push(`说明:${d.message}`)
  37. }
  38. if (d.after) lines.push(`阶段:${d.after}`)
  39. if (d.code) lines.push(`错误码:${d.code}`)
  40. } else if (event === 'config_change') {
  41. lines.push(`代理开关:${d.proxy_enabled === 1 ? '开' : '关'}`)
  42. if (d.area !== undefined) lines.push(`地区 area:「${d.area || '(空)'}」`)
  43. if (d.area_ex !== undefined) lines.push(`排除 area_ex:「${d.area_ex || '(空)'}」`)
  44. if (d.isp !== undefined) lines.push(`运营商 isp:${d.isp ?? '不限'}`)
  45. if (d.distinct_extract !== undefined) lines.push(`去重提取:${d.distinct_extract ? '是' : '否'}`)
  46. if (d.invalidate_cache) lines.push('已勾选清空服务端 IP 缓存')
  47. if (d.operator) lines.push(`操作者 UUID:${d.operator}`)
  48. } else if (Object.keys(d).length) {
  49. if (d._text) lines.push(String(d._text))
  50. else {
  51. Object.keys(d).slice(0, 6).forEach(k => {
  52. lines.push(`${k}:${typeof d[k] === 'object' ? JSON.stringify(d[k]) : d[k]}`)
  53. })
  54. }
  55. }
  56. let summary = lines.length ? lines.join(';') : '—'
  57. const meta = EVENT_META[event] || { label: event || '未知', color: 'gray' }
  58. const serverShown = record.server ? `节点 ${record.server}` : ''
  59. return {
  60. event_label: meta.label,
  61. event_color: meta.color,
  62. summary,
  63. detail_lines: lines,
  64. server_tip: serverShown || null,
  65. ...(record.server && record.deadline ? { deadline_tip: `${record.server} · ${record.deadline}` } : {})
  66. }
  67. }
  68. /**
  69. * 青果语义下的出口 IP(proxy_ip),非代理节点 host。
  70. */
  71. function extractEgressIp(record) {
  72. const d = parseDetail(record.detail)
  73. const p = d.proxy_ip
  74. if (p === null || p === undefined || p === '') return null
  75. const s = String(p).trim()
  76. return /^(\d{1,3}\.){3}\d{1,3}$/.test(s) ? s : null
  77. }
  78. module.exports = {
  79. summarizeLogRow,
  80. parseDetail,
  81. EVENT_META,
  82. extractEgressIp
  83. }