Files
MikrotikManager/deploy/run-beside-cdn-traefik.sh
DenozordecandCursor 0e9349e508
Docker images / prepare-release (push) Successful in 5s
Docker images / backend-image (push) Successful in 2m52s
Docker images / frontend-image (push) Successful in 2m23s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 38s
Docker images / publish-release (push) Successful in 8s
feat(auth): интегрировать SSO auth-portal
JWT на backend, handoff/callback на UI, RBAC mm:*, AUTH_* в compose.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-04 20:38:28 +07:00

186 lines
6.3 KiB
Bash

#!/usr/bin/env bash
# Run MikrotikManager beside an already-running CDNManager Traefik stack.
# Does NOT start a second Traefik — attaches frontend to network `cdnmanager`.
#
# Usage (on the VPS):
# curl -fsSL -o /tmp/run-beside-cdn-traefik.sh \
# https://git.shx.one/denozord/MikrotikManager/raw/branch/main/deploy/run-beside-cdn-traefik.sh
# chmod +x /tmp/run-beside-cdn-traefik.sh
# sudo MM_DOMAIN=mm.shnt.top /tmp/run-beside-cdn-traefik.sh
#
# Or copy this file to the server and run it.
#
# Env overrides:
# MM_DOMAIN=mm.shnt.top
# CORS_ORIGIN=https://mm.shnt.top
# MM_ROOT=/opt/mmapp
# MM_BACKEND_IMAGE_TAG=latest
# MM_FRONTEND_IMAGE_TAG=latest
# MM_UPDATER_IMAGE_TAG=latest
# REGISTRY_USERNAME=… REGISTRY_PASSWORD=… # optional private pull
set -euo pipefail
MM_DOMAIN="${MM_DOMAIN:-mm.shnt.top}"
CORS_ORIGIN="${CORS_ORIGIN:-https://${MM_DOMAIN}}"
MM_ROOT="${MM_ROOT:-/opt/mmapp}"
MM_BACKEND_IMAGE_TAG="${MM_BACKEND_IMAGE_TAG:-latest}"
MM_FRONTEND_IMAGE_TAG="${MM_FRONTEND_IMAGE_TAG:-latest}"
MM_UPDATER_IMAGE_TAG="${MM_UPDATER_IMAGE_TAG:-latest}"
REGISTRY="${REGISTRY:-git.shx.one}"
BACKEND_IMAGE="${REGISTRY}/denozord/mikrotikmanager-backend:${MM_BACKEND_IMAGE_TAG}"
FRONTEND_IMAGE="${REGISTRY}/denozord/mikrotikmanager-frontend:${MM_FRONTEND_IMAGE_TAG}"
UPDATER_IMAGE="${REGISTRY}/denozord/mikrotikmanager-updater:${MM_UPDATER_IMAGE_TAG}"
echo "==> Check CDNManager Traefik network"
if ! docker network inspect cdnmanager >/dev/null 2>&1; then
echo "ERROR: Docker network 'cdnmanager' not found." >&2
echo "Start CDNManager Traefik stack first (deploy/docker-compose.traefik.yml)." >&2
exit 1
fi
if ! docker inspect cdnmanager-traefik >/dev/null 2>&1; then
echo "WARN: container 'cdnmanager-traefik' not found — labels may not be routed." >&2
fi
echo "==> Prepare dirs under ${MM_ROOT}"
mkdir -p "${MM_ROOT}/data" "${MM_ROOT}/state" "${MM_ROOT}/updater"
TARGETS="${MM_ROOT}/updater/targets.json"
if [[ ! -f "${TARGETS}" ]]; then
cat >"${TARGETS}" <<'EOF'
{
"targets": [
{
"id": "backend",
"container_name": "mmapp-backend",
"image": "git.shx.one/denozord/mikrotikmanager-backend:latest",
"health": {
"type": "http",
"url": "http://backend:8000/health",
"expect_status": 200
}
},
{
"id": "frontend",
"container_name": "mmapp-frontend",
"image": "git.shx.one/denozord/mikrotikmanager-frontend:latest",
"health": {
"type": "http",
"url": "http://frontend:3000/dashboard",
"expect_status": 200
}
}
]
}
EOF
echo " wrote ${TARGETS}"
fi
echo "==> Ensure internal network mmapp"
docker network inspect mmapp >/dev/null 2>&1 || docker network create mmapp >/dev/null
if [[ -n "${REGISTRY_USERNAME:-}" && -n "${REGISTRY_PASSWORD:-}" ]]; then
echo "==> docker login ${REGISTRY}"
echo "${REGISTRY_PASSWORD}" | docker login "${REGISTRY}" -u "${REGISTRY_USERNAME}" --password-stdin
fi
echo "==> Pull images"
docker pull "${BACKEND_IMAGE}"
docker pull "${FRONTEND_IMAGE}"
docker pull "${UPDATER_IMAGE}"
stop_rm() {
local name="$1"
if docker inspect "${name}" >/dev/null 2>&1; then
docker stop "${name}" >/dev/null || true
docker rm "${name}" >/dev/null || true
fi
}
echo "==> Recreate mmapp-backend"
stop_rm mmapp-backend
docker run -d \
--name mmapp-backend \
--restart unless-stopped \
--network mmapp \
--network-alias backend \
-e NODE_ENV=production \
-e PORT=8000 \
-e DATABASE_PATH=/app/data/mikrotik.db \
-e "CORS_ORIGIN=${CORS_ORIGIN}" \
-e "AUTH_REQUIRED=${AUTH_REQUIRED:-true}" \
-e "AUTH_JWT_SECRET=${AUTH_JWT_SECRET:?set AUTH_JWT_SECRET}" \
-e "AUTH_ISSUER=${AUTH_ISSUER:-https://auth.shnt.top}" \
-e "AUTH_PORTAL_URL=${AUTH_PORTAL_URL:-https://auth.shnt.top}" \
-v "${MM_ROOT}/data:/app/data" \
--label mmapp.updater.managed=true \
--label mmapp.updater.target=backend \
--label "mmapp.updater.image=${BACKEND_IMAGE}" \
--health-cmd="node -e \"fetch('http://127.0.0.1:8000/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))\"" \
--health-interval=30s \
--health-timeout=5s \
--health-retries=3 \
--health-start-period=15s \
--log-driver json-file \
--log-opt max-size=10m \
--log-opt max-file=3 \
"${BACKEND_IMAGE}"
echo "==> Recreate mmapp-frontend (mmapp + cdnmanager)"
stop_rm mmapp-frontend
docker run -d \
--name mmapp-frontend \
--restart unless-stopped \
--network mmapp \
--network-alias frontend \
-e BACKEND_INTERNAL_URL=http://backend:8000 \
--label mmapp.updater.managed=true \
--label mmapp.updater.target=frontend \
--label "mmapp.updater.image=${FRONTEND_IMAGE}" \
--label traefik.enable=true \
--label traefik.docker.network=cdnmanager \
--label "traefik.http.routers.mmapp.rule=Host(\`${MM_DOMAIN}\`)" \
--label traefik.http.routers.mmapp.entrypoints=websecure \
--label traefik.http.routers.mmapp.tls=true \
--label traefik.http.routers.mmapp.tls.certresolver=letsencrypt \
--label traefik.http.services.mmapp.loadbalancer.server.port=3000 \
--health-cmd="node -e \"fetch('http://127.0.0.1:3000/dashboard').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))\"" \
--health-interval=30s \
--health-timeout=5s \
--health-retries=3 \
--health-start-period=25s \
--log-driver json-file \
--log-opt max-size=10m \
--log-opt max-file=3 \
"${FRONTEND_IMAGE}"
docker network connect cdnmanager mmapp-frontend
echo "==> Recreate mmapp-updater"
stop_rm mmapp-updater
docker run -d \
--name mmapp-updater \
--restart unless-stopped \
--network mmapp \
-e "REGISTRY=${REGISTRY}" \
-e "REGISTRY_USERNAME=${REGISTRY_USERNAME:-}" \
-e "REGISTRY_PASSWORD=${REGISTRY_PASSWORD:-}" \
-e "POLL_INTERVAL_SECONDS=${POLL_INTERVAL_SECONDS:-300}" \
-e "HEALTH_TIMEOUT_SECONDS=${HEALTH_TIMEOUT_SECONDS:-120}" \
-e "STOP_TIMEOUT_SECONDS=${STOP_TIMEOUT_SECONDS:-30}" \
-v /var/run/docker.sock:/var/run/docker.sock \
-v "${MM_ROOT}/state:/state" \
-v "${TARGETS}:/etc/updater/targets.json:ro" \
--log-driver json-file \
--log-opt max-size=10m \
--log-opt max-file=3 \
"${UPDATER_IMAGE}"
echo
echo "OK. UI: https://${MM_DOMAIN}"
echo "DNS: A/AAAA for ${MM_DOMAIN} → this VPS, Cloudflare proxy OFF (DNS only)."
echo "Check: curl -fsS https://${MM_DOMAIN}/health"
echo " docker ps --filter name=mmapp-"
echo " docker network inspect cdnmanager --format '{{range .Containers}}{{.Name}} {{end}}'"