Electronics Catalog: API, MCP server, frontend and deployment
Verified catalogue of mobiles and laptops sold in India, collected from real retail listings (FastAPI backend, React frontend, Postgres/pgvector). - REST API under /api/elec (read-only catalogue; admin endpoints need login) - MCP server (FastMCP) at /mcp/ with list_categories, search_products, get_product and price_history tools - Real ratings and reviews read from product pages and search results - Production Dockerfile (requirements-api.txt, no PyTorch) and .env.production.example; remote database only via an explicit ELEC_ALLOW_REMOTE_DB host/name allowlist - docs/API.md: endpoint and MCP reference with live examples Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
73
backend/app/api/schemas.py
Normal file
73
backend/app/api/schemas.py
Normal file
@@ -0,0 +1,73 @@
|
||||
"""Pydantic response models for the health endpoint. The electronics
|
||||
catalogue's own models live in app/electronics/api_models.py."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import List, Optional
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class ApiKeyInfoOut(BaseModel):
|
||||
"""One configured machine consumer, named but never quoted.
|
||||
|
||||
`fingerprint` is a truncated digest of name+secret, not the secret. It exists
|
||||
so a caller who was issued a key can confirm THAT key is the one this
|
||||
deployment loaded - the question a 401 cannot answer, since an undeployed key
|
||||
and a wrong key fail identically.
|
||||
"""
|
||||
|
||||
name: str
|
||||
role: str
|
||||
fingerprint: str
|
||||
|
||||
|
||||
class AuthConfigOut(BaseModel):
|
||||
"""
|
||||
The effective auth configuration, reported by /api/health.
|
||||
|
||||
Unauthenticated on purpose. The failure this exists to diagnose is "nobody
|
||||
can sign in", so anything gated behind an admin token is unreachable
|
||||
exactly when it is needed. Nothing here is a secret: the admin username is
|
||||
already the documented one, allow_any_login=true is a fact an operator
|
||||
urgently needs (and an attacker discovers with a single login attempt
|
||||
anyway), and the fingerprint is a truncated hash of a salted digest, not a
|
||||
password. The API key block follows the same rule: it names which consumers
|
||||
are configured and fingerprints their keys, so a caller can tell an
|
||||
undeployed key from a rejected one, but it never renders a secret. What it buys is a one-command answer to "is this deployment
|
||||
running the config I think it is?" - compare the fingerprint here against
|
||||
the one printed by scripts/make_auth_secrets.py --fingerprint.
|
||||
"""
|
||||
|
||||
enabled: bool
|
||||
allow_any_login: bool
|
||||
admin_username: str
|
||||
password_hash_valid: bool
|
||||
password_hash_iterations: Optional[int] = None
|
||||
password_hash_fingerprint: str
|
||||
# "process-env" | "env-file" | "default" - which one actually won.
|
||||
admin_username_source: str
|
||||
password_hash_source: str
|
||||
# Machine consumers. Names and fingerprints only - the secrets themselves are
|
||||
# never rendered here, and _parse_api_keys enforces enough entropy that the
|
||||
# fingerprints do not give them away. Defaulted so a client of this schema
|
||||
# still validates against a deployment predating these fields.
|
||||
api_keys_count: int = 0
|
||||
api_keys: List[ApiKeyInfoOut] = Field(default_factory=list)
|
||||
api_keys_source: str = "default"
|
||||
|
||||
|
||||
class SearchStatusOut(BaseModel):
|
||||
"""Which web-search providers discovery can use right now."""
|
||||
ddg: bool
|
||||
google_cse: bool
|
||||
|
||||
|
||||
class HealthOut(BaseModel):
|
||||
status: str
|
||||
database: bool
|
||||
database_name: str
|
||||
ollama: bool
|
||||
ollama_model: str
|
||||
embeddings_model: str
|
||||
search: SearchStatusOut
|
||||
auth: AuthConfigOut
|
||||
Reference in New Issue
Block a user