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; }}