- docs/VEZA_PROJECT_DOCUMENTATION.md - config/logging.toml - status.sh utility script Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
324 lines
17 KiB
Bash
Executable file
324 lines
17 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# VEZA — Project Status Dashboard
|
|
# Usage: ./status.sh (full report)
|
|
# ./status.sh --quick (infra + services only, no tests)
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
set -uo pipefail
|
|
|
|
# ── Colors & symbols ─────────────────────────────────────────────────────────
|
|
R=$'\033[0;31m' G=$'\033[0;32m' Y=$'\033[0;33m' B=$'\033[0;34m'
|
|
M=$'\033[0;35m' C=$'\033[0;36m' W=$'\033[1;37m' D=$'\033[0;90m'
|
|
NC=$'\033[0m' BOLD=$'\033[1m' DIM=$'\033[2m'
|
|
|
|
OK="${G}✓${NC}" FAIL="${R}✗${NC}" WARN="${Y}⚠${NC}" SKIP="${D}○${NC}"
|
|
SPIN="${C}…${NC}"
|
|
|
|
ROOT="$(cd "$(dirname "$0")" && pwd)"
|
|
cd "$ROOT"
|
|
|
|
QUICK=false
|
|
[[ "${1:-}" == "--quick" || "${1:-}" == "-q" ]] && QUICK=true
|
|
|
|
# ── Helpers ──────────────────────────────────────────────────────────────────
|
|
hr() { echo -e "${D}$(printf '─%.0s' {1..72})${NC}"; }
|
|
hdr() { echo -e "\n${BOLD}${C}▸ $1${NC}"; hr; }
|
|
ok() { echo -e " ${OK} $1"; }
|
|
ko() { echo -e " ${FAIL} $1"; }
|
|
wr() { echo -e " ${WARN} $1"; }
|
|
sk() { echo -e " ${SKIP} ${D}$1${NC}"; }
|
|
kv() { printf " %-24s " "$1"; echo -e "$2"; }
|
|
|
|
check_port() {
|
|
local port="$1"
|
|
if ss -tlnp 2>/dev/null | grep -q ":${port} " || nc -z 127.0.0.1 "$port" 2>/dev/null; then
|
|
echo "up"
|
|
else
|
|
echo "down"
|
|
fi
|
|
}
|
|
|
|
http_check() {
|
|
local url="$1" timeout="${2:-3}"
|
|
curl -s -o /dev/null -w '%{http_code}' --max-time "$timeout" "$url" 2>/dev/null || echo "000"
|
|
}
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# HEADER
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
echo
|
|
echo -e "${BOLD}${M} ╔═══════════════════════════════════════════════════════════════╗${NC}"
|
|
echo -e "${BOLD}${M} ║ VEZA — Project Status Dashboard ║${NC}"
|
|
echo -e "${BOLD}${M} ╚═══════════════════════════════════════════════════════════════╝${NC}"
|
|
echo -e " ${D}$(date '+%Y-%m-%d %H:%M:%S') • $(hostname)${NC}"
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# GIT & VERSION
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
hdr "GIT & VERSION"
|
|
|
|
branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "?")
|
|
commit=$(git rev-parse --short HEAD 2>/dev/null || echo "?")
|
|
commit_msg=$(git log -1 --format='%s' 2>/dev/null | head -c 60 || echo "")
|
|
tag=$(git describe --tags --abbrev=0 2>/dev/null || echo "none")
|
|
ahead=$(git rev-list "${tag}..HEAD" --count 2>/dev/null || echo "?")
|
|
dirty=$(git status --porcelain 2>/dev/null | wc -l | tr -d ' ')
|
|
|
|
kv "Branch:" "${W}${branch}${NC}"
|
|
kv "Latest tag:" "${G}${tag}${NC} ${D}(+${ahead} commits)${NC}"
|
|
kv "HEAD:" "${commit} — ${DIM}${commit_msg}${NC}"
|
|
|
|
if [[ "$dirty" -gt 0 ]]; then
|
|
staged=$(git diff --cached --numstat 2>/dev/null | wc -l | tr -d ' ')
|
|
unstaged=$(git diff --numstat 2>/dev/null | wc -l | tr -d ' ')
|
|
untracked=$(git ls-files --others --exclude-standard 2>/dev/null | wc -l | tr -d ' ')
|
|
kv "Working tree:" "${Y}${dirty} changes${NC} ${D}(staged:${staged} modified:${unstaged} untracked:${untracked})${NC}"
|
|
else
|
|
kv "Working tree:" "${G}clean${NC}"
|
|
fi
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# TOOLCHAIN
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
hdr "TOOLCHAIN"
|
|
|
|
go_ver=$(go version 2>/dev/null | awk '{print $3}' | sed 's/go//' || echo "not installed")
|
|
rust_ver=$(rustc --version 2>/dev/null | awk '{print $2}' || echo "not installed")
|
|
node_ver=$(node --version 2>/dev/null | sed 's/v//' || echo "not installed")
|
|
npm_ver=$(npm --version 2>/dev/null || echo "?")
|
|
docker_ver=$(docker --version 2>/dev/null | awk '{print $3}' | tr -d ',' || echo "not installed")
|
|
|
|
kv "Go:" "${go_ver}"
|
|
kv "Rust:" "${rust_ver}"
|
|
kv "Node:" "${node_ver} ${D}(npm ${npm_ver})${NC}"
|
|
kv "Docker:" "${docker_ver}"
|
|
|
|
air_ok=$(command -v air >/dev/null 2>&1 && echo "${G}yes${NC}" || echo "${D}no${NC}")
|
|
cargo_watch_ok=$(command -v cargo-watch >/dev/null 2>&1 && echo "${G}yes${NC}" || echo "${D}no${NC}")
|
|
kv "Hot reload:" "air=${air_ok} cargo-watch=${cargo_watch_ok}"
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# DOCKER INFRASTRUCTURE
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
hdr "DOCKER INFRASTRUCTURE"
|
|
|
|
if ! docker info >/dev/null 2>&1; then
|
|
ko "Docker daemon is not running"
|
|
else
|
|
for name in postgres redis rabbitmq clamav minio elasticsearch; do
|
|
container="veza_${name}"
|
|
status=$(docker inspect -f '{{.State.Status}}' "$container" 2>/dev/null || echo "absent")
|
|
health=$(docker inspect -f '{{.State.Health.Status}}' "$container" 2>/dev/null || echo "n/a")
|
|
label=$(printf '%-16s' "$name")
|
|
|
|
case "$status" in
|
|
running)
|
|
if [[ "$health" == "healthy" ]]; then
|
|
ok "${label} ${G}running${NC} ${G}healthy${NC}"
|
|
elif [[ "$health" == "unhealthy" ]]; then
|
|
wr "${label} ${G}running${NC} ${R}unhealthy${NC}"
|
|
else
|
|
ok "${label} ${G}running${NC} ${D}(no healthcheck)${NC}"
|
|
fi ;;
|
|
exited) ko "${label} ${R}exited${NC}" ;;
|
|
*) sk "${label} ${status}" ;;
|
|
esac
|
|
done
|
|
fi
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# APPLICATION SERVICES
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
hdr "APPLICATION SERVICES"
|
|
|
|
declare -A SVC_PORTS=( [backend-api]=18080 [stream-server]=18082 [frontend]=5173 )
|
|
declare -A SVC_HEALTH=( [backend-api]="/api/v1/health" [stream-server]="/health" [frontend]="/" )
|
|
|
|
for svc in backend-api stream-server frontend; do
|
|
port="${SVC_PORTS[$svc]}"
|
|
endpoint="${SVC_HEALTH[$svc]}"
|
|
url="http://localhost:${port}${endpoint}"
|
|
label=$(printf '%-16s' "$svc")
|
|
|
|
state=$(check_port "$port")
|
|
if [[ "$state" == "up" ]]; then
|
|
code=$(http_check "$url" 3)
|
|
if [[ "$code" =~ ^2 ]]; then
|
|
ok "${label} ${G}:${port}${NC} HTTP ${G}${code}${NC}"
|
|
elif [[ "$code" == "000" ]]; then
|
|
wr "${label} ${Y}:${port}${NC} ${Y}port open, no HTTP${NC}"
|
|
else
|
|
wr "${label} ${G}:${port}${NC} HTTP ${Y}${code}${NC}"
|
|
fi
|
|
else
|
|
ko "${label} ${R}:${port} not listening${NC}"
|
|
fi
|
|
done
|
|
|
|
# Rate limiting probe (only if backend is up)
|
|
if [[ "$(check_port 18080)" == "up" ]]; then
|
|
got429=false
|
|
for _ in $(seq 1 8); do
|
|
c=$(curl -s -o /dev/null -w '%{http_code}' --max-time 2 \
|
|
-X POST http://localhost:18080/api/v1/auth/login \
|
|
-H 'Content-Type: application/json' \
|
|
-d '{"email":"probe@test.invalid","password":"x"}' 2>/dev/null) || c="000"
|
|
[[ "$c" == "429" ]] && got429=true && break
|
|
done
|
|
if $got429; then
|
|
kv "Rate limiting:" "${Y}ACTIVE${NC} — use ${W}make dev-e2e${NC} for tests"
|
|
else
|
|
kv "Rate limiting:" "${G}disabled (test mode)${NC}"
|
|
fi
|
|
fi
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# DATABASE
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
hdr "DATABASE"
|
|
|
|
pg_port="${PORT_POSTGRES:-15432}"
|
|
if [[ "$(check_port "$pg_port")" == "up" ]]; then
|
|
pg_url="postgres://veza:${DB_PASSWORD:-password}@localhost:${pg_port}/veza?sslmode=disable"
|
|
|
|
tbl_count=$(psql "$pg_url" -t -c "SELECT count(*) FROM information_schema.tables WHERE table_schema='public'" 2>/dev/null | tr -d ' ' || echo "?")
|
|
kv "Tables:" "${tbl_count}"
|
|
|
|
mig_count=$(find veza-backend-api/migrations -maxdepth 1 -name '*.sql' ! -name '*_down*' 2>/dev/null | wc -l | tr -d ' ')
|
|
latest_mig=$(find veza-backend-api/migrations -maxdepth 1 -name '*.sql' ! -name '*_down*' -printf '%f\n' 2>/dev/null | sort -n | tail -1 || echo "none")
|
|
kv "Migrations:" "${mig_count} files — latest: ${D}${latest_mig}${NC}"
|
|
|
|
for tbl in users tracks playlists products; do
|
|
cnt=$(psql "$pg_url" -t -c "SELECT count(*) FROM ${tbl}" 2>/dev/null | tr -d ' ' || echo "—")
|
|
kv " ${tbl}:" "${cnt} rows"
|
|
done
|
|
else
|
|
sk "PostgreSQL not reachable on :${pg_port}"
|
|
fi
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# CODEBASE STATS
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
hdr "CODEBASE"
|
|
|
|
go_files=$(find veza-backend-api -name '*.go' ! -path '*/vendor/*' 2>/dev/null | wc -l | tr -d ' ')
|
|
rs_files=$(find veza-stream-server -name '*.rs' 2>/dev/null | wc -l | tr -d ' ')
|
|
ts_files=$(find apps/web/src \( -name '*.ts' -o -name '*.tsx' \) 2>/dev/null | wc -l | tr -d ' ')
|
|
e2e_files=$(find tests/e2e -name '*.spec.ts' 2>/dev/null | wc -l | tr -d ' ')
|
|
e2e_tests=$(grep -rch "test(" tests/e2e/*.spec.ts 2>/dev/null | awk '{s+=$1}END{print s+0}')
|
|
|
|
kv "Go (backend):" "${go_files} files"
|
|
kv "Rust (stream):" "${rs_files} files"
|
|
kv "TS/TSX (web):" "${ts_files} files"
|
|
kv "E2E specs:" "${e2e_files} files — ${W}~${e2e_tests} tests${NC}"
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# TESTS (skip with --quick)
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
if $QUICK; then
|
|
hdr "TESTS ${D}(skipped — run without --quick)${NC}"
|
|
else
|
|
hdr "TESTS"
|
|
|
|
# ── Go tests ──
|
|
echo -ne " ${SPIN} Running Go tests…\r"
|
|
go_out=$(cd veza-backend-api && go test ./internal/handlers/... ./internal/services/... -short -count=1 -timeout 60s 2>&1) || true
|
|
go_pass=$(echo "$go_out" | grep -c '^ok' || true)
|
|
go_fail=$(echo "$go_out" | grep -c '^FAIL' || true)
|
|
go_skip=$(echo "$go_out" | grep -c '\[no test files\]' || true)
|
|
printf '\r\033[2K'
|
|
if [[ "$go_fail" -eq 0 ]]; then
|
|
ok "Go: ${G}${go_pass} passed${NC} ${D}${go_skip} skipped${NC}"
|
|
else
|
|
ko "Go: ${G}${go_pass} passed${NC} ${R}${go_fail} FAILED${NC} ${D}${go_skip} skipped${NC}"
|
|
fi
|
|
|
|
# ── Rust tests ──
|
|
echo -ne " ${SPIN} Running Rust tests…\r"
|
|
rust_out=$(cd veza-stream-server && cargo test --lib -q 2>&1) || true
|
|
rust_summary=$(echo "$rust_out" | grep -E 'test result:' | head -1 || echo "")
|
|
printf '\r\033[2K'
|
|
if echo "$rust_summary" | grep -q "0 failed" 2>/dev/null; then
|
|
ok "Rust: ${G}${rust_summary}${NC}"
|
|
elif [[ -n "$rust_summary" ]]; then
|
|
ko "Rust: ${R}${rust_summary}${NC}"
|
|
else
|
|
rust_pass=$(echo "$rust_out" | grep -oP '\d+ passed' | head -1 || echo "? passed")
|
|
ok "Rust: ${G}${rust_pass}${NC}"
|
|
fi
|
|
|
|
# ── Frontend unit tests ──
|
|
echo -ne " ${SPIN} Running frontend tests…\r"
|
|
web_out=$(cd apps/web && npx vitest run --reporter=verbose 2>&1 | tail -30) || true
|
|
web_summary=$(echo "$web_out" | grep -iE 'Tests\s+\d|test files' | tail -1 || echo "")
|
|
printf '\r\033[2K'
|
|
if [[ -n "$web_summary" ]]; then
|
|
if echo "$web_summary" | grep -qi "failed"; then
|
|
ko "Web: ${web_summary}"
|
|
else
|
|
ok "Web: ${web_summary}"
|
|
fi
|
|
else
|
|
sk "Web: could not parse results"
|
|
fi
|
|
|
|
# ── E2E last run ──
|
|
last_run="tests/e2e/test-results/.last-run.json"
|
|
if [[ -f "$last_run" ]]; then
|
|
pw_status=$(python3 -c "import json,sys; d=json.load(open(sys.argv[1])); print(d.get('status','?'))" "$last_run" 2>/dev/null || echo "?")
|
|
kv "E2E last run:" "${pw_status}"
|
|
else
|
|
sk "E2E: no previous run — use ${W}npm run e2e${NC}"
|
|
fi
|
|
fi
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# VERSIONS ROADMAP
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
hdr "ROADMAP"
|
|
|
|
roadmap="VEZA_VERSIONS_ROADMAP.md"
|
|
if [[ -f "$roadmap" ]]; then
|
|
done_count=$(grep -c '✅ DONE' "$roadmap" || true)
|
|
todo_count=$(grep -c '⏳ TODO' "$roadmap" || true)
|
|
progress_count=$(grep -cE '🔄|🔨' "$roadmap" || true)
|
|
|
|
kv "Done:" "${G}${done_count}${NC} versions"
|
|
[[ "$progress_count" -gt 0 ]] && kv "In progress:" "${Y}${progress_count}${NC} versions"
|
|
[[ "$todo_count" -gt 0 ]] && kv "Todo:" "${D}${todo_count}${NC} versions"
|
|
|
|
# Extract next TODO version name (format: "## vX.Y.Z — Name")
|
|
next_line=$(grep -B5 '⏳ TODO' "$roadmap" | grep -oP 'v\d+\.\d+\.\d+\s*[—–-]\s*.*' | head -1 || true)
|
|
[[ -n "$next_line" ]] && kv "Next up:" "${W}${next_line}${NC}"
|
|
else
|
|
sk "Roadmap file not found"
|
|
fi
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# HEALTH SUMMARY
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
echo
|
|
hr
|
|
|
|
total_ok=0; total_ko=0
|
|
|
|
for svc in postgres redis rabbitmq; do
|
|
container="veza_${svc}"
|
|
s=$(docker inspect -f '{{.State.Status}}' "$container" 2>/dev/null || echo "absent")
|
|
h=$(docker inspect -f '{{.State.Health.Status}}' "$container" 2>/dev/null || echo "n/a")
|
|
if [[ "$s" == "running" && "$h" == "healthy" ]]; then ((total_ok++)); else ((total_ko++)) || true; fi
|
|
done
|
|
|
|
for port in 18080 18082 5173; do
|
|
if [[ "$(check_port "$port")" == "up" ]]; then ((total_ok++)); else ((total_ko++)) || true; fi
|
|
done
|
|
|
|
if [[ "$total_ko" -eq 0 ]]; then
|
|
echo -e " ${BOLD}${G}ALL SYSTEMS OPERATIONAL${NC} (${total_ok}/6 services up)"
|
|
elif [[ "$total_ko" -le 2 ]]; then
|
|
echo -e " ${BOLD}${Y}PARTIAL${NC} — ${total_ok} up, ${total_ko} down"
|
|
else
|
|
echo -e " ${BOLD}${R}DEGRADED${NC} — ${total_ok} up, ${total_ko} down"
|
|
fi
|
|
|
|
echo
|