Files
aza/AzA march 2026 - Kopie (27)/hetzner_verify_woo_deploy.sh
2026-05-20 00:09:28 +02:00

170 lines
6.2 KiB
Bash

#!/usr/bin/env bash
set -eu
export BK="/root/aza-app/backups/woocommerce_period_sync_env_deploy_20260518_221614"
python3 << 'PY'
import json, os, sqlite3, statistics
BK = os.environ["BK"]
def stats(path, label):
con = sqlite3.connect(path)
cur = con.cursor()
def one(q):
return cur.execute(q).fetchone()[0]
active = one("SELECT COUNT(*) FROM licenses WHERE lower(trim(status))='active'")
wc = one(
"SELECT COUNT(*) FROM licenses WHERE lower(trim(status))='active' AND subscription_id LIKE 'wc_sub_%'"
)
st = one(
"SELECT COUNT(*) FROM licenses WHERE lower(trim(status))='active' AND current_period_start IS NOT NULL"
)
en = one(
"SELECT COUNT(*) FROM licenses WHERE lower(trim(status))='active' AND current_period_end IS NOT NULL"
)
both = one(
"""SELECT COUNT(*) FROM licenses WHERE lower(trim(status))='active'
AND current_period_start IS NOT NULL AND current_period_end IS NOT NULL
AND current_period_start < current_period_end"""
)
rows = cur.execute(
"""SELECT current_period_end - current_period_start FROM licenses
WHERE lower(trim(status))='active' AND current_period_start IS NOT NULL
AND current_period_end IS NOT NULL AND current_period_start < current_period_end"""
).fetchall()
lengths = [int(r[0]) for r in rows if r[0] is not None]
med = int(statistics.median(lengths)) if lengths else None
mn = min(lengths) if lengths else None
mx = max(lengths) if lengths else None
con.close()
print(label, json.dumps({
"active_total": active,
"active_wc_sub": wc,
"active_with_start": st,
"active_with_end": en,
"active_start_before_end": both,
"period_len_sec_min": mn,
"period_len_sec_median": med,
"period_len_sec_max": mx,
}))
stats(os.path.join(BK, "stripe_webhook.sqlite"), "BEFORE_BACKUP_DB")
stats("/root/aza-app/data/stripe_webhook.sqlite", "AFTER_DEPLOY_PRE_SYNC")
PY
docker exec aza-api sh -c 'curl -sS -X POST http://127.0.0.1:8000/admin/woocommerce_sync_periods -H "X-Admin-Token: $AZA_ADMIN_TOKEN"' > /tmp/woo_sync_out.json
python3 << 'PY'
import json
with open("/tmp/woo_sync_out.json", "r", encoding="utf-8") as f:
d = json.load(f)
print("SYNC_OK", d.get("ok"))
for k in (
"total_active_wc_sub_rows",
"updated",
"skipped",
"failed_count",
):
print(k, d.get(k))
errs = d.get("errors") or []
print("errors_truncated_count", len(errs))
if errs:
safe = []
for e in errs[:8]:
if isinstance(e, dict):
safe.append(
{
"subscription_id_prefix": e.get("subscription_id_prefix"),
"detail": e.get("detail"),
}
)
print("errors_sample", json.dumps(safe))
PY
python3 << 'PY'
import json, sqlite3, statistics
def stats(path, label):
con = sqlite3.connect(path)
cur = con.cursor()
def one(q):
return cur.execute(q).fetchone()[0]
active = one("SELECT COUNT(*) FROM licenses WHERE lower(trim(status))='active'")
wc = one(
"SELECT COUNT(*) FROM licenses WHERE lower(trim(status))='active' AND subscription_id LIKE 'wc_sub_%'"
)
st = one(
"SELECT COUNT(*) FROM licenses WHERE lower(trim(status))='active' AND current_period_start IS NOT NULL"
)
en = one(
"SELECT COUNT(*) FROM licenses WHERE lower(trim(status))='active' AND current_period_end IS NOT NULL"
)
both = one(
"""SELECT COUNT(*) FROM licenses WHERE lower(trim(status))='active'
AND current_period_start IS NOT NULL AND current_period_end IS NOT NULL
AND current_period_start < current_period_end"""
)
rows = cur.execute(
"""SELECT current_period_end - current_period_start FROM licenses
WHERE lower(trim(status))='active' AND current_period_start IS NOT NULL
AND current_period_end IS NOT NULL AND current_period_start < current_period_end"""
).fetchall()
lengths = [int(r[0]) for r in rows if r[0] is not None]
med = int(statistics.median(lengths)) if lengths else None
mn = min(lengths) if lengths else None
mx = max(lengths) if lengths else None
con.close()
print(label, json.dumps({
"active_total": active,
"active_wc_sub": wc,
"active_with_start": st,
"active_with_end": en,
"active_start_before_end": both,
"period_len_sec_min": mn,
"period_len_sec_median": med,
"period_len_sec_max": mx,
}))
stats("/root/aza-app/data/stripe_webhook.sqlite", "AFTER_SYNC")
PY
rm -f /tmp/woo_sync_out.json
OVERVIEW_CODE=$(docker exec aza-api sh -c 'curl -sS -o /tmp/bo.json -w "%{http_code}" http://127.0.0.1:8000/admin/ai_budget_overview -H "X-Admin-Token: $AZA_ADMIN_TOKEN"')
echo "AI_BUDGET_OVERVIEW_HTTP" "$OVERVIEW_CODE"
docker cp aza-api:/tmp/bo.json /tmp/bo.json
python3 << 'PY'
import json
try:
with open("/tmp/bo.json", "r", encoding="utf-8") as f:
d = json.load(f)
except Exception as e:
print("AI_BUDGET_OVERVIEW_PARSE_ERROR", type(e).__name__)
raise SystemExit(0)
# summarize structure only
lic = d.get("licenses")
if isinstance(lic, list) and lic:
row = lic[0]
keys = sorted(row.keys())
need = {"budget_usd", "used_usd", "remaining_usd", "available_percent"}
print("overview_first_row_keys_has_budget_cols", {k: (k in row) for k in sorted(need)})
pe = row.get("current_period_end")
ps = row.get("current_period_start")
print("overview_first_row_period_start_present", ps is not None)
print("overview_first_row_period_end_present", pe is not None)
print("overview_license_row_count", len(lic) if isinstance(lic, list) else None)
PY
rm -f /tmp/bo.json
CSV_CODE=$(docker exec aza-api sh -c 'curl -sS -o /tmp/bc.csv -w "%{http_code}" "http://127.0.0.1:8000/admin/ai_budget_export.csv" -H "X-Admin-Token: $AZA_ADMIN_TOKEN"')
echo "AI_BUDGET_CSV_HTTP" "$CSV_CODE"
docker exec aza-api head -n 1 /tmp/bc.csv | python3 -c "import sys; line=sys.stdin.read().strip().split(','); need=['budget_usd','used_usd','remaining_usd','available_percent']; print('csv_header_has', {n:(n in line) for n in need}); print('csv_header_col_count', len(line))"
docker exec aza-api rm -f /tmp/bc.json /tmp/bo.json /tmp/bc.csv 2>/dev/null || true