#!/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 </dev/null </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 已启动。"