mirror of
https://github.com/Routstr/routstr-core.git
synced 2026-10-05 12:28:22 +00:00
fix: serve certification page on direct load, read certify key from env, fix sequential note
This commit is contained in:
+15
-12
@@ -340,6 +340,21 @@ async def providers() -> RedirectResponse:
|
||||
|
||||
UI_DIST_PATH = Path(__file__).parent.parent.parent / "ui_out"
|
||||
|
||||
# Every `ui/app/**/page.tsx` route needs an entry, or a direct load 404s.
|
||||
UI_PAGES = (
|
||||
"dashboard",
|
||||
"login",
|
||||
"model",
|
||||
"providers",
|
||||
"providers/certification",
|
||||
"settings",
|
||||
"transactions",
|
||||
"balances",
|
||||
"logs",
|
||||
"usage",
|
||||
"unauthorized",
|
||||
)
|
||||
|
||||
if UI_DIST_PATH.exists() and UI_DIST_PATH.is_dir():
|
||||
logger.info(f"Serving static UI from {UI_DIST_PATH}")
|
||||
|
||||
@@ -362,18 +377,6 @@ if UI_DIST_PATH.exists() and UI_DIST_PATH.is_dir():
|
||||
# with a slash (e.g. `/login/`). The proxy router catches `/{path:path}`
|
||||
# before FastAPI's `redirect_slashes` logic can normalize the URL, so we
|
||||
# must register both the with-slash and without-slash variants here.
|
||||
UI_PAGES = (
|
||||
"dashboard",
|
||||
"login",
|
||||
"model",
|
||||
"providers",
|
||||
"settings",
|
||||
"transactions",
|
||||
"balances",
|
||||
"logs",
|
||||
"usage",
|
||||
"unauthorized",
|
||||
)
|
||||
|
||||
def _register_ui_page(name: str) -> None:
|
||||
page_dir = UI_DIST_PATH / name
|
||||
|
||||
@@ -17,6 +17,7 @@ import argparse
|
||||
import asyncio
|
||||
import json
|
||||
import math
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from collections.abc import Callable
|
||||
@@ -1245,7 +1246,14 @@ def main(argv: list[str] | None = None) -> int:
|
||||
required=True,
|
||||
help="Upstream base URL (repeatable), e.g. https://api.example.com/v1",
|
||||
)
|
||||
parser.add_argument("--key", default="", help="Bearer API key for the upstream")
|
||||
parser.add_argument(
|
||||
"--key",
|
||||
default=os.environ.get("ROUTSTR_CERTIFY_KEY", ""),
|
||||
help=(
|
||||
"Bearer API key for the upstream (defaults to $ROUTSTR_CERTIFY_KEY; "
|
||||
"prefer the env var so the key stays out of shell history and ps)"
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--model",
|
||||
default=None,
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
"""The certification CLI reads the upstream key from the environment."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
|
||||
from routstr.upstream import certification
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def captured_keys(monkeypatch: pytest.MonkeyPatch) -> list[str]:
|
||||
keys: list[str] = []
|
||||
|
||||
async def fake_certify(url: str, *, api_key: str, **_: Any) -> dict[str, Any]:
|
||||
keys.append(api_key)
|
||||
return {"url": url, "rows": []}
|
||||
|
||||
monkeypatch.setattr(certification, "certify_upstream_url", fake_certify)
|
||||
monkeypatch.setattr(certification, "render_checklist", lambda _result: "")
|
||||
return keys
|
||||
|
||||
|
||||
def test_key_defaults_to_env_var(
|
||||
monkeypatch: pytest.MonkeyPatch, captured_keys: list[str]
|
||||
) -> None:
|
||||
monkeypatch.setenv("ROUTSTR_CERTIFY_KEY", "sk-from-env")
|
||||
assert certification.main(["--url", "http://localhost:1/v1"]) == 0
|
||||
assert captured_keys == ["sk-from-env"]
|
||||
|
||||
|
||||
def test_key_flag_overrides_env_var(
|
||||
monkeypatch: pytest.MonkeyPatch, captured_keys: list[str]
|
||||
) -> None:
|
||||
monkeypatch.setenv("ROUTSTR_CERTIFY_KEY", "sk-from-env")
|
||||
certification.main(["--url", "http://localhost:1/v1", "--key", "sk-flag"])
|
||||
assert captured_keys == ["sk-flag"]
|
||||
|
||||
|
||||
def test_key_is_empty_without_flag_or_env(
|
||||
monkeypatch: pytest.MonkeyPatch, captured_keys: list[str]
|
||||
) -> None:
|
||||
monkeypatch.delenv("ROUTSTR_CERTIFY_KEY", raising=False)
|
||||
certification.main(["--url", "http://localhost:1/v1"])
|
||||
assert captured_keys == [""]
|
||||
@@ -0,0 +1,19 @@
|
||||
"""Every static UI page must be served on a direct load, not the proxy 404."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from routstr.core import main as core_main
|
||||
|
||||
UI_APP_DIR = Path(__file__).resolve().parents[2] / "ui" / "app"
|
||||
|
||||
|
||||
def test_every_ui_app_page_is_in_ui_pages() -> None:
|
||||
routes = {
|
||||
page.parent.relative_to(UI_APP_DIR).as_posix()
|
||||
for page in UI_APP_DIR.rglob("page.tsx")
|
||||
if page.parent != UI_APP_DIR
|
||||
}
|
||||
assert routes, "no ui/app pages found"
|
||||
assert routes - set(core_main.UI_PAGES) == set()
|
||||
@@ -272,7 +272,8 @@ export function ProviderCertificationSetupPanel({
|
||||
)}
|
||||
{mode === 'all' && (
|
||||
<p className='text-muted-foreground text-xs'>
|
||||
All {paths.length} paths will run in parallel.
|
||||
All {paths.length} paths will run one after another, each
|
||||
with its own probe calls.
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user