From ca021d541e6954e5d75d9993ec8f8e16468aacb3 Mon Sep 17 00:00:00 2001 From: aaron <> Date: Mon, 8 Jun 2026 08:15:27 +0800 Subject: [PATCH] 1 --- app/web/routes_auth.py | 3 +++ app/web/shared.py | 4 +-- static/base.html | 3 ++- tests/test_pipeline_runs_api.py | 1 + tests/test_user_subscription_auth.py | 37 ++++++++++++++++++++-------- 5 files changed, 35 insertions(+), 13 deletions(-) diff --git a/app/web/routes_auth.py b/app/web/routes_auth.py index 17cc3f0..1be2a4f 100644 --- a/app/web/routes_auth.py +++ b/app/web/routes_auth.py @@ -11,6 +11,7 @@ from app.web.shared import ( SendCodeRequest, VerifyEmailRequest, auth_error, + has_active_subscription, require_user, ) @@ -108,6 +109,8 @@ async def api_auth_login(req: LoginRequest, request: Request = None): async def api_auth_me(altcoin_session: str = Cookie(default="")): user = require_user(altcoin_session) sub = auth_db.get_current_subscription(user["id"]) + if not user.get("local_debug") and not has_active_subscription(user): + raise HTTPException(status_code=402, detail="订阅已过期或未开通,请先开通订阅") return {"ok": True, "user": user, "subscription": sub, "subscription_active": bool(sub)} diff --git a/app/web/shared.py b/app/web/shared.py index 378b524..fdce4b1 100644 --- a/app/web/shared.py +++ b/app/web/shared.py @@ -148,10 +148,10 @@ def subscription_redirect(): def has_active_subscription(user) -> bool: - if is_local_request(): - return True if not user: return False + if user.get("local_debug"): + return True try: if auth_db.is_user_admin(user["id"]): return True diff --git a/static/base.html b/static/base.html index f9386ba..416cc26 100644 --- a/static/base.html +++ b/static/base.html @@ -266,7 +266,6 @@ a { color: inherit; text-decoration: none; } 订阅 邀请 - @@ -328,6 +327,8 @@ window.addEventListener('orientationchange', function(){ setTimeout(setAppViewpo async function loadUser() { try { var resp = await fetch(API + '/api/auth/me'); + if (resp.status === 401) { window.location.href = '/auth?tab=login'; return; } + if (resp.status === 402) { window.location.href = '/subscription?expired=1'; return; } if (!resp.ok) return; var data = await resp.json(); currentUser = data.user; diff --git a/tests/test_pipeline_runs_api.py b/tests/test_pipeline_runs_api.py index 6447b02..a417aa8 100644 --- a/tests/test_pipeline_runs_api.py +++ b/tests/test_pipeline_runs_api.py @@ -342,6 +342,7 @@ def test_sidebar_keeps_engineering_pages_in_admin_menu(temp_db): html = resp.text assert "机会中心" in html assert "诊断中心" in html + assert 'href="/operations"' not in html assert 'href="/llm-insights"' not in html assert 'href="/data-export"' not in html assert 'href="/strategy"' not in html diff --git a/tests/test_user_subscription_auth.py b/tests/test_user_subscription_auth.py index 6283c02..46b936e 100644 --- a/tests/test_user_subscription_auth.py +++ b/tests/test_user_subscription_auth.py @@ -223,12 +223,11 @@ def test_auth_page_hides_internal_requirements_and_has_modern_member_copy(temp_a assert forbidden not in html for expected in [ - "提前发现机会,别在强信号后追高", - "登录或开启免费体验", "创建账号", "会员登录", - "前往订阅中心", - "AlphaX Agent | Crypto", + "邮箱验证码", + "发送验证码", + "AlphaX Agent", ]: assert expected in html @@ -251,17 +250,35 @@ def test_subscription_page_owns_trial_and_plan_flow(temp_auth_db): assert "USDT 订阅已预留表结构" not in html -def test_app_shell_returns_200_for_all_users(temp_auth_db): - """v2: /app 是纯壳页,不校验登录/订阅(鉴权由 JS 调用 /api/auth/me 完成)""" +def test_app_shell_requires_active_subscription_for_real_users(temp_auth_db): client = TestClient(web_server.app) - # 未登录也能拿到壳页(JS自己判断跳转) - assert client.get("/app").status_code == 200 - assert "AlphaX Agent | Crypto" in client.get("/app").text - # 登录用户也一样 reg = auth_db.register_user("alice@example.com", "StrongPass123") auth_db.verify_email("alice@example.com", reg["verification_code"]) login = auth_db.login_user("alice@example.com", "StrongPass123") token = login["token"] client.cookies.set("altcoin_session", token) + + expired_at = (datetime.now() - timedelta(days=1)).isoformat(timespec="seconds") + conn = auth_db.get_conn() + conn.execute( + """ + INSERT INTO user_subscription (user_id, plan_code, start_at, end_at, status, source, order_id, created_at, updated_at) + VALUES (%s, 'free_trial_1m', %s, %s, 'active', 'test', 0, %s, %s) + """, + (login["user"]["id"], (datetime.now() - timedelta(days=31)).isoformat(timespec="seconds"), expired_at, expired_at, expired_at), + ) + conn.commit() + conn.close() + + page = client.get("/app", follow_redirects=False) + me = client.get("/api/auth/me") + + assert page.status_code == 302 + assert page.headers["location"] == "/subscription?expired=1" + assert me.status_code == 402 + + sub = auth_db.claim_free_trial(login["user"]["id"]) + assert sub["status"] == "active" assert client.get("/app").status_code == 200 + assert client.get("/api/auth/me").status_code == 200