feat: 初始提交

This commit is contained in:
anonymous
2025-10-21 09:38:26 +08:00
committed by t59688
parent 2965b8e28f
commit c9fc816fab
175 changed files with 23968 additions and 87 deletions

145
deploy/.env.example Normal file
View 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
View 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
View 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
View 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
View 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