mirror of
https://github.com/Routstr/routstr-core.git
synced 2026-10-05 12:28:22 +00:00
Remove unrelated non-UI branch changes
This commit is contained in:
@@ -38,8 +38,3 @@ proof_backups
|
|||||||
|
|
||||||
*.todo
|
*.todo
|
||||||
ui_out
|
ui_out
|
||||||
output/
|
|
||||||
.pnpm-store/
|
|
||||||
|
|
||||||
# env files
|
|
||||||
.env*
|
|
||||||
|
|||||||
+1
-1
@@ -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
@@ -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}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user