Federation
Federation 是服务端运行时容器,管理 Service 生命周期、鉴权和 HTTP 路由。
Federation 是 Downcity 服务端的核心实例(即文档里说的 Federation)。它封装了 HTTP 路由、鉴权、环境变量和数据库基础设施。客户端访问入口是 City,不要和它混淆。
最小使用
import { Federation } from "@downcity/city";
const base = new Federation({ db });Service 与 Action
每个 Service 是一组 Action。通过 base.use(service) 注册:
import { Service } from "@downcity/city";
const translate = new Service({ id: "translate", name: "翻译" });
// 注册 Action
translate.action("zh2en", async (ctx) => {
return { result: await api.translate(ctx.input.text, "en") };
});
translate.action("en2zh", async (ctx) => {
return { result: await api.translate(ctx.input.text, "zh") };
});
base.use(translate);Federation 自动为每个 Action 生成 HTTP 路由:
POST /v1/translate/zh2en → translate.action("zh2en")
POST /v1/translate/en2zh → translate.action("en2zh")AI Service
AIService 是内置的 Service,用于管理 AI 模型:
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
import { AIService, Provider, type OpenAICompatibleClientConfig } from "@downcity/city";
class DeepSeekProvider extends Provider {
constructor() {
super({
id: "deepseek",
baseURL: "https://api.deepseek.com/v1",
envKey: "DEEPSEEK_API_KEY",
});
}
protected createClient({ apiKey, baseURL }: OpenAICompatibleClientConfig) {
return createOpenAICompatible({ apiKey, baseURL, name: "deepseek" });
}
}
const deepseek = new DeepSeekProvider();
const ai = new AIService();
ai.use(deepseek.model({ id: "deepseek-v4-flash", name: "DeepSeek V4 Flash" }));
base.use(ai);Action 注册选项
// 默认 POST,auth=["user"]
svc.action("zh2en", fn);
// GET action
svc.action("list", fn, { method: "GET" });
// Admin action
svc.action("remove", fn, { auth: ["admin"] });
// Guest action(免登录)
svc.action("webhook", fn, { auth: [] });Hook 三层
// Action 级 hook(只在这个 action 触发)
const zh2en = svc.action("zh2en", fn);
zh2en.before(checkBalance).after(deductFee);
// Service 级 hook(所有 action 共享)
svc.hook.before(logRequest);
// 执行顺序:
// action.before → service.before → action.run → service.after → action.after路由启动
import { serve } from "@hono/node-server";
serve({ fetch: (request) => base.fetch(request), port: 43127, hostname: "127.0.0.1" });HTTP Middleware
base.middle() 用于注册 Federation 级 HTTP middleware。它们会在内部路由和 Action body 读取之前执行,因此调用方可以在 Federation 入口接入 CORS、安全响应头、body 大小限制、粗粒度限流、请求超时或其他 HTTP 策略。
@downcity/city 只定义 middleware 接口,不实现也不导出 CORS、限流、超时、安全响应头、body 限制、客户端 IP 读取等 helper。这些策略应由你的应用、平台或 HTTP 框架提供。
base.middle(async (ctx, next) => {
const started_at = Date.now();
const response = await next();
const headers = new Headers(response.headers);
headers.set("X-Downcity-Time", String(Date.now() - started_at));
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers,
});
}));middleware 按注册顺序进入,按相反顺序退出。middleware 可以直接返回 Response 来短路请求,也可以 await next() 后修改响应头。
instruction 文档聚合
Federation 可以直接返回当前实例的说明文档字符串:
const text = await base.instruction();这份文本会聚合:
- Federation 自身怎么使用
- 当前注册的 service
- 每个模块声明的 env 配置
- 服务补充的使用说明
如果你要从远程管理端读取同一份说明,可以调用:
GET /v1/base/instruction这个接口只允许管理端访问,返回 text/plain。
鉴权
Federation 自动处理 user_token 和 admin_secret_key 的校验。Action 默认要求 user_token;传 auth: ["admin"] 时只允许管理端访问,传 auth: [] 时表示免登录。
详见 信任边界。