mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 22:21:10 +02:00
feat(status-page): add app release tier section to status.mana.how
Parse tier data automatically from mana-apps.ts (awk, read-only volume mount) so the status page stays in sync without manual updates. Shows founder/alpha/beta/public cards with per-app development status. Tier data is also included in status.json for ManaScore consumption. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3fa218cbe0
commit
57db32f1b0
3 changed files with 171 additions and 13 deletions
|
|
@ -172,7 +172,7 @@ services:
|
|||
mc mb --ignore-existing myminio/manacore-storage;
|
||||
mc mb --ignore-existing myminio/picture-storage;
|
||||
mc mb --ignore-existing myminio/chat-storage;
|
||||
mc mb --ignore-existing myminio/manadeck-storage;
|
||||
mc mb --ignore-existing myminio/cards-storage;
|
||||
mc mb --ignore-existing myminio/presi-storage;
|
||||
mc mb --ignore-existing myminio/calendar-storage;
|
||||
mc mb --ignore-existing myminio/contacts-storage;
|
||||
|
|
@ -276,7 +276,7 @@ services:
|
|||
SMTP_PASS: ${SMTP_PASSWORD}
|
||||
SYNAPSE_OIDC_CLIENT_SECRET: ${SYNAPSE_OIDC_CLIENT_SECRET:-}
|
||||
MAX_DAILY_SIGNUPS: ${MAX_DAILY_SIGNUPS:-0}
|
||||
CORS_ORIGINS: https://mana.how,https://calendar.mana.how,https://chat.mana.how,https://clock.mana.how,https://contacts.mana.how,https://context.mana.how,https://docs.mana.how,https://element.mana.how,https://inventar.mana.how,https://link.mana.how,https://manadeck.mana.how,https://matrix.mana.how,https://mukke.mana.how,https://nutriphi.mana.how,https://photos.mana.how,https://picture.mana.how,https://planta.mana.how,https://playground.mana.how,https://presi.mana.how,https://questions.mana.how,https://skilltree.mana.how,https://storage.mana.how,https://times.mana.how,https://todo.mana.how,https://traces.mana.how,https://zitare.mana.how
|
||||
CORS_ORIGINS: https://mana.how,https://calendar.mana.how,https://chat.mana.how,https://clock.mana.how,https://contacts.mana.how,https://context.mana.how,https://docs.mana.how,https://element.mana.how,https://inventar.mana.how,https://link.mana.how,https://cards.mana.how,https://matrix.mana.how,https://mukke.mana.how,https://nutriphi.mana.how,https://photos.mana.how,https://picture.mana.how,https://planta.mana.how,https://playground.mana.how,https://presi.mana.how,https://questions.mana.how,https://skilltree.mana.how,https://storage.mana.how,https://times.mana.how,https://todo.mana.how,https://traces.mana.how,https://zitare.mana.how
|
||||
ports:
|
||||
- "3001:3001"
|
||||
healthcheck:
|
||||
|
|
@ -310,7 +310,7 @@ services:
|
|||
STRIPE_SECRET_KEY: ${STRIPE_SECRET_KEY:-}
|
||||
STRIPE_WEBHOOK_SECRET: ${STRIPE_CREDITS_WEBHOOK_SECRET:-}
|
||||
BASE_URL: https://credits.mana.how
|
||||
CORS_ORIGINS: https://mana.how,https://chat.mana.how,https://picture.mana.how,https://todo.mana.how,https://zitare.mana.how,https://calendar.mana.how,https://clock.mana.how,https://contacts.mana.how,https://manadeck.mana.how,https://presi.mana.how,https://storage.mana.how,https://nutriphi.mana.how,https://planta.mana.how,https://mukke.mana.how,https://context.mana.how,https://photos.mana.how,https://questions.mana.how,https://calc.mana.how
|
||||
CORS_ORIGINS: https://mana.how,https://chat.mana.how,https://picture.mana.how,https://todo.mana.how,https://zitare.mana.how,https://calendar.mana.how,https://clock.mana.how,https://contacts.mana.how,https://cards.mana.how,https://presi.mana.how,https://storage.mana.how,https://nutriphi.mana.how,https://planta.mana.how,https://mukke.mana.how,https://context.mana.how,https://photos.mana.how,https://questions.mana.how,https://calc.mana.how
|
||||
ports:
|
||||
- "3002:3002"
|
||||
healthcheck:
|
||||
|
|
@ -341,7 +341,7 @@ services:
|
|||
DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD:-mana123}@postgres:5432/mana_user
|
||||
MANA_CORE_AUTH_URL: http://mana-auth:3001
|
||||
MANA_CORE_SERVICE_KEY: ${MANA_CORE_SERVICE_KEY}
|
||||
CORS_ORIGINS: https://mana.how,https://calc.mana.how,https://calendar.mana.how,https://chat.mana.how,https://clock.mana.how,https://contacts.mana.how,https://context.mana.how,https://manadeck.mana.how,https://mukke.mana.how,https://nutriphi.mana.how,https://photos.mana.how,https://picture.mana.how,https://planta.mana.how,https://presi.mana.how,https://questions.mana.how,https://storage.mana.how,https://todo.mana.how,https://zitare.mana.how
|
||||
CORS_ORIGINS: https://mana.how,https://calc.mana.how,https://calendar.mana.how,https://chat.mana.how,https://clock.mana.how,https://contacts.mana.how,https://context.mana.how,https://cards.mana.how,https://mukke.mana.how,https://nutriphi.mana.how,https://photos.mana.how,https://picture.mana.how,https://planta.mana.how,https://presi.mana.how,https://questions.mana.how,https://storage.mana.how,https://todo.mana.how,https://zitare.mana.how
|
||||
ports:
|
||||
- "3062:3062"
|
||||
healthcheck:
|
||||
|
|
@ -799,7 +799,7 @@ services:
|
|||
MATRIX_CONTACTS_BOT_TOKEN: ${MATRIX_CONTACTS_BOT_TOKEN:-}
|
||||
MATRIX_CONTACTS_BOT_ROOMS: ${MATRIX_CONTACTS_BOT_ROOMS:-}
|
||||
MATRIX_CHAT_BOT_TOKEN: ${MATRIX_CHAT_BOT_TOKEN:-}
|
||||
MATRIX_MANADECK_BOT_TOKEN: ${MATRIX_MANADECK_BOT_TOKEN:-}
|
||||
MATRIX_CARDS_BOT_TOKEN: ${MATRIX_CARDS_BOT_TOKEN:-}
|
||||
MATRIX_NUTRIPHI_BOT_TOKEN: ${MATRIX_NUTRIPHI_BOT_TOKEN}
|
||||
MATRIX_NUTRIPHI_BOT_ROOMS: ${MATRIX_NUTRIPHI_BOT_ROOMS:-}
|
||||
MATRIX_PICTURE_BOT_TOKEN: ${MATRIX_PICTURE_BOT_TOKEN:-}
|
||||
|
|
@ -1082,20 +1082,20 @@ services:
|
|||
retries: 3
|
||||
start_period: 45s
|
||||
|
||||
manadeck-web:
|
||||
cards-web:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: apps/manadeck/apps/web/Dockerfile
|
||||
image: manadeck-web:local
|
||||
container_name: mana-app-manadeck-web
|
||||
dockerfile: apps/cards/apps/web/Dockerfile
|
||||
image: cards-web:local
|
||||
container_name: mana-app-cards-web
|
||||
restart: always
|
||||
mem_limit: 128m
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
PORT: 5023
|
||||
PUBLIC_BACKEND_URL: http://manadeck-backend:3036
|
||||
PUBLIC_BACKEND_URL: http://cards-backend:3036
|
||||
PUBLIC_MANA_CORE_AUTH_URL: http://mana-auth:3001
|
||||
PUBLIC_BACKEND_URL_CLIENT: https://manadeck-api.mana.how
|
||||
PUBLIC_BACKEND_URL_CLIENT: https://cards-api.mana.how
|
||||
PUBLIC_MANA_CORE_AUTH_URL_CLIENT: https://auth.mana.how
|
||||
ports:
|
||||
- "5023:5023"
|
||||
|
|
@ -1808,6 +1808,7 @@ services:
|
|||
OUTPUT_FILE: /output/index.html
|
||||
volumes:
|
||||
- ./scripts/generate-status-page.sh:/generate.sh:ro
|
||||
- ./packages/shared-branding/src/mana-apps.ts:/mana-apps.ts:ro
|
||||
- /Volumes/ManaData/landings/status:/output
|
||||
command:
|
||||
- sh
|
||||
|
|
|
|||
|
|
@ -163,6 +163,25 @@ docker logs -f manacore-chat-backend # Live-Logs
|
|||
Zeigt probe_success und probe_duration_seconds aller Dienste via Blackbox Exporter (Port 9115).
|
||||
Alerts: WebAppDown (2 min), APIDown (1 min), InfraToolDown (3 min), GPUServiceDown (5 min), SlowHTTPResponse (5 min > 5s).
|
||||
|
||||
### Public Status Page (status.mana.how)
|
||||
|
||||
Statische HTML-Seite, die alle 60 Sekunden vom Container `mana-status-gen` neu generiert wird.
|
||||
|
||||
| Komponente | Pfad |
|
||||
|---|---|
|
||||
| Generator-Script | `scripts/generate-status-page.sh` |
|
||||
| Docker-Service | `status-page-gen` in `docker-compose.macmini.yml` |
|
||||
| Output | `/Volumes/ManaData/landings/status/index.html` + `status.json` |
|
||||
| Nginx-Config | `docker/nginx/landings.conf` → `status.mana.how` |
|
||||
|
||||
**Datenquellen:**
|
||||
- **Service-Uptime:** VictoriaMetrics via Blackbox Exporter (`probe_success`, `probe_duration_seconds`)
|
||||
- **App Release Tiers:** Automatisch aus `packages/shared-branding/src/mana-apps.ts` geparst (per awk, read-only Volume-Mount). Zeigt welche Apps in welchem Tier (founder/alpha/beta/public) sind.
|
||||
|
||||
**Automatische Aktualisierung:** Änderungen an `requiredTier` in `mana-apps.ts` werden nach dem nächsten `git pull` auf dem Server automatisch beim nächsten 60s-Refresh auf der Statusseite sichtbar — kein Container-Restart nötig, da die Datei live gemountet ist.
|
||||
|
||||
**`status.json`** wird parallel generiert und enthält Service-Status + Tier-Daten als JSON (genutzt von ManaScore Live-Badges).
|
||||
|
||||
### Service Management
|
||||
|
||||
```bash
|
||||
|
|
|
|||
|
|
@ -4,7 +4,11 @@
|
|||
# Läuft in einem Alpine-Container im Docker-Netz (http://victoriametrics:9090)
|
||||
# oder auf dem Host (http://localhost:9090).
|
||||
#
|
||||
# Ausgabe: /output/index.html (gemountet als /Volumes/ManaData/landings/status/)
|
||||
# Datenquellen:
|
||||
# - Service-Uptime: Blackbox Exporter via VictoriaMetrics
|
||||
# - App Release Tiers: Automatisch aus mana-apps.ts geparst (Volume-Mount)
|
||||
#
|
||||
# Ausgabe: /output/index.html + /output/status.json
|
||||
|
||||
set -eu
|
||||
|
||||
|
|
@ -136,6 +140,97 @@ fi
|
|||
|
||||
TIMESTAMP="$(date -u '+%d. %B %Y, %H:%M Uhr UTC')"
|
||||
|
||||
# ── App Release Tiers (automatisch aus mana-apps.ts) ────────────────────────
|
||||
# Gemountet als /mana-apps.ts (read-only) im Container.
|
||||
# Format je Zeile: name|tier|status
|
||||
|
||||
MANA_APPS_TS="${MANA_APPS_TS:-/mana-apps.ts}"
|
||||
|
||||
if [ -f "$MANA_APPS_TS" ]; then
|
||||
TIER_APPS="$(awk '
|
||||
/^export const MANA_APPS/ { inside=1; next }
|
||||
/^];/ { inside=0 }
|
||||
!inside { next }
|
||||
/^\t\{/ { name=""; tier=""; st=""; archived=0 }
|
||||
/^\t\tname:/ { gsub(/.*name:[[:space:]]*'\''/, ""); gsub(/'\''.*/, ""); name=$0 }
|
||||
/^\t\trequiredTier:/ { gsub(/.*requiredTier:[[:space:]]*'\''/, ""); gsub(/'\''.*/, ""); tier=$0 }
|
||||
/^\t\tstatus:/ { gsub(/.*status:[[:space:]]*'\''/, ""); gsub(/'\''.*/, ""); st=$0 }
|
||||
/^\t\tarchived:[[:space:]]*true/ { archived=1 }
|
||||
/^\t\},/ { if (name != "" && tier != "" && archived == 0) print name "|" tier "|" st }
|
||||
' "$MANA_APPS_TS")"
|
||||
else
|
||||
echo "$(date '+%H:%M:%S') WARNUNG: $MANA_APPS_TS nicht gefunden — Tier-Sektion wird übersprungen"
|
||||
TIER_APPS=""
|
||||
fi
|
||||
|
||||
render_tier_rows() {
|
||||
tier="$1"
|
||||
echo "$TIER_APPS" | while IFS='|' read -r name t status; do
|
||||
[ -z "$name" ] && continue
|
||||
[ "$t" != "$tier" ] && continue
|
||||
case "$status" in
|
||||
published) status_class="published"; status_label="Published" ;;
|
||||
beta) status_class="beta-status"; status_label="Beta" ;;
|
||||
development) status_class="dev-status"; status_label="Development" ;;
|
||||
planning) status_class="plan-status"; status_label="Planned" ;;
|
||||
*) status_class="dev-status"; status_label="$status" ;;
|
||||
esac
|
||||
printf '<tr><td class="name">%s</td><td class="status-cell"><span class="badge %s">%s</span></td></tr>\n' \
|
||||
"$name" "$status_class" "$status_label"
|
||||
done
|
||||
}
|
||||
|
||||
count_tier() {
|
||||
tier="$1"
|
||||
echo "$TIER_APPS" | grep -c "|${tier}|" || echo "0"
|
||||
}
|
||||
|
||||
tier_founder_count="$(count_tier founder)"
|
||||
tier_alpha_count="$(count_tier alpha)"
|
||||
tier_beta_count="$(count_tier beta)"
|
||||
tier_public_count="$(count_tier public)"
|
||||
|
||||
render_tier_section() {
|
||||
[ -z "$TIER_APPS" ] && return
|
||||
cat << TIEREOF
|
||||
<div class="section">
|
||||
<div class="section-header">
|
||||
<h2>App Release Tiers</h2>
|
||||
<span class="fraction">${tier_public_count} public · ${tier_beta_count} beta · ${tier_alpha_count} alpha · ${tier_founder_count} founder</span>
|
||||
</div>
|
||||
<div class="tier-grid">
|
||||
<div class="tier-card tier-founder">
|
||||
<div class="tier-card-header">
|
||||
<h3>Founder</h3>
|
||||
<span class="tier-count">${tier_founder_count} Apps</span>
|
||||
</div>
|
||||
<table>
|
||||
$(render_tier_rows founder)
|
||||
</table>
|
||||
</div>
|
||||
<div class="tier-card tier-alpha">
|
||||
<div class="tier-card-header">
|
||||
<h3>Alpha</h3>
|
||||
<span class="tier-count">${tier_alpha_count} Apps</span>
|
||||
</div>
|
||||
<table>
|
||||
$(render_tier_rows alpha)
|
||||
</table>
|
||||
</div>
|
||||
<div class="tier-card tier-beta">
|
||||
<div class="tier-card-header">
|
||||
<h3>Beta</h3>
|
||||
<span class="tier-count">${tier_beta_count} Apps</span>
|
||||
</div>
|
||||
<table>
|
||||
$(render_tier_rows beta)
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
TIEREOF
|
||||
}
|
||||
|
||||
# ── HTML generieren ──────────────────────────────────────────────────────────
|
||||
|
||||
cat > "${OUTPUT}.tmp" << HTMLEOF
|
||||
|
|
@ -275,6 +370,34 @@ cat > "${OUTPUT}.tmp" << HTMLEOF
|
|||
|
||||
.no-data { color: var(--muted); font-size: 13px; text-align: center; padding: 20px; }
|
||||
|
||||
/* ── Tier Section ── */
|
||||
.tier-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 16px; }
|
||||
.tier-card { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; overflow: hidden; }
|
||||
.tier-card-header {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
padding: 12px 16px; border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.tier-card-header h3 { font-size: 13px; font-weight: 600; text-transform: uppercase; letter-spacing: .05em; }
|
||||
.tier-card-header .tier-count { font-size: 12px; color: var(--muted); }
|
||||
.tier-card table { border: none; border-radius: 0; }
|
||||
.tier-card tr:last-child { border-bottom: none; }
|
||||
.tier-card td { padding: 8px 16px; }
|
||||
.tier-card .name { font-size: 14px; }
|
||||
|
||||
.tier-founder .tier-card-header { background: rgba(168,85,247,.08); }
|
||||
.tier-founder .tier-card-header h3 { color: #a855f7; }
|
||||
.tier-alpha .tier-card-header { background: rgba(245,158,11,.08); }
|
||||
.tier-alpha .tier-card-header h3 { color: #f59e0b; }
|
||||
.tier-beta .tier-card-header { background: rgba(59,130,246,.08); }
|
||||
.tier-beta .tier-card-header h3 { color: #3b82f6; }
|
||||
.tier-public .tier-card-header { background: rgba(34,197,94,.08); }
|
||||
.tier-public .tier-card-header h3 { color: var(--green); }
|
||||
|
||||
.badge.published { background: rgba(34,197,94,.12); color: var(--green); }
|
||||
.badge.beta-status { background: rgba(59,130,246,.12); color: #3b82f6; }
|
||||
.badge.dev-status { background: rgba(245,158,11,.12); color: #f59e0b; }
|
||||
.badge.plan-status { background: rgba(102,102,102,.15); color: var(--muted); }
|
||||
|
||||
/* ── Footer ── */
|
||||
footer {
|
||||
margin-top: 40px;
|
||||
|
|
@ -357,6 +480,8 @@ $(render_rows blackbox-gpu)
|
|||
</table>
|
||||
</div>
|
||||
|
||||
$(render_tier_section)
|
||||
|
||||
<footer>
|
||||
<p>Powered by <a href="https://mana.how">ManaCore</a> · Metriken via Prometheus Blackbox Exporter</p>
|
||||
</footer>
|
||||
|
|
@ -374,10 +499,22 @@ echo "$(date '+%H:%M:%S') Status-Seite generiert → $OUTPUT (${total_up}/${tota
|
|||
JSON_OUTPUT="$(dirname "$OUTPUT")/status.json"
|
||||
TIMESTAMP_ISO="$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
|
||||
|
||||
# Tier-Daten als JSON-Array für jq
|
||||
TIER_JSON="[]"
|
||||
if [ -n "$TIER_APPS" ]; then
|
||||
TIER_JSON="$(echo "$TIER_APPS" | awk -F'|' '
|
||||
BEGIN { printf "[" }
|
||||
NR>1 { printf "," }
|
||||
{ printf "{\"name\":\"%s\",\"tier\":\"%s\",\"status\":\"%s\"}", $1, $2, $3 }
|
||||
END { printf "]" }
|
||||
')"
|
||||
fi
|
||||
|
||||
echo "$SUCCESS_JSON" | jq \
|
||||
--arg ts "$TIMESTAMP_ISO" \
|
||||
--argjson total_up "$total_up" \
|
||||
--argjson total_all "$total_all" \
|
||||
--argjson tiers "$TIER_JSON" \
|
||||
'{
|
||||
updated: $ts,
|
||||
summary: { up: $total_up, total: $total_all },
|
||||
|
|
@ -394,7 +531,8 @@ echo "$SUCCESS_JSON" | jq \
|
|||
),
|
||||
value: (.value[1] == "1")
|
||||
}) | from_entries
|
||||
)
|
||||
),
|
||||
tiers: ($tiers | group_by(.tier) | map({ key: .[0].tier, value: map({name, status}) }) | from_entries)
|
||||
}' > "${JSON_OUTPUT}.tmp" && mv "${JSON_OUTPUT}.tmp" "$JSON_OUTPUT"
|
||||
|
||||
echo "$(date '+%H:%M:%S') status.json generiert → $JSON_OUTPUT"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue