Files
JuYou/API/deploy/prepare_server.sh
2026-08-25 17:59:42 +08:00

167 lines
7.3 KiB
Bash

#!/usr/bin/env bash
set -euo pipefail
# Prepare the Linux host and install the uploaded environment file outside the app directory.
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
API_ROOT="$(realpath "${1:-$SCRIPT_DIR/..}")"
SOURCE_ENV="${JCF_ENV_SOURCE:-}"
SYSTEM_ENV_DIR="/etc/juyou_ai"
SYSTEM_ENV_FILE="$SYSTEM_ENV_DIR/juyou_ai.env"
ASR_CACHE_ROOT="/var/cache/juyou_ai"
ASR_HF_HOME="$ASR_CACHE_ROOT/huggingface"
ASR_MODEL_PATH="$API_ROOT/models/faster-whisper-small"
info(){ printf ' -> %s\n' "$*"; }
fail(){ printf ' ERR %s\n' "$*" >&2; exit 1; }
[ "$(id -u)" -eq 0 ] || fail "请使用 sudo 或 root 执行。"
info "安装 PostgreSQL、Redis、Nginx、FFmpeg 与基础工具..."
if command -v apt-get >/dev/null 2>&1; then
apt-get update -qq
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq sudo nginx postgresql postgresql-contrib redis-server ffmpeg curl socat ca-certificates openssl logrotate python3 python3-pip
elif command -v dnf >/dev/null 2>&1; then
dnf install -y sudo nginx postgresql-server postgresql-contrib redis ffmpeg curl socat ca-certificates openssl logrotate python3 python3-pip
elif command -v yum >/dev/null 2>&1; then
yum install -y sudo nginx postgresql-server postgresql-contrib redis ffmpeg curl socat ca-certificates openssl logrotate python3 python3-pip
else
fail "不支持当前 Linux 发行版:未找到 apt-get、dnf 或 yum。"
fi
if ! python3 -c 'import faster_whisper' >/dev/null 2>&1; then
PIP_ARGS=(install --disable-pip-version-check)
if python3 -m pip install --help 2>/dev/null | grep -q -- '--break-system-packages'; then
PIP_ARGS+=(--break-system-packages)
fi
python3 -m pip "${PIP_ARGS[@]}" 'faster-whisper==1.2.1'
fi
python3 -c 'import faster_whisper' >/dev/null 2>&1 || fail "faster-whisper installation failed."
if command -v postgresql-setup >/dev/null 2>&1 && [ ! -f /var/lib/pgsql/data/PG_VERSION ]; then
info "初始化 PostgreSQL 数据目录..."
postgresql-setup --initdb
fi
systemctl enable --now postgresql >/dev/null 2>&1 || fail "PostgreSQL 启动失败。"
if systemctl list-unit-files redis-server.service >/dev/null 2>&1; then
systemctl enable --now redis-server >/dev/null
else
systemctl enable --now redis >/dev/null || fail "Redis 启动失败。"
fi
systemctl enable --now nginx >/dev/null
sudo -u postgres pg_isready >/dev/null || fail "PostgreSQL 未就绪。"
redis-cli ping | grep -q '^PONG$' || fail "Redis 未就绪。"
install -d -m 700 -o root -g root "$SYSTEM_ENV_DIR"
if [ -n "$SOURCE_ENV" ] && [ -f "$SOURCE_ENV" ]; then
install -m 600 -o root -g root "$SOURCE_ENV" "$SYSTEM_ENV_FILE"
info "已将上传的环境配置安装到 $SYSTEM_ENV_FILE。"
elif [ -f "$SYSTEM_ENV_FILE" ]; then
info "未上传新配置,继续使用 $SYSTEM_ENV_FILE。"
else
fail "缺少上传的 API/.env,且服务器不存在 $SYSTEM_ENV_FILE。"
fi
# The application directory must not retain deployment secrets.
rm -f "$API_ROOT/.env"
env_value() {
local key="$1"
sed -n -E "s/^${key}=//p" "$SYSTEM_ENV_FILE" | head -n 1 | tr -d '\r' | sed -E 's/^"//;s/"$//'
}
set_env() {
local key="$1" value="$2" temporary
temporary="$(mktemp "$SYSTEM_ENV_DIR/env.XXXXXX")"
awk -v key="$key" -v value="$value" '
BEGIN { found=0 }
index($0, key "=")==1 { print key "=" value; found=1; next }
{ print }
END { if (!found) print key "=" value }
' "$SYSTEM_ENV_FILE" >"$temporary"
install -m 600 -o root -g root "$temporary" "$SYSTEM_ENV_FILE"
rm -f "$temporary"
}
require_env() {
local key="$1" value
value="$(env_value "$key")"
[ -n "$value" ] || fail "$key 未配置。"
[[ "$value" != *replace-with* && "$value" != *example.com* && "$value" != *change-me* && "$value" != "juchuang_dev" ]] || fail "$key 仍是示例或开发环境值。"
}
APP_PORT="8123"
DOMAIN_INPUT="${SAVED_DOMAIN_INPUT:-_}"
if ! [[ "$APP_PORT" =~ ^[0-9]+$ ]] || [ "$APP_PORT" -lt 1 ] || [ "$APP_PORT" -gt 65535 ]; then
fail "API 端口无效:$APP_PORT"
fi
CORS_ORIGINS=""
if [ "$DOMAIN_INPUT" != "_" ]; then
for domain in $DOMAIN_INPUT; do
[[ "$domain" =~ ^([A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?\.)+[A-Za-z]{2,63}$ ]] || fail "域名格式无效:$domain"
[ -n "$CORS_ORIGINS" ] && CORS_ORIGINS+=","
CORS_ORIGINS+="https://$domain,http://$domain"
done
fi
# Infrastructure values are normalized for native Linux services; application secrets stay unchanged.
set_env APP_ENV production
set_env DEBUG false
set_env SERVER_ADDRESS "127.0.0.1:$APP_PORT"
set_env DATABASE_HOST 127.0.0.1
set_env DATABASE_PORT 5432
set_env DATABASE_URL ""
set_env REDIS_HOST 127.0.0.1
set_env REDIS_PORT 6379
set_env HTTP_PROXY ""
set_env HTTPS_PROXY ""
set_env ALL_PROXY ""
set_env http_proxy ""
set_env https_proxy ""
set_env all_proxy ""
set_env CORS_ORIGINS "$CORS_ORIGINS"
set_env FFMPEG_PATH ffmpeg
set_env FFPROBE_PATH ffprobe
set_env PYTHON_PATH python3
set_env ASR_SCRIPT_PATH "$API_ROOT/workers/transcribe.py"
set_env JCF_ASR_CACHE_DIR "$ASR_CACHE_ROOT"
set_env HF_HOME "$ASR_HF_HOME"
set_env FASTER_WHISPER_MODEL_PATH "$ASR_MODEL_PATH"
[ -f "$API_ROOT/workers/transcribe.py" ] || fail "Missing ASR script: $API_ROOT/workers/transcribe.py"
for model_file in config.json model.bin tokenizer.json vocabulary.txt; do
[ -f "$ASR_MODEL_PATH/$model_file" ] || fail "离线 Whisper 模型不完整,缺少:$ASR_MODEL_PATH/$model_file。请重新执行 push.bat 上传模型。"
done
install -d -m 750 -o www-data -g www-data "$ASR_CACHE_ROOT" "$ASR_HF_HOME"
chown -R www-data:www-data "$ASR_MODEL_PATH"
info "使用离线 faster-whisper 模型:$ASR_MODEL_PATH。"
for key in DATABASE_NAME DATABASE_USER DATABASE_PASSWORD JWT_SECRET_KEY ADMIN_BOOTSTRAP_PASSWORD CONFIG_ENCRYPTION_KEY COS_SECRET_ID COS_SECRET_KEY COS_BUCKET COS_REGION COS_ENDPOINT COS_PUBLIC_BASE_URL; do
require_env "$key"
done
DB_NAME="$(env_value DATABASE_NAME)"
DB_USER="$(env_value DATABASE_USER)"
DB_PASSWORD="$(env_value DATABASE_PASSWORD)"
[[ "$DB_NAME" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]] || fail "DATABASE_NAME 只允许字母、数字和下划线。"
[[ "$DB_USER" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]] || fail "DATABASE_USER 只允许字母、数字和下划线。"
if ! sudo -u postgres psql -tAc "SELECT 1 FROM pg_roles WHERE rolname='$DB_USER'" | grep -q 1; then
sudo -u postgres psql -v ON_ERROR_STOP=1 --set=password="$DB_PASSWORD" >/dev/null <<SQL
SET password_encryption = 'scram-sha-256';
CREATE ROLE "$DB_USER" LOGIN PASSWORD :'password';
SQL
else
sudo -u postgres psql -v ON_ERROR_STOP=1 --set=password="$DB_PASSWORD" >/dev/null <<SQL
SET password_encryption = 'scram-sha-256';
ALTER ROLE "$DB_USER" WITH LOGIN PASSWORD :'password';
SQL
fi
if ! sudo -u postgres psql -tAc "SELECT 1 FROM pg_database WHERE datname='$DB_NAME'" | grep -q 1; then
sudo -u postgres createdb -O "$DB_USER" "$DB_NAME"
fi
sudo -u postgres psql -v ON_ERROR_STOP=1 -d "$DB_NAME" -c "ALTER DATABASE \"$DB_NAME\" SET timezone TO 'Asia/Shanghai';" >/dev/null
HBA_FILE="$(sudo -u postgres psql -Atc 'SHOW hba_file')"
if ! grep -q '^# JCF local application access$' "$HBA_FILE"; then
sed -i "1i# JCF local application access\nhost all all 127.0.0.1/32 scram-sha-256\nhost all all ::1/128 scram-sha-256" "$HBA_FILE"
systemctl reload postgresql
fi
info "PostgreSQL 数据库与用户已初始化,Redis 已启动。"