TalkingBot/deploy.sh

144 lines
5.2 KiB
Bash
Executable File

#!/usr/bin/env bash
# Sync this repo to the talkingbooklet server and reload PM2.
# Existing .env files on the server are kept. A chat-engine token is
# created once if the API does not already have one.
set -euo pipefail
ROOT="$(cd "$(dirname "$0")" && pwd)"
KEY="${DEPLOY_KEY:-$HOME/Documents/Keys/VirtualEngage.pem}"
HOST="${DEPLOY_HOST:-ubuntu@15.207.15.52}"
DEST="${DEPLOY_PATH:-/var/www/talkingbooklet}"
SITE="${DEPLOY_SITE:-https://talkingbooklet.virtualengage.in}"
if [[ ! -f "$KEY" ]]; then
echo "SSH key not found: $KEY" >&2
exit 1
fi
rsync -rlz --delete --omit-dir-times \
--no-perms --no-owner --no-group \
-e "ssh -i ${KEY} -o StrictHostKeyChecking=accept-new" \
--exclude node_modules \
--exclude venv \
--exclude .env \
--exclude frontend/dist \
--exclude .git \
--exclude .DS_Store \
--exclude __pycache__ \
--exclude .pytest_cache \
--exclude '*.pem' \
"${ROOT}/" "${HOST}:${DEST}/"
ENV_KEYS="$(mktemp)"
python3 - "$ROOT" <<'PY' > "$ENV_KEYS"
import pathlib, sys
root = pathlib.Path(sys.argv[1])
wanted = [
"BEDROCK_MODEL_CHEAP", "BEDROCK_MODEL_COUNT", "BEDROCK_MODEL_EMBED", "EMBED_DIMENSIONS",
"OPENSEARCH_INDEX_ALIAS", "OPENSEARCH_INDEX_VERSION", "INGEST_VERSION",
"MODEL_FULL_CONTEXT", "MODEL_HYBRID", "MODEL_SUMMARIZE_HISTORY", "MODEL_JUDGE",
"AUTO_FULL_CONTEXT_MAX_TOKENS", "AUTO_PREMIUM_PLANS",
"AGENT_MAX_ITERATIONS", "AGENT_MAX_TOOL_CALLS", "AGENT_TURN_TIMEOUT_MS",
"MAX_ANSWER_TOKENS_VOICE", "MAX_ANSWER_TOKENS_CHAT",
"CACHE_TTL", "CACHE_KEEPALIVE", "CACHE_KEEPALIVE_MAX", "CACHE_MIN_TOKENS",
"PERSONA", "HISTORY_VERBATIM_TURNS", "HISTORY_TOKEN_BUDGET",
"TOOL_RESULT_MAX_CHARS", "CHAPTER_RETURN_TOKEN_CAP", "RERANK_MODEL", "PRICE_TABLE_JSON",
]
found = {}
for name in ("ingestion/.env", "chat-engine/.env"):
path = root / name
if not path.exists():
continue
for line in path.read_text().splitlines():
if not line or line.startswith("#") or "=" not in line:
continue
key, value = line.split("=", 1)
if key in wanted and value != "":
found[key] = value
sys.stdout.write("".join(f"{key}={value}\n" for key, value in found.items()))
PY
ssh -i "$KEY" -o StrictHostKeyChecking=accept-new "$HOST" "python3 ${DEST}/deploy/merge_env.py ${DEST}/chat-engine/.env" < "$ENV_KEYS"
ssh -i "$KEY" -o StrictHostKeyChecking=accept-new "$HOST" "python3 ${DEST}/deploy/merge_env.py ${DEST}/ingestion/.env" < "$ENV_KEYS"
rm -f "$ENV_KEYS"
ssh -i "$KEY" -o StrictHostKeyChecking=accept-new "$HOST" \
"DEST=$(printf '%q' "$DEST") SITE=$(printf '%q' "$SITE") bash -s" <<'REMOTE'
set -euo pipefail
cd "$DEST"
npm install
(cd backend && npm install)
(cd frontend && npm install && npm run build)
./ai_agent/venv/bin/pip install -r ai_agent/requirements.txt
python3 - <<'PY'
import os, pathlib, re, secrets
root = pathlib.Path(os.environ["DEST"])
backend = (root / "backend" / ".env").read_text()
match = re.search(r"^CHAT_ENGINE_TOKEN=(.*)$", backend, re.M)
token = match.group(1).strip() if match and match.group(1).strip() else secrets.token_hex(32)
def upsert(text, key, value):
line = f"{key}={value}"
pattern = re.compile("^" + re.escape(key) + "=.*$", re.M)
if pattern.search(text):
return pattern.sub(line, text, count=1)
return text.rstrip() + "\n" + line + "\n"
site = os.environ["SITE"]
backend = upsert(backend, "CHAT_ENGINE_URL", "http://127.0.0.1:3001")
backend = upsert(backend, "CHAT_ENGINE_TOKEN", token)
backend = upsert(backend, "FRONTEND_ORIGIN", site)
backend = upsert(backend, "HOST", "127.0.0.1")
(root / "backend" / ".env").write_text(backend)
engine_path = root / "chat-engine" / ".env"
engine = engine_path.read_text() if engine_path.exists() else ""
engine = upsert(engine, "CHAT_ENGINE_HOST", "127.0.0.1")
engine = upsert(engine, "CHAT_ENGINE_PORT", "3001")
engine = upsert(engine, "CHAT_ENGINE_TOKEN", token)
engine_path.write_text(engine)
agent_path = root / "ai_agent" / ".env"
agent = agent_path.read_text() if agent_path.exists() else ""
agent = upsert(agent, "CHAT_ENGINE_URL", "http://127.0.0.1:3001")
agent = upsert(agent, "CHAT_ENGINE_TOKEN", token)
agent_path.write_text(agent)
print("env linked")
PY
sudo python3 - <<'PY'
from pathlib import Path
path = Path("/etc/nginx/sites-enabled/virtualpr")
text = path.read_text()
needle = "proxy_pass http://127.0.0.1:3000;"
extra = """proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 300s;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;"""
idx = text.find(needle)
nearby = text[idx:idx + 400] if idx >= 0 else ""
if idx >= 0 and "proxy_buffering off" not in nearby:
path.write_text(text.replace(needle, extra, 1))
print("nginx updated")
else:
print("nginx already streams /api")
PY
sudo nginx -t
sudo systemctl reload nginx
pm2 startOrReload "$DEST/ecosystem.config.cjs" --only talkingbooklet-engine
pm2 startOrReload "$DEST/ecosystem.config.cjs" --only talkingbooklet-api
pm2 startOrReload "$DEST/ecosystem.config.cjs" --only talkingbooklet-agent
pm2 save
sleep 2
curl -fsS http://127.0.0.1:3001/health
echo
curl -fsS http://127.0.0.1:3000/
echo
REMOTE