open-webui / start_with_sync.sh
wwforonce's picture
add readeck
940e285
#!/bin/bash
set -e
# Use /tmp for all writable data
export DATA_DIR="/tmp/open-webui-data"
export HF_STORAGE_REPO="${HF_STORAGE_REPO:-nxdev-org/open-webui-storage}"
export SYNC_INTERVAL="${SYNC_INTERVAL:-300}"
# Set all HuggingFace and cache directories to /tmp
export HF_HOME="/tmp/hf_cache"
export HUGGINGFACE_HUB_CACHE="/tmp/hf_cache"
export TRANSFORMERS_CACHE="/tmp/hf_cache"
export SENTENCE_TRANSFORMERS_HOME="/tmp/hf_cache"
# Override Open WebUI environment variables
export STATIC_DIR="/tmp/static"
export UPLOAD_DIR="/tmp/uploads"
echo "Starting Open WebUI with HF Dataset persistence..."
echo "Data directory: $DATA_DIR"
echo "HF Repository: $HF_STORAGE_REPO"
echo "HF Cache: $HF_HOME"
# insytall uv, custom installer
mkdir -p /tmp/bin
export UV_CACHE_DIR="/tmp/uv_cache"
curl -LsSf https://astral.sh/uv/install.sh | env UV_UNMANAGED_INSTALL="/tmp/bin" UV_CACHE_DIR="/tmp/uv_cache" sh
#source /tmp/bin/env
export PATH="/tmp/bin:$PATH"
# set nvm cache directory
export NPM_CONFIG_CACHE=/tmp/.npm_cache
export XDG_CONFIG_HOME=/tmp/.config
# <--- ADD THIS LINE for Caddy's config
export XDG_DATA_HOME=/tmp/.local/share
# <--- ADD THIS LINE for Caddy's data/locks (equivalent to ~/.local/share)
# Ensure the custom config and data directories exist and are writable
# These will be created by 'appuser' if `USER appuser` is active, or by root if before.
# Since they are in /tmp, 'appuser' will have write permissions regardless if created by root.
mkdir -p ${XDG_CONFIG_HOME}/caddy \
${XDG_DATA_HOME}/caddy/locks
# Ensure the custom cache directory exists for npm (though /tmp generally is fine)
# It's good practice to create it, and it will be owned by `appuser` if `USER appuser` is active.
# No need to mkdir for UV_CACHE_DIR as uv will create it if needed.
mkdir -p ${NPM_CONFIG_CACHE}
source ${HOME}/add_bash_util.sh
source "${NVM_DIR}/nvm.sh"
node -v && npm -v
uv venv /tmp/.venv
source /tmp/.venv/bin/activate
uv pip install --no-cache-dir \
huggingface_hub \
datasets
# Create all necessary directories
mkdir -p "$DATA_DIR" "$HF_HOME" "$STATIC_DIR" "$UPLOAD_DIR"
# Copy static files to writable location
if [ -d "/app/backend/open_webui/static" ]; then
echo "Copying static files to writable location..."
cp -r /app/backend/open_webui/static/* "$STATIC_DIR/" 2>/dev/null || true
fi
# Test write permissions
if touch "$DATA_DIR/test" 2>/dev/null; then
rm "$DATA_DIR/test"
echo "Data directory is writable"
else
echo "Warning: Data directory may not be writable"
fi
# Check if HF_TOKEN is set
if [ -z "$HF_TOKEN" ]; then
echo "Warning: HF_TOKEN not set. Sync functionality will be limited."
else
echo "HF_TOKEN is set, proceeding with sync..."
fi
# Download existing data on startup
echo "Syncing data from Hugging Face..."
python3 ${HOME}/sync_storage.py download
# Function to handle graceful shutdown
cleanup() {
echo "Shutting down gracefully..."
# Upload final data state
if [ -n "$HF_TOKEN" ]; then
echo "Uploading final data state..."
python3 ${HOME}/sync_storage.py upload
fi
# Kill background processes
kill $SYNC_PID 2>/dev/null || true
kill $WEBUI_PID 2>/dev/null || true
exit 0
}
# Set up signal handlers
trap cleanup SIGTERM SIGINT
# Background sync function
background_sync() {
if [ -n "$HF_TOKEN" ]; then
while true; do
sleep $SYNC_INTERVAL
echo "Periodic sync to Hugging Face..."
python3 ${HOME}/sync_storage.py upload
done
else
echo "Skipping background sync - no HF_TOKEN"
while true; do
sleep 3600
done
fi
}
# Start background sync
background_sync &
SYNC_PID=$!
# Start Open WebUI
echo "Starting Open WebUI..."
# Set environment variables for Open WebUI
export WEBUI_SECRET_KEY="${WEBUI_SECRET_KEY:-$(openssl rand -hex 32)}"
# Start Open WebUI in background
# deactivate uv
deactivate && /app/backend/start.sh &
WEBUI_PID=$!
# Wait for Open WebUI process
# wait $WEBUI_PID
# readeck
mkdir -p $DATA_DIR/readeck-data
readeck_url=$(curl -X 'GET' 'https://codeberg.org/api/v1/repos/readeck/readeck/releases/latest' -H 'accept: application/json' | jq -r '.assets[] | .browser_download_url | select(. | endswith("linux-amd64"))')
echo download readeck from $readeck_url
wget -q $readeck_url -O $DATA_DIR/readeck-data/readeck
chmod a+x $DATA_DIR/readeck-data/readeck
cp ${HOME}/readeck.toml $DATA_DIR/readeck-data/readeck.toml
echo "Starting readeck..."
cd $DATA_DIR/readeck-data && ./readeck serve -config ./readeck.toml &
gh_install caddyserver/caddy linux_amd64.tar.gz /tmp/caddy.tar.gz
mkdir -p /tmp/caddy
tar -xzf /tmp/caddy.tar.gz -C /tmp/caddy
# Start the Node.js application
git clone https://github.com/waxz/Gemini-CLI-2-API.git /tmp/Gemini-CLI-2-API -b waxz-patch-1
git clone https://github.com/waxz/gemini-cli-openai /tmp/gemini-cli-openai
git clone https://github.com/snailyp/gemini-balance.git /tmp/gemini-balance
source /tmp/.venv/bin/activate
uv pip install -r /tmp/gemini-balance/requirements.txt
cat << EOF | tee /tmp/Gemini-CLI-2-API/config.json
{
"REQUIRED_API_KEY": "${REQUIRED_API_KEY:-}",
"SERVER_PORT": 3000,
"HOST": "localhost",
"MODEL_PROVIDER": "gemini-cli-oauth",
"OPENAI_API_KEY": "${OPENAI_API_KEY:-}",
"OPENAI_BASE_URL": "https://api.openai.com/v1",
"CLAUDE_API_KEY": "${CLAUDE_API_KEY:-}",
"CLAUDE_BASE_URL": "https://api.anthropic.com/v1",
"PROJECT_ID": "${PROJECT_ID:-}",
"PROMPT_LOG_MODE": "console",
"GEMINI_OAUTH_CREDS_FILE_PATH":"/tmp/gemini_oauth_creds.json"
}
EOF
cat << EOF | tee /tmp/gemini_oauth_creds.json
{
"access_token": "${GEMINI_OAUTH_ACCESS_TOKEN:-}",
"refresh_token": "${GEMINI_OAUTH_REFRESH_TOKEN:-}",
"scope": "https://www.googleapis.com/auth/cloud-platform",
"token_type": "Bearer",
"expiry_date": 1753880406425
}
EOF
cat << EOF | tee /tmp/gemini-balance/.env
# 数据库配置
DATABASE_TYPE=mysql
#SQLITE_DATABASE=default_db
MYSQL_HOST=${MYSQL_HOST}
#MYSQL_SOCKET=/run/mysqld/mysqld.sock
MYSQL_PORT=${MYSQL_PORT}
MYSQL_USER=${MYSQL_USER}
MYSQL_PASSWORD=${MYSQL_PASSWORD}
MYSQL_DATABASE=${MYSQL_DATABASE}
API_KEYS=${GEMINI_API_KEYS}
ALLOWED_TOKENS=["${REQUIRED_API_KEY}"]
AUTH_TOKEN=${REQUIRED_API_KEY}
# For Vertex AI Platform API Keys
VERTEX_API_KEYS=["AQ.Abxxxxxxxxxxxxxxxxxxx"]
# For Vertex AI Platform Express API Base URL
VERTEX_EXPRESS_BASE_URL=https://aiplatform.googleapis.com/v1beta1/publishers/google
TEST_MODEL=gemini-1.5-flash
THINKING_MODELS=["gemini-2.5-flash-preview-04-17"]
THINKING_BUDGET_MAP={"gemini-2.5-flash-preview-04-17": 4000}
IMAGE_MODELS=["gemini-2.0-flash-exp"]
SEARCH_MODELS=["gemini-2.0-flash-exp","gemini-2.0-pro-exp"]
FILTERED_MODELS=["gemini-1.0-pro-vision-latest", "gemini-pro-vision", "chat-bison-001", "text-bison-001", "embedding-gecko-001"]
# 是否启用网址上下文,默认启用
URL_CONTEXT_ENABLED=false
URL_CONTEXT_MODELS=["gemini-2.5-pro","gemini-2.5-flash","gemini-2.5-flash-lite","gemini-2.0-flash","gemini-2.0-flash-live-001"]
TOOLS_CODE_EXECUTION_ENABLED=false
SHOW_SEARCH_LINK=true
SHOW_THINKING_PROCESS=true
BASE_URL=https://generativelanguage.googleapis.com/v1beta
MAX_FAILURES=10
MAX_RETRIES=3
CHECK_INTERVAL_HOURS=1
TIMEZONE=Asia/Shanghai
# 请求超时时间(秒)
TIME_OUT=300
# 代理服务器配置 (支持 http 和 socks5)
# 示例: PROXIES=["http://user:pass@host:port", "socks5://host:port"]
PROXIES=[]
# 对同一个API_KEY使用代理列表中固定的IP策略
PROXIES_USE_CONSISTENCY_HASH_BY_API_KEY=true
#########################image_generate 相关配置###########################
PAID_KEY=AIzaSyxxxxxxxxxxxxxxxxxxx
CREATE_IMAGE_MODEL=imagen-3.0-generate-002
UPLOAD_PROVIDER=smms
SMMS_SECRET_TOKEN=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
PICGO_API_KEY=xxxx
CLOUDFLARE_IMGBED_URL=https://xxxxxxx.pages.dev/upload
CLOUDFLARE_IMGBED_AUTH_CODE=xxxxxxxxx
CLOUDFLARE_IMGBED_UPLOAD_FOLDER=
##########################################################################
#########################stream_optimizer 相关配置########################
STREAM_OPTIMIZER_ENABLED=false
STREAM_MIN_DELAY=0.016
STREAM_MAX_DELAY=0.024
STREAM_SHORT_TEXT_THRESHOLD=10
STREAM_LONG_TEXT_THRESHOLD=50
STREAM_CHUNK_SIZE=5
##########################################################################
######################### 日志配置 #######################################
# 日志级别 (debug, info, warning, error, critical),默认为 info
LOG_LEVEL=info
# 是否开启自动删除错误日志
AUTO_DELETE_ERROR_LOGS_ENABLED=true
# 自动删除多少天前的错误日志 (1, 7, 30)
AUTO_DELETE_ERROR_LOGS_DAYS=7
# 是否开启自动删除请求日志
AUTO_DELETE_REQUEST_LOGS_ENABLED=false
# 自动删除多少天前的请求日志 (1, 7, 30)
AUTO_DELETE_REQUEST_LOGS_DAYS=30
##########################################################################
# 假流式配置 (Fake Streaming Configuration)
# 是否启用假流式输出
FAKE_STREAM_ENABLED=True
# 假流式发送空数据的间隔时间(秒)
FAKE_STREAM_EMPTY_DATA_INTERVAL_SECONDS=5
# 安全设置 (JSON 字符串格式)
# 注意:这里的示例值可能需要根据实际模型支持情况调整
SAFETY_SETTINGS=[{"category": "HARM_CATEGORY_HARASSMENT", "threshold": "OFF"}, {"category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "OFF"}, {"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", "threshold": "OFF"}, {"category": "HARM_CATEGORY_DANGEROUS_CONTENT", "threshold": "OFF"}, {"category": "HARM_CATEGORY_CIVIC_INTEGRITY", "threshold": "BLOCK_NONE"}]
URL_NORMALIZATION_ENABLED=false
# tts配置
TTS_MODEL=gemini-2.5-flash-preview-tts
TTS_VOICE_NAME=Zephyr
TTS_SPEED=normal
#########################Files API 相关配置########################
# 是否启用文件过期自动清理
FILES_CLEANUP_ENABLED=true
# 文件过期清理间隔(小时)
FILES_CLEANUP_INTERVAL_HOURS=1
# 是否启用用户文件隔离(每个用户只能看到自己上传的文件)
FILES_USER_ISOLATION_ENABLED=true
##########################################################################
EOF
cat << EOF | tee /tmp/gemini-cli-openai/.dev.vars.test
# Required: OAuth2 credentials JSON from Gemini CLI authentication
GCP_SERVICE_ACCOUNT=${GCP_SERVICE_ACCOUNT}
# Optional: Google Cloud Project ID (auto-discovered if not set)
GEMINI_PROJECT_ID=${GEMINI_PROJECT_ID}
# Optional: API key for authentication (if not set, API is public)
# When set, clients must include "Authorization: Bearer <your-api-key>" header
# Example: sk-1234567890abcdef1234567890abcdef
OPENAI_API_KEY=${REQUIRED_API_KEY}
EOF
${HOME}/crypt.sh decrypt ${HOME}/gemini/enc.dev.var.x -e GEMINI_AUTH_ENC_PASS -o /tmp/gemini-cli-openai/.dev.var.x
${HOME}/crypt.sh decrypt ${HOME}/gemini/enc.dev.var.j -e GEMINI_AUTH_ENC_PASS -o /tmp/gemini-cli-openai/.dev.var.j
${HOME}/crypt.sh decrypt ${HOME}/gemini/enc.dev.var.w -e GEMINI_AUTH_ENC_PASS -o /tmp/gemini-cli-openai/.dev.var.w
${HOME}/crypt.sh decrypt ${HOME}/gemini/enc.dev.var.l -e GEMINI_AUTH_ENC_PASS -o /tmp/gemini-cli-openai/.dev.var.l
cd /tmp/Gemini-CLI-2-API && npm install && node src/api-server.js &
cd /tmp/gemini-balance && uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload &
cd /tmp/gemini-cli-openai && npm i
cp /tmp/gemini-cli-openai/.dev.var.j /tmp/gemini-cli-openai/.dev.vars
echo "check /tmp/gemini-cli-openai/.dev.vars"
cat /tmp/gemini-cli-openai/.dev.vars
cd /tmp/gemini-cli-openai && npx wrangler dev --port 8881 --host 0.0.0.0 &
# cd /tmp/gemini-cli-openai && npm i && npx wrangler dev --port 8882 --host 0.0.0.0 --env j &
# cd /tmp/gemini-cli-openai && npm i && npx wrangler dev --port 8883 --host 0.0.0.0 --env w &
# cd /tmp/gemini-cli-openai && npm i && npx wrangler dev --port 8884 --host 0.0.0.0 --env l &
/tmp/caddy/caddy run --config ${HOME}/Caddyfile &
MAIN_PID=$!
# Wait for caddy process
wait $MAIN_PID