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
|
SDK sends — LibreChat, the CLI, third-party clients) and
|
||||||
``Authorization: Bearer <token>`` (curl, Cursor). 401 on missing /
|
``Authorization: Bearer <token>`` (curl, Cursor). 401 on missing /
|
||||||
unknown token; 403 on a known token whose scope doesn't cover
|
unknown token; 403 on a known token whose scope doesn't cover
|
||||||
``scope`` (Phase 4.3 — bootstrap tokens get ``"*"`` and pass
|
``scope``. Bootstrap tokens implicitly carry ``"*"`` and pass
|
||||||
everything).
|
every scope check.
|
||||||
"""
|
"""
|
||||||
api_key = request.headers.get("x-api-key")
|
api_key = request.headers.get("x-api-key")
|
||||||
identity = (
|
identity = (
|
||||||
|
|||||||
@@ -40,7 +40,11 @@ class Settings(BaseSettings):
|
|||||||
value per gateway since we open one Client total."""
|
value per gateway since we open one Client total."""
|
||||||
|
|
||||||
bootstrap_tokens: str = ""
|
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`,
|
Two tables — :class:`Token`, :class:`AuditLog` — plus a thin
|
||||||
:class:`AuditLog` — and a thin :class:`Database` wrapper around a sync
|
:class:`Database` wrapper around an async SQLAlchemy engine. The
|
||||||
SQLAlchemy engine. Phase 4.2 (auth migration) and Phase 4.3 (admin UI)
|
``GatewayRuntime`` carries the handle so auth (token verify / touch)
|
||||||
build on this; Phase 4.1 itself only schemas the data and exposes the
|
and admin (token CRUD + audit listing) can reach it.
|
||||||
``Database`` on :class:`GatewayRuntime` so later phases can reach it.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from beaver_gateway.storage.db import (
|
from beaver_gateway.storage.db import (
|
||||||
Database,
|
Database,
|
||||||
append_audit,
|
append_audit,
|
||||||
close_session,
|
|
||||||
create_token,
|
create_token,
|
||||||
list_active_tokens,
|
list_active_tokens,
|
||||||
list_audit_records,
|
list_audit_records,
|
||||||
list_tokens,
|
list_tokens,
|
||||||
revoke_token,
|
revoke_token,
|
||||||
touch_session,
|
|
||||||
touch_token,
|
touch_token,
|
||||||
upsert_session,
|
|
||||||
)
|
)
|
||||||
from beaver_gateway.storage.models import AuditLog, Session, Token
|
from beaver_gateway.storage.models import AuditLog, Token
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"AuditLog",
|
"AuditLog",
|
||||||
"Database",
|
"Database",
|
||||||
"Session",
|
|
||||||
"Token",
|
"Token",
|
||||||
"append_audit",
|
"append_audit",
|
||||||
"close_session",
|
|
||||||
"create_token",
|
"create_token",
|
||||||
"list_active_tokens",
|
"list_active_tokens",
|
||||||
"list_audit_records",
|
"list_audit_records",
|
||||||
"list_tokens",
|
"list_tokens",
|
||||||
"revoke_token",
|
"revoke_token",
|
||||||
"touch_session",
|
|
||||||
"touch_token",
|
"touch_token",
|
||||||
"upsert_session",
|
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ from sqlalchemy.ext.asyncio import create_async_engine
|
|||||||
from sqlmodel import SQLModel, select
|
from sqlmodel import SQLModel, select
|
||||||
from sqlmodel.ext.asyncio.session import AsyncSession
|
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:
|
if TYPE_CHECKING:
|
||||||
from collections.abc import Sequence
|
from collections.abc import Sequence
|
||||||
@@ -143,45 +143,6 @@ async def touch_token(session: AsyncSession, *, token_id: int) -> None:
|
|||||||
await session.commit()
|
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 --------------------------------------------------------------
|
# ---- Audit --------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
@@ -225,13 +186,10 @@ async def list_audit_records(
|
|||||||
__all__ = [
|
__all__ = [
|
||||||
"Database",
|
"Database",
|
||||||
"append_audit",
|
"append_audit",
|
||||||
"close_session",
|
|
||||||
"create_token",
|
"create_token",
|
||||||
"list_active_tokens",
|
"list_active_tokens",
|
||||||
"list_audit_records",
|
"list_audit_records",
|
||||||
"list_tokens",
|
"list_tokens",
|
||||||
"revoke_token",
|
"revoke_token",
|
||||||
"touch_session",
|
|
||||||
"touch_token",
|
"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
|
Two tables, both flat, no relationships modelled yet (``actor`` and
|
||||||
about ``actor`` and ``agent_name`` as strings — joining audit→token by
|
``agent_name`` are stored as strings — joining audit→token by name is
|
||||||
name is fine at this volume; we'll introduce FKs when the admin UI
|
fine at this volume; we'll introduce FKs when the admin UI actually
|
||||||
actually demands them).
|
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
|
Datetimes are stored UTC; we set ``default_factory`` rather than relying
|
||||||
on DB defaults so SQLite + Postgres behave identically. Every row that
|
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)
|
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):
|
class AuditLog(SQLModel, table=True):
|
||||||
"""Append-only record of who-did-what.
|
"""Append-only record of who-did-what.
|
||||||
|
|
||||||
@@ -77,4 +66,4 @@ class AuditLog(SQLModel, table=True):
|
|||||||
detail_json: str = Field(default="{}")
|
detail_json: str = Field(default="{}")
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["AuditLog", "Session", "Token"]
|
__all__ = ["AuditLog", "Token"]
|
||||||
|
|||||||
Reference in New Issue
Block a user