Reduce Supabase disk IO
This commit is contained in:
+13
-3
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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()
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user