Reduce Supabase disk IO

This commit is contained in:
2569718930@qq.com
2026-05-29 17:22:33 +08:00
parent 9e3a4e2f45
commit 2269aaefd7
63 changed files with 4637 additions and 410 deletions
+13 -3
View File
@@ -303,8 +303,10 @@ def _resolve_weekly_profile(request: Request) -> Dict[str, Any]:
def _assert_entitlement(request: Request) -> None:
if SUPABASE_ENTITLEMENT.enabled:
if _legacy_service_token_valid(request):
_bind_forwarded_supabase_identity(request)
return
if _bind_forwarded_supabase_identity(request):
return
if not extract_bearer_token(request.headers.get("authorization")):
return
if not _SUPABASE_AUTH_REQUIRED:
_bind_optional_supabase_identity(request)
return
@@ -321,11 +323,19 @@ def _assert_entitlement(request: Request) -> None:
identity = SUPABASE_ENTITLEMENT.get_identity(access_token)
if not identity:
raise HTTPException(status_code=401, detail="Unauthorized")
if not SUPABASE_ENTITLEMENT.has_active_subscription(identity.user_id):
skip_subscription_gate = bool(
getattr(request.state, "skip_subscription_gate", False)
)
if (
not skip_subscription_gate
and not SUPABASE_ENTITLEMENT.has_active_subscription(identity.user_id)
):
raise HTTPException(status_code=403, detail="Subscription required")
request.state.auth_user_id = identity.user_id
request.state.auth_email = identity.email
request.state.auth_points = identity.points
request.state.auth_created_at = identity.created_at
from src.utils.online_tracker import record_activity
record_activity(identity.user_id)
return
+10
View File
@@ -8,6 +8,7 @@ from web.services.ops_api import (
get_ops_analytics_funnel,
get_ops_config,
get_ops_memberships_growth,
get_ops_memberships_overview,
get_ops_health_check,
get_ops_logs,
get_ops_truth_history,
@@ -55,6 +56,15 @@ async def ops_memberships_growth(request: Request, days: int = 90):
return get_ops_memberships_growth(request, days=days)
@router.get("/api/ops/memberships/overview")
async def ops_memberships_overview(
request: Request,
limit: int = 200,
days: int = 90,
):
return get_ops_memberships_overview(request, limit=limit, days=days)
@router.get("/api/ops/payments/incidents")
async def ops_payment_incidents(
request: Request,
+51 -19
View File
@@ -12,9 +12,17 @@ from web.core import TelegramLoginRequest
import web.routes as legacy_routes
def get_auth_me_payload(request: Request) -> Dict[str, Any]:
def _require_auth_identity_without_subscription_gate(request: Request) -> Dict[str, str]:
request.state.skip_subscription_gate = True
legacy_routes._assert_entitlement(request)
legacy_routes._bind_optional_supabase_identity(request)
return legacy_routes._require_supabase_identity(request)
def get_auth_me_payload(request: Request) -> Dict[str, Any]:
request.state.skip_subscription_gate = True
legacy_routes._assert_entitlement(request)
if not str(getattr(request.state, "auth_user_id", "") or "").strip():
legacy_routes._bind_optional_supabase_identity(request)
user_id = getattr(request.state, "auth_user_id", None)
subscription_required = bool(
@@ -31,25 +39,48 @@ def get_auth_me_payload(request: Request) -> Dict[str, Any]:
if legacy_routes.SUPABASE_ENTITLEMENT.enabled and user_id:
try:
latest_subscription = (
legacy_routes.SUPABASE_ENTITLEMENT.get_latest_active_subscription(
user_id,
respect_requirement=False,
)
subscription_window = legacy_routes.SUPABASE_ENTITLEMENT.get_subscription_window(
user_id,
respect_requirement=False,
)
latest_subscription = None
latest_known_subscription = None
subscription_window_known = isinstance(subscription_window, dict)
if isinstance(subscription_window, dict):
current_subscription = subscription_window.get("current")
if isinstance(current_subscription, dict):
latest_subscription = current_subscription
rows = subscription_window.get("rows")
if not latest_subscription and isinstance(rows, list):
latest_known_subscription = next(
(row for row in rows if isinstance(row, dict)),
None,
)
if (
not latest_subscription
and not latest_known_subscription
and not subscription_window_known
and not subscription_required
):
latest_subscription = (
legacy_routes.SUPABASE_ENTITLEMENT.get_latest_active_subscription(
user_id,
respect_requirement=False,
)
)
latest_known_subscription = latest_subscription
subscription_active = bool(latest_subscription)
if subscription_required and not subscription_active:
raise HTTPException(status_code=403, detail="Subscription required")
if not latest_known_subscription:
latest_known_subscription = latest_subscription
if not latest_known_subscription:
latest_known_subscription = (
legacy_routes.SUPABASE_ENTITLEMENT.get_latest_subscription_any_status(
user_id
)
)
subscription_window = legacy_routes.SUPABASE_ENTITLEMENT.get_subscription_window(
user_id,
respect_requirement=False,
)
subscription_active = bool(latest_subscription)
if isinstance(latest_subscription, dict):
subscription_plan_code = latest_subscription.get("plan_code")
subscription_starts_at = latest_subscription.get("starts_at")
@@ -62,7 +93,11 @@ def get_auth_me_payload(request: Request) -> Dict[str, Any]:
subscription_total_expires_at = subscription_window.get("total_expires_at")
subscription_queued_days = int(subscription_window.get("queued_days") or 0)
subscription_queued_count = int(subscription_window.get("queued_count") or 0)
except HTTPException:
raise
except Exception:
if subscription_required:
raise HTTPException(status_code=403, detail="Subscription required")
subscription_active = None
subscription_plan_code = None
subscription_starts_at = None
@@ -124,8 +159,7 @@ def get_auth_me_payload(request: Request) -> Dict[str, Any]:
def login_with_telegram(request: Request, body: TelegramLoginRequest) -> Dict[str, Any]:
legacy_routes._assert_entitlement(request)
identity = legacy_routes._require_supabase_identity(request)
identity = _require_auth_identity_without_subscription_gate(request)
pricing = TelegramGroupPricing()
if not pricing.configured:
raise HTTPException(status_code=503, detail="telegram login is not configured")
@@ -156,8 +190,7 @@ def login_with_telegram(request: Request, body: TelegramLoginRequest) -> Dict[st
def bind_telegram_by_token(request: Request, body) -> Dict[str, Any]:
"""Bind Telegram identity using a one-time token from the bot /bind command."""
legacy_routes._assert_entitlement(request)
identity = legacy_routes._require_supabase_identity(request)
identity = _require_auth_identity_without_subscription_gate(request)
token = str(getattr(body, "token", "") or "").strip()
if not token:
@@ -196,8 +229,7 @@ def bind_telegram_by_token(request: Request, body) -> Dict[str, Any]:
def create_telegram_bot_bind_link(request: Request) -> Dict[str, Any]:
"""Create a one-time web-to-bot bind deep link for the authenticated account."""
legacy_routes._assert_entitlement(request)
identity = legacy_routes._require_supabase_identity(request)
identity = _require_auth_identity_without_subscription_gate(request)
db = DBManager()
token = db.create_web_bind_token(
+209 -73
View File
@@ -2,9 +2,11 @@
from __future__ import annotations
import os
from typing import Any, Dict
from fastapi import HTTPException, Request
import requests as _requests
from src.database.db_manager import DBManager
from web.core import GrantPointsRequest
@@ -29,19 +31,46 @@ def get_ops_weekly_leaderboard(request: Request, limit: int = 20) -> Dict[str, A
return {"leaderboard": db.get_weekly_leaderboard(limit=limit)}
def list_ops_memberships(request: Request, limit: int = 200) -> Dict[str, Any]:
_require_ops(request)
db = DBManager()
if getattr(legacy_routes.PAYMENT_CHECKOUT, "enabled", False):
try:
legacy_routes.PAYMENT_CHECKOUT.reconcile_recent_intents(
limit=min(max(int(limit or 200), 20), 200)
)
except Exception:
pass
subscriptions = legacy_routes.SUPABASE_ENTITLEMENT.list_active_subscriptions(
limit=limit
def _list_active_subscriptions_with_windows(
limit: int,
) -> tuple[list[dict[str, Any]], dict[str, dict[str, Any]], bool]:
active_window_query = getattr(
legacy_routes.SUPABASE_ENTITLEMENT,
"list_active_subscription_windows",
None,
)
if not callable(active_window_query):
return [], {}, False
try:
active_window_payload = active_window_query(limit=limit)
except Exception:
return [], {}, False
if not isinstance(active_window_payload, dict):
return [], {}, False
active_window_subscriptions = active_window_payload.get("subscriptions")
active_window_windows = active_window_payload.get("windows")
if not isinstance(active_window_subscriptions, list):
return [], {}, False
if not active_window_subscriptions and not (
isinstance(active_window_windows, dict) and active_window_windows
):
return [], {}, False
subscriptions = [
item for item in active_window_subscriptions if isinstance(item, dict)
]
subscription_windows = (
active_window_windows if isinstance(active_window_windows, dict) else {}
)
return subscriptions, subscription_windows, True
def _build_membership_rows(
db: DBManager,
subscriptions: list[dict[str, Any]],
subscription_windows: dict[str, dict[str, Any]],
) -> list[dict[str, Any]]:
subscription_user_ids = [str(item.get("user_id") or "") for item in subscriptions]
user_map = db.get_users_by_supabase_user_ids(subscription_user_ids)
unresolved_user_ids = [
@@ -56,18 +85,17 @@ def list_ops_memberships(request: Request, limit: int = 200) -> Dict[str, Any]:
auth_user_map = legacy_routes.SUPABASE_ENTITLEMENT.get_auth_users(
unresolved_user_ids
)
if not subscription_windows:
subscription_windows = legacy_routes.SUPABASE_ENTITLEMENT.list_subscription_windows(
subscription_user_ids,
bypass_cache=True,
)
deduped: dict[str, dict] = {}
for item in subscriptions:
user_id = str(item.get("user_id") or "").strip().lower()
local_user = user_map.get(user_id, {})
auth_user = auth_user_map.get(user_id, {})
subscription_window = (
legacy_routes.SUPABASE_ENTITLEMENT.get_subscription_window(
user_id,
respect_requirement=False,
bypass_cache=True,
)
)
subscription_window = subscription_windows.get(user_id, {})
current_expires_at = item.get("expires_at")
total_expires_at = (
subscription_window.get("total_expires_at")
@@ -112,22 +140,20 @@ def list_ops_memberships(request: Request, limit: int = 200) -> Dict[str, Any]:
current_expires = str(row.get("expires_at") or "")
if existing is None or current_expires > existing_expires:
deduped[user_id] = row
rows = sorted(
return sorted(
deduped.values(),
key=lambda item: str(item.get("expires_at") or ""),
)
return {"memberships": rows}
def get_ops_memberships_growth(request: Request, days: int = 90) -> dict[str, Any]:
_require_ops(request)
def _build_membership_growth(
subscriptions: list[dict[str, Any]],
days: int,
) -> dict[str, Any]:
from collections import defaultdict
from datetime import datetime, timedelta
safe_days = max(7, min(365, int(days or 90)))
subscriptions = legacy_routes.SUPABASE_ENTITLEMENT.list_active_subscriptions(
limit=5000
)
now = datetime.utcnow()
cutoff = now - timedelta(days=safe_days)
@@ -178,6 +204,78 @@ def get_ops_memberships_growth(request: Request, days: int = 90) -> dict[str, An
return {"days": safe_days, "daily": daily}
def list_ops_memberships(request: Request, limit: int = 200) -> Dict[str, Any]:
_require_ops(request)
db = DBManager()
reconcile_enabled = (
str(os.getenv("POLYWEATHER_OPS_MEMBERSHIPS_RECONCILE_ENABLED") or "")
.strip()
.lower()
in {"1", "true", "yes", "on"}
)
if reconcile_enabled and getattr(legacy_routes.PAYMENT_CHECKOUT, "enabled", False):
try:
legacy_routes.PAYMENT_CHECKOUT.reconcile_recent_intents(
limit=min(max(int(limit or 200), 20), 200)
)
except Exception:
pass
subscriptions, subscription_windows, used_active_window_query = (
_list_active_subscriptions_with_windows(limit)
)
if not used_active_window_query:
subscriptions = legacy_routes.SUPABASE_ENTITLEMENT.list_active_subscriptions(
limit=limit
)
return {
"memberships": _build_membership_rows(
db,
subscriptions,
subscription_windows,
)
}
def get_ops_memberships_growth(request: Request, days: int = 90) -> dict[str, Any]:
_require_ops(request)
subscriptions, _, used_active_window_query = _list_active_subscriptions_with_windows(
limit=5000
)
if not used_active_window_query:
subscriptions = legacy_routes.SUPABASE_ENTITLEMENT.list_active_subscriptions(
limit=5000
)
return _build_membership_growth(subscriptions, days)
def get_ops_memberships_overview(
request: Request,
limit: int = 200,
days: int = 90,
) -> dict[str, Any]:
_require_ops(request)
db = DBManager()
safe_limit = max(1, min(int(limit or 200), 1000))
query_limit = max(safe_limit, 5000)
subscriptions, subscription_windows, used_active_window_query = (
_list_active_subscriptions_with_windows(limit=query_limit)
)
if not used_active_window_query:
subscriptions = legacy_routes.SUPABASE_ENTITLEMENT.list_active_subscriptions(
limit=query_limit
)
membership_subscriptions = subscriptions[:safe_limit]
return {
"memberships": _build_membership_rows(
db,
membership_subscriptions,
subscription_windows,
),
**_build_membership_growth(subscriptions, days),
}
def list_ops_payment_incidents(
request: Request,
limit: int = 50,
@@ -415,6 +513,59 @@ def update_ops_config(request: Request, key: str, value: str) -> dict[str, Any]:
# ── Subscriptions ───────────────────────────────────────────────────
def _supabase_service_headers(
service_role_key: str,
*,
prefer: str | None = None,
) -> dict[str, str]:
headers = {
"apikey": service_role_key,
"Authorization": f"Bearer {service_role_key}",
"Content-Type": "application/json",
}
if prefer:
headers["Prefer"] = prefer
return headers
def _lookup_supabase_user_id_by_email(
supabase_url: str,
service_role_key: str,
email: str,
) -> str:
normalized_email = str(email or "").strip().lower()
if not normalized_email:
return ""
base = str(supabase_url or "").strip().rstrip("/")
headers = _supabase_service_headers(service_role_key)
profile_resp = _requests.get(
f"{base}/rest/v1/profiles",
headers=headers,
params={
"select": "id",
"email": f"eq.{normalized_email}",
"limit": "1",
},
timeout=10,
)
if profile_resp.ok:
profiles = profile_resp.json() if profile_resp.content else []
if isinstance(profiles, list) and profiles:
user_id = str((profiles[0] or {}).get("id") or "").strip()
if user_id:
return user_id
user_resp = _requests.get(
f"{base}/auth/v1/admin/users",
headers=headers,
params={"filter": f"email.eq.{normalized_email}"},
timeout=10,
)
users = user_resp.json().get("users", []) if user_resp.ok else []
return str(users[0].get("id") or "").strip() if users else ""
def grant_ops_subscription(
request: Request,
email: str,
@@ -423,9 +574,7 @@ def grant_ops_subscription(
deduct_points: int = 0,
) -> dict[str, Any]:
_require_ops(request)
import os
from datetime import datetime, timedelta
import requests as _requests
supabase_url = str(os.getenv("SUPABASE_URL") or "").strip().rstrip("/")
service_role_key = str(os.getenv("SUPABASE_SERVICE_ROLE_KEY") or "").strip()
@@ -444,22 +593,11 @@ def grant_ops_subscription(
if not normalized_email:
raise HTTPException(status_code=400, detail="email is required")
headers = {
"apikey": service_role_key,
"Authorization": f"Bearer {service_role_key}",
"Content-Type": "application/json",
"Prefer": "return=representation",
}
# Look up user by email
user_resp = _requests.get(
f"{supabase_url}/auth/v1/admin/users",
headers=headers,
params={"filter": f"email.eq.{normalized_email}"},
timeout=10,
user_id = _lookup_supabase_user_id_by_email(
supabase_url,
service_role_key,
normalized_email,
)
users = user_resp.json().get("users", []) if user_resp.ok else []
user_id = users[0].get("id") if users else None
if not user_id:
raise HTTPException(
status_code=404, detail=f"user not found: {normalized_email}"
@@ -481,7 +619,7 @@ def grant_ops_subscription(
resp = _requests.post(
f"{supabase_url}/rest/v1/subscriptions",
headers=headers,
headers=_supabase_service_headers(service_role_key, prefer="return=minimal"),
json=payload,
timeout=10,
)
@@ -489,6 +627,7 @@ def grant_ops_subscription(
raise HTTPException(
status_code=500, detail=f"Supabase insert failed: {resp.text[:200]}"
)
legacy_routes.SUPABASE_ENTITLEMENT.invalidate_subscription_cache(user_id)
result: dict[str, Any] = {
"ok": True,
@@ -516,9 +655,7 @@ def extend_ops_subscription(
additional_days: int = 30,
) -> dict[str, Any]:
_require_ops(request)
import os
from datetime import datetime, timedelta
import requests as _requests
supabase_url = str(os.getenv("SUPABASE_URL") or "").strip().rstrip("/")
service_role_key = str(os.getenv("SUPABASE_SERVICE_ROLE_KEY") or "").strip()
@@ -530,20 +667,26 @@ def extend_ops_subscription(
if not normalized_email:
raise HTTPException(status_code=400, detail="email is required")
headers = {
"apikey": service_role_key,
"Authorization": f"Bearer {service_role_key}",
"Content-Type": "application/json",
"Prefer": "return=representation",
}
headers = _supabase_service_headers(service_role_key)
user_id = _lookup_supabase_user_id_by_email(
supabase_url,
service_role_key,
normalized_email,
)
if not user_id:
raise HTTPException(
status_code=404, detail=f"user not found: {normalized_email}"
)
# Find latest active subscription
subs_resp = _requests.get(
f"{supabase_url}/rest/v1/subscriptions",
headers=headers,
params={
"select": "*",
"email": f"eq.{normalized_email}",
"select": "id,expires_at",
"user_id": f"eq.{user_id}",
"status": "eq.active",
"order": "expires_at.desc",
"limit": "1",
},
@@ -565,11 +708,12 @@ def extend_ops_subscription(
patch_resp = _requests.patch(
f"{supabase_url}/rest/v1/subscriptions?id=eq.{sub['id']}",
headers=headers,
headers=_supabase_service_headers(service_role_key, prefer="return=minimal"),
json={"expires_at": new_expiry},
timeout=10,
)
if patch_resp.ok:
legacy_routes.SUPABASE_ENTITLEMENT.invalidate_subscription_cache(user_id)
return {
"ok": True,
"email": normalized_email,
@@ -587,8 +731,6 @@ def get_ops_user_subscriptions(
) -> dict[str, Any]:
"""Return ALL subscription rows for a user (by email), regardless of status."""
_require_ops(request)
import os
import requests as _requests
supabase_url = str(os.getenv("SUPABASE_URL") or "").strip().rstrip("/")
service_role_key = str(os.getenv("SUPABASE_SERVICE_ROLE_KEY") or "").strip()
@@ -599,21 +741,13 @@ def get_ops_user_subscriptions(
if not normalized_email:
raise HTTPException(status_code=400, detail="email is required")
headers = {
"apikey": service_role_key,
"Authorization": f"Bearer {service_role_key}",
"Content-Type": "application/json",
}
headers = _supabase_service_headers(service_role_key)
# Resolve user_id from email via auth admin API
user_resp = _requests.get(
f"{supabase_url}/auth/v1/admin/users",
headers=headers,
params={"filter": f"email.eq.{normalized_email}"},
timeout=10,
user_id = _lookup_supabase_user_id_by_email(
supabase_url,
service_role_key,
normalized_email,
)
users = user_resp.json().get("users", []) if user_resp.ok else []
user_id = users[0].get("id") if users else None
if not user_id:
raise HTTPException(
status_code=404, detail=f"user not found: {normalized_email}"
@@ -1134,11 +1268,13 @@ def get_ops_telegram_audit(request: Request) -> Dict[str, Any]:
anomalies = []
valid_members = []
from web.services.ops_api import legacy_routes
active_subs = legacy_routes.SUPABASE_ENTITLEMENT.list_active_subscriptions(
active_subs, _, used_active_window_query = _list_active_subscriptions_with_windows(
limit=5000
)
if not used_active_window_query:
active_subs = legacy_routes.SUPABASE_ENTITLEMENT.list_active_subscriptions(
limit=5000
)
active_subs_map = {}
for sub in active_subs:
uid = str(sub.get("user_id") or "").strip().lower()
+1 -1
View File
@@ -27,6 +27,7 @@ def _raise_payment_error(exc: Exception) -> None:
def _require_payment_identity(request: Request) -> Dict[str, Any]:
request.state.skip_subscription_gate = True
legacy_routes._assert_entitlement(request)
identity = legacy_routes._require_supabase_identity(request)
user_id = str(identity.get("user_id") or "").strip()
@@ -36,7 +37,6 @@ def _require_payment_identity(request: Request) -> Dict[str, Any]:
def get_payment_config(request: Request) -> Dict[str, Any]:
legacy_routes._assert_entitlement(request)
try:
return legacy_routes.PAYMENT_CHECKOUT.get_config_payload()
except legacy_routes.PaymentCheckoutError as exc: