CI / Build & test (Node 20) (push) Canceled after 0s
CI / Build & test (Node 24) (push) Canceled after 0s
CodeQL / Analyze (javascript-typescript) (push) Canceled after 0s
OpenSSF Scorecard / Scorecard analysis (push) Canceled after 0s
Security / Secret scan (gitleaks) (push) Canceled after 0s
Security / Dependency scan (OSV-Scanner) (push) Canceled after 0s
Security / IaC/config scan (Trivy) (push) Canceled after 0s
Security / License compliance scan (Trivy) (push) Canceled after 0s
CI / Build & test (push) Canceled after 0s
They existed only in /opt/ops on the VPS — no history, no backup. Rebuilding that machine would have taken the whole publishing process with it, including the Android signing flow written today. /opt/ops now symlinks here so there is one copy rather than two that drift. No secrets move: the scripts read tokens and the signing key from paths under /root that stay on the VPS. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
53 lines
2.4 KiB
Bash
Executable File
53 lines
2.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Reports how many people are currently in a call on this instance.
|
|
#
|
|
# Asks LiveKit directly (ListRooms over its twirp API) rather than inferring
|
|
# from the app: LiveKit is the authority on who is actually connected to media.
|
|
# Prints the participant count and exits 0 when the instance is idle, 1 when
|
|
# someone is in a call — so it can gate a deploy.
|
|
set -euo pipefail
|
|
|
|
ENV_FILE=${ENV_FILE:-/opt/backspace/.env}
|
|
# `|| true`: under `set -o pipefail` a grep with no match aborts the script
|
|
# before the check below, exiting 1 — which in this script's contract means
|
|
# "someone is in a call" rather than "I could not tell".
|
|
KEY=$(grep -E '^LIVEKIT_API_KEY=' "$ENV_FILE" 2>/dev/null | cut -d= -f2- || true)
|
|
SECRET=$(grep -E '^LIVEKIT_API_SECRET=' "$ENV_FILE" 2>/dev/null | cut -d= -f2- || true)
|
|
[ -n "$KEY" ] && [ -n "$SECRET" ] || { echo "LiveKit credentials not found in $ENV_FILE" >&2; exit 2; }
|
|
|
|
b64url() { openssl base64 -A | tr '+/' '-_' | tr -d '='; }
|
|
|
|
now=$(date +%s)
|
|
header=$(printf '%s' '{"alg":"HS256","typ":"JWT"}' | b64url)
|
|
payload=$(printf '{"iss":"%s","sub":"%s","nbf":%d,"exp":%d,"video":{"roomList":true}}' \
|
|
"$KEY" "$KEY" "$now" "$((now + 60))" | b64url)
|
|
sig=$(printf '%s.%s' "$header" "$payload" \
|
|
| openssl dgst -sha256 -hmac "$SECRET" -binary | b64url)
|
|
jwt="$header.$payload.$sig"
|
|
|
|
resp=$(curl -sS --max-time 10 -w '\n%{http_code}' -X POST http://127.0.0.1:7880/twirp/livekit.RoomService/ListRooms \
|
|
-H "Authorization: Bearer $jwt" -H 'Content-Type: application/json' -d '{}') || {
|
|
echo "LiveKit unreachable — refusing to report idle" >&2; exit 2; }
|
|
|
|
code=$(printf '%s' "$resp" | tail -n1)
|
|
resp=$(printf '%s' "$resp" | sed '$d')
|
|
|
|
# A failed query returns no participant counts, which would read as "idle" and
|
|
# green-light a deploy on top of a live call. Anything but a well-formed 200 is
|
|
# treated as unknown, not as empty.
|
|
if [ "$code" != "200" ] || ! printf '%s' "$resp" | grep -q '"rooms"'; then
|
|
echo "LiveKit answered HTTP $code without a room list — status unknown, not deploying" >&2
|
|
exit 2
|
|
fi
|
|
|
|
# numParticipants is omitted by protobuf JSON when zero, so absence means empty.
|
|
total=$(printf '%s' "$resp" | grep -oE '"numParticipants":[0-9]+' | cut -d: -f2 | paste -sd+ - | bc 2>/dev/null || true)
|
|
total=${total:-0}
|
|
|
|
if [ "$total" -gt 0 ]; then
|
|
echo "EM CALL: $total participante(s) — nao deployar"
|
|
exit 1
|
|
fi
|
|
echo "OCIOSO: ninguem em call"
|
|
exit 0
|