revert useless dockfile change

This commit is contained in:
sylarchen1389 2025-11-13 11:13:13 +08:00
parent 73af7a4a4f
commit 88396a16e6
24 changed files with 653 additions and 944 deletions

View file

@ -1,10 +1,7 @@
{ {
"permissions": { "permissions": {
"allow": [ "allow": [
"Bash(tree:*)", "Bash(tree:*)"
"Bash(node:*)",
"Bash(npm --version:*)",
"Bash(test:*)"
], ],
"deny": [], "deny": [],
"ask": [] "ask": []

View file

@ -1,137 +0,0 @@
name: Docker Image CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch: # 允许手动触发
env:
IMAGE_NAME: open-webui-next
OUTPUT_DIR: /tmp/docker-images
KEEP_VERSIONS: 2
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: 检出代码
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: 设置环境变量
run: |
echo "IMAGE_TAG=${{ github.run_number }}" >> $GITHUB_ENV
echo "BUILD_NUMBER=${{ github.run_number }}" >> $GITHUB_ENV
echo "✓ 代码检出完成"
ls -la
- name: 验证 Dockerfile
run: |
if [ ! -f "Dockerfile" ]; then
echo "❌ 找不到 Dockerfile"
exit 1
fi
echo "✓ Dockerfile 存在"
head -10 Dockerfile
- name: 设置 Docker Buildx
uses: docker/setup-buildx-action@v3
- name: 构建 Docker 镜像
uses: docker/build-push-action@v5
with:
context: .
load: true
tags: |
${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }}
${{ env.IMAGE_NAME }}:latest
build-args: |
USE_SLIM=true
cache-from: type=gha
cache-to: type=gha,mode=max
- name: 验证镜像构建
run: |
echo "✓ 镜像构建完成"
docker images | grep ${{ env.IMAGE_NAME }}
- name: 创建输出目录
run: |
mkdir -p ${{ env.OUTPUT_DIR }}
echo "输出目录: ${{ env.OUTPUT_DIR }}"
- name: 导出镜像
run: |
echo "导出镜像..."
docker save ${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }} | gzip > ${{ env.OUTPUT_DIR }}/${{ env.IMAGE_NAME }}-${{ env.IMAGE_TAG }}.tar.gz
docker save ${{ env.IMAGE_NAME }}:latest | gzip > ${{ env.OUTPUT_DIR }}/${{ env.IMAGE_NAME }}-latest.tar.gz
echo "✓ 镜像导出完成"
ls -lh ${{ env.OUTPUT_DIR }}/${{ env.IMAGE_NAME }}*.tar.gz
- name: 上传镜像制品
uses: actions/upload-artifact@v4
with:
name: docker-images-${{ env.IMAGE_TAG }}
path: |
${{ env.OUTPUT_DIR }}/${{ env.IMAGE_NAME }}-${{ env.IMAGE_TAG }}.tar.gz
${{ env.OUTPUT_DIR }}/${{ env.IMAGE_NAME }}-latest.tar.gz
retention-days: 30
- name: 清理本地资源
if: always()
run: |
echo "========================================="
echo "当前构建: ${{ env.BUILD_NUMBER }}"
VERSION_TO_DELETE=$((BUILD_NUMBER - 2))
echo "准备清理版本: ${VERSION_TO_DELETE}"
echo "========================================="
# 清理旧版本的 Docker 镜像
echo "清理 Docker 镜像..."
docker rmi ${{ env.IMAGE_NAME }}:${VERSION_TO_DELETE} 2>/dev/null || echo "镜像 ${VERSION_TO_DELETE} 不存在或已清理"
# 清理旧版本的 tar.gz 文件
echo "清理导出文件..."
rm -f ${{ env.OUTPUT_DIR }}/${{ env.IMAGE_NAME }}-${VERSION_TO_DELETE}.tar.gz
# 清理未使用的 Docker 资源
echo "清理未使用的 Docker 资源..."
docker image prune -f
echo "✓ 清理完成"
echo ""
echo "剩余镜像:"
docker images | grep ${{ env.IMAGE_NAME }} || echo "无相关镜像"
echo ""
echo "剩余文件:"
ls -lh ${{ env.OUTPUT_DIR }}/${{ env.IMAGE_NAME }}*.tar.gz 2>/dev/null || echo "无相关文件"
- name: 显示磁盘使用情况
if: always()
run: |
echo "最终磁盘使用情况:"
df -h ${{ env.OUTPUT_DIR }}
- name: 构建摘要
if: success()
run: |
echo "=========================================" >> $GITHUB_STEP_SUMMARY
echo "✅ 构建成功!" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**镜像信息:**" >> $GITHUB_STEP_SUMMARY
echo "- 镜像名称: \`${{ env.IMAGE_NAME }}\`" >> $GITHUB_STEP_SUMMARY
echo "- 镜像标签: \`${{ env.IMAGE_TAG }}\`, \`latest\`" >> $GITHUB_STEP_SUMMARY
echo "- 构建编号: \`${{ github.run_number }}\`" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**导出文件:**" >> $GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
ls -lh ${{ env.OUTPUT_DIR }}/${{ env.IMAGE_NAME }}*.tar.gz >> $GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
echo "=========================================" >> $GITHUB_STEP_SUMMARY

View file

@ -1,92 +1,45 @@
# syntax=docker/dockerfile:1 # syntax=docker/dockerfile:1
# Initialize device type args # Initialize device type args
# use build args in the docker build command with --build-arg="BUILDARG=true"
ARG USE_CUDA=false ARG USE_CUDA=false
ARG USE_OLLAMA=false ARG USE_OLLAMA=false
ARG USE_SLIM=false ARG USE_SLIM=false
ARG USE_PERMISSION_HARDENING=false ARG USE_PERMISSION_HARDENING=false
# Tested with cu117 for CUDA 11 and cu121 for CUDA 12 (default)
ARG USE_CUDA_VER=cu128 ARG USE_CUDA_VER=cu128
# any sentence transformer model; models to use can be found at https://huggingface.co/models?library=sentence-transformers
# Leaderboard: https://huggingface.co/spaces/mteb/leaderboard
# for better performance and multilangauge support use "intfloat/multilingual-e5-large" (~2.5GB) or "intfloat/multilingual-e5-base" (~1.5GB)
# IMPORTANT: If you change the embedding model (sentence-transformers/all-MiniLM-L6-v2) and vice versa, you aren't able to use RAG Chat with your previous documents loaded in the WebUI! You need to re-embed them.
ARG USE_EMBEDDING_MODEL=sentence-transformers/all-MiniLM-L6-v2 ARG USE_EMBEDDING_MODEL=sentence-transformers/all-MiniLM-L6-v2
ARG USE_RERANKING_MODEL="" ARG USE_RERANKING_MODEL=""
# Tiktoken encoding name; models to use can be found at https://huggingface.co/models?library=tiktoken
ARG USE_TIKTOKEN_ENCODING_NAME="cl100k_base" ARG USE_TIKTOKEN_ENCODING_NAME="cl100k_base"
ARG BUILD_HASH=dev-build ARG BUILD_HASH=dev-build
# Override at your own risk - non-root configurations are untested
ARG UID=0 ARG UID=0
ARG GID=0 ARG GID=0
######## WebUI frontend ######## ######## WebUI frontend ########
FROM --platform=$BUILDPLATFORM node:20-alpine3.20 AS build FROM --platform=$BUILDPLATFORM node:22-alpine3.20 AS build
ARG BUILD_HASH ARG BUILD_HASH
# ========== 配置 Alpine 镜像源 ========== # Set Node.js options (heap limit Allocation failed - JavaScript heap out of memory)
RUN echo "https://mirrors.aliyun.com/alpine/v3.20/main" > /etc/apk/repositories && \ # ENV NODE_OPTIONS="--max-old-space-size=4096"
echo "https://mirrors.aliyun.com/alpine/v3.20/community" >> /etc/apk/repositories && \
apk update
# ========== 增加 Node.js 堆内存限制 ==========
ENV NODE_OPTIONS="--max-old-space-size=4096"
# ========== 配置 npm 镜像源 ==========
RUN npm config set registry https://registry.npmmirror.com && \
npm config set fetch-timeout 120000 && \
npm config set fetch-retries 5 && \
npm config set fetch-retry-mintimeout 20000 && \
npm config set fetch-retry-maxtimeout 120000 && \
npm config set maxsockets 5
# ========== 配置二进制包镜像 ==========
ENV ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ \
SASS_BINARY_SITE=https://npmmirror.com/mirrors/node-sass/ \
PHANTOMJS_CDNURL=https://nppmirror.com/mirrors/phantomjs/ \
CHROMEDRIVER_CDNURL=https://npmmirror.com/mirrors/chromedriver/ \
OPERADRIVER_CDNURL=https://npmmirror.com/mirrors/operadriver/ \
PYTHON_MIRROR=https://npmmirror.com/mirrors/python/
# ========== 配置代理(可选)==========
ARG HTTP_PROXY
ARG HTTPS_PROXY
ENV HTTP_PROXY=${HTTP_PROXY}
ENV HTTPS_PROXY=${HTTPS_PROXY}
ENV NO_PROXY=localhost,127.0.0.1,mirrors.aliyun.com,registry.nppmirror.com,nppmirror.com
# ========== 安装必要工具 ==========
RUN apk add --no-cache git python3 make g++ && \
if [ -n "$HTTP_PROXY" ]; then \
git config --global http.proxy ${HTTP_PROXY} && \
git config --global https.proxy ${HTTPS_PROXY} && \
git config --global http.sslVerify false; \
fi
WORKDIR /app WORKDIR /app
# ========== 安装依赖(不使用 --ignore-scripts========== # to store git revision in build
RUN apk add --no-cache git
COPY package.json package-lock.json ./ COPY package.json package-lock.json ./
RUN npm ci --force
RUN echo "==================================" && \
echo "Starting npm install" && \
echo "Time: $(date)" && \
echo "==================================" && \
npm cache clean --force && \
npm install --legacy-peer-deps --no-audit --no-fund || \
(echo "First attempt failed, retrying..." && \
rm -rf node_modules package-lock.json && \
npm install --legacy-peer-deps --no-audit --no-fund) && \
echo "==================================" && \
echo "npm install completed" && \
echo "Time: $(date)" && \
echo "=================================="
# ========== 构建前端 ==========
COPY . . COPY . .
ENV APP_BUILD_HASH=${BUILD_HASH} ENV APP_BUILD_HASH=${BUILD_HASH}
RUN npm run build
RUN echo "==================================" && \
echo "Starting frontend build" && \
echo "Time: $(date)" && \
echo "==================================" && \
npm run build && \
echo "==================================" && \
echo "Build completed" && \
echo "Time: $(date)" && \
echo "=================================="
######## WebUI backend ######## ######## WebUI backend ########
FROM python:3.11-slim-bookworm AS base FROM python:3.11-slim-bookworm AS base
@ -105,6 +58,7 @@ ARG GID
## Basis ## ## Basis ##
ENV ENV=prod \ ENV ENV=prod \
PORT=8080 \ PORT=8080 \
# pass build args to the build
USE_OLLAMA_DOCKER=${USE_OLLAMA} \ USE_OLLAMA_DOCKER=${USE_OLLAMA} \
USE_CUDA_DOCKER=${USE_CUDA} \ USE_CUDA_DOCKER=${USE_CUDA} \
USE_SLIM_DOCKER=${USE_SLIM} \ USE_SLIM_DOCKER=${USE_SLIM} \
@ -124,23 +78,31 @@ ENV OPENAI_API_KEY="" \
ANONYMIZED_TELEMETRY=false ANONYMIZED_TELEMETRY=false
#### Other models ######################################################### #### Other models #########################################################
## whisper TTS model settings ##
ENV WHISPER_MODEL="base" \ ENV WHISPER_MODEL="base" \
WHISPER_MODEL_DIR="/app/backend/data/cache/whisper/models" \ WHISPER_MODEL_DIR="/app/backend/data/cache/whisper/models"
RAG_EMBEDDING_MODEL="$USE_EMBEDDING_MODEL_DOCKER" \
RAG_RERANKING_MODEL="$USE_RERANKING_MODEL_DOCKER" \
SENTENCE_TRANSFORMERS_HOME="/app/backend/data/cache/embedding/models" \
TIKTOKEN_ENCODING_NAME="cl100k_base" \
TIKTOKEN_CACHE_DIR="/app/backend/data/cache/tiktoken" \
HF_HOME="/app/backend/data/cache/embedding/models"
# ========== 配置 Hugging Face 镜像 ========== ## RAG Embedding model settings ##
ENV HF_ENDPOINT=https://hf-mirror.com ENV RAG_EMBEDDING_MODEL="$USE_EMBEDDING_MODEL_DOCKER" \
RAG_RERANKING_MODEL="$USE_RERANKING_MODEL_DOCKER" \
SENTENCE_TRANSFORMERS_HOME="/app/backend/data/cache/embedding/models"
## Tiktoken model settings ##
ENV TIKTOKEN_ENCODING_NAME="cl100k_base" \
TIKTOKEN_CACHE_DIR="/app/backend/data/cache/tiktoken"
## Hugging Face download cache ##
ENV HF_HOME="/app/backend/data/cache/embedding/models"
## Torch Extensions ##
# ENV TORCH_EXTENSIONS_DIR="/.cache/torch_extensions"
#### Other models ##########################################################
WORKDIR /app/backend WORKDIR /app/backend
ENV HOME=/root ENV HOME=/root
# Create user and group if not root
# ========== 创建用户和组 ==========
RUN if [ $UID -ne 0 ]; then \ RUN if [ $UID -ne 0 ]; then \
if [ $GID -ne 0 ]; then \ if [ $GID -ne 0 ]; then \
addgroup --gid $GID app; \ addgroup --gid $GID app; \
@ -148,15 +110,13 @@ RUN if [ $UID -ne 0 ]; then \
adduser --uid $UID --gid $GID --home $HOME --disabled-password --no-create-home app; \ adduser --uid $UID --gid $GID --home $HOME --disabled-password --no-create-home app; \
fi fi
RUN mkdir -p $HOME/.cache/chroma && \ RUN mkdir -p $HOME/.cache/chroma
echo -n 00000000-0000-0000-0000-000000000000 > $HOME/.cache/chroma/telemetry_user_id && \ RUN echo -n 00000000-0000-0000-0000-000000000000 > $HOME/.cache/chroma/telemetry_user_id
chown -R $UID:$GID /app $HOME
# ========== 配置 Debian 镜像源 ========== # Make sure the user has access to the app and root directory
RUN sed -i 's@deb.debian.org@mirrors.aliyun.com@g' /etc/apt/sources.list.d/debian.sources && \ RUN chown -R $UID:$GID /app $HOME
sed -i 's@security.debian.org@mirrors.aliyun.com@g' /etc/apt/sources.list.d/debian.sources
# ========== 安装系统依赖 ========== # Install common system dependencies
RUN apt-get update && \ RUN apt-get update && \
apt-get install -y --no-install-recommends \ apt-get install -y --no-install-recommends \
git build-essential pandoc gcc netcat-openbsd curl jq \ git build-essential pandoc gcc netcat-openbsd curl jq \
@ -164,100 +124,56 @@ RUN apt-get update && \
ffmpeg libsm6 libxext6 \ ffmpeg libsm6 libxext6 \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
# ========== 配置 pip 镜像源 ========== # install python dependencies
RUN pip3 config set global.index-url https://mirrors.aliyun.com/pypi/simple/ && \
pip3 config set install.trusted-host mirrors.aliyun.com && \
pip3 config set global.timeout 600
# ========== 配置 uv 使用镜像源 ==========
ENV UV_INDEX_URL=https://mirrors.aliyun.com/pypi/simple/ \
UV_EXTRA_INDEX_URL="" \
UV_NO_CACHE=0
# ========== 安装 Python 依赖 ==========
COPY --chown=$UID:$GID ./backend/requirements.txt ./requirements.txt COPY --chown=$UID:$GID ./backend/requirements.txt ./requirements.txt
RUN echo "==================================" && \ RUN pip3 install --no-cache-dir uv && \
echo "Installing Python dependencies" && \
echo "Time: $(date)" && \
echo "==================================" && \
pip3 install uv && \
if [ "$USE_CUDA" = "true" ]; then \ if [ "$USE_CUDA" = "true" ]; then \
echo "Installing PyTorch with CUDA support..." && \ # If you use CUDA the whisper and embedding model will be downloaded on first use
pip3 install torch torchvision torchaudio \ pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/$USE_CUDA_DOCKER_VER --no-cache-dir && \
--index-url https://mirrors.aliyun.com/pypi/simple/ \ uv pip install --system -r requirements.txt --no-cache-dir && \
--trusted-host mirrors.aliyun.com || \ python -c "import os; from sentence_transformers import SentenceTransformer; SentenceTransformer(os.environ['RAG_EMBEDDING_MODEL'], device='cpu')" && \
(echo "Aliyun failed, trying Tsinghua mirror..." && \ python -c "import os; from faster_whisper import WhisperModel; WhisperModel(os.environ['WHISPER_MODEL'], device='cpu', compute_type='int8', download_root=os.environ['WHISPER_MODEL_DIR'])"; \
pip3 install torch torchvision torchaudio \ python -c "import os; import tiktoken; tiktoken.get_encoding(os.environ['TIKTOKEN_ENCODING_NAME'])"; \
--index-url https://pypi.tuna.tsinghua.edu.cn/simple/ \
--trusted-host pypi.tuna.tsinghua.edu.cn) || \
(echo "Mirrors failed, trying official PyTorch repo..." && \
pip3 install torch torchvision torchaudio \
--index-url https://download.pytorch.org/whl/$USE_CUDA_DOCKER_VER) && \
echo "Installing other requirements with uv..." && \
uv pip install --system -r requirements.txt \
--index-url https://mirrors.aliyun.com/pypi/simple/ && \
if [ "$USE_SLIM" != "true" ]; then \
echo "Downloading models..." && \
python -c "import os; from sentence_transformers import SentenceTransformer; SentenceTransformer(os.environ['RAG_EMBEDDING_MODEL'], device='cpu')" && \
python -c "import os; from faster_whisper import WhisperModel; WhisperModel(os.environ['WHISPER_MODEL'], device='cpu', compute_type='int8', download_root=os.environ['WHISPER_MODEL_DIR'])" && \
python -c "import os; import tiktoken; tiktoken.get_encoding(os.environ['TIKTOKEN_ENCODING_NAME'])"; \
fi; \
else \ else \
echo "Installing PyTorch CPU version..." && \ pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu --no-cache-dir && \
pip3 install torch torchvision torchaudio \ uv pip install --system -r requirements.txt --no-cache-dir && \
--index-url https://mirrors.aliyun.com/pypi/simple/ \ if [ "$USE_SLIM" != "true" ]; then \
--trusted-host mirrors.aliyun.com || \ python -c "import os; from sentence_transformers import SentenceTransformer; SentenceTransformer(os.environ['RAG_EMBEDDING_MODEL'], device='cpu')" && \
(echo "Aliyun failed, trying Tsinghua mirror..." && \ python -c "import os; from faster_whisper import WhisperModel; WhisperModel(os.environ['WHISPER_MODEL'], device='cpu', compute_type='int8', download_root=os.environ['WHISPER_MODEL_DIR'])"; \
pip3 install torch torchvision torchaudio \ python -c "import os; import tiktoken; tiktoken.get_encoding(os.environ['TIKTOKEN_ENCODING_NAME'])"; \
--index-url https://pypi.tuna.tsinghua.edu.cn/simple/ \ fi; \
--trusted-host pypi.tuna.tsinghua.edu.cn) || \ fi; \
(echo "Tsinghua failed, trying USTC mirror..." && \
pip3 install torch torchvision torchaudio \
--index-url https://mirrors.ustc.edu.cn/pypi/web/simple/ \
--trusted-host mirrors.ustc.edu.cn) || \
(echo "All mirrors failed, trying official PyTorch CPU repo..." && \
pip3 install torch torchvision torchaudio \
--index-url https://download.pytorch.org/whl/cpu) && \
echo "Installing other requirements with uv..." && \
uv pip install --system -r requirements.txt \
--index-url https://mirrors.aliyun.com/pypi/simple/ && \
if [ "$USE_SLIM" != "true" ]; then \
echo "Downloading models..." && \
python -c "import os; from sentence_transformers import SentenceTransformer; SentenceTransformer(os.environ['RAG_EMBEDDING_MODEL'], device='cpu')" && \
python -c "import os; from faster_whisper import WhisperModel; WhisperModel(os.environ['WHISPER_MODEL'], device='cpu', compute_type='int8', download_root=os.environ['WHISPER_MODEL_DIR'])" && \
python -c "import os; import tiktoken; tiktoken.get_encoding(os.environ['TIKTOKEN_ENCODING_NAME'])"; \
fi; \
fi && \
mkdir -p /app/backend/data && chown -R $UID:$GID /app/backend/data/ && \ mkdir -p /app/backend/data && chown -R $UID:$GID /app/backend/data/ && \
echo "==================================" && \ rm -rf /var/lib/apt/lists/*;
echo "Python dependencies installed" && \
echo "Time: $(date)" && \
echo "=================================="
# ========== 安装 Ollama ========== # Install Ollama if requested
RUN if [ "$USE_OLLAMA" = "true" ]; then \ RUN if [ "$USE_OLLAMA" = "true" ]; then \
date +%s > /tmp/ollama_build_hash && \ date +%s > /tmp/ollama_build_hash && \
echo "Installing Ollama..." && \ echo "Cache broken at timestamp: `cat /tmp/ollama_build_hash`" && \
export HF_ENDPOINT=https://hf-mirror.com && \ curl -fsSL https://ollama.com/install.sh | sh && \
curl -fsSL https://ollama.com/install.sh | sh || \ rm -rf /var/lib/apt/lists/*; \
(echo "Ollama installation failed, trying with proxy..." && \
export http_proxy=http://host.docker.internal:7897 && \
export https_proxy=http://host.docker.internal:7897 && \
curl -fsSL https://ollama.com/install.sh | sh); \
fi fi
# ========== 复制构建文件 ========== # copy embedding weight from build
# RUN mkdir -p /root/.cache/chroma/onnx_models/all-MiniLM-L6-v2
# COPY --from=build /app/onnx /root/.cache/chroma/onnx_models/all-MiniLM-L6-v2/onnx
# copy built frontend files
COPY --chown=$UID:$GID --from=build /app/build /app/build COPY --chown=$UID:$GID --from=build /app/build /app/build
COPY --chown=$UID:$GID --from=build /app/CHANGELOG.md /app/CHANGELOG.md COPY --chown=$UID:$GID --from=build /app/CHANGELOG.md /app/CHANGELOG.md
COPY --chown=$UID:$GID --from=build /app/package.json /app/package.json COPY --chown=$UID:$GID --from=build /app/package.json /app/package.json
# copy backend files
COPY --chown=$UID:$GID ./backend . COPY --chown=$UID:$GID ./backend .
EXPOSE 8080 EXPOSE 8080
HEALTHCHECK CMD curl --silent --fail http://localhost:${PORT:-8080}/health | jq -ne 'input.status == true' || exit 1 HEALTHCHECK CMD curl --silent --fail http://localhost:${PORT:-8080}/health | jq -ne 'input.status == true' || exit 1
# ========== 权限加固 ========== # Minimal, atomic permission hardening for OpenShift (arbitrary UID):
# - Group 0 owns /app and /root
# - Directories are group-writable and have SGID so new files inherit GID 0
RUN if [ "$USE_PERMISSION_HARDENING" = "true" ]; then \ RUN if [ "$USE_PERMISSION_HARDENING" = "true" ]; then \
set -eux; \ set -eux; \
chgrp -R 0 /app /root || true; \ chgrp -R 0 /app /root || true; \
@ -272,4 +188,4 @@ ARG BUILD_HASH
ENV WEBUI_BUILD_VERSION=${BUILD_HASH} ENV WEBUI_BUILD_VERSION=${BUILD_HASH}
ENV DOCKER=true ENV DOCKER=true
CMD [ "bash", "start.sh"] CMD [ "bash", "start.sh"]

168
Jenkinsfile vendored
View file

@ -1,168 +0,0 @@
pipeline {
agent any
environment {
REPO_URL = 'git@github.com:ai-friend-coming/open-webui-next.git'
IMAGE_NAME = 'open-webui-custom'
IMAGE_TAG = "${BUILD_NUMBER}"
OUTPUT_DIR = '/var/docker-images'
DOCKER_FILE_PATH = 'Dockerfile'
}
options {
buildDiscarder(logRotator(numToKeepStr: '10'))
timeout(time: 1, unit: 'HOURS')
timestamps()
}
stages {
stage('准备工作') {
steps {
script {
echo "========================================="
echo "开始构建 Build #${BUILD_NUMBER}"
echo "仓库: ${REPO_URL}"
echo "镜像: ${IMAGE_NAME}:${IMAGE_TAG}"
echo "========================================="
// 检查Docker是否可用
sh 'docker --version'
sh 'docker info'
}
}
}
stage('检出代码') {
steps {
script {
echo "从 ${REPO_URL} 检出代码..."
}
// 使用更简单的checkout方式
checkout([
$class: 'GitSCM',
branches: [[name: '*/main']],
userRemoteConfigs: [[
url: "${REPO_URL}",
credentialsId: 'github-ssh' // 改成你实际创建的凭据ID
]]
])
script {
echo "代码检出完成"
sh 'ls -la'
sh 'git log --oneline -1 || echo "无法获取git日志"'
}
}
}
stage('验证 Dockerfile') {
steps {
script {
echo "检查 Dockerfile..."
sh """
if [ ! -f "${DOCKER_FILE_PATH}" ]; then
echo "错误: 找不到 Dockerfile: ${DOCKER_FILE_PATH}"
echo "当前目录内容:"
ls -la
exit 1
fi
echo "Dockerfile 存在"
echo "--- Dockerfile 内容 (前20行) ---"
head -20 "${DOCKER_FILE_PATH}"
"""
}
}
}
stage('创建输出目录') {
steps {
script {
echo "创建输出目录: ${OUTPUT_DIR}"
sh """
sudo mkdir -p ${OUTPUT_DIR}
sudo chmod 777 ${OUTPUT_DIR}
ls -ld ${OUTPUT_DIR}
"""
}
}
}
stage('构建 Docker 镜像') {
steps {
script {
echo "开始构建镜像: ${IMAGE_NAME}:${IMAGE_TAG}"
sh """
docker build \
-t ${IMAGE_NAME}:${IMAGE_TAG} \
-t ${IMAGE_NAME}:latest \
-f ${DOCKER_FILE_PATH} \
.
echo "镜像构建完成"
docker images | grep ${IMAGE_NAME} || echo "未找到镜像"
"""
}
}
}
stage('导出镜像') {
steps {
script {
echo "导出镜像到 ${OUTPUT_DIR}"
sh """
echo "导出 ${IMAGE_NAME}:${IMAGE_TAG}..."
docker save ${IMAGE_NAME}:${IMAGE_TAG} | gzip > ${OUTPUT_DIR}/${IMAGE_NAME}-${IMAGE_TAG}.tar.gz
echo "导出 ${IMAGE_NAME}:latest..."
docker save ${IMAGE_NAME}:latest | gzip > ${OUTPUT_DIR}/${IMAGE_NAME}-latest.tar.gz
echo "导出完成"
ls -lh ${OUTPUT_DIR}/${IMAGE_NAME}*.tar.gz
"""
}
}
}
stage('清理旧镜像') {
steps {
script {
echo "清理本地镜像..."
sh """
docker rmi ${IMAGE_NAME}:${IMAGE_TAG} 2>/dev/null || echo "镜像已删除或不存在"
# 保留latest标签方便下次使用
# docker rmi ${IMAGE_NAME}:latest 2>/dev/null || true
# 清理悬空镜像
docker image prune -f --filter "dangling=true" || true
echo "清理完成"
"""
}
}
}
}
post {
success {
script {
echo "========================================="
echo "✅ 构建成功!"
echo "镜像文件位置: ${OUTPUT_DIR}"
sh "ls -lh ${OUTPUT_DIR}/${IMAGE_NAME}*.tar.gz || true"
echo "========================================="
}
}
failure {
echo "❌ 构建失败,请检查上方日志"
}
always {
script {
echo "流水线执行完成"
// 可选:清理工作空间(注释掉以便调试)
// cleanWs()
}
}
}
}

View file

@ -1,291 +0,0 @@
# Open WebUI 本地开发环境搭建指南
本指南提供 Open WebUI 项目的本地开发环境配置和运行步骤。
## 系统要求
- **Node.js**: v20.19.5 或更高版本
- **Python**: 3.12.x (推荐使用 pyenv 管理)
- **npm**: 10.8.2 或更高版本
- **操作系统**: macOS / Linux / Windows
## 技术栈
### 前端
- **框架**: SvelteKit 4 + TypeScript
- **构建工具**: Vite 5
- **样式**: Tailwind CSS 4
### 后端
- **语言**: Python 3.12
- **框架**: FastAPI
- **数据库**: SQLite (开发环境) / PostgreSQL (生产环境)
- **ORM**: SQLAlchemy + Peewee
## 环境准备
### 1. 安装 pyenv (如果系统 Python 版本不是 3.12)
```bash
# macOS (使用 Homebrew)
brew install pyenv
# 配置 shell 环境变量 (添加到 ~/.zshrc 或 ~/.bash_profile)
export PATH="$HOME/.pyenv/bin:$PATH"
eval "$(pyenv init -)"
```
### 2. 安装 Python 3.12
```bash
# 使用 pyenv 安装 Python 3.12
pyenv install 3.12
# 验证安装
pyenv versions
```
## 安装依赖
### 前端依赖
```bash
# 在项目根目录执行
npm install --legacy-peer-deps
```
**注意**: 需要使用 `--legacy-peer-deps` 标志来解决 @tiptap 包的版本冲突问题。
### 后端依赖
```bash
# 进入后端目录
cd backend
# 创建 Python 3.12 虚拟环境
/Users/你的用户名/.pyenv/versions/3.12.12/bin/python3 -m venv venv
# 或者,如果系统 Python 已是 3.12
python3 -m venv venv
# 激活虚拟环境
source venv/bin/activate # macOS/Linux
# 或
venv\Scripts\activate # Windows
# 升级 pip
pip install --upgrade pip
# 安装依赖
pip install -r requirements.txt
```
## 运行开发服务器
### 启动后端服务 (端口 8080)
```bash
# 在 backend 目录下,激活虚拟环境后
cd backend
source venv/bin/activate
python -m uvicorn open_webui.main:app --reload --port 8080 --host 0.0.0.0
```
后端服务将运行在: **http://localhost:8080**
后端启动时会自动:
- 运行数据库迁移 (Alembic)
- 初始化 SQLite 数据库
- 配置向量数据库 (ChromaDB)
### 启动前端服务 (端口 5050)
```bash
# 在项目根目录
npm run dev:5050
```
前端服务将运行在: **http://localhost:5050**
首次启动时会自动:
- 下载 Pyodide 包 (浏览器内 Python 运行时)
- 预加载常用 Python 包 (numpy, pandas, matplotlib 等)
## 访问应用
打开浏览器访问: **http://localhost:5050**
前端会通过 Vite 代理将 API 请求转发到后端 (8080 端口)。
## 开发工作流
### 目录结构
```
open-webui-next/
├── src/ # 前端源码
│ ├── routes/ # SvelteKit 路由
│ ├── lib/
│ │ ├── apis/ # API 客户端
│ │ ├── components/ # Svelte 组件
│ │ ├── stores/ # 全局状态管理
│ │ └── i18n/ # 国际化
├── backend/ # 后端源码
│ ├── open_webui/
│ │ ├── main.py # FastAPI 入口
│ │ ├── routers/ # API 路由
│ │ ├── models/ # 数据模型
│ │ ├── utils/ # 工具函数
│ │ └── migrations/ # 数据库迁移
│ ├── requirements.txt
│ └── venv/ # Python 虚拟环境
└── package.json
```
### 常用开发命令
#### 前端
```bash
npm run dev # 启动开发服务器 (默认端口 5173)
npm run dev:5050 # 启动开发服务器 (端口 5050)
npm run build # 构建生产版本
npm run lint # 代码检查
npm run format # 代码格式化
npm run test:frontend # 运行单元测试
npm run i18n:parse # 解析并更新翻译文件
```
#### 后端
```bash
# 在 backend 目录下,激活虚拟环境后
# 启动开发服务器 (自动重载)
python -m uvicorn open_webui.main:app --reload --port 8080
# 代码格式化
black .
# 数据库迁移
cd backend
alembic revision --autogenerate -m "描述" # 生成迁移脚本
alembic upgrade head # 执行迁移
```
### 热重载
- **前端**: Vite 自动检测文件变化并热重载
- **后端**: uvicorn 的 `--reload` 参数自动检测 Python 代码变化并重启
## 常见问题
### 1. npm install 失败
**问题**: 依赖版本冲突
**解决方案**:
```bash
npm install --legacy-peer-deps
```
### 2. 后端 Python 版本不兼容
**问题**: `unstructured` 包不支持 Python 3.13+
**解决方案**: 使用 Python 3.12:
```bash
pyenv install 3.12
cd backend
rm -rf venv
/Users/你的用户名/.pyenv/versions/3.12.12/bin/python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
```
### 3. 端口被占用
**问题**: 8080 或 5050 端口已被使用
**解决方案**:
```bash
# 查找占用端口的进程
lsof -i :8080
lsof -i :5050
# 终止进程
kill -9 <PID>
# 或者使用不同端口
# 前端:
npm run dev -- --port 3000
# 后端:
python -m uvicorn open_webui.main:app --reload --port 8000
```
### 4. 前端 Pyodide 下载慢
**问题**: 首次启动下载 Pyodide 包较慢
**解决方案**: 耐心等待,包会缓存在 `node_modules` 中,后续启动会很快。
### 5. 数据库迁移错误
**问题**: Alembic 迁移失败
**解决方案**:
```bash
# 删除数据库重新初始化 (仅开发环境)
rm backend/data/webui.db
python -m uvicorn open_webui.main:app --reload --port 8080
```
## 环境变量配置
后端可通过环境变量配置,创建 `backend/.env` 文件:
```bash
# 数据库
DATABASE_URL=sqlite:///data/webui.db
# 向量数据库
VECTOR_DB=chroma
EMBEDDING_MODEL=sentence-transformers/all-MiniLM-L6-v2
# CORS (开发环境)
CORS_ALLOW_ORIGIN=*
# 日志级别
LOG_LEVEL=INFO
```
## 生产部署
生产环境使用 Docker 部署,详见项目根目录的 `Dockerfile``docker-compose.yaml`
```bash
# 构建镜像
docker build -t open-webui .
# 运行容器
docker run -d -p 8080:8080 -v open-webui:/app/backend/data open-webui
```
## 更多资源
- **项目文档**: [CLAUDE.md](./CLAUDE.md)
- **API 文档**: http://localhost:8080/docs (启动后端后访问)
- **官方仓库**: https://github.com/open-webui/open-webui
## 贡献指南
1. Fork 项目
2. 创建功能分支 (`git checkout -b feature/AmazingFeature`)
3. 提交更改 (`git commit -m 'Add some AmazingFeature'`)
4. 推送到分支 (`git push origin feature/AmazingFeature`)
5. 创建 Pull Request
---
**最后更新**: 2025-11-08

View file

@ -1,88 +1,36 @@
# 本地开发故障排除 # Open WebUI Troubleshooting Guide
## 问题: "Open WebUI 需要后端服务" 错误 ## Understanding the Open WebUI Architecture
### 🔧 快速解决方案 The Open WebUI system is designed to streamline interactions between the client (your browser) and the Ollama API. At the heart of this design is a backend reverse proxy, enhancing security and resolving CORS issues.
**请在浏览器中进行硬刷新:** - **How it Works**: The Open WebUI is designed to interact with the Ollama API through a specific route. When a request is made from the WebUI to Ollama, it is not directly sent to the Ollama API. Initially, the request is sent to the Open WebUI backend via `/ollama` route. From there, the backend is responsible for forwarding the request to the Ollama API. This forwarding is accomplished by using the route specified in the `OLLAMA_BASE_URL` environment variable. Therefore, a request made to `/ollama` in the WebUI is effectively the same as making a request to `OLLAMA_BASE_URL` in the backend. For instance, a request to `/ollama/api/tags` in the WebUI is equivalent to `OLLAMA_BASE_URL/api/tags` in the backend.
- **macOS**: `Cmd + Shift + R` - **Security Benefits**: This design prevents direct exposure of the Ollama API to the frontend, safeguarding against potential CORS (Cross-Origin Resource Sharing) issues and unauthorized access. Requiring authentication to access the Ollama API further enhances this security layer.
- **Windows/Linux**: `Ctrl + Shift + R`
然后检查是否解决问题。 ## Open WebUI: Server Connection Error
### 📋 详细排查步骤 If you're experiencing connection issues, its often due to the WebUI docker container not being able to reach the Ollama server at 127.0.0.1:11434 (host.docker.internal:11434) inside the container . Use the `--network=host` flag in your docker command to resolve this. Note that the port changes from 3000 to 8080, resulting in the link: `http://localhost:8080`.
#### 1. 打开浏览器开发者工具 **Example Docker Command**:
- **macOS**: `Cmd + Option + I`
- **Windows/Linux**: `F12`
#### 2. 检查 Console (控制台)
查找是否有错误消息,特别是:
- 红色的错误信息
- 网络请求失败
- CORS 相关错误
#### 3. 检查 Network (网络) 标签
1. 切换到 Network 标签
2. 刷新页面
3. 查找对 `http://localhost:8080/api/config` 的请求
4. 如果找到,点击查看:
- **Status** 应该是 `200`
- **Response** 应该包含 JSON 配置
#### 4. 清除本地存储
1. 在开发者工具中,转到 **Application** 标签
2. 左侧找到 **Local Storage**
3. 展开并点击 `http://localhost:5050`
4. 点击右键 → **Clear**
5. 刷新页面
### ✅ 验证服务状态
在终端运行:
```bash ```bash
# 测试后端 API docker run -d --network=host -v open-webui:/app/backend/data -e OLLAMA_BASE_URL=http://127.0.0.1:11434 --name open-webui --restart always ghcr.io/open-webui/open-webui:main
curl http://localhost:8080/api/config
# 检查端口占用
lsof -i :8080 -i :5050 | grep LISTEN
``` ```
如果 curl 命令返回 JSON 配置,说明后端正常运行。 ### Error on Slow Responses for Ollama
### 🔄 重启服务 (如果需要) Open WebUI has a default timeout of 5 minutes for Ollama to finish generating the response. If needed, this can be adjusted via the environment variable AIOHTTP_CLIENT_TIMEOUT, which sets the timeout in seconds.
如果上述方法无效,停止当前服务 (`Ctrl + C`) 并重新启动: ### General Connection Errors
**后端:** **Ensure Ollama Version is Up-to-Date**: Always start by checking that you have the latest version of Ollama. Visit [Ollama's official site](https://ollama.com/) for the latest updates.
```bash
cd backend
source venv/bin/activate
python -m uvicorn open_webui.main:app --reload --port 8080 --host 0.0.0.0
```
**前端:** **Troubleshooting Steps**:
```bash
npm run dev:5050
```
### 🌐 尝试不同端口 1. **Verify Ollama URL Format**:
- When running the Web UI container, ensure the `OLLAMA_BASE_URL` is correctly set. (e.g., `http://192.168.1.1:11434` for different host setups).
- In the Open WebUI, navigate to "Settings" > "General".
- Confirm that the Ollama Server URL is correctly set to `[OLLAMA URL]` (e.g., `http://localhost:11434`).
如果端口冲突,可以使用不同端口: By following these enhanced troubleshooting steps, connection issues should be effectively resolved. For further assistance or queries, feel free to reach out to us on our community Discord.
**前端:**
```bash
npm run dev -- --port 3000
```
然后访问 `http://localhost:3000`
---
**还有问题?** 查看 `/Users/sylar/my_ws/open-webui-next/LOCAL_SETUP.md` 获取完整设置指南。

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB

View file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

View file

@ -0,0 +1,21 @@
{
"name": "Open WebUI",
"short_name": "WebUI",
"icons": [
{
"src": "/static/web-app-manifest-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "/static/web-app-manifest-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

View file

@ -0,0 +1 @@
Name,Email,Password,Role
1 Name Email Password Role

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

646
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -141,7 +141,6 @@
"vega-lite": "^6.4.1", "vega-lite": "^6.4.1",
"vite-plugin-static-copy": "^2.2.0", "vite-plugin-static-copy": "^2.2.0",
"y-prosemirror": "^1.3.7", "y-prosemirror": "^1.3.7",
"y-protocols": "^1.0.6",
"yaml": "^2.7.1", "yaml": "^2.7.1",
"yjs": "^13.6.27" "yjs": "^13.6.27"
}, },