Skip to content

前后端分开部署

LLM Fence 的管理端前端是 SvelteKit 静态构建产物,后端是 Rust 二进制。默认 release 包会把前端嵌入后端二进制,但也可以把前端静态文件交给 Nginx、对象存储或 CDN,后端只负责 /api/admin/*/v1/*

推荐拓扑

推荐使用“构建和运行分离,但浏览器同源访问”的方式:

text
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 中构建:

bash
pnpm install --frozen-lockfile
pnpm build

构建产物在:

text
app/build

前端启动时会加载 /config.js。同源反向代理时保持默认即可:

js
window.__LLM_FENCE_CONFIG__ = {
  baseApi: ""
};

baseApi 为空表示前端请求当前站点下的 /api/admin/*

配置后端

后端 llm-fence.toml

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 的外部地址。飞书回调地址会按它生成:

text
https://llm.example.com/api/admin/auth/providers/feishu/callback

frontend_origin 是登录、OAuth 成功后跳回前端页面的地址。同源部署时和 public_origin 相同。

Nginx 示例

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,避免流式响应被代理缓冲。

前端独立域名

如果前端和后端必须使用不同域名,例如:

text
前端:https://console.example.com
后端:https://api.example.com

前端 /config.js 需要改为:

js
window.__LLM_FENCE_CONFIG__ = {
  baseApi: "https://api.example.com"
};

后端配置:

toml
[server]
public_origin = "https://api.example.com"
frontend_origin = "https://console.example.com"
cookie_secure = true
cookie_same_site = "none"

跨域部署还必须让后端响应包含 CORS 头,并允许凭证请求。当前 release 二进制不会自动开启生产 CORS;需要在反向代理层处理:

nginx
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: *

本地开发

本地前端开发常见配置:

toml
[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:

bash
cargo run -- --dev-cors

该开关只用于 debug 构建。生产部署不要依赖它。

前端开发服务器:

bash
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.jsbaseApi 错误,或反向代理没有转发 /api/admin/*
流式响应一次性返回/v1/ 被代理缓冲,关闭 proxy_buffering