新增终端大洲分组与移动端卡片 CSS 模块

包含分组标题行、移动端 Tab 隐藏滚动条、信号卡片样式及浅色主题适配。
This commit is contained in:
2569718930@qq.com
2026-05-25 01:55:55 +08:00
parent 0b47f4b487
commit 9a49ff3f5e
14 changed files with 237 additions and 392 deletions
-1
View File
@@ -94,7 +94,6 @@ SUPABASE_HTTP_TIMEOUT_SEC=8
SUPABASE_AUTH_CACHE_TTL_SEC=30
SUPABASE_SUB_CACHE_TTL_SEC=60
POLYWEATHER_BACKEND_ENTITLEMENT_TOKEN=
POLYWEATHER_SIGNUP_TRIAL_ENABLED=false
POLYWEATHER_TELEGRAM_JOIN_INELIGIBLE_ACTION=decline
########################################
-4
View File
@@ -30,10 +30,6 @@ POLYWEATHER_AUTH_ENABLED=false
# false: 登录可选,访客可浏览
POLYWEATHER_AUTH_REQUIRED=false
# 可选:分享式看板访问令牌
# 设置后,可通过 /?access_token=<token> 打开受保护看板
POLYWEATHER_DASHBOARD_ACCESS_TOKEN=
# 可选:前端 API Route 转发到后端时附带的共享令牌
# 仅当后端启用了 entitlement / 订阅校验时需要
POLYWEATHER_BACKEND_ENTITLEMENT_TOKEN=
+24 -18
View File
@@ -2,14 +2,18 @@
import { RefreshCw } from "lucide-react";
import { useEffect } from "react";
import { I18nProvider, useI18n } from "@/hooks/useI18n";
export default function AccountErrorPage({
function AccountErrorContent({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
const { locale } = useI18n();
const isEn = locale === "en-US";
useEffect(() => {
console.error("Account page error:", error);
}, [error]);
@@ -38,7 +42,7 @@ export default function AccountErrorPage({
color: "var(--color-accent-primary, #4DA3FF)",
}}
>
{isEn ? "Account Page Error" : "账户页面出错"}
</h1>
<p
style={{
@@ -49,21 +53,9 @@ export default function AccountErrorPage({
lineHeight: 1.7,
}}
>
MetaMask
Rabby
</p>
<p
style={{
color: "var(--color-text-muted, #7D8FA3)",
fontSize: "0.8rem",
margin: 0,
maxWidth: 420,
lineHeight: 1.6,
}}
>
If this happened during payment or wallet binding, the most common cause is
conflicting wallet extensions. Try disabling other wallet extensions (e.g.
MetaMask + Rabby) and refresh.
{isEn
? "If this happened during payment or wallet binding, the most common cause is conflicting wallet extensions (e.g. MetaMask and Rabby open at the same time). Try disabling other wallet extensions and refresh."
: "如果是在支付或绑定钱包时出现此问题,常见原因是钱包插件冲突(例如同时开启了 MetaMask 和 Rabby)。请尝试关闭其他钱包插件后刷新页面重试。"}
</p>
<button
type="button"
@@ -84,8 +76,22 @@ export default function AccountErrorPage({
}}
>
<RefreshCw size={14} />
{isEn ? "Retry" : "重试"}
</button>
</div>
);
}
export default function AccountErrorPage({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
return (
<I18nProvider>
<AccountErrorContent error={error} reset={reset} />
</I18nProvider>
);
}
@@ -0,0 +1,131 @@
"use client";
import Link from "next/link";
import { useI18n } from "@/hooks/useI18n";
export function EntitlementRequiredClient({ nextPath }: { nextPath: string }) {
const { locale } = useI18n();
const isEn = locale === "en-US";
const copy = {
title: isEn ? "Sign in required" : "需要登录方可访问此页面",
desc: isEn
? "This page requires you to sign in. Please sign in and try again."
: "本页面需要登录权限。请登录后重试。",
signIn: isEn ? "Sign in" : "去登录",
backHome: isEn ? "Back to Home" : "返回首页",
legacyNote: isEn ? "Legacy token mode still supported" : "传统令牌模式仍支持",
requestedPath: isEn ? "Requested path" : "请求路径",
};
return (
<main
style={{
minHeight: "100vh",
display: "grid",
placeItems: "center",
background:
"radial-gradient(circle at 20% 20%, #13264f 0%, #071127 45%, #040812 100%)",
color: "#d6e2ff",
padding: "24px",
}}
>
<section
style={{
width: "100%",
maxWidth: 480,
border: "1px solid rgba(68, 92, 140, 0.45)",
borderRadius: 16,
padding: 24,
background: "rgba(9, 18, 36, 0.88)",
boxShadow: "0 20px 50px rgba(0, 0, 0, 0.35)",
textAlign: "center",
}}
>
<h1
style={{
margin: 0,
fontSize: 22,
lineHeight: 1.3,
fontWeight: 800,
}}
>
{copy.title}
</h1>
<p style={{ marginTop: 12, color: "#9fb2da", lineHeight: 1.6 }}>
{copy.desc}
</p>
<div
style={{
marginTop: 20,
display: "flex",
gap: 12,
justifyContent: "center",
flexWrap: "wrap",
}}
>
<Link
href={`/auth/login?next=${encodeURIComponent(nextPath)}`}
style={{
display: "inline-flex",
alignItems: "center",
gap: 6,
minHeight: 40,
padding: "8px 20px",
borderRadius: 12,
background: "linear-gradient(135deg, #2563EB, #4F46E5)",
color: "#fff",
fontWeight: 700,
textDecoration: "none",
fontSize: 14,
}}
>
{copy.signIn}
</Link>
<Link
href="/"
style={{
display: "inline-flex",
alignItems: "center",
gap: 6,
minHeight: 40,
padding: "8px 20px",
borderRadius: 12,
border: "1px solid rgba(68, 92, 140, 0.45)",
background: "rgba(68, 92, 140, 0.2)",
color: "#d6e2ff",
fontWeight: 600,
textDecoration: "none",
fontSize: 14,
}}
>
{copy.backHome}
</Link>
</div>
<p
style={{
marginTop: 20,
fontSize: 12,
color: "#7891b5",
lineHeight: 1.5,
}}
>
{copy.legacyNote}{" "}
<code
style={{
background: "rgba(255,255,255,0.06)",
padding: "2px 6px",
borderRadius: 4,
}}
>
?access_token=&lt;your-token&gt;
</code>
<br />
<span style={{ marginTop: 4, display: "inline-block" }}>
{copy.requestedPath}: <code>{nextPath}</code>
</span>
</p>
</section>
</main>
);
}
+5 -94
View File
@@ -1,4 +1,5 @@
import Link from "next/link";
import { EntitlementRequiredClient } from "./EntitlementRequiredClient";
import { I18nProvider } from "@/hooks/useI18n";
type Props = {
searchParams?: Promise<{ next?: string }>;
@@ -9,98 +10,8 @@ export default async function EntitlementRequiredPage({ searchParams }: Props) {
const nextPath = params.next || "/";
return (
<main
style={{
minHeight: "100vh",
display: "grid",
placeItems: "center",
background:
"radial-gradient(circle at 20% 20%, #13264f 0%, #071127 45%, #040812 100%)",
color: "#d6e2ff",
padding: "24px",
}}
>
<section
style={{
width: "100%",
maxWidth: 480,
border: "1px solid rgba(68, 92, 140, 0.45)",
borderRadius: 16,
padding: 24,
background: "rgba(9, 18, 36, 0.88)",
boxShadow: "0 20px 50px rgba(0, 0, 0, 0.35)",
textAlign: "center",
}}
>
<h1
style={{
margin: 0,
fontSize: 22,
lineHeight: 1.3,
fontWeight: 800,
}}
>
访
</h1>
<p style={{ marginTop: 12, color: "#9fb2da", lineHeight: 1.6 }}>
<br />
Sign in required to access this page.
</p>
<div style={{ marginTop: 20, display: "flex", gap: 12, justifyContent: "center", flexWrap: "wrap" }}>
<Link
href={`/auth/login?next=${encodeURIComponent(nextPath)}`}
style={{
display: "inline-flex",
alignItems: "center",
gap: 6,
minHeight: 40,
padding: "8px 20px",
borderRadius: 12,
background: "linear-gradient(135deg, #2563EB, #4F46E5)",
color: "#fff",
fontWeight: 700,
textDecoration: "none",
fontSize: 14,
}}
>
/ Sign in
</Link>
<Link
href="/"
style={{
display: "inline-flex",
alignItems: "center",
gap: 6,
minHeight: 40,
padding: "8px 20px",
borderRadius: 12,
border: "1px solid rgba(68, 92, 140, 0.45)",
background: "rgba(68, 92, 140, 0.2)",
color: "#d6e2ff",
fontWeight: 600,
textDecoration: "none",
fontSize: 14,
}}
>
/ Back to Home
</Link>
</div>
<p
style={{
marginTop: 20,
fontSize: 12,
color: "#7891b5",
lineHeight: 1.5,
}}
>
<code style={{ background: "rgba(255,255,255,0.06)", padding: "2px 6px", borderRadius: 4 }}>?access_token=&lt;your-token&gt;</code>
<br />
<span style={{ marginTop: 4, display: "inline-block" }}>
/ Requested path: <code>{nextPath}</code>
</span>
</p>
</section>
</main>
<I18nProvider>
<EntitlementRequiredClient nextPath={nextPath} />
</I18nProvider>
);
}
@@ -888,7 +888,6 @@ export function AccountCenter() {
const joinedAt = formatTime(user?.created_at, locale);
const isSubscribed = Boolean(backend?.subscription_active);
const planCode = String(backend?.subscription_plan_code || "").trim();
const isTrialPlan = /trial/i.test(planCode);
const currentExpiryRaw = String(
backend?.subscription_expires_at || user?.user_metadata?.pro_expiry || "",
).trim();
@@ -904,7 +903,7 @@ export function AccountCenter() {
);
const hasQueuedExtension = Boolean(isSubscribed && queuedExtensionDays > 0);
const canAccessPaidTelegramGroup = Boolean(
isSubscribed && (!isTrialPlan || hasQueuedExtension),
isSubscribed,
);
const telegramBound = Number(backend?.telegram_pricing?.telegram_id || 0) > 0;
const displayExpiryRaw = isSubscribed ? totalExpiryRaw : currentExpiryRaw;
@@ -933,7 +932,7 @@ export function AccountCenter() {
const paymentFeatureReady = paymentReadyForRecovery;
const canOpenCheckoutOverlay = Boolean(
paymentFeatureReady &&
(!isSubscribed || isTrialPlan || showExpiringSoon || showExpiredReminder),
(!isSubscribed || showExpiringSoon || showExpiredReminder),
);
const subscriptionStatusTitle = showExpiredReminder
? copy.proExpiredTitle
@@ -2237,7 +2236,7 @@ export function AccountCenter() {
>
{isSubscribed
? copy.proMember
: copy.freeTier}
: isEn ? "UNSUBSCRIBED" : "未订阅"}
</span>
</div>
<p className="mb-4 font-mono text-sm text-slate-500">
@@ -129,7 +129,6 @@ export function createAccountCopy(isEn: boolean): Record<string, string> {
? "Wallet bound. Creating order and sending payment..."
: "钱包已绑定,正在创建订单并发起支付...",
proMember: "PRO MEMBER",
freeTier: isEn ? "UNSUBSCRIBED" : "未订阅",
proPendingSync: isEn ? "Activated (pending sync)" : "已开通(待同步)",
noProSubscription: isEn ? "No Pro subscription" : "暂无 Pro 订阅",
proEndsSoonTitle: isEn ? "Pro renewal due soon" : "Pro 即将到期",
@@ -0,0 +1,65 @@
/* ScanTerminalContinent — continent group headers and mobile cards */
.root {
--gap-green: #16a34a;
--gap-orange: #ea580c;
--gap-slate: #475569;
--gap-gray: #94a3b8;
--gap-red: #dc2626;
}
/* Group header row */
.groupHeader {
background: #eef2f6;
border-bottom: 1px solid #cbd5e1;
}
.groupHeader:hover {
background: #e2e8f0;
}
/* Mobile: hide scrollbar on tab bar */
.mobileTabs {
scrollbar-width: none;
-ms-overflow-style: none;
}
.mobileTabs::-webkit-scrollbar {
display: none;
}
/* Mobile card */
.mobileCard {
border: 1px solid #e2e8f0;
background: #ffffff;
border-radius: 8px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
}
.mobileCard:hover {
background: #f0f7ff;
}
/* Signal badge colors */
.signalActive {
color: #059669;
}
.signalWatch {
color: #d97706;
}
.signalClosed {
color: #94a3b8;
}
.signalData {
color: #dc2626;
}
/* Light theme overrides — match scan-terminal's .scan-terminal.light scope */
:global(.scan-terminal.light) .groupHeader {
background: #f8fafc;
border-bottom-color: #e2e8f0;
}
:global(.scan-terminal.light) .groupHeader:hover {
background: #f1f5f9;
}
:global(.scan-terminal.light) .mobileCard {
background: #ffffff;
border-color: #e2e8f0;
}
@@ -22,6 +22,7 @@ import scanTerminalListStyles from "./ScanTerminalList.module.css";
import scanTerminalMobileStyles from "./ScanTerminalMobile.module.css";
import scanTerminalOpportunityStyles from "./ScanTerminalOpportunity.module.css";
import scanTerminalShellStyles from "./ScanTerminalShell.module.css";
import scanTerminalContinentStyles from "./ScanTerminalContinent.module.css";
import scanTerminalStateStyles from "./ScanTerminalState.module.css";
export const scanRootClass = clsx(
@@ -38,6 +39,7 @@ export const scanRootClass = clsx(
scanTerminalStateStyles.root,
scanTerminalOpportunityStyles.root,
scanTerminalCardStyles.root,
scanTerminalContinentStyles.root,
scanTerminalMobileStyles.root,
detailChromeStyles.root,
detailContentStyles.root,
-12
View File
@@ -1260,18 +1260,6 @@ export function DashboardStoreProvider({
});
}
const isTrialPlan = /trial/i.test(
String(proAccess.subscriptionPlanCode || ""),
);
if (
isTrialPlan &&
markAnalyticsOnce(`signup-completed:${proAccess.userId}`, "local")
) {
trackAppEvent("signup_completed", {
source: "auth_me_trial",
subscription_plan_code: proAccess.subscriptionPlanCode,
});
}
}, [
proAccess.authenticated,
proAccess.loading,
+1 -50
View File
@@ -5,8 +5,6 @@ import {
} from "@/lib/supabase/server";
import { isLocalFullAccessHost } from "@/lib/local-dev-access";
const SESSION_COOKIE = "polyweather_entitlement";
function readEnvBool(name: string, fallback: boolean) {
const raw = process.env[name];
if (raw == null) return fallback;
@@ -25,7 +23,6 @@ function isPublicPage(pathname: string) {
pathname === "/" ||
pathname.startsWith("/docs") ||
pathname.startsWith("/subscription-help") ||
pathname === "/entitlement-required" ||
pathname.startsWith("/auth/login") ||
pathname.startsWith("/auth/callback")
);
@@ -93,52 +90,6 @@ async function handleTerminalGate(request: NextRequest): Promise<NextResponse> {
return NextResponse.redirect(loginUrl);
}
function handleLegacyTokenGate(request: NextRequest) {
const requiredToken = process.env.POLYWEATHER_DASHBOARD_ACCESS_TOKEN?.trim();
if (!requiredToken) {
return NextResponse.next();
}
const { pathname, searchParams } = request.nextUrl;
if (isPublicPage(pathname) || isPublicApi(pathname)) {
return NextResponse.next();
}
const cookieToken = request.cookies.get(SESSION_COOKIE)?.value;
if (cookieToken && cookieToken === requiredToken) {
return NextResponse.next();
}
const queryToken = searchParams.get("access_token");
if (queryToken && queryToken === requiredToken) {
const cleanUrl = request.nextUrl.clone();
cleanUrl.searchParams.delete("access_token");
const response = NextResponse.redirect(cleanUrl);
response.cookies.set(SESSION_COOKIE, requiredToken, {
httpOnly: true,
sameSite: "lax",
secure: cleanUrl.protocol === "https:",
path: "/",
maxAge: 60 * 60 * 12,
});
return response;
}
if (pathname.startsWith("/api/")) {
return NextResponse.json(
{ error: "Unauthorized", detail: "Entitlement token required" },
{ status: 401 },
);
}
const deniedUrl = request.nextUrl.clone();
deniedUrl.pathname = "/entitlement-required";
deniedUrl.search = "";
deniedUrl.searchParams.set("next", pathname);
return NextResponse.redirect(deniedUrl);
}
async function handleSupabaseAuthGate(request: NextRequest) {
const { pathname } = request.nextUrl;
if (isPublicPage(pathname) || isPublicApi(pathname)) {
@@ -224,7 +175,7 @@ export async function middleware(request: NextRequest) {
}
return handleSupabaseOptionalSession(request);
}
return handleLegacyTokenGate(request);
return NextResponse.next();
}
export const config = {
+1 -146
View File
@@ -4,7 +4,7 @@ import os
import threading
import time
from dataclasses import dataclass
from datetime import datetime, timedelta, timezone
from datetime import datetime, timezone
from typing import Dict, List, Optional
import requests
@@ -65,27 +65,10 @@ class SupabaseEntitlementService:
self.timeout_sec = max(3, _env_int("SUPABASE_HTTP_TIMEOUT_SEC", 8))
self.cache_ttl_sec = max(5, _env_int("SUPABASE_AUTH_CACHE_TTL_SEC", 30))
self.sub_cache_ttl_sec = max(5, _env_int("SUPABASE_SUB_CACHE_TTL_SEC", 60))
self.signup_trial_enabled = _env_bool(
"POLYWEATHER_SIGNUP_TRIAL_ENABLED",
True,
)
self.signup_trial_days = max(
0,
_env_int("POLYWEATHER_SIGNUP_TRIAL_DAYS", 3),
)
self.signup_trial_plan_code = str(
os.getenv("POLYWEATHER_SIGNUP_TRIAL_PLAN_CODE") or "signup_trial_3d"
).strip() or "signup_trial_3d"
self.signup_trial_source = str(
os.getenv("POLYWEATHER_SIGNUP_TRIAL_SOURCE") or "signup_trial"
).strip() or "signup_trial"
self._identity_cache: Dict[str, Dict[str, object]] = {}
self._identity_cache_lock = threading.Lock()
self._sub_cache: Dict[str, Dict[str, object]] = {}
self._sub_cache_lock = threading.Lock()
self._trial_locks: Dict[str, threading.Lock] = {}
self._trial_locks_guard = threading.Lock()
def invalidate_subscription_cache(self, user_id: str) -> None:
key = str(user_id or "").strip()
@@ -375,134 +358,6 @@ class SupabaseEntitlementService:
return row
return None
def _get_trial_lock(self, user_id: str) -> threading.Lock:
key = str(user_id or "").strip()
with self._trial_locks_guard:
lock = self._trial_locks.get(key)
if lock is None:
lock = threading.Lock()
self._trial_locks[key] = lock
return lock
def _emit_signup_trial_event(
self,
*,
user_id: str,
starts_at: datetime,
expires_at: datetime,
) -> None:
if not self.service_role_key:
return
try:
now_iso = datetime.now(timezone.utc).isoformat()
requests.post(
self._entitlement_events_endpoint(),
headers={
**self._request_headers_for_service_role(),
"Content-Type": "application/json",
"Prefer": "return=minimal",
},
json={
"user_id": user_id,
"action": "subscription_granted",
"reason": "signup_trial",
"detail": f"{self.signup_trial_days}d signup trial granted",
"payload": {
"plan_code": self.signup_trial_plan_code,
"source": self.signup_trial_source,
"starts_at": starts_at.isoformat(),
"expires_at": expires_at.isoformat(),
},
"created_at": now_iso,
},
timeout=self.timeout_sec,
)
except Exception as exc:
logger.warning(f"supabase signup trial event insert failed user_id={user_id}: {exc}")
def ensure_signup_trial(
self,
user_id: str,
*,
created_at: Optional[str] = None,
) -> Optional[Dict[str, object]]:
normalized_user_id = str(user_id or "").strip()
if not normalized_user_id:
return None
if (
not self.signup_trial_enabled
or self.signup_trial_days <= 0
or not self.service_role_key
):
return None
lock = self._get_trial_lock(normalized_user_id)
with lock:
existing_active = self._query_latest_active_subscription(normalized_user_id)
if isinstance(existing_active, dict):
return existing_active
existing_any = self._query_latest_subscription_any_status(normalized_user_id)
if isinstance(existing_any, dict):
return None
starts_at = self._parse_iso_datetime(created_at)
if starts_at is None:
admin_users = self.get_auth_users([normalized_user_id])
starts_at = self._parse_iso_datetime(
str((admin_users.get(normalized_user_id) or {}).get("created_at") or "")
)
if starts_at is None:
return None
expires_at = starts_at + timedelta(days=self.signup_trial_days)
now = datetime.now(timezone.utc)
if expires_at <= now:
return None
payload = {
"user_id": normalized_user_id,
"plan_code": self.signup_trial_plan_code,
"status": "active",
"starts_at": starts_at.isoformat(),
"expires_at": expires_at.isoformat(),
"source": self.signup_trial_source,
"created_at": now.isoformat(),
"updated_at": now.isoformat(),
}
try:
response = requests.post(
self._subscription_endpoint(),
headers={
**self._request_headers_for_service_role(),
"Content-Type": "application/json",
"Prefer": "return=representation",
},
json=payload,
timeout=self.timeout_sec,
)
if response.status_code not in (200, 201):
logger.warning(
"supabase signup trial insert failed user_id={} status={}",
normalized_user_id,
response.status_code,
)
return self._query_latest_active_subscription(normalized_user_id)
rows = response.json() if response.content else []
row = rows[0] if isinstance(rows, list) and rows else None
self.invalidate_subscription_cache(normalized_user_id)
self._emit_signup_trial_event(
user_id=normalized_user_id,
starts_at=starts_at,
expires_at=expires_at,
)
if isinstance(row, dict):
return row
return self._query_latest_active_subscription(normalized_user_id)
except Exception as exc:
logger.warning(f"supabase signup trial insert error user_id={normalized_user_id}: {exc}")
return self._query_latest_active_subscription(normalized_user_id)
def _query_active_subscription(self, user_id: str) -> bool:
return self._query_latest_active_subscription(user_id) is not None
-52
View File
@@ -14,58 +14,6 @@ class _Response:
return self._payload
def test_ensure_signup_trial_grants_three_day_subscription(monkeypatch):
monkeypatch.setenv("SUPABASE_URL", "https://example.supabase.co")
monkeypatch.setenv("SUPABASE_ANON_KEY", "anon-key")
monkeypatch.setenv("SUPABASE_SERVICE_ROLE_KEY", "service-role")
monkeypatch.setenv("POLYWEATHER_SIGNUP_TRIAL_ENABLED", "true")
monkeypatch.setenv("POLYWEATHER_SIGNUP_TRIAL_DAYS", "3")
service = SupabaseEntitlementService()
monkeypatch.setattr(service, "_query_latest_active_subscription", lambda user_id: None)
monkeypatch.setattr(service, "_query_latest_subscription_any_status", lambda user_id: None)
captured_posts = []
def _fake_post(url, headers=None, json=None, timeout=None):
captured_posts.append({"url": url, "headers": headers, "json": json, "timeout": timeout})
if url.endswith("/rest/v1/subscriptions"):
return _Response(
201,
[
{
"user_id": json["user_id"],
"plan_code": json["plan_code"],
"status": json["status"],
"starts_at": json["starts_at"],
"expires_at": json["expires_at"],
"source": json["source"],
}
],
)
return _Response(201, {})
monkeypatch.setattr(entitlement_module.requests, "post", _fake_post)
starts_at = datetime.now(timezone.utc) - timedelta(hours=1)
result = service.ensure_signup_trial(
"user-1",
created_at=starts_at.isoformat(),
)
assert result is not None
assert result["plan_code"] == "signup_trial_3d"
assert result["status"] == "active"
assert result["starts_at"] == starts_at.isoformat()
assert result["expires_at"] == (starts_at + timedelta(days=3)).isoformat()
subscription_insert = next(
item for item in captured_posts if item["url"].endswith("/rest/v1/subscriptions")
)
assert subscription_insert["json"]["user_id"] == "user-1"
assert subscription_insert["json"]["source"] == "signup_trial"
def test_latest_active_subscription_ignores_future_start(monkeypatch):
monkeypatch.setenv("SUPABASE_URL", "https://example.supabase.co")
monkeypatch.setenv("SUPABASE_ANON_KEY", "anon-key")
+5 -10
View File
@@ -31,17 +31,12 @@ 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.ensure_signup_trial(
user_id,
created_at=getattr(request.state, "auth_created_at", None),
)
if not latest_subscription:
latest_subscription = (
legacy_routes.SUPABASE_ENTITLEMENT.get_latest_active_subscription(
user_id,
respect_requirement=False,
)
latest_subscription = (
legacy_routes.SUPABASE_ENTITLEMENT.get_latest_active_subscription(
user_id,
respect_requirement=False,
)
)
latest_known_subscription = latest_subscription
if not latest_known_subscription: