169 lines
5.7 KiB
Bash
169 lines
5.7 KiB
Bash
#!/usr/bin/env bash
|
|
set -Eeuo pipefail
|
|
|
|
APP_DIR="${APP_DIR:-/opt/cmpp-platform}"
|
|
ENV_FILE="${ENV_FILE:-/etc/cmpp-platform/cmpp-platform.env}"
|
|
ADMIN_CREDENTIAL_FILE="${ADMIN_CREDENTIAL_FILE:-/root/cmpp-platform-admin.txt}"
|
|
|
|
if [[ "$(id -u)" -ne 0 ]]; then
|
|
echo "Run as root." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ ! -f "$ENV_FILE" ]]; then
|
|
echo "Missing environment file: $ENV_FILE" >&2
|
|
exit 1
|
|
fi
|
|
|
|
set -a
|
|
source "$ENV_FILE"
|
|
set +a
|
|
|
|
if [[ "${API_ENABLE_SEND_WORKER:-}" != "true" ]]; then
|
|
echo "API_ENABLE_SEND_WORKER=true is required in $ENV_FILE; refusing to deploy with SMS sending disabled." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ ! "${API_SEND_WORKER_CONCURRENCY:-}" =~ ^[1-9][0-9]*$ ]]; then
|
|
echo "API_SEND_WORKER_CONCURRENCY must be a positive integer in $ENV_FILE." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "${CMPP_INBOUND_FAST_PATH_ENABLED:-}" != "true" || "${CMPP_INBOUND_WORKFLOW_WORKER_ENABLED:-}" != "true" ]]; then
|
|
echo "CMPP_INBOUND_FAST_PATH_ENABLED=true and CMPP_INBOUND_WORKFLOW_WORKER_ENABLED=true are required in $ENV_FILE." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ ! "${API_INBOUND_WORKFLOW_CONCURRENCY:-}" =~ ^[1-9][0-9]*$ ]]; then
|
|
echo "API_INBOUND_WORKFLOW_CONCURRENCY must be a positive integer in $ENV_FILE." >&2
|
|
exit 1
|
|
fi
|
|
if [[ ! "${API_DB_POOL_MAX:-}" =~ ^[1-9][0-9]*$ || ! "${API_WORKER_DB_POOL_MAX:-}" =~ ^[1-9][0-9]*$ ]]; then
|
|
echo "API_DB_POOL_MAX and API_WORKER_DB_POOL_MAX must be positive integers in $ENV_FILE." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ -z "${CMPP_PUBLIC_HOST:-}" || ! "${CMPP_PUBLIC_PORT:-}" =~ ^[1-9][0-9]*$ ]]; then
|
|
echo "CMPP_PUBLIC_HOST and a positive CMPP_PUBLIC_PORT are required in $ENV_FILE; these are the customer-facing CMPP endpoint." >&2
|
|
exit 1
|
|
fi
|
|
|
|
cd "$APP_DIR"
|
|
|
|
echo "[deploy] Installing dependencies"
|
|
npm ci --include=dev
|
|
npm --prefix api ci --include=dev
|
|
|
|
echo "[deploy] Verifying dependency security mitigations"
|
|
npm run security:verify
|
|
npm run deploy:verify
|
|
|
|
echo "[deploy] Generating Prisma client and applying migrations"
|
|
npm --prefix api run prisma:generate
|
|
npm --prefix api run prisma:migrate:deploy
|
|
|
|
echo "[deploy] Building frontend, API and gateway"
|
|
rm -rf api/dist api/tsconfig.build.tsbuildinfo "$APP_DIR/dist/cmpp-gateway"
|
|
npm run build
|
|
npm --prefix api run build
|
|
(cd gateway && GOPROXY="${GOPROXY:-https://goproxy.cn,direct}" /usr/local/bin/go build -o "$APP_DIR/dist/cmpp-gateway" ./cmd/gateway)
|
|
(cd gateway && GOPROXY="${GOPROXY:-https://goproxy.cn,direct}" /usr/local/bin/go build -o "$APP_DIR/dist/cmpp-security-agent" ./cmd/security-agent)
|
|
chmod 755 "$APP_DIR/dist" "$APP_DIR/dist/assets"
|
|
find "$APP_DIR/dist/assets" -type d -exec chmod 755 {} +
|
|
find "$APP_DIR/dist/assets" -type f -exec chmod 644 {} +
|
|
chmod 644 "$APP_DIR/dist/index.html"
|
|
|
|
echo "[deploy] Ensuring production admin"
|
|
PROD_ADMIN_CREDENTIAL_FILE="$ADMIN_CREDENTIAL_FILE" node tools/deploy/ensure-production-admin.mjs
|
|
chmod 600 "$ADMIN_CREDENTIAL_FILE" || true
|
|
|
|
echo "[deploy] Ensuring runtime log directories"
|
|
install -d -m 0755 "$APP_DIR/logs/api" "$APP_DIR/logs/send-worker" "$APP_DIR/logs/gateway"
|
|
|
|
echo "[deploy] Installing split API and send-worker services"
|
|
node_bin="$(command -v node)"
|
|
install -d -m 0755 /etc/systemd/system/cmpp-api.service.d
|
|
cat >/etc/systemd/system/cmpp-api.service.d/process-role.conf <<'EOF'
|
|
[Service]
|
|
Environment=CMPP_PROCESS_ROLE=api
|
|
EOF
|
|
cat >/etc/systemd/system/cmpp-send-worker.service <<EOF
|
|
[Unit]
|
|
Description=CMPP durable send workflow worker
|
|
After=network.target postgresql.service redis.service cmpp-minio.service
|
|
|
|
[Service]
|
|
User=cmpp-api
|
|
Group=cmpp-security
|
|
WorkingDirectory=$APP_DIR/api
|
|
EnvironmentFile=$ENV_FILE
|
|
Environment=CMPP_PROCESS_ROLE=worker
|
|
ExecStart=$node_bin dist/send-worker.js
|
|
Restart=always
|
|
RestartSec=5
|
|
StandardOutput=append:$APP_DIR/logs/send-worker/stdout.log
|
|
StandardError=append:$APP_DIR/logs/send-worker/stderr.log
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
EOF
|
|
|
|
echo "[deploy] Installing restricted security boundary"
|
|
bash "$APP_DIR/tools/security/install-security-agent.sh"
|
|
|
|
echo "[deploy] Ensuring HTTP response compression"
|
|
compression_config=/etc/nginx/conf.d/cmpp-compression.conf
|
|
: >"$compression_config"
|
|
if grep -RqsE --exclude='cmpp-compression.conf' '^[[:space:]]*gzip[[:space:]]+on;' \
|
|
/etc/nginx/nginx.conf /etc/nginx/conf.d /etc/nginx/sites-enabled 2>/dev/null; then
|
|
echo "[deploy] Reusing existing Nginx gzip configuration"
|
|
else
|
|
cat >"$compression_config" <<'EOF'
|
|
gzip on;
|
|
gzip_vary on;
|
|
gzip_min_length 1024;
|
|
gzip_comp_level 5;
|
|
gzip_types application/json application/javascript text/javascript text/css text/plain text/csv image/svg+xml;
|
|
EOF
|
|
fi
|
|
nginx -t
|
|
|
|
echo "[deploy] Restarting services"
|
|
systemctl daemon-reload
|
|
if [[ "${OBJECT_STORAGE_DRIVER:-minio}" == "local" ]]; then
|
|
systemctl disable --now cmpp-minio 2>/dev/null || true
|
|
systemctl enable --now cmpp-api cmpp-send-worker cmpp-gateway nginx
|
|
else
|
|
systemctl enable --now cmpp-minio
|
|
systemctl restart cmpp-minio
|
|
systemctl enable --now cmpp-api cmpp-send-worker cmpp-gateway nginx
|
|
fi
|
|
systemctl restart cmpp-gateway
|
|
systemctl restart cmpp-security-agent
|
|
systemctl restart cmpp-api
|
|
systemctl restart cmpp-send-worker
|
|
systemctl restart nginx
|
|
|
|
echo "[deploy] Health checks"
|
|
wait_for_http() {
|
|
local name="$1"
|
|
local url="$2"
|
|
local attempt
|
|
# Nest初始化和活动通道恢复会随生产数据量波动;固定等待会把正常的慢启动误判为发布失败。
|
|
for attempt in $(seq 1 60); do
|
|
if curl -fsS "$url" >/dev/null 2>&1; then
|
|
return 0
|
|
fi
|
|
sleep 1
|
|
done
|
|
echo "$name did not become healthy within 60 seconds: $url" >&2
|
|
return 1
|
|
}
|
|
wait_for_http "API" "http://127.0.0.1:${API_PORT:-3000}/api/health"
|
|
wait_for_http "Send worker metrics" "http://127.0.0.1:${API_WORKER_METRICS_PORT:-9465}/metrics"
|
|
wait_for_http "Gateway" "http://127.0.0.1:8090/health"
|
|
redis-cli -h "${REDIS_HOST:-127.0.0.1}" -p "${REDIS_PORT:-6379}" ping >/dev/null
|
|
pg_isready -d "${DATABASE_URL%%\?*}" >/dev/null
|
|
|
|
echo "[deploy] Done"
|