1
This commit is contained in:
parent
ce3084056c
commit
94b1cffa40
@ -94,6 +94,10 @@ def build_router(templates, repo_root: Path, stock_report_template: str):
|
||||
user, redirect = require_page_user(request)
|
||||
if redirect:
|
||||
return redirect
|
||||
try:
|
||||
require_admin(request.cookies.get("altcoin_session", ""))
|
||||
except HTTPException as exc:
|
||||
return HTMLResponse(content=f"<meta charset=utf-8><h2>需要管理员权限</h2><p>{exc.detail}</p><a href=/app>返回看板</a>", status_code=exc.status_code)
|
||||
return render_page("paper_trading.html", request, active_nav="paper_trading")
|
||||
|
||||
@router.get("/strategy", response_class=HTMLResponse)
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
from fastapi import APIRouter, Cookie
|
||||
|
||||
from app.db.paper_trading import get_paper_trading_summary, list_paper_trades
|
||||
from app.web.shared import require_api_user_with_subscription
|
||||
from app.web.shared import require_admin
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
@ -9,7 +9,7 @@ router = APIRouter()
|
||||
|
||||
@router.get("/api/paper-trading/summary")
|
||||
async def api_paper_trading_summary(days: int = 30, altcoin_session: str = Cookie(default="")):
|
||||
require_api_user_with_subscription(altcoin_session)
|
||||
require_admin(altcoin_session)
|
||||
return get_paper_trading_summary(days=days)
|
||||
|
||||
|
||||
@ -20,5 +20,5 @@ async def api_paper_trading_trades(
|
||||
status: str = "",
|
||||
altcoin_session: str = Cookie(default=""),
|
||||
):
|
||||
require_api_user_with_subscription(altcoin_session)
|
||||
require_admin(altcoin_session)
|
||||
return list_paper_trades(limit=limit, offset=offset, status=status)
|
||||
|
||||
@ -175,7 +175,7 @@ a { color: inherit; text-decoration: none; }
|
||||
<nav class="sidebar-nav">
|
||||
<div class="sidebar-section-label">交易</div>
|
||||
<a class="sidebar-link {% if active_nav | default('app') == 'app' %}active{% endif %}" href="/app"><svg class="link-icon"><use href="#svg-dashboard"/></svg>看板</a>
|
||||
<a class="sidebar-link {% if active_nav == 'paper_trading' %}active{% endif %}" href="/paper-trading"><svg class="link-icon"><use href="#svg-paper"/></svg>模拟交易</a>
|
||||
<a class="sidebar-link admin-link {% if active_nav == 'paper_trading' %}active{% endif %}" href="/paper-trading" style="display:none"><svg class="link-icon"><use href="#svg-paper"/></svg>模拟交易</a>
|
||||
<div class="sidebar-section-label">研究</div>
|
||||
<a class="sidebar-link {% if active_nav == 'market' %}active{% endif %}" href="/market"><svg class="link-icon"><use href="#svg-target"/></svg>市场总览</a>
|
||||
<a class="sidebar-link {% if active_nav == 'sentiment' %}active{% endif %}" href="/sentiment"><svg class="link-icon"><use href="#svg-sentiment"/></svg>实时舆情</a>
|
||||
|
||||
62
tests/test_paper_trading_admin_access.py
Normal file
62
tests/test_paper_trading_admin_access.py
Normal file
@ -0,0 +1,62 @@
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app.db import auth_db
|
||||
from app.web import web_server
|
||||
|
||||
|
||||
def _login_user(email: str, password: str = "StrongPass123", admin: bool = False) -> str:
|
||||
reg = auth_db.register_user(email, password)
|
||||
auth_db.verify_email(email, reg["verification_code"])
|
||||
user = auth_db.get_user_by_email(email)
|
||||
auth_db.claim_free_trial(user["id"])
|
||||
if admin:
|
||||
auth_db.set_user_admin(email, True)
|
||||
return auth_db.login_user(email, password)["token"]
|
||||
|
||||
|
||||
def test_paper_trading_page_requires_admin_for_normal_subscriber():
|
||||
token = _login_user("normal-paper@example.com")
|
||||
client = TestClient(web_server.app)
|
||||
client.cookies.set("altcoin_session", token)
|
||||
|
||||
resp = client.get("/paper-trading")
|
||||
|
||||
assert resp.status_code == 403
|
||||
assert "需要管理员权限" in resp.text
|
||||
|
||||
|
||||
def test_paper_trading_api_requires_admin_for_normal_subscriber():
|
||||
token = _login_user("normal-api-paper@example.com")
|
||||
client = TestClient(web_server.app)
|
||||
client.cookies.set("altcoin_session", token)
|
||||
|
||||
summary = client.get("/api/paper-trading/summary")
|
||||
trades = client.get("/api/paper-trading/trades")
|
||||
|
||||
assert summary.status_code == 403
|
||||
assert trades.status_code == 403
|
||||
|
||||
|
||||
def test_paper_trading_admin_can_access_page_and_api():
|
||||
token = _login_user("admin-paper@example.com", admin=True)
|
||||
client = TestClient(web_server.app)
|
||||
client.cookies.set("altcoin_session", token)
|
||||
|
||||
page = client.get("/paper-trading")
|
||||
summary = client.get("/api/paper-trading/summary")
|
||||
|
||||
assert page.status_code == 200
|
||||
assert "模拟交易" in page.text
|
||||
assert summary.status_code == 200
|
||||
assert "account_equity_usdt" in summary.json()
|
||||
|
||||
|
||||
def test_sidebar_hides_paper_trading_with_admin_link_class():
|
||||
client = TestClient(web_server.app)
|
||||
resp = client.get("/app")
|
||||
|
||||
assert resp.status_code == 200
|
||||
assert 'href="/paper-trading" style="display:none"' in resp.text
|
||||
assert 'admin-link' in resp.text
|
||||
Loading…
Reference in New Issue
Block a user