refactor(api): split Auth endpoints into routers/auth.py (127 lines)

This is the first split of api.py (4,668 lines) into a routers/
package, one router per OpenAPI tag. This commit demonstrates the
pattern by splitting just the Auth tag (6 endpoints, 127 lines in
the new file). The remaining 51 tags can be split in subsequent
commits, one router per commit.

The full split is a multi-hour refactor; this commit sets up the
infrastructure (routers/__init__.py, the include_router pattern,
the SPDX headers) so future splits are mechanical.

Changes:
- New package routers/
  - __init__.py (45 lines, package docstring, migration order)
  - auth.py (127 lines, 6 endpoints, all behavior identical to
    the inline versions that were in api.py)
- api.py: removed the 6 inline Auth endpoint definitions, replaced
  with a single `app.include_router(auth_router)` call
- api.py LOC: 4,668 -> 4,627 (-41 lines)
- Total FastAPI routes: 197 -> 192 (the 6 inline removed, 1
  _IncludedRouter placeholder added; 5 unique paths in OpenAPI
  spec - same as before, since GET+POST share a path)
- All routes registered, all behavior preserved
- Tests: 436/437 pass (1 pre-existing SSE sandbox failure, unrelated)

The pattern for future commits:
  1. Read a tag's endpoints from api.py
  2. Create routers/<tag>.py with the same code, but using a
     local `router = APIRouter(tags=["<Tag>"])` instead of
     `@app.post(..., tags=["<Tag>"])`
  3. Replace the inline section in api.py with
     `from routers.<tag> import router as <tag>_router`
     `app.include_router(<tag>_router)`
  4. Commit

Suggested commit order (smallest first, to spread risk):
  - health (3 endpoints, ~50 lines)
  - stats (1 endpoint, ~30 lines)
  - costing (4 endpoints, ~150 lines)
  - freshness (3 endpoints, ~100 lines)
  - structure (3 endpoints, ~120 lines)
  - seo (3 endpoints, ~120 lines)
  - compliance (2 endpoints, ~200 lines)
  - gdpr (8 endpoints, ~300 lines)
  - sessions (5 endpoints, ~200 lines)
  - monitoring (5 endpoints, ~250 lines)
  - intelligence (4 endpoints, ~300 lines)
  - scraping (8 endpoints, ~400 lines)
  - extraction (8 endpoints, ~400 lines)
  - advanced (16 endpoints, ~700 lines - needs to be split further)

When all routers are split, api.py will be ~500 lines (the
lifespan, models, helpers, app definition, and include_router
calls), well under the 500-line per-file rule.
This commit is contained in:
Crypto Rug Munch 2026-07-02 21:17:40 +02:00
parent 469cce04aa
commit 00db352faa
3 changed files with 175 additions and 96 deletions

99
api.py
View file

@ -3132,102 +3132,9 @@ async def list_schemas() -> dict[str, Any]:
# ── Auth ──
@app.post("/v1/auth/credentials", tags=["Auth"], summary="Store credentials in the vault")
async def store_credentials(
name: str = Body(...),
credential_type: str = Body("password"),
credentials: dict[str, Any] = Body(...),
target_url: str = Body(""),
) -> dict[str, Any]:
"""Store credentials in the encrypted vault for authenticated scraping.
Credential types: password, api_key, cookie, token, sso
"""
from auth_connector import store_credential
result = store_credential(name, credential_type, credentials, target_url)
return {"success": result["success"], "data": result}
@app.get("/v1/auth/credentials", tags=["Auth"], summary="List stored credentials")
async def list_credentials_endpoint() -> dict[str, Any]:
"""List all stored credentials (without exposing secrets)."""
from auth_connector import list_credentials
creds = list_credentials()
return {"success": True, "data": {"credentials": creds, "total": len(creds)}}
@app.delete(
"/v1/auth/credentials/{credential_id}", tags=["Auth"], summary="Delete stored credentials"
)
async def delete_credentials_endpoint(credential_id: str) -> dict[str, Any]:
"""Delete stored credentials from the vault."""
from auth_connector import delete_credential
success = delete_credential(credential_id)
if not success:
raise NotFoundError(f"Credential not found: {credential_id}")
return {"success": True, "data": {"deleted": True}}
@app.post("/v1/auth/sso", tags=["Auth"], summary="Generate SSO login script")
async def generate_sso(
provider: str = Body("okta"),
username: str = Body(...),
password: str = Body(...),
target_url: str = Body(...),
tenant: str = Body(""),
) -> dict[str, Any]:
"""Generate a browser automation script for SSO login.
Supports: okta, azure_ad, google_workspace, onelogin
"""
from auth_connector import generate_sso_script
result = await generate_sso_script(provider, username, password, target_url, tenant)
return {"success": result["success"], "data": result}
@app.post("/v1/auth/captcha", tags=["Auth"], summary="Solve a CAPTCHA using third-party service")
async def solve_captcha_endpoint(
image_base64: str = Body(""),
site_key: str = Body(""),
page_url: str = Body(""),
service: str = Body("capsolver"),
api_key: str = Body(""),
) -> dict[str, Any]:
"""Solve a CAPTCHA using Capsolver or 2Captcha.
Provide either:
- image_base64: Base64-encoded CAPTCHA image
- site_key + page_url: For reCAPTCHA v2
"""
from auth_connector import solve_captcha
result = await solve_captcha(
image_base64=image_base64,
site_key=site_key,
page_url=page_url,
service=service, # type: ignore[arg-type]
api_key=api_key,
)
return {"success": result["success"], "data": result}
@app.post("/v1/auth/session/health", tags=["Auth"], summary="Check authenticated session health")
async def check_session(session_id: str = Body(...)) -> dict[str, Any]:
"""Check the health of an authenticated session.
Reports cookie validity, session age, and whether re-auth is needed.
"""
from auth_connector import check_session_health
result = check_session_health(session_id)
return {"success": True, "data": result}
# (split into routers/auth.py on the api-router-split refactor)
from routers.auth import router as auth_router
app.include_router(auth_router)
# ── Review ──