refactor: remove dead session code
This commit is contained in:
@@ -210,8 +210,8 @@ async def _require_token(
|
||||
SDK sends — LibreChat, the CLI, third-party clients) and
|
||||
``Authorization: Bearer <token>`` (curl, Cursor). 401 on missing /
|
||||
unknown token; 403 on a known token whose scope doesn't cover
|
||||
``scope`` (Phase 4.3 — bootstrap tokens get ``"*"`` and pass
|
||||
everything).
|
||||
``scope``. Bootstrap tokens implicitly carry ``"*"`` and pass
|
||||
every scope check.
|
||||
"""
|
||||
api_key = request.headers.get("x-api-key")
|
||||
identity = (
|
||||
|
||||
@@ -40,7 +40,11 @@ class Settings(BaseSettings):
|
||||
value per gateway since we open one Client total."""
|
||||
|
||||
bootstrap_tokens: str = ""
|
||||
"""Phase 1.3 seed: ``name1:value1,name2:value2``. Empty = no auth wired yet.
|
||||
"""Out-of-band token seed: ``name1:value1,name2:value2``. Empty is
|
||||
fine — DB-issued tokens (admin UI) carry steady-state auth.
|
||||
|
||||
Phase 4 moves tokens into the DB. Until then this is the only source.
|
||||
This env channel layers alongside the DB store and is kept for
|
||||
first-run setup, disaster recovery (admin password lost), and
|
||||
``examples/`` smoke tests. Bootstrap tokens implicitly carry
|
||||
scope ``"*"`` and never get a ``last_used_at`` stamp (no DB row).
|
||||
"""
|
||||
|
||||
@@ -1,40 +1,32 @@
|
||||
"""SQLModel-backed persistence (Phase 4.1).
|
||||
"""SQLModel-backed persistence.
|
||||
|
||||
The storage layer carries three tables — :class:`Token`, :class:`Session`,
|
||||
:class:`AuditLog` — and a thin :class:`Database` wrapper around a sync
|
||||
SQLAlchemy engine. Phase 4.2 (auth migration) and Phase 4.3 (admin UI)
|
||||
build on this; Phase 4.1 itself only schemas the data and exposes the
|
||||
``Database`` on :class:`GatewayRuntime` so later phases can reach it.
|
||||
Two tables — :class:`Token`, :class:`AuditLog` — plus a thin
|
||||
:class:`Database` wrapper around an async SQLAlchemy engine. The
|
||||
``GatewayRuntime`` carries the handle so auth (token verify / touch)
|
||||
and admin (token CRUD + audit listing) can reach it.
|
||||
"""
|
||||
|
||||
from beaver_gateway.storage.db import (
|
||||
Database,
|
||||
append_audit,
|
||||
close_session,
|
||||
create_token,
|
||||
list_active_tokens,
|
||||
list_audit_records,
|
||||
list_tokens,
|
||||
revoke_token,
|
||||
touch_session,
|
||||
touch_token,
|
||||
upsert_session,
|
||||
)
|
||||
from beaver_gateway.storage.models import AuditLog, Session, Token
|
||||
from beaver_gateway.storage.models import AuditLog, Token
|
||||
|
||||
__all__ = [
|
||||
"AuditLog",
|
||||
"Database",
|
||||
"Session",
|
||||
"Token",
|
||||
"append_audit",
|
||||
"close_session",
|
||||
"create_token",
|
||||
"list_active_tokens",
|
||||
"list_audit_records",
|
||||
"list_tokens",
|
||||
"revoke_token",
|
||||
"touch_session",
|
||||
"touch_token",
|
||||
"upsert_session",
|
||||
]
|
||||
|
||||
@@ -22,7 +22,7 @@ from sqlalchemy.ext.asyncio import create_async_engine
|
||||
from sqlmodel import SQLModel, select
|
||||
from sqlmodel.ext.asyncio.session import AsyncSession
|
||||
|
||||
from beaver_gateway.storage.models import AuditLog, Session, Token
|
||||
from beaver_gateway.storage.models import AuditLog, Token
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Sequence
|
||||
@@ -143,45 +143,6 @@ async def touch_token(session: AsyncSession, *, token_id: int) -> None:
|
||||
await session.commit()
|
||||
|
||||
|
||||
# ---- Session bookkeeping ------------------------------------------------
|
||||
|
||||
|
||||
async def upsert_session(
|
||||
session: AsyncSession, *, session_id: str, agent_name: str, fingerprint: str
|
||||
) -> Session:
|
||||
"""Insert-or-bump a Session row. Agent/fingerprint never change for an id."""
|
||||
row = await session.get(Session, session_id)
|
||||
if row is None:
|
||||
row = Session(id=session_id, agent_name=agent_name, fingerprint=fingerprint)
|
||||
else:
|
||||
row.last_active_at = _utcnow()
|
||||
session.add(row)
|
||||
await session.commit()
|
||||
await session.refresh(row)
|
||||
return row
|
||||
|
||||
|
||||
async def touch_session(session: AsyncSession, *, session_id: str) -> None:
|
||||
"""Bump ``last_active_at`` without changing fingerprint/agent."""
|
||||
row = await session.get(Session, session_id)
|
||||
if row is None:
|
||||
return
|
||||
row.last_active_at = _utcnow()
|
||||
session.add(row)
|
||||
await session.commit()
|
||||
|
||||
|
||||
async def close_session(session: AsyncSession, *, session_id: str) -> bool:
|
||||
"""Mark a session closed. Returns ``False`` if no such row."""
|
||||
row = await session.get(Session, session_id)
|
||||
if row is None or row.closed_at is not None:
|
||||
return False
|
||||
row.closed_at = _utcnow()
|
||||
session.add(row)
|
||||
await session.commit()
|
||||
return True
|
||||
|
||||
|
||||
# ---- Audit --------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -225,13 +186,10 @@ async def list_audit_records(
|
||||
__all__ = [
|
||||
"Database",
|
||||
"append_audit",
|
||||
"close_session",
|
||||
"create_token",
|
||||
"list_active_tokens",
|
||||
"list_audit_records",
|
||||
"list_tokens",
|
||||
"revoke_token",
|
||||
"touch_session",
|
||||
"touch_token",
|
||||
"upsert_session",
|
||||
]
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
"""SQLModel tables — see PRD §9.
|
||||
"""SQLModel tables.
|
||||
|
||||
Three tables, all flat, no relationships modelled yet (Phase 4 talks
|
||||
about ``actor`` and ``agent_name`` as strings — joining audit→token by
|
||||
name is fine at this volume; we'll introduce FKs when the admin UI
|
||||
actually demands them).
|
||||
Two tables, both flat, no relationships modelled yet (``actor`` and
|
||||
``agent_name`` are stored as strings — joining audit→token by name is
|
||||
fine at this volume; we'll introduce FKs when the admin UI actually
|
||||
demands them).
|
||||
|
||||
A ``Session`` table originally lived here for live-session
|
||||
observability. It was dropped after we decided the gateway stays
|
||||
stateless about identity (claude-code-api's in-memory fingerprint pool
|
||||
is the source of truth) and that conversation persistence belongs in a
|
||||
future Obsidian-sync frontend, not a sessions table.
|
||||
|
||||
Datetimes are stored UTC; we set ``default_factory`` rather than relying
|
||||
on DB defaults so SQLite + Postgres behave identically. Every row that
|
||||
@@ -39,23 +45,6 @@ class Token(SQLModel, table=True):
|
||||
revoked_at: datetime | None = Field(default=None)
|
||||
|
||||
|
||||
class Session(SQLModel, table=True):
|
||||
"""Mirror of one live ``claude-code-api`` session.
|
||||
|
||||
The id is the ``session_id`` claude itself assigns on the first
|
||||
turn; we don't generate it. Rows here are for admin observability
|
||||
(live count, last activity) — the actual pool lives in
|
||||
``claude_code_api.ClaudeCodeBackend`` and is the source of truth.
|
||||
"""
|
||||
|
||||
id: str = Field(primary_key=True)
|
||||
agent_name: str = Field(index=True)
|
||||
fingerprint: str = Field(index=True)
|
||||
created_at: datetime = Field(default_factory=_utcnow)
|
||||
last_active_at: datetime = Field(default_factory=_utcnow)
|
||||
closed_at: datetime | None = Field(default=None)
|
||||
|
||||
|
||||
class AuditLog(SQLModel, table=True):
|
||||
"""Append-only record of who-did-what.
|
||||
|
||||
@@ -77,4 +66,4 @@ class AuditLog(SQLModel, table=True):
|
||||
detail_json: str = Field(default="{}")
|
||||
|
||||
|
||||
__all__ = ["AuditLog", "Session", "Token"]
|
||||
__all__ = ["AuditLog", "Token"]
|
||||
|
||||
Reference in New Issue
Block a user