api.ts 1.69 KB
// 引擎 API 客户端。所有业务 URL 均由后端场景 Schema 给出(commandApi/optionsSource),前端不硬编码业务地址。
// 主体身份经 X-Principal 头传递(演示态;生产应换真实认证)。
const BASE = '/api'

async function get<T = any>(path: string): Promise<T> {
  const r = await fetch(BASE + path)
  if (!r.ok) throw new Error(`GET ${path} -> ${r.status}`)
  return r.json()
}

async function post<T = any>(path: string, body: any, principal?: string): Promise<T> {
  const headers: Record<string, string> = { 'Content-Type': 'application/json' }
  if (principal) headers['X-Principal'] = principal
  const r = await fetch(BASE + path, { method: 'POST', headers, body: JSON.stringify(body) })
  if (!r.ok) {
    let msg = `POST ${path} -> ${r.status}`
    try { const j = await r.json(); if (j?.message) msg = j.message } catch { /* ignore */ }
    throw new Error(msg)
  }
  return r.json()
}

export const api = {
  scenes: () => get('/meta/scenes'),
  sceneSchema: (sceneId: string) => get(`/meta/scene/${sceneId}`),
  aggregate: (aggregateId: string) => get(`/meta/aggregate/${aggregateId}`),
  models: () => get('/meta/models'),
  model: (code: string) => get(`/meta/model/${code}`),
  reload: (principal?: string) => post('/meta/reload', {}, principal),
  options: (aggregateId: string) => get(`/data/${aggregateId}`),
  list: (aggregateId: string) => get(`/data/${aggregateId}/list`),
  events: () => get('/data/events/recent'),
  precheck: (sceneId: string, payload: any, principal?: string) => post(`/scene/${sceneId}/precheck`, payload, principal),
  execute: (sceneId: string, payload: any, principal?: string) => post(`/scene/${sceneId}/execute`, payload, principal),
}