前后端分开部署
LLM Fence 的管理端前端是 SvelteKit 静态构建产物,后端是 Rust 二进制。默认 release 包会把前端嵌入后端二进制,但也可以把前端静态文件交给 Nginx、对象存储或 CDN,后端只负责 /api/admin/* 和 /v1/*。
推荐拓扑
推荐使用“构建和运行分离,但浏览器同源访问”的方式:
https://llm.example.com/
-> 前端静态文件
https://llm.example.com/api/admin/*
-> LLM Fence 后端
https://llm.example.com/v1/*
-> LLM Fence 后端这种方式不需要浏览器跨域,admin_session Cookie、CSRF、飞书 OAuth 回调都最简单。
构建前端
在 D:\rust\llm-fence\app 中构建:
pnpm install --frozen-lockfile
pnpm build构建产物在:
app/build前端启动时会加载 /config.js。同源反向代理时保持默认即可:
window.__LLM_FENCE_CONFIG__ = {
baseApi: ""
};baseApi 为空表示前端请求当前站点下的 /api/admin/*。
配置后端
后端 llm-fence.toml:
[server]
host = "127.0.0.1"
port = 3000
admin_ui = false
public_origin = "https://llm.example.com"
frontend_origin = "https://llm.example.com"
cookie_secure = true
cookie_same_site = "lax"
[mongo]
uri = "mongodb://mongo-1:27017,mongo-2:27017,mongo-3:27017/llm_fence?replicaSet=rs0"
db = "llm_fence"admin_ui = false 表示后端根路径 / 不再提供内置管理端页面。管理 API 和代理 API 不受影响。
public_origin 必须是浏览器访问后端 API 的外部地址。飞书回调地址会按它生成:
https://llm.example.com/api/admin/auth/providers/feishu/callbackfrontend_origin 是登录、OAuth 成功后跳回前端页面的地址。同源部署时和 public_origin 相同。
Nginx 示例
server {
listen 443 ssl http2;
server_name llm.example.com;
root /opt/llm-fence/app/build;
index index.html;
location /api/admin/ {
proxy_pass http://127.0.0.1:3000/api/admin/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
location /v1/ {
proxy_pass http://127.0.0.1:3000/v1/;
proxy_http_version 1.1;
proxy_buffering off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
location / {
try_files $uri $uri/ /fallback.html;
}
}/v1/ 建议关闭 proxy_buffering,避免流式响应被代理缓冲。
前端独立域名
如果前端和后端必须使用不同域名,例如:
前端:https://console.example.com
后端:https://api.example.com前端 /config.js 需要改为:
window.__LLM_FENCE_CONFIG__ = {
baseApi: "https://api.example.com"
};后端配置:
[server]
public_origin = "https://api.example.com"
frontend_origin = "https://console.example.com"
cookie_secure = true
cookie_same_site = "none"跨域部署还必须让后端响应包含 CORS 头,并允许凭证请求。当前 release 二进制不会自动开启生产 CORS;需要在反向代理层处理:
map $http_origin $cors_origin {
default "";
"https://console.example.com" $http_origin;
}
server {
listen 443 ssl http2;
server_name api.example.com;
location /api/admin/ {
if ($request_method = OPTIONS) {
add_header Access-Control-Allow-Origin $cors_origin always;
add_header Access-Control-Allow-Credentials "true" always;
add_header Access-Control-Allow-Headers "Content-Type, X-CSRF-Token" always;
add_header Access-Control-Allow-Methods "GET, POST, PATCH, PUT, DELETE, OPTIONS" always;
return 204;
}
add_header Access-Control-Allow-Origin $cors_origin always;
add_header Access-Control-Allow-Credentials "true" always;
proxy_pass http://127.0.0.1:3000/api/admin/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
location /v1/ {
proxy_pass http://127.0.0.1:3000/v1/;
proxy_buffering off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
}跨站 Cookie 依赖 SameSite=None; Secure,因此必须使用 HTTPS。浏览器请求管理 API 时前端会使用 credentials: "include",CORS 不能使用 Access-Control-Allow-Origin: *。
本地开发
本地前端开发常见配置:
[server]
host = "127.0.0.1"
port = 3000
admin_ui = false
public_origin = "http://localhost:3000"
frontend_origin = "http://localhost:5173"
cookie_secure = false
cookie_same_site = "lax"Debug 构建下可以临时开启开发 CORS:
cargo run -- --dev-cors该开关只用于 debug 构建。生产部署不要依赖它。
前端开发服务器:
pnpm -C app dev --host 0.0.0.0如果没有开启 CORS,可以用前端开发服务器或本地 Nginx 把 /api/admin/* 代理到 http://localhost:3000,保持浏览器同源访问。
排错
| 现象 | 常见原因 |
|---|---|
| 登录成功后仍显示未登录 | Cookie 没有写入或跨站请求未带凭证;检查 cookie_same_site、HTTPS 和 CORS |
| 飞书登录回调 404 | 飞书平台配置的回调地址与 {public_origin}/api/admin/auth/providers/feishu/callback 不一致 |
| 写操作 401/403 | 缺少 admin_session Cookie 或 X-CSRF-Token |
| 前端提示 API 连接失败 | /config.js 的 baseApi 错误,或反向代理没有转发 /api/admin/* |
| 流式响应一次性返回 | /v1/ 被代理缓冲,关闭 proxy_buffering |