Multi-Source Integration Phase 3.2. /api/v1/search with cross-source filters + POST body params + Redis 5min hot cache. New files: app/schemas/search.py — SearchParams (~20 filters, Pydantic v2) app/schemas/search_response.py — SearchResponse + SearchResultItem app/services/search_query.py — build_search_query (psycopg v3, CAST(:x AS type)) app/services/cache.py — SearchCache async wrapper (singleton lru_cache) app/api/v1/search.py — POST /search endpoint tests/test_search_api.py — unit + integration tests Edited: app/main.py — include search.router prefix=/api/v1 app/core/config.py — settings.redis_url default pyproject.toml — add redis>=5.0.0 Queries listings_search_mv (matview from PR #469). Cache failures swallowed (degrade to slow-but-correct). Sort whitelisted dict (no SQL injection). Refs Master Plan sec 9.1 + 7.2.
61 lines
2.3 KiB
Python
61 lines
2.3 KiB
Python
"""Trade-In MVP — FastAPI entry point.
|
|
|
|
Standalone версия, выделена из основного gendesign repo для локальной разработки.
|
|
Только trade-in фича; никаких других роутеров (concepts/parcels/analytics/etc) нет.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
|
|
import sentry_sdk
|
|
from fastapi import FastAPI
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
|
|
from app.api.v1 import admin, brand, geocode, search, trade_in
|
|
from app.core.config import settings
|
|
from app.core.ratelimit import RateLimitMiddleware
|
|
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format="%(asctime)s %(levelname)s %(name)s: %(message)s",
|
|
)
|
|
|
|
# Мониторинг ошибок — GlitchTip (Sentry-совместимый, #396).
|
|
# DSN из env GLITCHTIP_DSN; пусто (dev) → мониторинг не инициализируется.
|
|
if settings.glitchtip_dsn:
|
|
sentry_sdk.init(
|
|
dsn=settings.glitchtip_dsn,
|
|
environment=settings.environment,
|
|
traces_sample_rate=0.0, # только ошибки, без performance-трейсов
|
|
send_default_pii=False, # не шлём client_name / client_phone в отчёты
|
|
)
|
|
logging.getLogger("app.main").info("GlitchTip monitoring enabled")
|
|
|
|
app = FastAPI(
|
|
title="Trade-In MVP API",
|
|
description="Оценка вторичного жилья (выкупная стоимость) — копия trade-in feature из gendesign",
|
|
version="0.1.0",
|
|
)
|
|
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=settings.cors_origins,
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
# Rate-limit публичного API (per-IP sliding window) — защита от абуза.
|
|
app.add_middleware(RateLimitMiddleware)
|
|
|
|
|
|
@app.get("/health")
|
|
def health() -> dict[str, str]:
|
|
return {"status": "ok", "environment": settings.environment}
|
|
|
|
|
|
app.include_router(geocode.router, prefix="/api/v1/geocode", tags=["geocode"])
|
|
app.include_router(admin.router, prefix="/api/v1/admin", tags=["admin"])
|
|
app.include_router(brand.router, prefix="/api/v1/brand", tags=["brand"])
|
|
app.include_router(trade_in.router, prefix="/api/v1/trade-in", tags=["trade-in"])
|
|
app.include_router(search.router, prefix="/api/v1", tags=["search"])
|