#!/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