feat(auth): single-admin password login (signed cookie) — gate tuning + Sources catalog, keep chat and document viewer public

This commit is contained in:
2026-08-23 19:58:39 -04:00
parent fc0d9a2d5c
commit cbc263a4b2
46 changed files with 1555 additions and 691 deletions
+15 -3
View File
@@ -13,6 +13,7 @@ from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy import func, select
from sqlalchemy.orm import Session
from app.core.auth import require_admin
from app.db import get_db
from app.models import Chunk, Document
from app.schemas import DocContent, DocList, DocSummary
@@ -28,11 +29,17 @@ def doc_format(path: str) -> str:
@router.get("/docs", response_model=DocList)
def list_documents(db: Session = Depends(get_db)) -> DocList: # noqa: B008
def list_documents(
db: Session = Depends(get_db), # noqa: B008
_admin: None = Depends(require_admin), # noqa: B008
) -> DocList:
"""All indexed documents with per-document chunk counts.
An empty list means the knowledge base has not been imported yet —
the Sources page renders its designed empty state in that case.
Admin-only (phase 16 — the catalog is what the sign-in gates; the
document viewer itself stays public, see below). Anonymous callers
get 403 ``admin only`` and the Sources page renders its sign-in gate
instead. An empty list means the knowledge base has not been imported
yet — the Sources page renders its designed empty state in that case.
"""
rows = db.execute(
select(
@@ -71,6 +78,11 @@ def get_document_content(
Stateless (A10) and database-only: unknown pairs — including traversal
strings such as ``../../etc/passwd`` — are just non-existent rows and
map to 404 ``{detail: "document not found"}``.
Deliberately PUBLIC for anonymous callers (phase 16 soft rule, owner
decision 2026-08-22): the *catalog* (``GET /api/docs``) is what the
sign-in gates, not the viewer — chat cites documents and anyone may
open a cited document by direct URL.
"""
row = db.execute(
select(Document, func.count(Chunk.id).label("chunks"))