Remove unrelated non-UI branch changes

This commit is contained in:
Evan Yang
2026-03-09 15:37:13 +08:00
parent 8dbf036558
commit 7e89b5bc6d
6 changed files with 24 additions and 58 deletions
-5
View File
@@ -38,8 +38,3 @@ proof_backups
*.todo *.todo
ui_out ui_out
output/
.pnpm-store/
# env files
.env*
+1 -1
View File
@@ -1,6 +1,6 @@
[project] [project]
name = "routstr" name = "routstr"
version = "0.4.0" version = "0.3.0"
description = "Payment proxy for your LLM endpoint using cashu and nostr." description = "Payment proxy for your LLM endpoint using cashu and nostr."
readme = "README.md" readme = "README.md"
requires-python = ">=3.11" requires-python = ">=3.11"
+18 -46
View File
@@ -2,7 +2,6 @@ import json
import secrets import secrets
from datetime import datetime, timezone from datetime import datetime, timezone
from pathlib import Path from pathlib import Path
from typing import NoReturn
from fastapi import APIRouter, Depends, HTTPException, Query, Request from fastapi import APIRouter, Depends, HTTPException, Query, Request
from pydantic import BaseModel from pydantic import BaseModel
@@ -27,55 +26,20 @@ logger = get_logger(__name__)
admin_router = APIRouter(prefix="/admin", include_in_schema=False) admin_router = APIRouter(prefix="/admin", include_in_schema=False)
admin_sessions: dict[str, int] = {} admin_sessions: dict[str, int] = {}
ADMIN_SESSION_DURATION = 12 * 60 * 60 ADMIN_SESSION_DURATION = 3600
# Usage analytics remain queryable up to 12 months. # Usage analytics remain queryable up to 12 months.
MAX_USAGE_ANALYTICS_HOURS = 365 * 24 MAX_USAGE_ANALYTICS_HOURS = 365 * 24
def _current_timestamp() -> int:
return int(datetime.now(timezone.utc).timestamp())
def _cleanup_expired_admin_sessions(now_timestamp: int | None = None) -> None:
current_timestamp = (
now_timestamp if now_timestamp is not None else _current_timestamp()
)
expired_tokens = [
token
for token, expiry_timestamp in admin_sessions.items()
if expiry_timestamp <= current_timestamp
]
for token in expired_tokens:
admin_sessions.pop(token, None)
def _raise_unauthorized(detail: str) -> NoReturn:
raise HTTPException(
status_code=401,
detail=detail,
headers={"WWW-Authenticate": "Bearer"},
)
def require_admin_api(request: Request) -> None: def require_admin_api(request: Request) -> None:
auth_header = request.headers.get("Authorization", "") auth_header = request.headers.get("Authorization")
if not auth_header.startswith("Bearer "): if auth_header and auth_header.startswith("Bearer "):
_raise_unauthorized("Missing bearer token") token = auth_header.split(" ", 1)[1]
expiry = admin_sessions.get(token)
if expiry and expiry > int(datetime.now(timezone.utc).timestamp()):
return
token = auth_header.split(" ", 1)[1].strip() raise HTTPException(status_code=403, detail="Unauthorized")
if not token:
_raise_unauthorized("Missing bearer token")
now_timestamp = _current_timestamp()
expiry_timestamp = admin_sessions.get(token)
if expiry_timestamp is None:
_raise_unauthorized("Invalid session token")
if expiry_timestamp <= now_timestamp:
admin_sessions.pop(token, None)
_raise_unauthorized("Session expired")
_cleanup_expired_admin_sessions(now_timestamp)
@admin_router.get("/api/temporary-balances", dependencies=[Depends(require_admin_api)]) @admin_router.get("/api/temporary-balances", dependencies=[Depends(require_admin_api)])
@@ -244,10 +208,18 @@ async def admin_login(
raise HTTPException(status_code=401, detail="Invalid password") raise HTTPException(status_code=401, detail="Invalid password")
token = secrets.token_urlsafe(32) token = secrets.token_urlsafe(32)
expiry_timestamp = _current_timestamp() + ADMIN_SESSION_DURATION expiry_timestamp = (
int(datetime.now(timezone.utc).timestamp()) + ADMIN_SESSION_DURATION
)
admin_sessions[token] = expiry_timestamp admin_sessions[token] = expiry_timestamp
_cleanup_expired_admin_sessions() expired_tokens = [
t
for t, exp in admin_sessions.items()
if exp <= int(datetime.now(timezone.utc).timestamp())
]
for t in expired_tokens:
del admin_sessions[t]
return {"ok": True, "token": token, "expires_in": ADMIN_SESSION_DURATION} return {"ok": True, "token": token, "expires_in": ADMIN_SESSION_DURATION}
+2 -2
View File
@@ -31,9 +31,9 @@ setup_logging()
logger = get_logger(__name__) logger = get_logger(__name__)
if os.getenv("VERSION_SUFFIX") is not None: if os.getenv("VERSION_SUFFIX") is not None:
__version__ = f"0.4.0-{os.getenv('VERSION_SUFFIX')}" __version__ = f"0.3.0-{os.getenv('VERSION_SUFFIX')}"
else: else:
__version__ = "0.4.0" __version__ = "0.3.0"
@asynccontextmanager @asynccontextmanager
@@ -264,13 +264,12 @@ async def test_models_endpoint_accept_headers(integration_client: AsyncClient) -
async def test_admin_endpoint_unauthenticated( async def test_admin_endpoint_unauthenticated(
integration_client: AsyncClient, db_snapshot: Any integration_client: AsyncClient, db_snapshot: Any
) -> None: ) -> None:
"""Test unauthenticated access to admin settings endpoint is rejected.""" """Test GET /admin/ endpoint redirects to /"""
await db_snapshot.capture() await db_snapshot.capture()
response = await integration_client.get("/admin/api/settings") response = await integration_client.get("/admin/api/settings")
assert response.status_code == 401 assert response.status_code == 403
assert response.headers.get("www-authenticate") == "Bearer"
diff = await db_snapshot.diff() diff = await db_snapshot.diff()
assert len(diff["api_keys"]["added"]) == 0 assert len(diff["api_keys"]["added"]) == 0
Generated
+1 -1
View File
@@ -1878,7 +1878,7 @@ wheels = [
[[package]] [[package]]
name = "routstr" name = "routstr"
version = "0.4.0" version = "0.3.0"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "aiosqlite" }, { name = "aiosqlite" },