Add personal notes, DeepSearch history, backup offload, and gateway auth throttling

Also streamline the top navigation: drop the Tools dropdown, make Quizzes
and Battles icon-only entries, and remove the Workspace, Containers and
Editor entry points from the project detail page.
This commit is contained in:
2026-09-12 20:32:31 +02:00
parent c4f7d01b2d
commit cae139ead9
158 changed files with 6156 additions and 377 deletions
@@ -0,0 +1,69 @@
# retoor <retoor@molodetz.nl>
from devplacepy.services.openai_gateway import auth_throttle
def test_is_throttled_false_below_threshold():
auth_throttle.clear()
try:
for _ in range(9):
auth_throttle.record_failure("1.1.1.1", 60)
assert auth_throttle.is_throttled("1.1.1.1", 10, 60) is False
finally:
auth_throttle.clear()
def test_is_throttled_true_at_threshold():
auth_throttle.clear()
try:
for _ in range(10):
auth_throttle.record_failure("2.2.2.2", 60)
assert auth_throttle.is_throttled("2.2.2.2", 10, 60) is True
finally:
auth_throttle.clear()
def test_is_throttled_scoped_per_ip():
auth_throttle.clear()
try:
for _ in range(10):
auth_throttle.record_failure("3.3.3.3", 60)
assert auth_throttle.is_throttled("3.3.3.3", 10, 60) is True
assert auth_throttle.is_throttled("4.4.4.4", 10, 60) is False
finally:
auth_throttle.clear()
def test_record_failure_returns_running_count():
auth_throttle.clear()
try:
assert auth_throttle.record_failure("5.5.5.5", 60) == 1
assert auth_throttle.record_failure("5.5.5.5", 60) == 2
assert auth_throttle.record_failure("5.5.5.5", 60) == 3
finally:
auth_throttle.clear()
def test_throttle_resets_after_window_passes(monkeypatch):
auth_throttle.clear()
try:
now = [1000.0]
monkeypatch.setattr(auth_throttle.time, "time", lambda: now[0])
for _ in range(10):
auth_throttle.record_failure("6.6.6.6", 60)
assert auth_throttle.is_throttled("6.6.6.6", 10, 60) is True
now[0] += 61
assert auth_throttle.is_throttled("6.6.6.6", 10, 60) is False
finally:
auth_throttle.clear()
def test_reset_clears_one_ip():
auth_throttle.clear()
try:
for _ in range(10):
auth_throttle.record_failure("7.7.7.7", 60)
auth_throttle.reset("7.7.7.7")
assert auth_throttle.is_throttled("7.7.7.7", 10, 60) is False
finally:
auth_throttle.clear()
@@ -7,6 +7,7 @@ from devplacepy.database import get_table, set_setting
from devplacepy.utils import generate_uid
import devplacepy.services.openai_gateway.gateway as gwmod
from devplacepy.services.openai_gateway import GatewayService
from devplacepy.services.openai_gateway import auth_throttle
class FakeResp_openai_gateway:
def __init__(
self,
@@ -257,6 +258,7 @@ def test_authorize_static_access_key(local_db):
finally:
set_setting("gateway_access_key", "")
set_setting("gateway_require_auth", "1")
auth_throttle.clear()
def test_authorize_admin_and_user_toggles(local_db):
@@ -282,6 +284,7 @@ def test_authorize_admin_and_user_toggles(local_db):
set_setting("gateway_allow_users", "1")
set_setting("gateway_require_auth", "1")
set_setting("gateway_access_key", "")
auth_throttle.clear()
def test_authorize_require_auth_off_is_open(local_db):
@@ -293,6 +296,124 @@ def test_authorize_require_auth_off_is_open(local_db):
set_setting("gateway_require_auth", "1")
def test_authorize_throttles_repeated_failed_auth_from_one_ip(local_db):
import pytest
from fastapi import HTTPException
set_setting("gateway_require_auth", "1")
set_setting("gateway_access_key", "")
set_setting("gateway_auth_throttle_max_failures", "3")
set_setting("gateway_auth_throttle_window_seconds", "60")
auth_throttle.clear()
try:
svc = GatewayService()
ip = "203.0.113.10"
for _ in range(3):
assert (
svc.authorize(_make_request_openai_gateway(headers={"X-Real-IP": ip}))
is False
)
with pytest.raises(HTTPException) as excinfo:
svc.authorize(_make_request_openai_gateway(headers={"X-Real-IP": ip}))
assert excinfo.value.status_code == 429
assert "Retry-After" in excinfo.value.headers
finally:
set_setting("gateway_auth_throttle_max_failures", "10")
set_setting("gateway_auth_throttle_window_seconds", "60")
set_setting("gateway_require_auth", "1")
auth_throttle.clear()
def test_authorize_throttle_does_not_affect_other_ips(local_db):
set_setting("gateway_require_auth", "1")
set_setting("gateway_access_key", "")
set_setting("gateway_auth_throttle_max_failures", "3")
set_setting("gateway_auth_throttle_window_seconds", "60")
auth_throttle.clear()
try:
svc = GatewayService()
throttled_ip = "203.0.113.20"
other_ip = "203.0.113.21"
for _ in range(3):
svc.authorize(_make_request_openai_gateway(headers={"X-Real-IP": throttled_ip}))
assert auth_throttle.is_throttled(throttled_ip, 3, 60) is True
assert (
svc.authorize(_make_request_openai_gateway(headers={"X-Real-IP": other_ip}))
is False
)
assert auth_throttle.is_throttled(other_ip, 3, 60) is False
finally:
set_setting("gateway_auth_throttle_max_failures", "10")
set_setting("gateway_auth_throttle_window_seconds", "60")
set_setting("gateway_require_auth", "1")
auth_throttle.clear()
def test_authorize_valid_credentials_succeed_from_throttled_ip(local_db):
set_setting("gateway_require_auth", "1")
set_setting("gateway_access_key", "")
set_setting("gateway_allow_users", "1")
set_setting("gateway_auth_throttle_max_failures", "3")
set_setting("gateway_auth_throttle_window_seconds", "60")
auth_throttle.clear()
try:
svc = GatewayService()
ip = "203.0.113.30"
_, member_key = _make_admin_openai_gateway(role="Member")
for _ in range(3):
svc.authorize(_make_request_openai_gateway(headers={"X-Real-IP": ip}))
assert auth_throttle.is_throttled(ip, 3, 60) is True
assert (
svc.authorize(
_make_request_openai_gateway(
headers={"X-Real-IP": ip, "X-API-KEY": member_key}
)
)
is True
)
set_setting("gateway_access_key", "topsecret")
assert (
svc.authorize(
_make_request_openai_gateway(
headers={"X-Real-IP": ip, "X-API-KEY": "topsecret"}
)
)
is True
)
finally:
set_setting("gateway_access_key", "")
set_setting("gateway_auth_throttle_max_failures", "10")
set_setting("gateway_auth_throttle_window_seconds", "60")
set_setting("gateway_require_auth", "1")
auth_throttle.clear()
def test_authorize_throttle_resets_after_window(local_db, monkeypatch):
set_setting("gateway_require_auth", "1")
set_setting("gateway_access_key", "")
set_setting("gateway_auth_throttle_max_failures", "3")
set_setting("gateway_auth_throttle_window_seconds", "60")
auth_throttle.clear()
try:
now = [2000.0]
monkeypatch.setattr(auth_throttle.time, "time", lambda: now[0])
svc = GatewayService()
ip = "203.0.113.40"
for _ in range(3):
svc.authorize(_make_request_openai_gateway(headers={"X-Real-IP": ip}))
assert auth_throttle.is_throttled(ip, 3, 60) is True
now[0] += 61
assert (
svc.authorize(_make_request_openai_gateway(headers={"X-Real-IP": ip}))
is False
)
finally:
set_setting("gateway_auth_throttle_max_failures", "10")
set_setting("gateway_auth_throttle_window_seconds", "60")
set_setting("gateway_require_auth", "1")
auth_throttle.clear()
class FakeEmbedClient_openai_gateway:
def __init__(self, *a, **k):
self.calls = []
@@ -381,6 +502,36 @@ def test_embeddings_disabled_returns_503(local_db, monkeypatch):
assert resp.status_code == 503
def test_embeddings_missing_key_returns_clean_error_not_unauthenticated_call(
local_db, monkeypatch
):
monkeypatch.setattr(gwmod.httpx, "AsyncClient", FakeEmbedClient_openai_gateway)
monkeypatch.delenv("OPENROUTER_API_KEY", raising=False)
monkeypatch.delenv("DEEPSEEK_API_KEY", raising=False)
set_setting("gateway_api_key", "")
set_setting("gateway_vision_key", "")
set_setting("gateway_embed_key", "")
try:
svc = GatewayService()
cfg = svc.effective_config()
assert cfg["gateway_embed_key"] == ""
cfg["gateway_embed_enabled"] = True
rt = svc.runtime()
resp = run_async(
rt.handle_embeddings(
{"input": "hello"}, cfg, ("guest", "no_key_probe"), "test", "default"
)
)
assert resp.status_code == 503
payload = json.loads(bytes(resp.body).decode())
assert payload["error"]["type"] == "embeddings_not_configured"
assert rt._client is None
finally:
set_setting("gateway_api_key", "")
set_setting("gateway_vision_key", "")
set_setting("gateway_embed_key", "")
class FakeImageClient_openai_gateway:
def __init__(self, *a, **k):
self.calls = []
@@ -1942,3 +2093,121 @@ def test_hostile_upstream_model_header_is_ignored_end_to_end(local_db, monkeypat
)
)
assert resp.headers["X-Gateway-Model"] == "deepseek-chat"
def test_bypass_preamble_skips_the_operator_preamble_when_allowed(local_db, monkeypatch):
monkeypatch.setattr(gwmod.httpx, "AsyncClient", FakeClient_openai_gateway)
svc = GatewayService()
cfg = svc.effective_config()
cfg["gateway_system_preamble"] = "OPERATOR-PREAMBLE-TEXT"
rt = svc.runtime()
run_async(
rt.handle_chat(
{
"messages": [
{"role": "system", "content": "Reply with strict JSON only."},
{"role": "user", "content": "hi"},
],
"bypass_preamble": True,
},
cfg,
("internal", "devii"),
"test",
"default",
bypass_allowed=True,
)
)
sent = rt._client.calls[-1][1]
system_message = sent["messages"][0]
assert system_message["role"] == "system"
assert "OPERATOR-PREAMBLE-TEXT" not in system_message["content"]
assert "Current date" in system_message["content"]
assert "Reply with strict JSON only." in system_message["content"]
assert "bypass_preamble" not in sent
def test_bypass_preamble_ignored_without_internal_credentials(local_db, monkeypatch):
monkeypatch.setattr(gwmod.httpx, "AsyncClient", FakeClient_openai_gateway)
svc = GatewayService()
cfg = svc.effective_config()
cfg["gateway_system_preamble"] = "OPERATOR-PREAMBLE-TEXT"
rt = svc.runtime()
run_async(
rt.handle_chat(
{"messages": [{"role": "user", "content": "hi"}], "bypass_preamble": True},
cfg,
("user", "someuser"),
"test",
"default",
bypass_allowed=False,
)
)
sent = rt._client.calls[-1][1]
system_message = sent["messages"][0]
assert "OPERATOR-PREAMBLE-TEXT" in system_message["content"]
assert "bypass_preamble" not in sent
def test_bypass_preamble_keeps_the_clients_own_system_content(local_db, monkeypatch):
monkeypatch.setattr(gwmod.httpx, "AsyncClient", FakeClient_openai_gateway)
svc = GatewayService()
cfg = svc.effective_config()
cfg["gateway_system_preamble"] = "OPERATOR-PREAMBLE-TEXT"
rt = svc.runtime()
run_async(
rt.handle_chat(
{
"messages": [
{"role": "system", "content": "Grade strictly. Reply JSON only."},
{"role": "user", "content": "hi"},
],
"bypass_preamble": True,
},
cfg,
("internal", "devii"),
"test",
"default",
bypass_allowed=True,
)
)
sent = rt._client.calls[-1][1]
system_message = sent["messages"][0]
assert "OPERATOR-PREAMBLE-TEXT" not in system_message["content"]
assert "Grade strictly. Reply JSON only." in system_message["content"]
def test_internal_bypass_allowed_requires_the_exact_internal_key(local_db):
set_setting("gateway_internal_key", "test-internal-secret")
try:
svc = GatewayService()
cfg = svc.effective_config()
assert (
svc.internal_bypass_allowed(
_make_request_openai_gateway(
headers={"X-Gateway-Internal-Key": "test-internal-secret"}
),
cfg,
)
is True
)
assert (
svc.internal_bypass_allowed(
_make_request_openai_gateway(
headers={"X-Gateway-Internal-Key": "wrong-secret"}
),
cfg,
)
is False
)
assert (
svc.internal_bypass_allowed(
_make_request_openai_gateway(
headers={"Authorization": "Bearer test-internal-secret"}
),
cfg,
)
is False
)
assert svc.internal_bypass_allowed(_make_request_openai_gateway(), cfg) is False
finally:
set_setting("gateway_internal_key", "")
@@ -267,6 +267,32 @@ def test_embed_route_and_kind_isolation(local_db):
_cleanup(["utemb"], ["ut-embed"])
def test_embed_overlay_blank_provider_key_preserves_top_level_key(local_db):
r.provider_store.set(
r.ProviderIn(
name="utemblank",
base_url="https://emb.example/v1/chat/completions",
api_key="",
)
)
r.model_store.set(
r.ModelRouteIn(
source_model="ut-embed-blank",
provider="utemblank",
target_model="vendor/embed",
kind="embed",
)
)
try:
base_cfg = {"gateway_embed_key": "top-level-fallback-key"}
overlay = r.embed_overlay("ut-embed-blank", base_cfg)
assert "gateway_embed_key" not in overlay
merged = {**base_cfg, **overlay}
assert merged["gateway_embed_key"] == "top-level-fallback-key"
finally:
_cleanup(["utemblank"], ["ut-embed-blank"])
def test_inactive_route_ignored(local_db):
r.model_store.set(
r.ModelRouteIn(