#!/usr/bin/env bash set -Eeuo pipefail APP_NAME="${APP_NAME:-pc_nuxt}" BRANCH="${BRANCH:-}" NUXT_PUBLIC_API_BASE="${NUXT_PUBLIC_API_BASE:-https://api.aionline.cc}" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" export NUXT_PUBLIC_API_BASE cd "$SCRIPT_DIR" log() { printf '\n[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*" } require_command() { if ! command -v "$1" >/dev/null 2>&1; then log "Missing command: $1" exit 1 fi } validate_api_base() { node - "$NUXT_PUBLIC_API_BASE" <<'NODE' const value = process.argv[2] let url try { url = new URL(value) } catch { console.error(`[pc_nuxt] Invalid NUXT_PUBLIC_API_BASE: ${value || '(empty)'}`) process.exit(1) } const hostname = url.hostname.toLowerCase() const isPrivate = hostname === 'localhost' || hostname === '0.0.0.0' || /^127\./.test(hostname) || hostname === '[::1]' || hostname.endsWith('.local') || /^10\./.test(hostname) || /^192\.168\./.test(hostname) || /^169\.254\./.test(hostname) || /^172\.(1[6-9]|2\d|3[01])\./.test(hostname) if (!['http:', 'https:'].includes(url.protocol) || isPrivate) { console.error(`[pc_nuxt] Production API must be a public http(s) URL: ${value}`) process.exit(1) } NODE } validate_tls_security() { if [ "${NODE_TLS_REJECT_UNAUTHORIZED:-}" = "0" ]; then log "Refusing deployment: NODE_TLS_REJECT_UNAUTHORIZED=0 disables TLS certificate verification." exit 1 fi } resolve_branch() { if [ -z "$BRANCH" ]; then BRANCH="$(git rev-parse --abbrev-ref HEAD)" fi if [ "$BRANCH" = "HEAD" ]; then log "Cannot detect git branch. Please run with BRANCH=main ./restart.sh" exit 1 fi } sync_latest_code() { log "Force sync code from origin/$BRANCH" git fetch origin "$BRANCH" if [ -n "$(git status --porcelain)" ]; then log "Discard local working tree changes before deploy" git status --short fi git reset --hard "origin/$BRANCH" git clean -fd } install_dependencies() { log "Install dependencies" pnpm install --frozen-lockfile } build_app() { log "Build Nuxt app" pnpm run build if [ ! -f ".output/server/index.mjs" ]; then log "Build output missing: .output/server/index.mjs" exit 1 fi } restart_app() { log "Reload app with pm2 ecosystem config" mkdir -p logs APP_NAME="$APP_NAME" pm2 startOrReload ecosystem.config.cjs --env production --update-env pm2 save >/dev/null 2>&1 || true } main() { require_command git require_command node require_command pnpm require_command pm2 validate_tls_security validate_api_base resolve_branch log "Deploy start: $APP_NAME" log "Node: $(node -v 2>/dev/null || echo 'unknown')" log "pnpm: $(pnpm -v)" log "pm2: $(pm2 -v)" log "API: $NUXT_PUBLIC_API_BASE" sync_latest_code install_dependencies build_app restart_app log "Deploy done: $APP_NAME" } main "$@"