feat: 初始提交
This commit is contained in:
145
deploy/.env.example
Normal file
145
deploy/.env.example
Normal file
@@ -0,0 +1,145 @@
|
||||
# ===================================================================
|
||||
# Docker Compose 环境变量配置示例
|
||||
# ===================================================================
|
||||
#
|
||||
# 使用说明:
|
||||
# 1. 将此文件复制为 .env 文件: `cp .env.example .env`
|
||||
# 2. 根据您的实际需求,仔细检查并修改以下配置项。
|
||||
# 3. 使用默认的 SQLite 数据库快速启动,只需运行:
|
||||
# docker compose up -d
|
||||
#
|
||||
# ===================================================================
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# A. 核心应用配置
|
||||
# -------------------------------------------------------------------
|
||||
# [必需] 应用对外暴露的网络端口
|
||||
APP_PORT=8088
|
||||
|
||||
# [必需] 用于 JWT token 等安全功能的加密密钥。
|
||||
# 警告:为了生产环境的安全,请务必将其修改为一个长且随机的复杂字符串!
|
||||
# 您可以使用此命令生成: openssl rand -hex 32
|
||||
SECRET_KEY=your-secret-key-change-me-to-random-string
|
||||
|
||||
# [可选] 应用环境,设置为 "development" 可以启用更详细的调试日志
|
||||
ENVIRONMENT=production
|
||||
|
||||
# [可选] 是否开启调试模式,生产环境中应始终为 "false"
|
||||
DEBUG=false
|
||||
# [可选] 控制应用日志级别,可选值:CRITICAL/ERROR/WARNING/INFO/DEBUG/NOTSET
|
||||
LOGGING_LEVEL=INFO
|
||||
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# B. 数据库配置
|
||||
# -------------------------------------------------------------------
|
||||
# [必需] 选择数据库类型。可选值: "sqlite", "mysql"。
|
||||
# 默认为 "sqlite",这是最简单的配置,无需外部数据库服务。
|
||||
DB_PROVIDER=sqlite
|
||||
|
||||
# --- B1. SQLite 配置 (默认) ---
|
||||
# [可选] SQLite 数据库文件的存储位置。
|
||||
# 如果留空或注释掉,将使用名为 'sqlite-data' 的 Docker 卷进行持久化。
|
||||
# 若希望存储在宿主机上,可设置为路径,例如: ./storage
|
||||
# SQLITE_STORAGE_SOURCE=./storage
|
||||
|
||||
# --- B2. MySQL 配置 (可选) ---
|
||||
# 如果您希望使用 MySQL,请先将上方的 DB_PROVIDER 设置为 "mysql",然后根据您的场景选择并配置以下两种方案之一。
|
||||
|
||||
# ▼▼▼ 方案一:使用内置的 MySQL 服务 (推荐用于快速体验) ▼▼▼
|
||||
# 启动命令: DB_PROVIDER=mysql docker compose --profile mysql up -d
|
||||
#
|
||||
# 注意:以下变量由 docker-compose.yml 中的 `db` 服务使用。
|
||||
MYSQL_HOST=db # 使用内置服务时,请勿修改此项
|
||||
MYSQL_PORT=3306
|
||||
MYSQL_USER=arboris
|
||||
MYSQL_PASSWORD=your-database-password-change-me
|
||||
MYSQL_DATABASE=arboris
|
||||
MYSQL_ROOT_PASSWORD=your-root-password-change-me # 内置数据库需要设置 root 密码
|
||||
|
||||
# ▼▼▼ 方案二:连接到外部的 MySQL 数据库 ▼▼▼
|
||||
# 启动命令: DB_PROVIDER=mysql docker compose up -d
|
||||
#
|
||||
# 注意:请确保应用容器可以访问到您的外部数据库地址。
|
||||
# `host.docker.internal` 是一个特殊的DNS名,可用于从容器内部访问宿主机。
|
||||
# MYSQL_HOST=host.docker.internal
|
||||
# MYSQL_PORT=3306
|
||||
# MYSQL_USER=your-external-db-user
|
||||
# MYSQL_PASSWORD=your-external-db-password
|
||||
# MYSQL_DATABASE=your-external-db-name
|
||||
# 使用外部数据库时,无需设置 MYSQL_ROOT_PASSWORD。
|
||||
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# C. 初始化管理员账户
|
||||
# -------------------------------------------------------------------
|
||||
# 应用首次启动时,将使用以下凭据创建默认的管理员账户。
|
||||
ADMIN_DEFAULT_USERNAME=admin
|
||||
ADMIN_DEFAULT_PASSWORD=ChangeMe123!
|
||||
ADMIN_DEFAULT_EMAIL=admin@example.com
|
||||
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# D. 大语言模型 (LLM) 与 RAG 配置
|
||||
# -------------------------------------------------------------------
|
||||
# --- D1. 主要生成模型配置 ---
|
||||
OPENAI_API_KEY=sk-your-api-key-here
|
||||
OPENAI_API_BASE_URL=https://api.openai.com/v1
|
||||
OPENAI_MODEL_NAME=your-model-here
|
||||
WRITER_CHAPTER_VERSION_COUNT=2
|
||||
|
||||
# --- D2. 嵌入模型配置 (用于 RAG 检索) ---
|
||||
# [必需] 嵌入模型提供方,可选 "openai" 或 "ollama"
|
||||
EMBEDDING_PROVIDER=openai
|
||||
|
||||
# [可选] OpenAI / 兼容服务的 Base URL,如果留空,将复用上方的 OPENAI_API_BASE_URL
|
||||
EMBEDDING_BASE_URL=https://api.openai.com/v1
|
||||
# [可选] 嵌入模型专用的 API Key,如果留空,将复用上方的 OPENAI_API_KEY
|
||||
EMBEDDING_API_KEY=sk-your-api-key-here
|
||||
# [必需] 默认嵌入模型名称
|
||||
EMBEDDING_MODEL=text-embedding-3-large
|
||||
# [必需] 嵌入模型的向量维度,必须与模型匹配;若不确定可保持默认
|
||||
EMBEDDING_MODEL_VECTOR_SIZE=3072
|
||||
|
||||
# [可选] 如果使用 Ollama 本地模型 (EMBEDDING_PROVIDER=ollama),请配置其服务地址与模型。
|
||||
OLLAMA_EMBEDDING_BASE_URL=http://host.docker.internal:11434
|
||||
OLLAMA_EMBEDDING_MODEL=nomic-embed-text:latest
|
||||
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# E. 向量数据库配置 (用于 RAG)
|
||||
# -------------------------------------------------------------------
|
||||
# [可选] RAG 功能依赖向量数据库,默认使用本地文件。
|
||||
VECTOR_DB_URL=file:./storage/rag_vectors.db
|
||||
VECTOR_DB_AUTH_TOKEN=
|
||||
VECTOR_TOP_K_CHUNKS=5
|
||||
VECTOR_TOP_K_SUMMARIES=3
|
||||
VECTOR_CHUNK_SIZE=480
|
||||
VECTOR_CHUNK_OVERLAP=120
|
||||
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# F. 可选服务与功能开关
|
||||
# -------------------------------------------------------------------
|
||||
# --- F1. SMTP 邮件发送配置 ---
|
||||
# [可选] 用于发送注册验证、密码重置等邮件。
|
||||
SMTP_SERVER=smtp.example.com
|
||||
SMTP_PORT=465
|
||||
SMTP_USERNAME=no-reply@example.com
|
||||
SMTP_PASSWORD=your-smtp-password
|
||||
EMAIL_FROM=拯救小说家
|
||||
|
||||
# --- F2. 功能开关 ---
|
||||
# 是否允许新用户注册
|
||||
ALLOW_USER_REGISTRATION=false
|
||||
# 是否启用 Linux.do 第三方登录
|
||||
ENABLE_LINUXDO_LOGIN=false
|
||||
|
||||
# --- F3. Linux.do OAuth 配置 (如果启用) ---
|
||||
# 仅当 ENABLE_LINUXDO_LOGIN=true 时需要配置。
|
||||
LINUXDO_CLIENT_ID=your_client_id
|
||||
LINUXDO_CLIENT_SECRET=your_client_secret
|
||||
LINUXDO_REDIRECT_URI=https://your-domain.com/api/auth/linuxdo/register
|
||||
LINUXDO_AUTH_URL=https://connect.linux.do/oauth2/authorize
|
||||
LINUXDO_TOKEN_URL=https://connect.linux.do/oauth2/token
|
||||
LINUXDO_USER_INFO_URL=https://connect.linux.do/api/user
|
||||
74
deploy/Dockerfile
Normal file
74
deploy/Dockerfile
Normal file
@@ -0,0 +1,74 @@
|
||||
# ============================================
|
||||
# 第一阶段:构建前端静态资源
|
||||
# ============================================
|
||||
FROM node:20-slim AS frontend-builder
|
||||
|
||||
WORKDIR /frontend
|
||||
|
||||
# 配置 npm 使用中国镜像源
|
||||
RUN npm config set registry https://registry.npmmirror.com
|
||||
|
||||
# 复制前端依赖文件
|
||||
COPY frontend/package*.json ./
|
||||
|
||||
RUN npm install
|
||||
|
||||
# 安装前端依赖
|
||||
RUN npm ci --prefer-offline --no-audit
|
||||
|
||||
# 复制前端源码
|
||||
COPY frontend/ ./
|
||||
|
||||
# 构建前端
|
||||
RUN npm run build
|
||||
|
||||
# ============================================
|
||||
# 第二阶段:构建最终镜像(后端 + nginx)
|
||||
# ============================================
|
||||
FROM python:3.11-slim
|
||||
|
||||
# 配置 apt 使用中国镜像源
|
||||
RUN sed -i 's/deb.debian.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list.d/debian.sources && \
|
||||
sed -i 's|security.debian.org/debian-security|mirrors.tuna.tsinghua.edu.cn/debian-security|g' /etc/apt/sources.list.d/debian.sources
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# 安装系统依赖:nginx、supervisor、curl、mysql客户端等
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
gcc \
|
||||
default-libmysqlclient-dev \
|
||||
pkg-config \
|
||||
nginx \
|
||||
supervisor \
|
||||
curl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 配置 pip 使用中国镜像源
|
||||
RUN pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
|
||||
|
||||
# 复制后端依赖文件
|
||||
COPY backend/requirements.txt .
|
||||
|
||||
# 安装 Python 依赖
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
# 复制后端应用代码
|
||||
COPY backend/ ./
|
||||
|
||||
# 从前端构建阶段复制静态资源到 nginx 默认目录
|
||||
COPY --from=frontend-builder /frontend/dist /usr/share/nginx/html
|
||||
|
||||
# 复制部署配置
|
||||
COPY deploy/nginx.conf /etc/nginx/sites-available/default
|
||||
COPY deploy/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
||||
|
||||
# 创建非 root 用户(供 supervisor 使用)
|
||||
RUN useradd -m -u 1000 appuser && \
|
||||
chown -R appuser:appuser /app
|
||||
|
||||
# 暴露端口(nginx 80端口)
|
||||
EXPOSE 80
|
||||
|
||||
# 使用 supervisor 启动 nginx 和 uvicorn
|
||||
# 注意:容器以 root 启动,supervisor 会根据配置降权运行各个进程
|
||||
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
|
||||
108
deploy/docker-compose.yml
Normal file
108
deploy/docker-compose.yml
Normal file
@@ -0,0 +1,108 @@
|
||||
services:
|
||||
app:
|
||||
image: tiechui251/arboris-app:latest
|
||||
container_name: arboris-app
|
||||
ports:
|
||||
- "${APP_PORT:-80}:80"
|
||||
environment:
|
||||
SECRET_KEY: ${SECRET_KEY:?请设置SECRET_KEY}
|
||||
ENVIRONMENT: ${ENVIRONMENT:-production}
|
||||
DEBUG: ${DEBUG:-false}
|
||||
LOGGING_LEVEL: ${LOGGING_LEVEL:-INFO}
|
||||
|
||||
DB_PROVIDER: ${DB_PROVIDER:-sqlite}
|
||||
|
||||
MYSQL_HOST: ${MYSQL_HOST:-db}
|
||||
MYSQL_PORT: ${MYSQL_PORT:-3306}
|
||||
MYSQL_USER: ${MYSQL_USER:-arboris}
|
||||
MYSQL_PASSWORD: ${MYSQL_PASSWORD:?请设置数据库密码}
|
||||
MYSQL_DATABASE: ${MYSQL_DATABASE:-arboris}
|
||||
|
||||
ADMIN_DEFAULT_USERNAME: ${ADMIN_DEFAULT_USERNAME:-admin}
|
||||
ADMIN_DEFAULT_PASSWORD: ${ADMIN_DEFAULT_PASSWORD:-ChangeMe123!}
|
||||
ADMIN_DEFAULT_EMAIL: ${ADMIN_DEFAULT_EMAIL:-admin@example.com}
|
||||
|
||||
OPENAI_API_KEY: ${OPENAI_API_KEY}
|
||||
OPENAI_API_BASE_URL: ${OPENAI_API_BASE_URL:-https://api.openai.com/v1}
|
||||
OPENAI_MODEL_NAME: ${OPENAI_MODEL_NAME:-gpt-3.5-turbo}
|
||||
WRITER_CHAPTER_VERSION_COUNT: ${WRITER_CHAPTER_VERSION_COUNT:-2}
|
||||
|
||||
EMBEDDING_PROVIDER: ${EMBEDDING_PROVIDER:-openai}
|
||||
EMBEDDING_BASE_URL: ${EMBEDDING_BASE_URL:-https://api.openai.com/v1}
|
||||
EMBEDDING_API_KEY: ${EMBEDDING_API_KEY:-${OPENAI_API_KEY}}
|
||||
EMBEDDING_MODEL: ${EMBEDDING_MODEL:-text-embedding-3-large}
|
||||
EMBEDDING_MODEL_VECTOR_SIZE: ${EMBEDDING_MODEL_VECTOR_SIZE:-3072}
|
||||
OLLAMA_EMBEDDING_BASE_URL: ${OLLAMA_EMBEDDING_BASE_URL:-http://localhost:11434}
|
||||
OLLAMA_EMBEDDING_MODEL: ${OLLAMA_EMBEDDING_MODEL:-nomic-embed-text:latest}
|
||||
|
||||
VECTOR_DB_URL: ${VECTOR_DB_URL:-file:./storage/rag_vectors.db}
|
||||
VECTOR_DB_AUTH_TOKEN: ${VECTOR_DB_AUTH_TOKEN:-}
|
||||
VECTOR_TOP_K_CHUNKS: ${VECTOR_TOP_K_CHUNKS:-5}
|
||||
VECTOR_TOP_K_SUMMARIES: ${VECTOR_TOP_K_SUMMARIES:-3}
|
||||
VECTOR_CHUNK_SIZE: ${VECTOR_CHUNK_SIZE:-480}
|
||||
VECTOR_CHUNK_OVERLAP: ${VECTOR_CHUNK_OVERLAP:-120}
|
||||
|
||||
SMTP_SERVER: ${SMTP_SERVER:-smtp.example.com}
|
||||
SMTP_PORT: ${SMTP_PORT:-465}
|
||||
SMTP_USERNAME: ${SMTP_USERNAME:-no-reply@example.com}
|
||||
SMTP_PASSWORD: ${SMTP_PASSWORD}
|
||||
EMAIL_FROM: ${EMAIL_FROM:-Arboris}
|
||||
|
||||
ALLOW_USER_REGISTRATION: ${ALLOW_USER_REGISTRATION:-true}
|
||||
ENABLE_LINUXDO_LOGIN: ${ENABLE_LINUXDO_LOGIN:-false}
|
||||
|
||||
LINUXDO_CLIENT_ID: ${LINUXDO_CLIENT_ID}
|
||||
LINUXDO_CLIENT_SECRET: ${LINUXDO_CLIENT_SECRET}
|
||||
LINUXDO_REDIRECT_URI: ${LINUXDO_REDIRECT_URI}
|
||||
LINUXDO_AUTH_URL: ${LINUXDO_AUTH_URL:-https://connect.linux.do/oauth2/authorize}
|
||||
LINUXDO_TOKEN_URL: ${LINUXDO_TOKEN_URL:-https://connect.linux.do/oauth2/token}
|
||||
LINUXDO_USER_INFO_URL: ${LINUXDO_USER_INFO_URL:-https://connect.linux.do/api/user}
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ${SQLITE_STORAGE_SOURCE:-sqlite-data}:/app/storage
|
||||
networks:
|
||||
- app-network
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1:8000/api/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 90s
|
||||
|
||||
# MySQL 数据库服务(通过 profile mysql 启用)
|
||||
db:
|
||||
image: mysql:8.0
|
||||
container_name: arboris-db
|
||||
profiles:
|
||||
- mysql
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-ChangeMe_RootPassword123}
|
||||
MYSQL_DATABASE: ${MYSQL_DATABASE:-arboris}
|
||||
MYSQL_USER: ${MYSQL_USER:-arboris}
|
||||
MYSQL_PASSWORD: ${MYSQL_PASSWORD:-ChangeMe_Password123}
|
||||
TZ: Asia/Shanghai
|
||||
volumes:
|
||||
- mysql-data:/var/lib/mysql
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- app-network
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD}"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
command:
|
||||
- --character-set-server=utf8mb4
|
||||
- --collation-server=utf8mb4_unicode_ci
|
||||
- --max_connections=1000
|
||||
|
||||
volumes:
|
||||
mysql-data:
|
||||
driver: local
|
||||
sqlite-data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
app-network:
|
||||
driver: bridge
|
||||
65
deploy/nginx.conf
Normal file
65
deploy/nginx.conf
Normal file
@@ -0,0 +1,65 @@
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name _;
|
||||
|
||||
# 前端静态资源
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# 客户端最大上传大小
|
||||
client_max_body_size 10M;
|
||||
|
||||
# API 后端代理
|
||||
location /api/ {
|
||||
proxy_pass http://127.0.0.1:8000;
|
||||
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 $scheme;
|
||||
|
||||
# WebSocket 支持
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
# 超时设置 - 针对AI生成操作的长时间响应
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
|
||||
# 缓冲设置
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
}
|
||||
|
||||
# 后台管理 API 代理
|
||||
location /admin/ {
|
||||
proxy_pass http://127.0.0.1:8000;
|
||||
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 $scheme;
|
||||
}
|
||||
|
||||
# 前端路由支持 (SPA)
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
# Gzip 压缩
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 1024;
|
||||
gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json;
|
||||
|
||||
# 静态资源缓存
|
||||
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
|
||||
expires 30d;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
# 日志
|
||||
access_log /var/log/nginx/access.log;
|
||||
error_log /var/log/nginx/error.log;
|
||||
}
|
||||
31
deploy/supervisord.conf
Normal file
31
deploy/supervisord.conf
Normal file
@@ -0,0 +1,31 @@
|
||||
[supervisord]
|
||||
nodaemon=true
|
||||
logfile=/dev/stdout
|
||||
logfile_maxbytes=0
|
||||
pidfile=/tmp/supervisord.pid
|
||||
user=root
|
||||
|
||||
[program:uvicorn]
|
||||
command=uvicorn app.main:app --host 127.0.0.1 --port 8000 --workers 1 --timeout-keep-alive 600 --proxy-headers --forwarded-allow-ips="*"
|
||||
directory=/app
|
||||
user=appuser
|
||||
autostart=true
|
||||
autorestart=true
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
stopasgroup=true
|
||||
killasgroup=true
|
||||
|
||||
[program:nginx]
|
||||
command=/usr/sbin/nginx -g "daemon off;"
|
||||
user=root
|
||||
autostart=true
|
||||
autorestart=true
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
startsecs=3
|
||||
stopsignal=QUIT
|
||||
Reference in New Issue
Block a user