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:
68
backend/app/electronics/db/connection.py
Normal file
68
backend/app/electronics/db/connection.py
Normal file
@@ -0,0 +1,68 @@
|
||||
"""Connections to the local electronics database.
|
||||
|
||||
settings.py has already refused to load unless DB_HOST is local and DB_NAME is
|
||||
electronics_catalog, so nothing here can reach another database.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from contextlib import contextmanager
|
||||
from typing import Iterator
|
||||
|
||||
import psycopg
|
||||
from psycopg.rows import dict_row
|
||||
|
||||
from app.infrastructure.settings import (
|
||||
DB_CONNECT_TIMEOUT_SECONDS,
|
||||
DB_HOST,
|
||||
DB_NAME,
|
||||
DB_PASSWORD,
|
||||
DB_PORT,
|
||||
DB_USER,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def connect(*, autocommit: bool = False) -> psycopg.Connection:
|
||||
conn = psycopg.connect(
|
||||
host=DB_HOST,
|
||||
port=DB_PORT,
|
||||
dbname=DB_NAME,
|
||||
user=DB_USER,
|
||||
password=DB_PASSWORD,
|
||||
connect_timeout=DB_CONNECT_TIMEOUT_SECONDS,
|
||||
autocommit=autocommit,
|
||||
row_factory=dict_row,
|
||||
)
|
||||
try:
|
||||
from pgvector.psycopg import register_vector
|
||||
|
||||
register_vector(conn)
|
||||
except Exception: # noqa: BLE001 - the extension is created by migration 0001
|
||||
pass
|
||||
return conn
|
||||
|
||||
|
||||
@contextmanager
|
||||
def transaction() -> Iterator[psycopg.Connection]:
|
||||
"""A connection whose work is committed on success, rolled back on error."""
|
||||
conn = connect()
|
||||
try:
|
||||
yield conn
|
||||
conn.commit()
|
||||
except Exception:
|
||||
conn.rollback()
|
||||
raise
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def check_connection() -> bool:
|
||||
try:
|
||||
with connect(autocommit=True) as conn:
|
||||
conn.execute("SELECT 1")
|
||||
return True
|
||||
except Exception as exc: # noqa: BLE001 - a health probe reports, never raises
|
||||
logger.debug("Database unreachable: %s", exc)
|
||||
return False
|
||||
Reference in New Issue
Block a user