167 lines
7.3 KiB
Bash
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 已启动。"
|