From 0ac421da93b84f33a120d4f7dde17403b0d20cdb Mon Sep 17 00:00:00 2001 From: valenti Date: Wed, 29 Apr 2026 11:50:59 -0400 Subject: [PATCH] fastcheck_api fix --- fastcheck_api/app/api/v1/routers/auth.py | 2 - fastcheck_api/app/api/v1/routers/checks.py | 178 +------------------ fastcheck_api/app/schemas/checks.py | 5 - fastcheck_api/app/services/auth_service.py | 3 - fastcheck_api/app/services/check_service.py | 8 +- fastcheck_api/app/services/legacy_service.py | 110 +++--------- server/external_providers/sheriff_v2/main.py | 24 +-- server/src/services/sheriffService.ts | 8 - server/src/services/sheriffV2Service.ts | 2 - 9 files changed, 27 insertions(+), 313 deletions(-) diff --git a/fastcheck_api/app/api/v1/routers/auth.py b/fastcheck_api/app/api/v1/routers/auth.py index 29b01f4..028531a 100644 --- a/fastcheck_api/app/api/v1/routers/auth.py +++ b/fastcheck_api/app/api/v1/routers/auth.py @@ -38,8 +38,6 @@ async def login(payload: LoginRequest, db: Annotated[AsyncIOMotorDatabase, Depen raise except ValueError: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid credentials") - except RuntimeError as e: - raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail=str(e)) @router.get( diff --git a/fastcheck_api/app/api/v1/routers/checks.py b/fastcheck_api/app/api/v1/routers/checks.py index 6ef4e8f..2c39e9a 100644 --- a/fastcheck_api/app/api/v1/routers/checks.py +++ b/fastcheck_api/app/api/v1/routers/checks.py @@ -1,12 +1,9 @@ from __future__ import annotations -import logging import re import time from typing import Annotated -import datetime as dt -from bson import ObjectId from fastapi import APIRouter, Depends, HTTPException, Query, Request, status from motor.motor_asyncio import AsyncIOMotorDatabase @@ -17,16 +14,13 @@ from fastcheck_api.app.schemas.checks import ( EvaluationJobOut, ListJobsResponse, ListResultsResponse, - LookupResponse, ) from fastcheck_api.app.services.audit_service import AuditService from fastcheck_api.app.services.check_service import CheckService, InsufficientCreditsError -from fastcheck_api.app.services.legacy_service import legacy_service -from fastcheck_api.app.utils.mongo import jsonable, sanitize_rut +from fastcheck_api.app.utils.mongo import sanitize_rut router = APIRouter(prefix="/checks", tags=["checks"]) -logger = logging.getLogger("fastcheck.checks") _MASK_REPLACEMENTS: dict[str, str] = { "sheriff": "provider_a", @@ -132,176 +126,6 @@ async def create_check( ) -@router.post( - "/lookup", - response_model=LookupResponse, - status_code=status.HTTP_201_CREATED, - summary="Lookup a RUT (sync)", - description="Create a check, run the legacy lookup immediately, persist the result, and return job+result in a single request.", -) -async def lookup( - request: Request, - payload: CreateCheckRequest, - user: Annotated[CurrentUser, Depends(require_permission("evaluation:create"))], - db: Annotated[AsyncIOMotorDatabase, Depends(get_db)], -): - start = time.perf_counter() - clean_rut = sanitize_rut(payload.supplier.rut) - token = legacy_service.get_user_token(user_id=user.id) - if not token: - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail="Legacy token missing or expired; login again", - ) - - try: - job = await CheckService.create_single_check( - db=db, - tenant_id=user.tenant, - user_id=user.id, - rut=clean_rut, - name=payload.supplier.name, - enqueue=False, - ) - await AuditService.log_consulta( - db=db, - tenant_id=user.tenant, - user_id=user.id, - consulta_type="individual", - rut=clean_rut, - endpoint="/api/v1/checks/lookup", - response_status=status.HTTP_201_CREATED, - request_data={"supplier": {"rut": clean_rut, "name": payload.supplier.name}}, - processing_time_ms=int((time.perf_counter() - start) * 1000), - credits_used=1, - ip_address=request.client.host if request.client else None, - user_agent=request.headers.get("user-agent"), - metadata={"evaluationType": "single", "mode": "sync"}, - ) - except InsufficientCreditsError: - await AuditService.log_consulta( - db=db, - tenant_id=user.tenant, - user_id=user.id, - consulta_type="individual", - rut=clean_rut, - endpoint="/api/v1/checks/lookup", - response_status=status.HTTP_402_PAYMENT_REQUIRED, - request_data={"supplier": {"rut": clean_rut, "name": payload.supplier.name}}, - processing_time_ms=int((time.perf_counter() - start) * 1000), - credits_used=0, - ip_address=request.client.host if request.client else None, - user_agent=request.headers.get("user-agent"), - metadata={"reason": "insufficient_credits", "mode": "sync"}, - ) - raise HTTPException(status_code=status.HTTP_402_PAYMENT_REQUIRED, detail="Insufficient credits") - - job_id = str(job.get("_id") or "") - try: - job_oid = ObjectId(job_id) - except Exception: - raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Invalid job id") - - now = dt.datetime.now(dt.timezone.utc) - - try: - result_data = await legacy_service.lookup_rut(rut=clean_rut, token=token) - ai_summary = result_data.get("aiAnalysis") if isinstance(result_data, dict) else None - if ai_summary and isinstance(ai_summary, str) and ai_summary.strip(): - try: - await legacy_service.save_fastcheck_summary(rut=clean_rut, summary=ai_summary, token=token) - except Exception: - pass - - insert = await db["evaluationresults"].insert_one( - { - "jobId": job_oid, - "tenantId": user.tenant, - "rut": clean_rut, - "name": payload.supplier.name, - "status": "success", - "data": result_data, - "createdAt": now, - "updatedAt": now, - } - ) - await db["evaluationjobs"].update_one( - {"_id": job_oid, "tenantId": user.tenant}, - { - "$set": { - "completedEvaluations": 1, - "failedEvaluations": 0, - "status": "completed", - "updatedAt": now, - } - }, - ) - - stored_job = await db["evaluationjobs"].find_one({"_id": job_oid, "tenantId": user.tenant}) - stored_result = await db["evaluationresults"].find_one({"_id": insert.inserted_id}) - - response_payload = { - "job": jsonable(stored_job) if stored_job else job, - "result": jsonable(stored_result) - if stored_result - else jsonable( - { - "jobId": str(job_oid), - "tenantId": user.tenant, - "rut": clean_rut, - "name": payload.supplier.name, - "status": "success", - "data": result_data, - "createdAt": now, - "updatedAt": now, - } - ), - } - return _mask_json(response_payload) - except Exception as e: - request_id = getattr(request.state, "request_id", None) - logger.exception( - "lookup_failed", - extra={ - "request_id": request_id, - "tenant_id": user.tenant, - "user_id": user.id, - "rut": clean_rut, - }, - ) - - reason = str(e).replace("\n", " ").strip() - if not reason: - reason = e.__class__.__name__ - if len(reason) > 500: - reason = reason[:500] + "..." - - await db["evaluationresults"].insert_one( - { - "jobId": job_oid, - "tenantId": user.tenant, - "rut": clean_rut, - "name": payload.supplier.name, - "status": "failed", - "error": reason, - "createdAt": now, - "updatedAt": now, - } - ) - await db["evaluationjobs"].update_one( - {"_id": job_oid, "tenantId": user.tenant}, - { - "$set": { - "completedEvaluations": 0, - "failedEvaluations": 1, - "status": "failed", - "updatedAt": now, - } - }, - ) - raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail=reason) - - @router.get( "/{job_id}", response_model=EvaluationJobOut, diff --git a/fastcheck_api/app/schemas/checks.py b/fastcheck_api/app/schemas/checks.py index 239c683..3dc7530 100644 --- a/fastcheck_api/app/schemas/checks.py +++ b/fastcheck_api/app/schemas/checks.py @@ -55,8 +55,3 @@ class ListResultsResponse(BaseModel): results: list[EvaluationResultOut] total: int = Field(description="Total results in this job.", examples=[1]) pages: int = Field(description="Total pages for the current limit.", examples=[1]) - - -class LookupResponse(BaseModel): - job: EvaluationJobOut = Field(description="Created evaluation job.") - result: EvaluationResultOut = Field(description="Immediate evaluation result saved for this job.") diff --git a/fastcheck_api/app/services/auth_service.py b/fastcheck_api/app/services/auth_service.py index f74c703..594ee44 100644 --- a/fastcheck_api/app/services/auth_service.py +++ b/fastcheck_api/app/services/auth_service.py @@ -4,7 +4,6 @@ from bson import ObjectId from motor.motor_asyncio import AsyncIOMotorDatabase from fastcheck_api.app.core.security import create_access_token, verify_password -from fastcheck_api.app.services.legacy_service import legacy_service class AuthService: @@ -37,8 +36,6 @@ class AuthService: tenant_id=tenant_id_str, ) - await legacy_service.login_user(user_id=str(user["_id"]), email=email.lower(), password=password) - return { "token": token, "user": { diff --git a/fastcheck_api/app/services/check_service.py b/fastcheck_api/app/services/check_service.py index c69506d..4632970 100644 --- a/fastcheck_api/app/services/check_service.py +++ b/fastcheck_api/app/services/check_service.py @@ -24,7 +24,6 @@ class CheckService: user_id: str, rut: str, name: str | None, - enqueue: bool = True, ) -> dict[str, Any]: now = dt.datetime.now(dt.timezone.utc) tenant_oid = to_object_id(tenant_id) @@ -77,10 +76,9 @@ class CheckService: insert = await db["evaluationjobs"].insert_one(job_doc) job_id = str(insert.inserted_id) - if enqueue: - await rabbitmq_service.publish_evaluation( - {"rut": rut, "name": name, "jobId": job_id, "tenantId": tenant_id, "userId": user_id} - ) + await rabbitmq_service.publish_evaluation( + {"rut": rut, "name": name, "jobId": job_id, "tenantId": tenant_id, "userId": user_id} + ) stored = await db["evaluationjobs"].find_one({"_id": insert.inserted_id}) if not stored: diff --git a/fastcheck_api/app/services/legacy_service.py b/fastcheck_api/app/services/legacy_service.py index b5147f3..3597c61 100644 --- a/fastcheck_api/app/services/legacy_service.py +++ b/fastcheck_api/app/services/legacy_service.py @@ -1,8 +1,5 @@ from __future__ import annotations -import base64 -import json -import time from typing import Any import httpx @@ -16,100 +13,33 @@ class LegacyService: base_url=settings.legacy_base_url, timeout=settings.legacy_http_timeout_seconds, ) - self._user_tokens: dict[str, tuple[str, float | None]] = {} async def close(self) -> None: await self._client.aclose() - def _extract_exp(self, token: str) -> float | None: - try: - parts = token.split(".") - if len(parts) < 2: - return None - payload_b64 = parts[1] - payload_b64 += "=" * (-len(payload_b64) % 4) - payload_raw = base64.urlsafe_b64decode(payload_b64.encode("utf-8")) - payload = json.loads(payload_raw.decode("utf-8")) - exp = payload.get("exp") - if isinstance(exp, (int, float)): - return float(exp) - except Exception: - return None - return None - - def set_user_token(self, *, user_id: str, token: str) -> None: - exp = self._extract_exp(token) - self._user_tokens[user_id] = (token, exp) - - def get_user_token(self, *, user_id: str) -> str | None: - entry = self._user_tokens.get(user_id) - if not entry: - return None - token, exp = entry - if exp is None: - return token - if (time.time() + 60) < exp: - return token - self._user_tokens.pop(user_id, None) - return None - - async def login_user(self, *, user_id: str, email: str, password: str) -> str: - try: - resp = await self._client.post("auth/login", json={"email": email, "password": password}) - resp.raise_for_status() - data = resp.json() - token = data.get("token") if isinstance(data, dict) else None - if not token or not isinstance(token, str): - raise RuntimeError("Legacy login did not return a token") - self.set_user_token(user_id=user_id, token=token) - return token - except httpx.HTTPStatusError as e: - body = (e.response.text or "").replace("\n", " ").strip() - if len(body) > 500: - body = body[:500] + "..." - raise RuntimeError(f"Legacy login failed ({e.response.status_code}): {body or 'no body'}") from e - except httpx.RequestError as e: - raise RuntimeError(f"Legacy login request error: {str(e)}") from e - async def lookup_rut(self, *, rut: str, token: str) -> dict[str, Any]: - try: - resp = await self._client.post( - "rut/lookup", - headers={"Authorization": f"Bearer {token}"}, - json={"rut": rut, "isMonitoring": False, "type": "masiva"}, - ) - resp.raise_for_status() - data = resp.json() - if not isinstance(data, dict): - raise RuntimeError("Unexpected legacy response") - return data - except httpx.HTTPStatusError as e: - body = (e.response.text or "").replace("\n", " ").strip() - if len(body) > 500: - body = body[:500] + "..." - raise RuntimeError(f"Legacy lookup failed ({e.response.status_code}): {body or 'no body'}") from e - except httpx.RequestError as e: - raise RuntimeError(f"Legacy lookup request error: {str(e)}") from e + resp = await self._client.post( + "rut/lookup", + headers={"Authorization": f"Bearer {token}"}, + json={"rut": rut, "isMonitoring": False, "type": "masiva"}, + ) + resp.raise_for_status() + data = resp.json() + if not isinstance(data, dict): + raise RuntimeError("Unexpected legacy response") + return data async def save_fastcheck_summary(self, *, rut: str, summary: str, token: str) -> dict[str, Any]: - try: - resp = await self._client.post( - f"rut/fast-check-summary/{rut}", - headers={"Authorization": f"Bearer {token}"}, - json={"summary": summary}, - ) - resp.raise_for_status() - data = resp.json() - if not isinstance(data, dict): - raise RuntimeError("Unexpected legacy response") - return data - except httpx.HTTPStatusError as e: - body = (e.response.text or "").replace("\n", " ").strip() - if len(body) > 500: - body = body[:500] + "..." - raise RuntimeError(f"Legacy save summary failed ({e.response.status_code}): {body or 'no body'}") from e - except httpx.RequestError as e: - raise RuntimeError(f"Legacy save summary request error: {str(e)}") from e + resp = await self._client.post( + f"rut/fast-check-summary/{rut}", + headers={"Authorization": f"Bearer {token}"}, + json={"summary": summary}, + ) + resp.raise_for_status() + data = resp.json() + if not isinstance(data, dict): + raise RuntimeError("Unexpected legacy response") + return data legacy_service = LegacyService() diff --git a/server/external_providers/sheriff_v2/main.py b/server/external_providers/sheriff_v2/main.py index 04a65ab..37ecd9e 100644 --- a/server/external_providers/sheriff_v2/main.py +++ b/server/external_providers/sheriff_v2/main.py @@ -18,10 +18,10 @@ from typing import Dict, Any, Optional from datetime import datetime, timedelta import requests -from fastapi import FastAPI, HTTPException, Header, Request +from fastapi import FastAPI, HTTPException, Header from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse -from pydantic import BaseModel, ValidationError +from pydantic import BaseModel from dotenv import load_dotenv # Configure logging @@ -530,25 +530,7 @@ async def get_credit_score(rut: str): return execute_sheriff_get(path) @app.post("/queryRut") -async def query_rut(request: Request): - raw = await request.body() - if not raw: - raise HTTPException(status_code=400, detail="Missing JSON body") - - try: - parsed = json.loads(raw.decode("utf-8")) - except Exception: - snippet = raw[:500].decode("utf-8", errors="replace") - raise HTTPException(status_code=400, detail=f"Invalid JSON body: {snippet}") - - if not isinstance(parsed, dict): - raise HTTPException(status_code=400, detail="Body must be a JSON object") - - try: - payload = QueryRutRequest(**parsed) - except ValidationError as e: - raise HTTPException(status_code=422, detail=e.errors()) - +async def query_rut(payload: QueryRutRequest): cargar_body = { "rut": payload.rut.strip(), "isMonitoring": False, diff --git a/server/src/services/sheriffService.ts b/server/src/services/sheriffService.ts index e3a83cf..c007c54 100644 --- a/server/src/services/sheriffService.ts +++ b/server/src/services/sheriffService.ts @@ -228,8 +228,6 @@ export class SheriffService { if (loadRutData == null) { console.error('loadRutData failed is null'); - await logEntry.save(); - return logEntry.toObject(); } //must wait at least 30 seconds before starting the next await new Promise(resolve => setTimeout(resolve, parseInt(process.env.SHERIFF_API_DELAY || '30000'))); @@ -237,12 +235,6 @@ export class SheriffService { const summaryData = await makeApiCall('summaryData', 'get', `/helper/${sanitizedRut}/summary?complete=true`); //added complete true console.log(`[DEBUG] After summaryData call, sanitizedRut is still: ${sanitizedRut}`); - if (summaryData == null) { - console.error('summaryData failed is null'); - await logEntry.save(); - return logEntry.toObject(); - } - let societaryMeshExternal: any[] | null = null; if (summaryData.data.societaryMeshExternal && summaryData.data.societaryMeshExternal.length > 0) { diff --git a/server/src/services/sheriffV2Service.ts b/server/src/services/sheriffV2Service.ts index 0c2fac8..c589446 100644 --- a/server/src/services/sheriffV2Service.ts +++ b/server/src/services/sheriffV2Service.ts @@ -227,8 +227,6 @@ export class sheriffV2Service { //console.log(`[DEBUG] loadRutData response RUT: ${loadRutData?.data?.rut || 'No RUT in response'}`); if (summaryData == null) { console.error('loadRutData failed is null'); - await logEntry.save(); - return logEntry.toObject(); } if(!summaryData.data) { summaryData.data = {};