此版本独立安装PostgreSQL、(Casdoor或Logto),使用Cloudflare R2 进行替代S3进行存储。
我们先安装PostgreSQL和身份验证(Casdoor或Logto二选一)
域名和配套服务端口说明如下:
- https://chat.xxx.com, LobeChat 服务端域名,需要反向代理到 LobeChat 服务端口,默认为
3210
- https://auth.xxx.com, Logto 服务端域名,需要反向代理到 Logto API 服务端口,默认为
3003
- https://auth-admin.xxx.com, Logto UI 域名,需要反向代理到 Logto WebUI 服务端口,默认为
3004
- https://chat.xxx.com/api/auth/callback/logto, Logto重定向网址
- https://casdoor-cdn.xxx.com,Casdoor S3 访问网址
- https://s3-for-chat.xxx.com, LobeChat S3 公开访问网址
PostgreSQL
由于其他服务也需要使用PostgreSQL,所以就不重复安装PostgreSQL容器,多个业务使用同一个容器的不同数据库即可。
LobeChat需要使用pgvector,所以我在安装PostgreSQL的时候直接使用LobeChat推荐的镜像:pgvector/pgvector:pg16
,直接使用1Panel应用商店进行安装,在安装的时候勾上编辑 compose 文件
,然后将镜像更换一下安装即可。
身份证验证
Casdoor
docker-compose.yml
services:
casdoor:
image: casbin/casdoor:latest
ports:
- "8121:8000"
volumes:
- ./app.conf:/conf/app.conf
networks:
- 1panel-network # 使用已有的网络
networks:
1panel-network: # 引用已存在的网络
external: true # 标记为外部网络
app.conf:
appname = casdoor
httpport = 8000
runmode = prod # dev或prod
copyrequestbody = true
driverName = mysql
dataSourceName = casdoor:123456@tcp(1Panel-mysql-bnhU:3306)/ # MySQL信息
dbName = casdoor # 数据库名称
tableNamePrefix =
showSql = false
redisEndpoint =1Panel-redis-hvtn:6379,100,redis_SdrfzC # Redis信息,100是指定数据库,便于后期迁移备份
defaultStorageProvider =
isCloudIntranet = false
authState = "casdoor"
socks5Proxy = "127.0.0.1:10808"
verificationCodeTimeout = 10
initScore = 0
logPostOnly = true
isUsernameLowered = false
origin = "https://auth.xxx.com" #
originFrontend =
staticBaseUrl = "https://casdoor-cdn.xxx.com"
isDemoMode = false
batchSize = 100
enableErrorMask = false
enableGzip = true
ldapServerPort = 389
radiusServerPort = 1812
radiusSecret = "secret"
quota = {"organization": -1, "user": -1, "application": -1, "provider": -1}
logConfig = {"filename": "logs/casdoor.log", "maxdays":99999, "perm":"0770"}
initDataFile = "./init_data.json"
frontendBaseDir = "../casdoor"
app.conf 主要修改了runmode,dataSourceName,dbName,redisEndpoint,origin,staticBaseUrl,其中staticBaseUrl我设置的是cloudflare R2的访问网址。
按照以上配置以后,会出现casdoor后台图片都无法正常显示,所以我把对应的图片都上传到R2,从仓库 https://github.com/casbin/static 里下载对应的图片,将仓库下的flag-icons
和img
两个文件夹上传到R2.
CORS策略:
[
{
"AllowedOrigins": [
"https://auth.xxx.com"
],
"AllowedMethods": [
"GET",
"PUT",
"HEAD",
"POST",
"DELETE"
],
"AllowedHeaders": [
"*"
]
}
]
R2FileManager.py
import boto3
import os
from botocore.client import Config
from concurrent.futures import ThreadPoolExecutor, as_completed
# Cloudflare R2 相关配置
R2_ACCESS_KEY_ID = '你的 Access Key ID'
R2_SECRET_ACCESS_KEY = '你的 Secret Access Key'
R2_ENDPOINT_URL = 'https://<你的AccountID>.r2.cloudflarestorage.com' # 替换 <你的AccountID>
R2_BUCKET_NAME = '你的存储桶名称'
# 创建 S3 兼容的客户端
s3 = boto3.client(
's3',
endpoint_url=R2_ENDPOINT_URL,
aws_access_key_id=R2_ACCESS_KEY_ID,
aws_secret_access_key=R2_SECRET_ACCESS_KEY,
config=Config(signature_version='s3v4'),
region_name='auto'
)
# 上传单个文件到 R2 指定文件夹
def upload_single_file_to_r2(local_file_path, bucket_name, target_folder):
file_name = os.path.basename(local_file_path)
r2_key = os.path.join(target_folder, file_name).replace("\\", "/") # 确保兼容Windows系统路径
try:
s3.upload_file(local_file_path, bucket_name, r2_key)
print(f'文件已上传: {r2_key}')
except Exception as e:
print(f'文件上传失败: {r2_key}, 错误: {e}')
# 上传整个文件夹到 R2
def upload_folder_to_r2(local_folder, bucket_name, target_folder, max_workers=5):
futures = []
with ThreadPoolExecutor(max_workers=max_workers) as executor:
for root, dirs, files in os.walk(local_folder):
for file in files:
local_file_path = os.path.join(root, file)
relative_path = os.path.relpath(local_file_path, local_folder)
# 提交任务给线程池
futures.append(executor.submit(upload_single_file_to_r2, local_file_path, bucket_name, target_folder))
# 等待所有任务完成
for future in as_completed(futures):
future.result() # 检查任务的执行结果,便于捕获异常
# 删除 R2 中的指定文件
def delete_file_from_r2(bucket_name, target_file_key):
try:
s3.delete_object(Bucket=bucket_name, Key=target_file_key)
print(f'文件已删除: {target_file_key}')
except Exception as e:
print(f'删除文件失败: {target_file_key}, 错误: {e}')
# 清空 R2 中指定文件夹的所有文件
def clear_folder_in_r2(bucket_name, target_folder_key):
try:
response = s3.list_objects_v2(Bucket=bucket_name, Prefix=target_folder_key)
if 'Contents' in response:
files_to_delete = [{'Key': obj['Key']} for obj in response['Contents']]
delete_response = s3.delete_objects(Bucket=bucket_name, Delete={'Objects': files_to_delete})
deleted_files = delete_response.get('Deleted', [])
for file in deleted_files:
print(f'文件已删除: {file["Key"]}')
else:
print(f'文件夹为空或不存在: {target_folder_key}')
except Exception as e:
print(f'清空文件夹失败: {target_folder_key}, 错误: {e}')
# 删除 R2 中的文件夹及其所有文件
def delete_folder_in_r2(bucket_name, target_folder_key):
clear_folder_in_r2(bucket_name, target_folder_key) # 首先清空文件夹
try:
s3.delete_object(Bucket=bucket_name, Key=target_folder_key)
print(f'文件夹已删除: {target_folder_key}')
except Exception as e:
print(f'删除文件夹失败: {target_folder_key}, 错误: {e}')
# 示例调用:
# 上传整个文件夹
upload_folder_to_r2('/Users/UserName/static/img', R2_BUCKET_NAME, 'img/', max_workers=10)
# 上传单个文件
# upload_single_file_to_r2('/path/to/local/file.jpg', R2_BUCKET_NAME, 'img/')
# 删除文件
# delete_file_from_r2(R2_BUCKET_NAME, 'img/somefile.jpg')
# 清空文件夹
# clear_folder_in_r2(R2_BUCKET_NAME, 'img/')
# 删除文件夹
# delete_folder_in_r2(R2_BUCKET_NAME, 'img/')
Logto
docker-compose.yml:
services:
logto:
image: svhd/logto
container_name: logto
ports:
- '3003:3001'
- '3004:3002'
networks:
- 1panel-network # 使用已有的网络
restart: always
environment:
- 'TRUST_PROXY_HEADER=1'
- 'DB_URL=postgres://logto:123456@postgresql:5432/logto'
- 'ENDPOINT=https://auth.xxx.com'
- 'ADMIN_ENDPOINT=https://auth-admin.xxx.com'
entrypoint: ['sh', '-c', 'npm run cli db seed -- --swe && npm start']
networks:
1panel-network: # 引用已存在的网络
external: true # 标记为外部网络
反代将3003
端口对应auth.xxx.com
,3004
端口对应auth-admin.xxx.com
创建 Logto 应用
在 Applications
里创建一个Next.js (App Router)
应用,名称随意
配置 Logto
- 配置
Redirect URI
为 https://chat.xxx.com/api/auth/callback/logto,`Post sign-out redirect URI` 为 https://chat.xxx.com/ - 配置 CORS allowed origins 为 https://chat.xxx.com/
创建成功后, 将 Client ID 和 Client Secret 保存下来。
配置环境变量
将获取到的 Client ID 和 Client Secret,设为 LobeChat 环境变量中的 LOGTO_CLIENT_ID 和 LOGTO_CLIENT_SECRET。
配置 LobeChat 环境变量中 LOGTO_ISSUER 为:https://auth.xxx.com/oidc,
在部署 LobeChat 时,你需要配置以下环境变量:
环境变量 | 类型 | 描述 |
---|---|---|
NEXT_AUTH_SECRET | 必选 | 用于加密 Auth.js 会话令牌的密钥。您可以使用以下命令生成秘钥: openssl rand -base64 32 |
NEXT_AUTH_SSO_PROVIDERS | 必选 | 选择 LoboChat 的单点登录提供商。使用 Logto 请填写 logto 。 |
LOGTO_CLIENT_ID | 必选 | Logto App 详情页的 Client ID |
LOGTO_CLIENT_SECRET | 必选 | Logto App 详情页的 Client Secret |
LOGTO_ISSUER | 必选 | Logto 提供程序的 OpenID Connect 颁发者 |
NEXTAUTH_URL | 可选 | 该 URL 用于指定 Auth.js 在执行 OAuth 验证时的回调地址,当默认生成的重定向地址发生不正确时才需要设置,https://chat.xxx.com/api/auth |
更多信息查看官方教程
LobeChat
docker-compose.yml:
services:
lobe:
image: lobehub/lobe-chat-database
container_name: lobe-database
ports:
- '3210:3210'
networks:
- 1panel-network # 使用已有的网络
env_file:
- .env
restart: always
volumes:
data:
driver: local
networks:
1panel-network: # 引用已存在的网络
external: true # 标记为外部网络
.env:
# 必填,LobeChat 域名,用于 tRPC 调用
# 请保证此域名在你的 NextAuth 鉴权服务提供商、S3 服务商的 CORS 白名单中
APP_URL=https://chat.xxx.com/
# Postgres 相关,也即 DB 必需的环境变量
# 必填,用于加密敏感信息的密钥,可以使用 openssl rand -base64 32 生成
KEY_VAULTS_SECRET=Kix2wcUONd4CX51E/ZPAd36BqM4wzJgKjPtz2sGztqQ=
# 必填,Postgres 数据库连接字符串,用于连接到数据库
# 格式:postgresql://username:password@host:port/dbname,如果你的 pg 实例为 Docker 容器且位于同一 docker-compose 文件中,亦可使用容器名作为 host
DATABASE_URL=postgresql://lobechat:123456@postgresql:5432/lobechat
# NEXT_AUTH 相关,也即鉴权服务必需的环境变量
# 可以使用 auth0、Azure AD、GitHub、Authentik、Zitadel、Logto 等,如有其他接入诉求欢迎提 PR
# 目前支持的鉴权服务提供商请参考:https://lobehub.com/zh/docs/self-hosting/advanced/auth#next-auth
# 如果你有 ACCESS_CODE,请务必清空,我们以 NEXT_AUTH 作为唯一鉴权来源
# 必填,用于 NextAuth 的密钥,可以使用 openssl rand -base64 32 生成
NEXT_AUTH_SECRET=NX2kaPE923dt6BL2U8e9oSre5RfoT7hg
# 必填,指定鉴权服务提供商,这里以 Logto 为例
NEXT_AUTH_SSO_PROVIDERS=logto
# 必填,NextAuth 的 URL,用于 NextAuth 的回调
NEXTAUTH_URL=https://chat.xxx.com/api/auth
# NextAuth 鉴权服务提供商部分,以 Logto 为例
# 其他鉴权服务提供商所需的环境变量,请参考:https://lobehub.com/zh/docs/self-hosting/environment-variables/auth
AUTH_LOGTO_ID=YOUR_LOGTO_CLIENT_ID
AUTH_LOGTO_SECRET=YOUR_LOGTO_CLIENT_SECRET
AUTH_LOGTO_ISSUER=https://auth.xxx.com/oidc
# 代理相关,如果你需要的话(比如你使用 GitHub 作为鉴权服务提供商)
# HTTP_PROXY=http://localhost:7890
# HTTPS_PROXY=http://localhost:7890
# S3 相关,也即非结构化数据(文件、图片等)存储必需的环境变量
# 必填,S3 的 Access Key ID
S3_ACCESS_KEY_ID=YOUR_S3_ACCESS_KEY_ID
# 必填,S3 的 Secret Access Key
S3_SECRET_ACCESS_KEY=YOUR_S3_SECRET_ACCESS_KEY
# 必填,S3 的 Endpoint,用于服务端/客户端连接到 S3 API
S3_ENDPOINT=https://<cloudflare账户ID>.r2.cloudflarestorage.com
# 必填,S3 的 Bucket
S3_BUCKET=lobe-chat
# 必填,S3 的 Public Domain,用于客户端通过公开连接访问非结构化数据
S3_PUBLIC_DOMAIN=https://s3-for-chat.xxx.com
# 选填,S3 的 Enable Path Style
# 对于主流 S3 Cloud 服务商,一般填 0 即可;对于自部署的 MinIO,请填 1
# 请参考:https://lobehub.com/zh/docs/self-hosting/advanced/s3#s-3-enable-path-style
S3_ENABLE_PATH_STYLE=0
# 其他基础环境变量,视需求而定。注意不要有 ACCESS_CODE
# 请参考:https://lobehub.com/zh/docs/self-hosting/environment-variables/basic
# 请注意,对于服务端版本,其 API 必须支持嵌入(OpenAI text-embedding-3-small)模型,否则无法对上传文件进行处理,但你无需在 OPENAI_MODEL_LIST 中指定此模型
ENABLED_OPENAI=1
OPENAI_API_KEY=sk-xxxx
OPENAI_PROXY_URL=https://one.xxx.com/v1
OPENAI_MODEL_LIST=-all,+gemini-1.5-flash=Gemini 1.5 Flash<16000:vision:fc:file>,+gemini-1.5-pro=Gemini 1.5 Pro<16000:vision:fc:file>,+deepseek-chat=DeepSeek V2.5<128000:fc>
DEFAULT_AGENT_CONFIG=model=deepseek-chat;plugins=search-engine,lobe-image-designer;params.max_tokens=300;chatConfig.historyCount=5