Files
ai-agent-admin/ops/deploy-non-docker.sh
T

136 lines
3.6 KiB
Bash

#!/usr/bin/env bash
set -euo pipefail
APP_NAME="${APP_NAME:-ai-agent-admin}"
PROJECT_DIR="${PROJECT_DIR:-/opt/ai-agent-admin/current}"
BACKEND_DIR="${BACKEND_DIR:-${PROJECT_DIR}/backend-fastapi}"
WEB_DIST_DIR="${WEB_DIST_DIR:-${PROJECT_DIR}/web/apps/web-ele/dist}"
WEB_ROOT="${WEB_ROOT:-/var/www/ai-agent-admin}"
SERVICE_NAME="${SERVICE_NAME:-ai-agent-admin}"
PYTHON_BIN="${PYTHON_BIN:-python3}"
ENV_FILE="${ENV_FILE:-${BACKEND_DIR}/env/prod.env}"
BACKEND_PORT="${BACKEND_PORT:-8001}"
RUN_MIGRATIONS="${RUN_MIGRATIONS:-1}"
IMPORT_INIT_DATA="${IMPORT_INIT_DATA:-auto}"
SEED_MULTICA_AGENTS="${SEED_MULTICA_AGENTS:-1}"
BUILD_WEB="${BUILD_WEB:-0}"
VERIFY_RUNTIME="${VERIFY_RUNTIME:-1}"
VERIFY_BASE_URL="${VERIFY_BASE_URL:-http://127.0.0.1:${BACKEND_PORT}}"
VERIFY_USERNAME="${VERIFY_USERNAME:-superadmin}"
VERIFY_PASSWORD="${VERIFY_PASSWORD:-123456}"
VERIFY_ATTEMPTS="${VERIFY_ATTEMPTS:-6}"
VERIFY_SLEEP_SECONDS="${VERIFY_SLEEP_SECONDS:-5}"
log() {
printf '[%s] %s\n' "${APP_NAME}" "$*"
}
require_file() {
if [ ! -f "$1" ]; then
printf 'Missing required file: %s\n' "$1" >&2
exit 1
fi
}
require_dir() {
if [ ! -d "$1" ]; then
printf 'Missing required directory: %s\n' "$1" >&2
exit 1
fi
}
require_dir "${PROJECT_DIR}"
require_dir "${BACKEND_DIR}"
require_file "${ENV_FILE}"
if [ "${BUILD_WEB}" = "1" ]; then
log "building web-ele"
(cd "${PROJECT_DIR}/web" && pnpm --filter @vben/web-ele build)
fi
require_file "${WEB_DIST_DIR}/index.html"
log "publishing web dist to ${WEB_ROOT}"
install -d "${WEB_ROOT}"
rsync -a --delete "${WEB_DIST_DIR}/" "${WEB_ROOT}/"
log "preparing backend venv"
cd "${BACKEND_DIR}"
if [ ! -x ".venv/bin/python" ]; then
"${PYTHON_BIN}" -m venv .venv
fi
# shellcheck source=/dev/null
. .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -r requirements.txt
if [ "${RUN_MIGRATIONS}" = "1" ]; then
log "running alembic migrations"
ENV=prod python -m alembic upgrade head
fi
if [ "${IMPORT_INIT_DATA}" = "auto" ]; then
USER_COUNT="$(
ENV=prod python - <<'PY'
import asyncio
from sqlalchemy import text
from app.database import AsyncSessionLocal
async def main():
try:
async with AsyncSessionLocal() as db:
result = await db.execute(text("SELECT COUNT(*) FROM core_user"))
print(result.scalar() or 0)
except Exception:
print(0)
asyncio.run(main())
PY
)"
if [ "${USER_COUNT}" = "0" ] && [ -f "db_init.json" ]; then
log "importing initial data"
ENV=prod python scripts/loaddata.py db_init.json
fi
fi
if [ "${SEED_MULTICA_AGENTS}" = "1" ] && [ -f "scripts/seed_multica_org_agents.py" ]; then
log "seeding Multica organization agents"
ENV=prod python scripts/seed_multica_org_agents.py --apply
fi
log "restarting ${SERVICE_NAME}"
if command -v systemctl >/dev/null 2>&1; then
systemctl daemon-reload
systemctl restart "${SERVICE_NAME}"
fi
if command -v nginx >/dev/null 2>&1; then
log "reloading nginx"
nginx -t
if command -v systemctl >/dev/null 2>&1; then
systemctl reload nginx
else
nginx -s reload
fi
fi
if [ "${VERIFY_RUNTIME}" = "1" ]; then
log "verifying AI Agent Admin runtime"
for attempt in $(seq 1 "${VERIFY_ATTEMPTS}"); do
if ENV=prod python scripts/verify_ai_agent_admin.py \
--base-url "${VERIFY_BASE_URL}" \
--username "${VERIFY_USERNAME}" \
--password "${VERIFY_PASSWORD}"; then
break
fi
if [ "${attempt}" = "${VERIFY_ATTEMPTS}" ]; then
exit 1
fi
log "runtime verification failed, retrying in ${VERIFY_SLEEP_SECONDS}s (${attempt}/${VERIFY_ATTEMPTS})"
sleep "${VERIFY_SLEEP_SECONDS}"
done
fi
log "done: backend port ${BACKEND_PORT}, web root ${WEB_ROOT}"