Ship TLS nginx for backoffice, keep HTTP bootstrap until certs exist, proxy /monitoring to sentry-relay, and tunnel server/edge Sentry through Finland.
111 lines
4.1 KiB
Bash
Executable File
111 lines
4.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# Build and run admin on the VPS (idempotent).
|
||
# Expects app sources in APP_DIR (CI rsync) and a filled APP_DIR/.env.
|
||
set -euo pipefail
|
||
|
||
APP_DIR="${APP_DIR:-/opt/ghabilee-admin}"
|
||
SRC_DIR="${SRC_DIR:-$APP_DIR/src}"
|
||
IMAGE_TAG="${IMAGE_TAG:-ghabilee-admin:local}"
|
||
|
||
cd "$APP_DIR"
|
||
|
||
if [ ! -f "$APP_DIR/.env" ]; then
|
||
echo "Missing $APP_DIR/.env — copy from .env.example and fill production values." >&2
|
||
exit 2
|
||
fi
|
||
|
||
if [ ! -f "$SRC_DIR/Dockerfile" ]; then
|
||
echo "Missing $SRC_DIR/Dockerfile — rsync the repo before deploy." >&2
|
||
exit 2
|
||
fi
|
||
|
||
install -m 0644 "$SRC_DIR/deploy/docker-compose.production.yml" "$APP_DIR/docker-compose.yml"
|
||
if [ -f "$SRC_DIR/deploy/nginx/backoffice.conf" ]; then
|
||
if [ -s /etc/letsencrypt/live/backoffice.ghabilee.ir/fullchain.pem ]; then
|
||
install -m 0644 "$SRC_DIR/deploy/nginx/backoffice.conf" /etc/nginx/sites-available/backoffice
|
||
elif [ -f "$SRC_DIR/deploy/nginx/backoffice-http-bootstrap.conf" ]; then
|
||
install -m 0644 "$SRC_DIR/deploy/nginx/backoffice-http-bootstrap.conf" /etc/nginx/sites-available/backoffice
|
||
else
|
||
install -m 0644 "$SRC_DIR/deploy/nginx/backoffice.conf" /etc/nginx/sites-available/backoffice
|
||
fi
|
||
ln -sfn /etc/nginx/sites-available/backoffice /etc/nginx/sites-enabled/backoffice
|
||
nginx -t
|
||
systemctl reload nginx
|
||
fi
|
||
|
||
# shellcheck disable=SC1091
|
||
set -a
|
||
# shellcheck disable=SC1090
|
||
source "$APP_DIR/.env"
|
||
set +a
|
||
|
||
env_or_empty() {
|
||
local key="$1"
|
||
printf '%s' "${!key-}"
|
||
}
|
||
|
||
sanitize_api_proxy_target() {
|
||
local target
|
||
target="$(env_or_empty API_PROXY_TARGET)"
|
||
case "$target" in
|
||
*127.0.0.1*|*localhost*|*'::1'*)
|
||
echo "Warning: refusing loopback API_PROXY_TARGET for production image build" >&2
|
||
printf ''
|
||
;;
|
||
*)
|
||
printf '%s' "$target"
|
||
;;
|
||
esac
|
||
}
|
||
|
||
BUILD_ARGS=(
|
||
--build-arg "NEXT_PUBLIC_API_URL=$(env_or_empty NEXT_PUBLIC_API_URL)"
|
||
--build-arg "NEXT_PUBLIC_FILE_SERVER_URL=$(env_or_empty NEXT_PUBLIC_FILE_SERVER_URL)"
|
||
--build-arg "MAP_API_KEY=$(env_or_empty MAP_API_KEY)"
|
||
--build-arg "NEXT_PUBLIC_MAP_API_KEY=$(env_or_empty NEXT_PUBLIC_MAP_API_KEY)"
|
||
--build-arg "NEXT_PUBLIC_VAPID_PUBLIC_KEY=$(env_or_empty NEXT_PUBLIC_VAPID_PUBLIC_KEY)"
|
||
--build-arg "NEXT_PUBLIC_BASE_PATH=$(env_or_empty NEXT_PUBLIC_BASE_PATH)"
|
||
--build-arg "NEXT_PUBLIC_SITE_URL=$(env_or_empty NEXT_PUBLIC_SITE_URL)"
|
||
--build-arg "NEXT_PUBLIC_OBSERVABILITY_ENDPOINT=$(env_or_empty NEXT_PUBLIC_OBSERVABILITY_ENDPOINT)"
|
||
--build-arg "NEXT_PUBLIC_SENTRY_DSN=$(env_or_empty NEXT_PUBLIC_SENTRY_DSN)"
|
||
--build-arg "NEXT_PUBLIC_SENTRY_ENVIRONMENT=$(env_or_empty NEXT_PUBLIC_SENTRY_ENVIRONMENT)"
|
||
--build-arg "NEXT_PUBLIC_SENTRY_TRACES_SAMPLE_RATE=$(env_or_empty NEXT_PUBLIC_SENTRY_TRACES_SAMPLE_RATE)"
|
||
--build-arg "NEXT_PUBLIC_ARCAPTCHA_SITE_KEY=$(env_or_empty NEXT_PUBLIC_ARCAPTCHA_SITE_KEY)"
|
||
--build-arg "SENTRY_AUTH_TOKEN=$(env_or_empty SENTRY_AUTH_TOKEN)"
|
||
--build-arg "SENTRY_ORG=$(env_or_empty SENTRY_ORG)"
|
||
--build-arg "SENTRY_PROJECT=$(env_or_empty SENTRY_PROJECT)"
|
||
--build-arg "API_PROXY_TARGET=$(sanitize_api_proxy_target)"
|
||
)
|
||
|
||
echo "Building ${IMAGE_TAG} on VPS (build arguments redacted)"
|
||
docker build \
|
||
--platform linux/amd64 \
|
||
-t "$IMAGE_TAG" \
|
||
"${BUILD_ARGS[@]}" \
|
||
"$SRC_DIR"
|
||
|
||
ADMIN_IMAGE="$IMAGE_TAG" docker compose -f "$APP_DIR/docker-compose.yml" up -d --no-deps ghabilee-admin
|
||
|
||
for attempt in $(seq 1 36); do
|
||
health="$(docker inspect --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}' ghabilee-admin 2>/dev/null || echo missing)"
|
||
echo "Admin health ${attempt}/36: ${health}"
|
||
if [ "$health" = "healthy" ]; then
|
||
# فقط tagهای قدیمی admin؛ prune -af کش/لایههای قابلاستفاده مجدد را پاک میکند
|
||
docker images --format '{{.Repository}}:{{.Tag}}' \
|
||
| awk -v keep="$IMAGE_TAG" '$0 ~ /^ghabilee-admin:/ && $0 != keep { print }' \
|
||
| while IFS= read -r old; do
|
||
docker rmi "$old" >/dev/null 2>&1 || true
|
||
done
|
||
docker image prune -f >/dev/null 2>&1 || true
|
||
echo "Deploy OK"
|
||
exit 0
|
||
fi
|
||
case "$health" in
|
||
unhealthy|exited|dead|missing) exit 1 ;;
|
||
esac
|
||
sleep 5
|
||
done
|
||
|
||
echo "Timed out waiting for healthy admin container" >&2
|
||
exit 1
|