BR 6.3 — Admin BFF

Добавить proxy-роут для AI-агента (SSE-стрим pass-through).

Файлы для создания

bff/src/routes/agent.ts — новый файл:

import type { FastifyInstance } from 'fastify';
 
const AGENT_URL = process.env.AGENT_SERVICE_URL ?? 'http://openclaw-agent:3031';
 
export async function agentRoutes(app: FastifyInstance) {
  // SSE pass-through
  app.post('/api/v1/admin/agent/chat', async (req, reply) => {
    const upstream = await fetch(`${AGENT_URL}/api/v1/admin/agent/chat`, {
      method: 'POST',
      headers: {
        'Authorization': req.headers.authorization ?? '',
        'Content-Type': 'application/json',
        'Accept': 'text/event-stream',
      },
      body: JSON.stringify(req.body),
    });
    
    if (!upstream.ok || !upstream.body) {
      reply.code(upstream.status);
      return upstream.json().catch(() => ({ error: { code: 'AGENT_ERROR' } }));
    }
    
    reply.raw.writeHead(200, {
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache, no-transform',
      'Connection': 'keep-alive',
      'X-Accel-Buffering': 'no', // nginx — не буферизуй
    });
    
    const reader = upstream.body.getReader();
    while (true) {
      const { done, value } = await reader.read();
      if (done) break;
      reply.raw.write(value);
    }
    reply.raw.end();
  });
  
  // CRUD пробрасываем как обычные REST
  ['GET', 'DELETE', 'POST'].forEach(method => {
    app[method.toLowerCase() as 'get'](`/api/v1/admin/agent/conversations*`, async (req, reply) => {
      const url = `${AGENT_URL}${req.url}`;
      const r = await fetch(url, {
        method,
        headers: { 'Authorization': req.headers.authorization ?? '' },
        body: method === 'GET' ? undefined : JSON.stringify(req.body),
      });
      reply.code(r.status);
      return r.json().catch(() => ({}));
    });
  });
}

Регистрация

bff/src/server.ts:

import { agentRoutes } from './routes/agent.js';
// ...
app.register(agentRoutes);

Env

envs/admin-bff.env:

AGENT_SERVICE_URL=http://openclaw-agent:3031

Nginx

erp-infrastructure/nginx/conf.d/erp.conf — admin-bff проксируется по /api/v1/admin/*. Изменений не требуется, но проверить что для /api/v1/admin/agent/chat отключено буфферирование:

location /api/v1/admin/agent/chat {
    proxy_pass http://admin-bff:3020;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
    proxy_buffering off;
    proxy_cache off;
    proxy_read_timeout 600s;
}

(Если общее правило location /api/v1/admin/ уже отключает буферизацию для всего admin-API — отдельное правило не нужно.)

Тесты

  • E2E через тестовый клиент: SSE-стрим от admin-bff пробрасывается без задержек > 100ms

Готовность

  • Файл bff/src/routes/agent.ts создан
  • Зарегистрирован в server.ts
  • Env-переменная добавлена в envs/admin-bff.env и в .env.example
  • Nginx проверен/обновлён
  • Локально curl --no-buffer -X POST .../api/v1/admin/agent/chat стримит данные с задержкой ≤ 100ms

Ссылки