Files
arthur_chen f69efb67ad Initial commit: abyssVpn project
- Tauri desktop app (apps/desktop)
- Python Django API service (services/api)
- Deployment scripts and docs
2026-05-15 09:06:10 +08:00

158 lines
4.9 KiB
Bash

#!/usr/bin/env bash
set -euo pipefail
APP_DIR="${REMOTE_APP_DIR:-/opt/abyssinfo-home-vpn}"
API_DIR="${APP_DIR}/api"
VENV_DIR="${APP_DIR}/.venv-api"
ZIP_FILE="${API_ZIP_FILE:-/tmp/abyssinfo-api.zip}"
API_DOMAIN="${API_DOMAIN:-vpn-api.nm.101-96-214-81.sslip.io}"
API_LEGACY_DOMAIN="${API_LEGACY_DOMAIN:-vpn-api.nm.101-96-214-81.nip.io}"
API_PORT="${API_PORT:-8000}"
NETMAKER_DOMAIN="${NETMAKER_BASE_DOMAIN:-nm.101-96-214-81.nip.io}"
echo "[AbyssInfo] Deploying Django API"
echo "[AbyssInfo] API domain: ${API_DOMAIN}"
if [[ "$(id -u)" != "0" ]]; then
echo "[AbyssInfo] Please run as root." >&2
exit 1
fi
if [[ ! -f "${ZIP_FILE}" ]]; then
echo "[AbyssInfo] Missing ${ZIP_FILE}" >&2
exit 1
fi
echo "[AbyssInfo] Installing Python runtime ..."
dnf install -y python3.12 python3.12-pip unzip
mkdir -p "${API_DIR}"
PREVIOUS_ENV="/tmp/abyssinfo-api.previous.env"
if [[ -f "${API_DIR}/.env" ]]; then
cp "${API_DIR}/.env" "${PREVIOUS_ENV}"
else
rm -f "${PREVIOUS_ENV}"
fi
find "${API_DIR}" -mindepth 1 \
! -name db.sqlite3 \
! -path "${API_DIR}/db.sqlite3" \
-exec rm -rf {} +
echo "[AbyssInfo] Extracting API package ..."
set +e
unzip -q "${ZIP_FILE}" -d "${API_DIR}"
UNZIP_EXIT=$?
set -e
if [[ "${UNZIP_EXIT}" -gt 1 ]]; then
echo "[AbyssInfo] Failed to unzip API package. Exit code ${UNZIP_EXIT}" >&2
exit "${UNZIP_EXIT}"
fi
if [[ ! -f "${API_DIR}/.env" ]]; then
echo "[AbyssInfo] Missing ${API_DIR}/.env" >&2
exit 1
fi
echo "[AbyssInfo] Normalizing production env ..."
python3.12 - <<PY
from pathlib import Path
import secrets
env_path = Path("${API_DIR}/.env")
previous_env_path = Path("${PREVIOUS_ENV}")
values = {}
for candidate in [previous_env_path, env_path]:
if not candidate.exists():
continue
for line in candidate.read_text(encoding="utf-8").splitlines():
if not line or line.startswith("#") or "=" not in line:
continue
key, value = line.split("=", 1)
key = key.lstrip("\\ufeff")
if value or key not in values:
values[key] = value
if not values.get("DJANGO_SECRET_KEY"):
values["DJANGO_SECRET_KEY"] = secrets.token_urlsafe(48)
if not values.get("JWT_SECRET"):
values["JWT_SECRET"] = secrets.token_urlsafe(48)
values["DJANGO_DEBUG"] = "false"
values["DJANGO_ALLOWED_HOSTS"] = "127.0.0.1,localhost,${API_DOMAIN},${API_LEGACY_DOMAIN}"
values["CORS_ALLOWED_ORIGINS"] = "http://localhost:5173,http://127.0.0.1:5173,http://tauri.localhost,https://tauri.localhost,tauri://localhost,https://${API_DOMAIN}"
values["FEISHU_REDIRECT_URI"] = "https://${API_DOMAIN}/auth/feishu/callback"
ordered = [
"DJANGO_SECRET_KEY", "DJANGO_DEBUG", "DJANGO_ALLOWED_HOSTS", "CORS_ALLOWED_ORIGINS",
"FEISHU_APP_ID", "FEISHU_APP_SECRET", "FEISHU_REDIRECT_URI", "FEISHU_EVENT_VERIFICATION_TOKEN", "TAURI_DEEP_LINK_SCHEME",
"JWT_SECRET", "JWT_EXPIRES_SECONDS", "OAUTH_STATE_EXPIRES_SECONDS", "LOGIN_CODE_EXPIRES_SECONDS",
"NETMAKER_BASE_URL", "NETMAKER_API_TOKEN", "NETMAKER_NETWORK_ID",
"NETMAKER_ENROLLMENT_TAGS", "NETMAKER_ENROLLMENT_TTL_SECONDS",
]
env_path.write_text("\\n".join(f"{key}={values.get(key, '')}" for key in ordered) + "\\n", encoding="utf-8")
PY
echo "[AbyssInfo] Creating virtual environment ..."
if [[ ! -x "${VENV_DIR}/bin/python" ]]; then
python3.12 -m venv "${VENV_DIR}"
fi
"${VENV_DIR}/bin/python" -m pip install --upgrade pip
"${VENV_DIR}/bin/pip" install -r "${API_DIR}/requirements.txt"
echo "[AbyssInfo] Running Django checks and migrations ..."
cd "${API_DIR}"
"${VENV_DIR}/bin/python" manage.py check
"${VENV_DIR}/bin/python" manage.py migrate --noinput
"${VENV_DIR}/bin/python" manage.py collectstatic --noinput
echo "[AbyssInfo] Installing systemd service ..."
cat >/etc/systemd/system/abyssinfo-api.service <<SERVICE
[Unit]
Description=AbyssInfo Home VPN Django API
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
WorkingDirectory=${API_DIR}
Environment=PYTHONUNBUFFERED=1
ExecStart=${VENV_DIR}/bin/gunicorn config.wsgi:application --bind 0.0.0.0:${API_PORT} --workers 2 --timeout 60
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target
SERVICE
systemctl daemon-reload
systemctl enable --now abyssinfo-api
systemctl restart abyssinfo-api
echo "[AbyssInfo] Updating Netmaker Caddy reverse proxy ..."
CADDYFILE="/root/netmaker/Caddyfile"
if [[ ! -f "${CADDYFILE}" ]]; then
echo "[AbyssInfo] Missing ${CADDYFILE}" >&2
exit 1
fi
if ! grep -q "https://${API_DOMAIN}" "${CADDYFILE}"; then
cat >>"${CADDYFILE}" <<CADDY
# AbyssInfo Django API
https://${API_DOMAIN} {
reverse_proxy http://host.docker.internal:${API_PORT}
}
CADDY
fi
(cd /root/netmaker && docker compose exec -T caddy caddy reload --config /etc/caddy/Caddyfile)
echo "[AbyssInfo] Verifying API health ..."
curl -fsS "http://127.0.0.1:${API_PORT}/health/" >/dev/null
curl -kfsS "https://${API_DOMAIN}/health/" >/dev/null
echo "[AbyssInfo] Django API deployed:"
echo " https://${API_DOMAIN}/health/"