From 84b903a6c2cb611e5a5bf81c765ba8ed488bc097 Mon Sep 17 00:00:00 2001 From: Shroominic Date: Sun, 17 Aug 2025 15:48:12 -0300 Subject: [PATCH] multi mint admin dashboard --- routstr/core/admin.py | 179 ++++++++++++++++++++++++++++++++++-------- routstr/wallet.py | 100 +++++++++++++++++++++++ 2 files changed, 247 insertions(+), 32 deletions(-) diff --git a/routstr/core/admin.py b/routstr/core/admin.py index 5a547347..1c993a6a 100644 --- a/routstr/core/admin.py +++ b/routstr/core/admin.py @@ -8,7 +8,14 @@ from fastapi.responses import HTMLResponse from pydantic import BaseModel from sqlmodel import select -from ..wallet import get_balance, send_token +from ..wallet import ( + TRUSTED_MINTS, + fetch_all_balances, + get_proofs_per_mint_and_unit, + get_wallet, + send_token, + slow_filter_spend_proofs, +) from .db import ApiKey, create_session from .logging import get_logger @@ -19,6 +26,8 @@ admin_router = APIRouter(prefix="/admin", include_in_schema=False) class WithdrawRequest(BaseModel): amount: int + mint_url: str | None = None + unit: str = "sat" def login_form() -> str: @@ -105,12 +114,13 @@ async def dashboard(request: Request) -> str: f"{key.hashed_key}{key.balance}{key.total_spent}{key.total_requests}{key.refund_address}{'{} ({} UTC)'.format(key.key_expiry_time, expiry_time_human_readable) if key.key_expiry_time else key.key_expiry_time}" ) - # Calculate the total balance of all API keys using integer arithmetic to - # avoid rounding issues. - total_user_balance = sum(key.balance for key in api_keys) // 1000 - # Fetch balance from cashu - current_balance = await get_balance("sat") - owner_balance = current_balance - total_user_balance + # Fetch all balances using the abstracted function + ( + balance_details, + total_wallet_balance_sats, + total_user_balance_sats, + owner_balance, + ) = await fetch_all_balances() return f""" @@ -137,6 +147,14 @@ async def dashboard(request: Request) -> str: .balance-label {{ color: #718096; }} .balance-value {{ font-size: 1.5rem; font-weight: 700; color: #2d3748; }} .balance-primary {{ color: #48bb78; }} + .currency-grid {{ margin-top: 1rem; font-size: 0.9rem; }} + .currency-row {{ display: grid; grid-template-columns: 2fr 1fr 1fr 1fr; gap: 0.5rem; padding: 0.4rem 0; border-bottom: 1px solid #f0f0f0; align-items: center; }} + .currency-row:last-child {{ border-bottom: none; }} + .currency-header {{ font-weight: 600; color: #4a5568; border-bottom: 2px solid #e2e8f0; padding-bottom: 0.5rem; }} + .mint-name {{ color: #2d3748; font-size: 0.85rem; word-break: break-all; }} + .balance-num {{ text-align: right; font-family: monospace; }} + .owner-positive {{ color: #22c55e; }} + .error-row {{ color: #dc2626; font-style: italic; }} #token-result {{ margin-top: 20px; padding: 20px; background: #e6fffa; border: 1px solid #38b2ac; border-radius: 8px; display: none; }} #token-text {{ font-family: 'Monaco', monospace; font-size: 13px; background: #2d3748; color: #68d391; padding: 15px; border-radius: 6px; margin: 10px 0; word-break: break-all; }} .copy-btn {{ background: #38a169; padding: 6px 12px; font-size: 14px; }} @@ -146,15 +164,16 @@ async def dashboard(request: Request) -> str: @keyframes slideIn {{ from {{ transform: translateY(-20px); opacity: 0; }} to {{ transform: translateY(0); opacity: 1; }} }} .close {{ color: #a0aec0; float: right; font-size: 28px; font-weight: bold; cursor: pointer; margin: -10px -10px 0 0; }} .close:hover {{ color: #2d3748; }} - input[type="number"], input[type="text"] {{ width: 100%; padding: 10px; margin: 10px 0; border: 2px solid #e2e8f0; border-radius: 6px; font-size: 16px; transition: border 0.2s; }} - input[type="number"]:focus, input[type="text"]:focus {{ outline: none; border-color: #4299e1; }} + input[type="number"], input[type="text"], select {{ width: 100%; padding: 10px; margin: 10px 0; border: 2px solid #e2e8f0; border-radius: 6px; font-size: 16px; transition: border 0.2s; }} + input[type="number"]:focus, input[type="text"]:focus, select:focus {{ outline: none; border-color: #4299e1; }} .warning {{ color: #e53e3e; font-weight: 600; margin: 10px 0; padding: 10px; background: #fff5f5; border-radius: 6px; }}