From 4830b0596ca420511bc92633cbf548dc3e28f570 Mon Sep 17 00:00:00 2001 From: notplants <@notplants> Date: Wed, 29 Apr 2026 14:47:51 -0400 Subject: [PATCH] adding many abra commands --- abra.sh | 151 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) diff --git a/abra.sh b/abra.sh index eed97b0..c3a3916 100644 --- a/abra.sh +++ b/abra.sh @@ -13,6 +13,49 @@ export ADMIN_CONFIG_VERSION=v1 export COMPRESS_STATE_ENTRYPOINT_VERSION=v5 # See https://levans.fr/shrink-synapse-database.html +db_size() { + echo "=== Database size ===" + psql -U synapse -d synapse -c "SELECT pg_size_pretty(pg_database_size('synapse')) AS db_size;" + echo "" + echo "=== Top 10 largest tables ===" + psql -U synapse -d synapse -c " + SELECT nspname || '.' || relname AS table, + pg_size_pretty(pg_total_relation_size(C.oid)) AS total_size + FROM pg_class C + LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) + WHERE nspname NOT IN ('pg_catalog', 'information_schema') + ORDER BY pg_total_relation_size(C.oid) DESC + LIMIT 10;" +} + +state_bloat() { + echo "=== Rooms with most state bloat ===" + psql -U synapse -d synapse -c " + SELECT room_id, count(*) AS state_entries + FROM state_groups_state + GROUP BY room_id + ORDER BY state_entries DESC + LIMIT 20;" +} + +empty_rooms() { + echo "=== Rooms with no local members ===" + psql -U synapse -d synapse -c " + SELECT room_id, room_version + FROM rooms + WHERE room_id NOT IN ( + SELECT room_id FROM local_current_membership WHERE membership = 'join' + );" +} + +reindex() { + echo "WARNING: REINDEX locks tables. Synapse should be stopped before running this." + echo "Running REINDEX on synapse database..." + psql -U synapse -d synapse -c "REINDEX (VERBOSE) DATABASE synapse;" + echo "REINDEX complete." + psql -U synapse -d synapse -c "SELECT pg_size_pretty(pg_database_size('synapse')) AS db_size;" +} + vacuum_full() { echo "WARNING: VACUUM FULL locks tables and requires temporary disk space." echo "Synapse should be stopped before running this." @@ -22,6 +65,114 @@ vacuum_full() { psql -U synapse -d synapse -c "SELECT pg_size_pretty(pg_database_size('synapse')) AS db_size;" } +# Purge commands — run via: abra app cmd app +# These use the Synapse admin API and require an admin access token. + +register_admin() { + USER="${1}" + PASS="${2}" + if [ -z "$USER" ] || [ -z "$PASS" ]; then + echo "Usage: register_admin " + return 1 + fi + register_new_matrix_user -u "$USER" -p "$PASS" -a -c /data/homeserver.yaml http://localhost:8008 +} + +get_token() { + USER="${1}" + PASS="${2}" + if [ -z "$USER" ] || [ -z "$PASS" ]; then + echo "Usage: get_token " + echo "Returns an admin access token for use with purge commands." + return 1 + fi + curl -s -X POST "http://localhost:8008/_matrix/client/r0/login" \ + -H "Content-Type: application/json" \ + -d "{\"type\":\"m.login.password\",\"user\":\"$USER\",\"password\":\"$PASS\"}" \ + | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('access_token', d.get('error', 'unknown error')))" +} + +purge_remote_media() { + DAYS="${1:-30}" + TOKEN="${2}" + if [ -z "$TOKEN" ]; then + echo "Usage: purge_remote_media " + return 1 + fi + BEFORE_TS=$(( $(date +%s) * 1000 - DAYS * 86400000 )) + echo "Purging remote media older than $DAYS days..." + curl -s -X POST "http://localhost:8008/_synapse/admin/v1/purge_media_cache?before_ts=$BEFORE_TS" \ + -H "Authorization: Bearer $TOKEN" + echo "" +} + +purge_room() { + ROOM_ID="${1}" + TOKEN="${2}" + if [ -z "$ROOM_ID" ] || [ -z "$TOKEN" ]; then + echo "Usage: purge_room " + return 1 + fi + echo "Purging room $ROOM_ID..." + curl -s -X DELETE "http://localhost:8008/_synapse/admin/v1/rooms/$ROOM_ID" \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"purge": true}' + echo "" +} + +purge_history() { + ROOM_ID="${1}" + DAYS="${2:-90}" + TOKEN="${3}" + if [ -z "$ROOM_ID" ] || [ -z "$TOKEN" ]; then + echo "Usage: purge_history " + return 1 + fi + BEFORE_TS=$(( $(date +%s) * 1000 - DAYS * 86400000 )) + echo "Purging history older than $DAYS days from $ROOM_ID..." + curl -s -X POST "http://localhost:8008/_synapse/admin/v1/purge_history/$ROOM_ID" \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d "{\"purge_up_to_ts\": $BEFORE_TS}" + echo "" +} + +purge_empty_rooms() { + TOKEN="${1}" + if [ -z "$TOKEN" ]; then + echo "Usage: purge_empty_rooms " + return 1 + fi + echo "Fetching rooms with no local members..." + ROOMS=$(curl -s "http://localhost:8008/_synapse/admin/v1/rooms?limit=1000" \ + -H "Authorization: Bearer $TOKEN" \ + | python3 -c " +import sys, json +data = json.load(sys.stdin) +for r in data.get('rooms', []): + if r.get('joined_local_members', 0) == 0: + print(r['room_id']) +") + COUNT=$(echo "$ROOMS" | grep -c '.' || true) + echo "Found $COUNT empty rooms." + if [ "$COUNT" -eq 0 ]; then + echo "Nothing to purge." + return 0 + fi + echo "$ROOMS" + echo "" + echo "Purging..." + for ROOM_ID in $ROOMS; do + echo " Purging $ROOM_ID" + curl -s -X DELETE "http://localhost:8008/_synapse/admin/v1/rooms/$ROOM_ID" \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"purge": true}' > /dev/null + done + echo "Done." +} + set_admin () { admin=akadmin if [ -n "$1" ]