订单需管理员审核后才开通节点,并更新登录页文案。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+68
-5
@@ -4,15 +4,17 @@ from datetime import date, timedelta
|
||||
from decimal import Decimal
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from fastapi import APIRouter, Depends, Request
|
||||
from fastapi.responses import Response
|
||||
from sqlalchemy import case, func, select
|
||||
from sqlalchemy.orm import Session, selectinload
|
||||
|
||||
from app.clock import utcnow
|
||||
from app.config import settings
|
||||
from app.database import get_db
|
||||
from app.deps import AdminUser, CurrentUser
|
||||
from app.errors import AppError
|
||||
from app.models import ComputeNode, ComputeTask, User, UserRole, WithdrawalLog, WithdrawalStatus
|
||||
from app.models import ComputeNode, ComputeTask, LicenseOrder, User, UserRole, WithdrawalLog, WithdrawalStatus
|
||||
from app.money import money
|
||||
from app.schemas import (
|
||||
AdminUserOut,
|
||||
@@ -23,6 +25,9 @@ from app.schemas import (
|
||||
HeartbeatOut,
|
||||
LoginIn,
|
||||
MeNodeCreate,
|
||||
OrderCreate,
|
||||
OrderOut,
|
||||
OrderView,
|
||||
MeWithdrawalCreate,
|
||||
NodeCreate,
|
||||
NodeOut,
|
||||
@@ -45,7 +50,8 @@ from app.schemas import (
|
||||
from app.security import create_access_token
|
||||
from app.services.auth import authenticate, register_account
|
||||
from app.services.common import get_node_by_sn, get_user_by_wallet
|
||||
from app.services.heartbeat import record_heartbeat
|
||||
from app.services.heartbeat import assert_node_token, record_heartbeat
|
||||
from app.services.orders import approve_order, list_orders, place_order, reject_order, render_client
|
||||
from app.services.portal import (
|
||||
authorized_amount,
|
||||
live_nodes,
|
||||
@@ -95,10 +101,32 @@ def my_dashboard(user: CurrentUser, db: Db) -> DashboardOut:
|
||||
settled_points=settled_points(db, user.id),
|
||||
today_points=today,
|
||||
nodes=[NodeView.model_validate(item) for item in nodes],
|
||||
orders=[_order_out(item) for item in list_orders(db, user.id)],
|
||||
license_price=money(settings.license_price),
|
||||
license_asset=settings.license_asset,
|
||||
withdrawals=[WithdrawalOut.model_validate(item) for item in withdrawals],
|
||||
)
|
||||
|
||||
|
||||
@router.post("/me/orders", response_model=OrderOut)
|
||||
def create_my_order(body: OrderCreate, user: CurrentUser, db: Db) -> OrderOut:
|
||||
return _order_out(place_order(db, user.id, body.tx_id))
|
||||
|
||||
|
||||
@router.get("/me/nodes/{sn}/client")
|
||||
def download_client(sn: str, request: Request, user: CurrentUser, db: Db) -> Response:
|
||||
node = _owned_node(db, user, sn)
|
||||
if not node.node_token:
|
||||
raise AppError("节点尚未开通,不能下载客户端", 403)
|
||||
script = render_client(node.sn, node.node_token, str(request.base_url))
|
||||
filename = f"hypernode-{node.sn}.py"
|
||||
return Response(
|
||||
content=script,
|
||||
media_type="text/x-python; charset=utf-8",
|
||||
headers={"Content-Disposition": f'attachment; filename="{filename}"'},
|
||||
)
|
||||
|
||||
|
||||
@router.post("/me/nodes", response_model=NodeOut)
|
||||
def create_my_node(body: MeNodeCreate, user: CurrentUser, db: Db) -> NodeOut:
|
||||
return register_node(db, user.wallet_address, body.sn, body.purchase_amount, body.callback_url)
|
||||
@@ -125,6 +153,21 @@ def admin_nodes(_: AdminUser, db: Db) -> list[NodeView]:
|
||||
return [NodeView.model_validate(item) for item in live_nodes(db)]
|
||||
|
||||
|
||||
@router.get("/admin/orders", response_model=list[OrderView])
|
||||
def admin_orders(_: AdminUser, db: Db) -> list[OrderView]:
|
||||
return [_order_view(row) for row in list_orders(db)]
|
||||
|
||||
|
||||
@router.post("/admin/orders/{order_id}/approve", response_model=OrderView)
|
||||
def admin_approve_order(order_id: int, _: AdminUser, db: Db) -> OrderView:
|
||||
return _order_view(approve_order(db, order_id))
|
||||
|
||||
|
||||
@router.post("/admin/orders/{order_id}/reject", response_model=OrderView)
|
||||
def admin_reject_order(order_id: int, body: WithdrawalReject, _: AdminUser, db: Db) -> OrderView:
|
||||
return _order_view(reject_order(db, order_id, body.note))
|
||||
|
||||
|
||||
@router.get("/admin/withdrawals", response_model=list[WithdrawalView])
|
||||
def admin_withdrawals(_: AdminUser, db: Db) -> list[WithdrawalView]:
|
||||
return [_withdrawal_view(row) for row in _withdrawal_rows(db)]
|
||||
@@ -173,7 +216,7 @@ def get_node(sn: str, user: CurrentUser, db: Db) -> NodeOut:
|
||||
|
||||
@router.post("/node/heartbeat", response_model=HeartbeatOut)
|
||||
def heartbeat(body: HeartbeatIn, db: Db) -> HeartbeatOut:
|
||||
node = record_heartbeat(db, body.sn)
|
||||
node = record_heartbeat(db, body.sn, body.token)
|
||||
return HeartbeatOut(
|
||||
sn=node.sn,
|
||||
status=node.status,
|
||||
@@ -183,12 +226,14 @@ def heartbeat(body: HeartbeatIn, db: Db) -> HeartbeatOut:
|
||||
|
||||
|
||||
@router.get("/node/{sn}/tasks", response_model=list[TaskOut])
|
||||
def pending_tasks(sn: str, db: Db) -> list[TaskOut]:
|
||||
def pending_tasks(sn: str, db: Db, token: str | None = None) -> list[TaskOut]:
|
||||
assert_node_token(get_node_by_sn(db, sn), token)
|
||||
return [_task_out(task) for task in list_pending_tasks(db, sn)]
|
||||
|
||||
|
||||
@router.post("/node/task/report", response_model=TaskOut)
|
||||
def report(body: TaskReportIn, db: Db) -> TaskOut:
|
||||
assert_node_token(get_node_by_sn(db, body.sn), body.token)
|
||||
return _task_out(report_task(db, body.sn, body.task_id, body.success, body.nonce))
|
||||
|
||||
|
||||
@@ -293,6 +338,24 @@ def _withdrawal_rows(db: Session) -> list[WithdrawalLog]:
|
||||
)
|
||||
|
||||
|
||||
def _order_out(order: LicenseOrder) -> OrderOut:
|
||||
return OrderOut(
|
||||
id=order.id,
|
||||
user_id=order.user_id,
|
||||
amount=order.amount,
|
||||
asset_symbol=order.asset_symbol,
|
||||
status=order.status,
|
||||
tx_id=order.tx_id,
|
||||
created_at=order.created_at,
|
||||
reviewed_at=order.reviewed_at,
|
||||
sn=order.node.sn if order.node is not None else None,
|
||||
)
|
||||
|
||||
|
||||
def _order_view(order: LicenseOrder) -> OrderView:
|
||||
return OrderView(**_order_out(order).model_dump(), wallet_address=order.user.wallet_address)
|
||||
|
||||
|
||||
def _withdrawal_view(row: WithdrawalLog) -> WithdrawalView:
|
||||
return WithdrawalView(
|
||||
**WithdrawalOut.model_validate(row).model_dump(),
|
||||
|
||||
Reference in New Issue
Block a user