fixes 2
This commit is contained in:
parent
40ec5b1137
commit
2108e1be4f
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
venv/bin/python main.py
|
.venv/bin/python main.py
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
fastapi==0.104.1
|
|
||||||
uvicorn[standard]==0.24.0
|
|
||||||
pydantic==2.8.0
|
|
||||||
requests==2.31.0
|
|
||||||
python-dotenv==1.0.0
|
|
||||||
python-multipart==0.0.6
|
|
||||||
29
server/external_providers/dequienes/setup_venv.sh
Executable file
29
server/external_providers/dequienes/setup_venv.sh
Executable file
|
|
@ -0,0 +1,29 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Check if the .venv directory exists
|
||||||
|
if [ ! -d ".venv" ]; then
|
||||||
|
echo ".venv directory does not exist. Creating a virtual environment..."
|
||||||
|
|
||||||
|
# Create a virtual environment
|
||||||
|
python3 -m venv .venv
|
||||||
|
|
||||||
|
echo "Virtual environment created."
|
||||||
|
else
|
||||||
|
echo ".venv directory already exists."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Activate the virtual environment
|
||||||
|
source .venv/bin/activate
|
||||||
|
|
||||||
|
# Upgrade pip (optional)
|
||||||
|
pip install --upgrade pip
|
||||||
|
|
||||||
|
# Install requirements from requirements.txt
|
||||||
|
if [ -f "requirements.txt" ]; then
|
||||||
|
echo "Installing requirements from requirements.txt..."
|
||||||
|
pip install -r requirements.txt
|
||||||
|
else
|
||||||
|
echo "No requirements.txt file found."
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Setup complete."
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
venv/bin/python main.py
|
.venv/bin/python main.py
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
|
pymongo==4.6.0
|
||||||
fastapi==0.104.1
|
fastapi==0.104.1
|
||||||
uvicorn[standard]==0.24.0
|
uvicorn[standard]==0.24.0
|
||||||
|
pydantic==2.8.0
|
||||||
requests==2.31.0
|
requests==2.31.0
|
||||||
python-dotenv==1.0.0
|
python-dotenv==1.0.0
|
||||||
pymongo==4.6.0
|
python-multipart==0.0.6
|
||||||
pydantic==2.8.0
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
fastapi==0.104.1
|
|
||||||
uvicorn[standard]==0.24.0
|
|
||||||
pydantic==2.8.0
|
|
||||||
requests==2.31.0
|
|
||||||
python-dotenv==1.0.0
|
|
||||||
python-multipart==0.0.6
|
|
||||||
29
server/external_providers/equifax/setup_venv.sh
Executable file
29
server/external_providers/equifax/setup_venv.sh
Executable file
|
|
@ -0,0 +1,29 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Check if the .venv directory exists
|
||||||
|
if [ ! -d ".venv" ]; then
|
||||||
|
echo ".venv directory does not exist. Creating a virtual environment..."
|
||||||
|
|
||||||
|
# Create a virtual environment
|
||||||
|
python3 -m venv .venv
|
||||||
|
|
||||||
|
echo "Virtual environment created."
|
||||||
|
else
|
||||||
|
echo ".venv directory already exists."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Activate the virtual environment
|
||||||
|
source .venv/bin/activate
|
||||||
|
|
||||||
|
# Upgrade pip (optional)
|
||||||
|
pip install --upgrade pip
|
||||||
|
|
||||||
|
# Install requirements from requirements.txt
|
||||||
|
if [ -f "requirements.txt" ]; then
|
||||||
|
echo "Installing requirements from requirements.txt..."
|
||||||
|
pip install -r requirements.txt
|
||||||
|
else
|
||||||
|
echo "No requirements.txt file found."
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Setup complete."
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
SHERIFF_V2_BASE_URL=sheriff.example.com
|
|
||||||
SHERIFF_V2_ACCESS_KEY=your_access_key
|
|
||||||
SHERIFF_V2_ACCESS_SECRET=your_access_secret
|
|
||||||
SHERIFF_V2_CLIENT_IDENTIFIER=SheriffSecureClient-v1
|
|
||||||
SHERIFF_V2_MONGODB_URI=mongodb://localhost:27017/duxiter
|
|
||||||
SHERIFF_V2_CACHE_TTL_SECONDS=604800
|
|
||||||
SHERIFF_V2_TOKEN_TTL_SECONDS=1800
|
|
||||||
|
|
@ -1,708 +0,0 @@
|
||||||
{
|
|
||||||
"info": {
|
|
||||||
"_postman_id": "db3f9c3b-5e0b-4dc4-bdb8-665fab04b4b9",
|
|
||||||
"name": "Sheriff API v2",
|
|
||||||
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
|
|
||||||
"_exporter_id": "31069565"
|
|
||||||
},
|
|
||||||
"item": [
|
|
||||||
{
|
|
||||||
"name": "🔒 Autenticación y Tokens",
|
|
||||||
"item": [
|
|
||||||
{
|
|
||||||
"name": "🔑 getToken (Obtener Token de Acceso)",
|
|
||||||
"event": [
|
|
||||||
{
|
|
||||||
"listen": "test",
|
|
||||||
"script": {
|
|
||||||
"exec": [
|
|
||||||
"var response = pm.response.json(); // Utiliza el método moderno para obtener el JSON",
|
|
||||||
"",
|
|
||||||
"if (response && response.data) {",
|
|
||||||
" // La respuesta contiene el token en la propiedad 'data'",
|
|
||||||
" pm.environment.set(\"bearer_token\", response.data);",
|
|
||||||
" ",
|
|
||||||
" // Opcional: Imprime el token en la Consola de Postman",
|
|
||||||
" console.log(\"Token guardado automáticamente en 'bearer_token': \" + response.data);",
|
|
||||||
"} else {",
|
|
||||||
" console.log(\"Error: No se pudo obtener el token de la respuesta. Verifica la estructura JSON.\");",
|
|
||||||
"}"
|
|
||||||
],
|
|
||||||
"type": "text/javascript",
|
|
||||||
"packages": {},
|
|
||||||
"requests": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"request": {
|
|
||||||
"auth": {
|
|
||||||
"type": "noauth"
|
|
||||||
},
|
|
||||||
"method": "POST",
|
|
||||||
"header": [
|
|
||||||
{
|
|
||||||
"key": "Content-Type",
|
|
||||||
"value": "application/json"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"body": {
|
|
||||||
"mode": "raw",
|
|
||||||
"raw": "{\n \"accessKey\": \"{{accessKey}}\",\n \"accessSecret\": \"{{accessSecret}}\"\n}",
|
|
||||||
"options": {
|
|
||||||
"raw": {
|
|
||||||
"language": "json"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"url": {
|
|
||||||
"raw": "https://{{base_url}}/api/clients/v2/apiCredentials/getToken",
|
|
||||||
"protocol": "https",
|
|
||||||
"host": [
|
|
||||||
"{{base_url}}"
|
|
||||||
],
|
|
||||||
"path": [
|
|
||||||
"api",
|
|
||||||
"clients",
|
|
||||||
"v2",
|
|
||||||
"apiCredentials",
|
|
||||||
"getToken"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"response": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "👤 Plataforma",
|
|
||||||
"item": [
|
|
||||||
{
|
|
||||||
"name": "Perfil - Detalle",
|
|
||||||
"item": [
|
|
||||||
{
|
|
||||||
"name": "⚖️ Judiciales",
|
|
||||||
"item": [
|
|
||||||
{
|
|
||||||
"name": "civil (Causas Civiles)",
|
|
||||||
"request": {
|
|
||||||
"method": "GET",
|
|
||||||
"header": [
|
|
||||||
{
|
|
||||||
"key": "accept",
|
|
||||||
"value": "application/json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "Authorization",
|
|
||||||
"value": "Bearer {{bearer_token}}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "x-client-identifier",
|
|
||||||
"value": "SheriffSecureClient-v1"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"url": {
|
|
||||||
"raw": "https://{{base_url}}/api/clients/v2/helper/judicial/{{rut}}/civil",
|
|
||||||
"protocol": "https",
|
|
||||||
"host": [
|
|
||||||
"{{base_url}}"
|
|
||||||
],
|
|
||||||
"path": [
|
|
||||||
"api",
|
|
||||||
"clients",
|
|
||||||
"v2",
|
|
||||||
"helper",
|
|
||||||
"judicial",
|
|
||||||
"{{rut}}",
|
|
||||||
"civil"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"response": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "cobranza (Causas Cobranza)",
|
|
||||||
"request": {
|
|
||||||
"method": "GET",
|
|
||||||
"header": [
|
|
||||||
{
|
|
||||||
"key": "accept",
|
|
||||||
"value": "application/json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "Authorization",
|
|
||||||
"value": "Bearer {{bearer_token}}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "x-client-identifier",
|
|
||||||
"value": "SheriffSecureClient-v1"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"url": {
|
|
||||||
"raw": "https://{{base_url}}/api/clients/v2/helper/judicial/{{rut}}/cobranza",
|
|
||||||
"protocol": "https",
|
|
||||||
"host": [
|
|
||||||
"{{base_url}}"
|
|
||||||
],
|
|
||||||
"path": [
|
|
||||||
"api",
|
|
||||||
"clients",
|
|
||||||
"v2",
|
|
||||||
"helper",
|
|
||||||
"judicial",
|
|
||||||
"{{rut}}",
|
|
||||||
"cobranza"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"response": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "laboral (Causas Laborales)",
|
|
||||||
"request": {
|
|
||||||
"method": "GET",
|
|
||||||
"header": [
|
|
||||||
{
|
|
||||||
"key": "accept",
|
|
||||||
"value": "application/json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "Authorization",
|
|
||||||
"value": "Bearer {{bearer_token}}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "x-client-identifier",
|
|
||||||
"value": "SheriffSecureClient-v1"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"url": {
|
|
||||||
"raw": "https://{{base_url}}/api/clients/v2/helper/judicial/{{rut}}/laboral",
|
|
||||||
"protocol": "https",
|
|
||||||
"host": [
|
|
||||||
"{{base_url}}"
|
|
||||||
],
|
|
||||||
"path": [
|
|
||||||
"api",
|
|
||||||
"clients",
|
|
||||||
"v2",
|
|
||||||
"helper",
|
|
||||||
"judicial",
|
|
||||||
"{{rut}}",
|
|
||||||
"laboral"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"response": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "💼 Cobranza Laboral",
|
|
||||||
"item": [
|
|
||||||
{
|
|
||||||
"name": "multaLaboral (Multas Laborales)",
|
|
||||||
"request": {
|
|
||||||
"method": "GET",
|
|
||||||
"header": [
|
|
||||||
{
|
|
||||||
"key": "accept",
|
|
||||||
"value": "application/json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "Authorization",
|
|
||||||
"value": "Bearer {{bearer_token}}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "x-client-identifier",
|
|
||||||
"value": "SheriffSecureClient-v1"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"url": {
|
|
||||||
"raw": "https://{{base_url}}/api/clients/v2/helper/cobranzaLaboral/{{rut}}/multaLaboral",
|
|
||||||
"protocol": "https",
|
|
||||||
"host": [
|
|
||||||
"{{base_url}}"
|
|
||||||
],
|
|
||||||
"path": [
|
|
||||||
"api",
|
|
||||||
"clients",
|
|
||||||
"v2",
|
|
||||||
"helper",
|
|
||||||
"cobranzaLaboral",
|
|
||||||
"{{rut}}",
|
|
||||||
"multaLaboral"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"response": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "moraPrevisional (Mora Previsional)",
|
|
||||||
"request": {
|
|
||||||
"method": "GET",
|
|
||||||
"header": [
|
|
||||||
{
|
|
||||||
"key": "accept",
|
|
||||||
"value": "application/json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "Authorization",
|
|
||||||
"value": "Bearer {{bearer_token}}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "x-client-identifier",
|
|
||||||
"value": "SheriffSecureClient-v1"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"url": {
|
|
||||||
"raw": "https://{{base_url}}/api/clients/v2/helper/cobranzaLaboral/{{rut}}/moraPrevisional",
|
|
||||||
"protocol": "https",
|
|
||||||
"host": [
|
|
||||||
"{{base_url}}"
|
|
||||||
],
|
|
||||||
"path": [
|
|
||||||
"api",
|
|
||||||
"clients",
|
|
||||||
"v2",
|
|
||||||
"helper",
|
|
||||||
"cobranzaLaboral",
|
|
||||||
"{{rut}}",
|
|
||||||
"moraPrevisional"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"response": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "🛡️ Compliance",
|
|
||||||
"item": [
|
|
||||||
{
|
|
||||||
"name": "compliance (Detalle de Cumplimiento)",
|
|
||||||
"request": {
|
|
||||||
"method": "GET",
|
|
||||||
"header": [
|
|
||||||
{
|
|
||||||
"key": "accept",
|
|
||||||
"value": "application/json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "Authorization",
|
|
||||||
"value": "Bearer {{bearer_token}}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "x-client-identifier",
|
|
||||||
"value": "SheriffSecureClient-v1"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"url": {
|
|
||||||
"raw": "https://{{base_url}}/api/clients/v2/helper/compliance/{{rut}}",
|
|
||||||
"protocol": "https",
|
|
||||||
"host": [
|
|
||||||
"{{base_url}}"
|
|
||||||
],
|
|
||||||
"path": [
|
|
||||||
"api",
|
|
||||||
"clients",
|
|
||||||
"v2",
|
|
||||||
"helper",
|
|
||||||
"compliance",
|
|
||||||
"{{rut}}"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"response": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "🔗 Legal",
|
|
||||||
"item": [
|
|
||||||
{
|
|
||||||
"name": "mallaSocietaria (Malla Societaria)",
|
|
||||||
"request": {
|
|
||||||
"method": "GET",
|
|
||||||
"header": [
|
|
||||||
{
|
|
||||||
"key": "accept",
|
|
||||||
"value": "application/json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "Authorization",
|
|
||||||
"value": "Bearer {{bearer_token}}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "x-client-identifier",
|
|
||||||
"value": "SheriffSecureClient-v1"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"url": {
|
|
||||||
"raw": "https://{{base_url}}/api/clients/v2/helper/legal/{{rut}}/mallaSocietaria",
|
|
||||||
"protocol": "https",
|
|
||||||
"host": [
|
|
||||||
"{{base_url}}"
|
|
||||||
],
|
|
||||||
"path": [
|
|
||||||
"api",
|
|
||||||
"clients",
|
|
||||||
"v2",
|
|
||||||
"helper",
|
|
||||||
"legal",
|
|
||||||
"{{rut}}",
|
|
||||||
"mallaSocietaria"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"response": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "💯 Score",
|
|
||||||
"item": [
|
|
||||||
{
|
|
||||||
"name": "💳 creditScore (Puntaje Crediticio)",
|
|
||||||
"request": {
|
|
||||||
"method": "GET",
|
|
||||||
"header": [
|
|
||||||
{
|
|
||||||
"key": "accept",
|
|
||||||
"value": "application/json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "Authorization",
|
|
||||||
"value": "Bearer {{bearer_token}}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "x-client-identifier",
|
|
||||||
"value": "SheriffSecureClient-v1"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"url": {
|
|
||||||
"raw": "https://{{base_url}}/api/clients/v2/creditScore/{{rut}}",
|
|
||||||
"protocol": "https",
|
|
||||||
"host": [
|
|
||||||
"{{base_url}}"
|
|
||||||
],
|
|
||||||
"path": [
|
|
||||||
"api",
|
|
||||||
"clients",
|
|
||||||
"v2",
|
|
||||||
"creditScore",
|
|
||||||
"{{rut}}"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"response": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "📈 Uso y Plan",
|
|
||||||
"item": [
|
|
||||||
{
|
|
||||||
"name": "usage (Consumo de la Cuenta)",
|
|
||||||
"request": {
|
|
||||||
"method": "GET",
|
|
||||||
"header": [
|
|
||||||
{
|
|
||||||
"key": "accept",
|
|
||||||
"value": "application/json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "Authorization",
|
|
||||||
"value": "Bearer {{bearer_token}}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "x-client-identifier",
|
|
||||||
"value": "SheriffSecureClient-v1"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"url": {
|
|
||||||
"raw": "https://{{base_url}}/api/clients/v2/usage",
|
|
||||||
"protocol": "https",
|
|
||||||
"host": [
|
|
||||||
"{{base_url}}"
|
|
||||||
],
|
|
||||||
"path": [
|
|
||||||
"api",
|
|
||||||
"clients",
|
|
||||||
"v2",
|
|
||||||
"usage"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"response": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "↩️ Webhooks/Callbacks (Asíncronos)",
|
|
||||||
"item": [
|
|
||||||
{
|
|
||||||
"name": "📋 Reportes (summaryByRut)",
|
|
||||||
"item": [
|
|
||||||
{
|
|
||||||
"name": "summaryByRut (PDF de Resumen por RUT)",
|
|
||||||
"request": {
|
|
||||||
"method": "GET",
|
|
||||||
"header": [
|
|
||||||
{
|
|
||||||
"key": "accept",
|
|
||||||
"value": "application/pdf"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "Content-Disposition",
|
|
||||||
"value": "attachment; filename={{rut}}.pdf"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "Authorization",
|
|
||||||
"value": "Bearer {{bearer_token}}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "x-client-identifier",
|
|
||||||
"value": "SheriffSecureClient-v1"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"url": {
|
|
||||||
"raw": "https://{{base_url}}/api/clients/v2/webhook/profile/{{rut}}/resumen?format=pdf",
|
|
||||||
"protocol": "https",
|
|
||||||
"host": [
|
|
||||||
"{{base_url}}"
|
|
||||||
],
|
|
||||||
"path": [
|
|
||||||
"api",
|
|
||||||
"clients",
|
|
||||||
"v2",
|
|
||||||
"webhook",
|
|
||||||
"profile",
|
|
||||||
"{{rut}}",
|
|
||||||
"resumen"
|
|
||||||
],
|
|
||||||
"query": [
|
|
||||||
{
|
|
||||||
"key": "format",
|
|
||||||
"value": "pdf"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"response": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "🛡️ Compliance (Asíncrono)",
|
|
||||||
"item": [
|
|
||||||
{
|
|
||||||
"name": "complianceByName (Cargar Perfil Compliance por Nombre)",
|
|
||||||
"request": {
|
|
||||||
"method": "POST",
|
|
||||||
"header": [
|
|
||||||
{
|
|
||||||
"key": "accept",
|
|
||||||
"value": "application/json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "Content-Type",
|
|
||||||
"value": "application/json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "Authorization",
|
|
||||||
"value": "Bearer {{bearer_token}}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "x-client-identifier",
|
|
||||||
"value": "SheriffSecureClient-v1"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"body": {
|
|
||||||
"mode": "raw",
|
|
||||||
"raw": "{\n \"fullName\": \"Nombre Empresa\",\n \"isMonitoring\": true,\n \"isJuridicalPerson\": true,\n \"searchErrors\": 0\n }",
|
|
||||||
"options": {
|
|
||||||
"raw": {
|
|
||||||
"language": "json"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"url": {
|
|
||||||
"raw": "https://{{base_url}}/api/clients/v2/webhook/compliance/cargarPerfil",
|
|
||||||
"protocol": "https",
|
|
||||||
"host": [
|
|
||||||
"{{base_url}}"
|
|
||||||
],
|
|
||||||
"path": [
|
|
||||||
"api",
|
|
||||||
"clients",
|
|
||||||
"v2",
|
|
||||||
"webhook",
|
|
||||||
"compliance",
|
|
||||||
"cargarPerfil"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"response": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "📄 resumen (Obtener Resumen del Perfil)",
|
|
||||||
"request": {
|
|
||||||
"method": "GET",
|
|
||||||
"header": [
|
|
||||||
{
|
|
||||||
"key": "accept",
|
|
||||||
"value": "application/json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "Authorization",
|
|
||||||
"value": "Bearer {{bearer_token}}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "x-client-identifier",
|
|
||||||
"value": "SheriffSecureClient-v1"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"url": {
|
|
||||||
"raw": "https://{{base_url}}/api/clients/v2/helper/resumen?rut={{rut}}",
|
|
||||||
"protocol": "https",
|
|
||||||
"host": [
|
|
||||||
"{{base_url}}"
|
|
||||||
],
|
|
||||||
"path": [
|
|
||||||
"api",
|
|
||||||
"clients",
|
|
||||||
"v2",
|
|
||||||
"helper",
|
|
||||||
"resumen"
|
|
||||||
],
|
|
||||||
"query": [
|
|
||||||
{
|
|
||||||
"key": "rut",
|
|
||||||
"value": "{{rut}}"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"response": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "📜 loadedProfiles (Perfiles Cargados)",
|
|
||||||
"request": {
|
|
||||||
"method": "GET",
|
|
||||||
"header": [
|
|
||||||
{
|
|
||||||
"key": "accept",
|
|
||||||
"value": "application/json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "Authorization",
|
|
||||||
"value": "Bearer {{bearer_token}}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "x-client-identifier",
|
|
||||||
"value": "SheriffSecureClient-v1"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"url": {
|
|
||||||
"raw": "https://{{base_url}}/api/clients/v2/profiles",
|
|
||||||
"protocol": "https",
|
|
||||||
"host": [
|
|
||||||
"{{base_url}}"
|
|
||||||
],
|
|
||||||
"path": [
|
|
||||||
"api",
|
|
||||||
"clients",
|
|
||||||
"v2",
|
|
||||||
"profiles"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"response": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "➕ cargarRut (Cargar Perfil)",
|
|
||||||
"request": {
|
|
||||||
"method": "POST",
|
|
||||||
"header": [
|
|
||||||
{
|
|
||||||
"key": "accept",
|
|
||||||
"value": "application/json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "Authorization",
|
|
||||||
"value": "Bearer {{bearer_token}}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "x-client-identifier",
|
|
||||||
"value": "SheriffSecureClient-v1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "Content-Type",
|
|
||||||
"value": "application/json"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"body": {
|
|
||||||
"mode": "raw",
|
|
||||||
"raw": "{\n \"rut\": \"{{rut}}\",\n \"isMonitoring\": false,\n \"includeEquifax\": false\n}",
|
|
||||||
"options": {
|
|
||||||
"raw": {
|
|
||||||
"language": "json"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"url": {
|
|
||||||
"raw": "https://{{base_url}}/api/clients/v2/helper/cargarRut",
|
|
||||||
"protocol": "https",
|
|
||||||
"host": [
|
|
||||||
"{{base_url}}"
|
|
||||||
],
|
|
||||||
"path": [
|
|
||||||
"api",
|
|
||||||
"clients",
|
|
||||||
"v2",
|
|
||||||
"helper",
|
|
||||||
"cargarRut"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"response": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"auth": {
|
|
||||||
"type": "bearer",
|
|
||||||
"bearer": [
|
|
||||||
{
|
|
||||||
"key": "token",
|
|
||||||
"value": "{{bearer_token}}",
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"event": [
|
|
||||||
{
|
|
||||||
"listen": "prerequest",
|
|
||||||
"script": {
|
|
||||||
"type": "text/javascript",
|
|
||||||
"exec": [
|
|
||||||
""
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"listen": "test",
|
|
||||||
"script": {
|
|
||||||
"type": "text/javascript",
|
|
||||||
"exec": [
|
|
||||||
""
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
|
|
||||||
venv/bin/python main.py
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,723 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
FastAPI Sheriff v2 Provider
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import json
|
|
||||||
import time
|
|
||||||
import asyncio
|
|
||||||
import logging
|
|
||||||
import hashlib
|
|
||||||
import glob
|
|
||||||
import shutil
|
|
||||||
import threading
|
|
||||||
import re
|
|
||||||
from pathlib import Path
|
|
||||||
from typing import Dict, Any, Optional
|
|
||||||
from datetime import datetime, timedelta
|
|
||||||
|
|
||||||
import requests
|
|
||||||
from fastapi import FastAPI, HTTPException, Header
|
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
|
||||||
from fastapi.responses import JSONResponse
|
|
||||||
from pydantic import BaseModel
|
|
||||||
from dotenv import load_dotenv
|
|
||||||
|
|
||||||
# Configure logging
|
|
||||||
logging.basicConfig(
|
|
||||||
level=logging.INFO,
|
|
||||||
format='%(asctime)s - %(levelname)s - %(message)s',
|
|
||||||
datefmt='%Y-%m-%d %H:%M:%S'
|
|
||||||
)
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
load_dotenv()
|
|
||||||
|
|
||||||
CACHE_TTL_SECONDS = int(os.getenv("SHERIFF_V2_CACHE_TTL_SECONDS", "604800"))
|
|
||||||
SHERIFF_RESPONSES_DIR = Path(__file__).resolve().parents[2] / "sheriff_v2_response"
|
|
||||||
|
|
||||||
# Json Cache Manager Implementation
|
|
||||||
class JsonCacheManager:
|
|
||||||
def __init__(self, directory: Path):
|
|
||||||
self.directory = directory
|
|
||||||
self.backup_directory = directory / "backups"
|
|
||||||
self.directory.mkdir(parents=True, exist_ok=True)
|
|
||||||
self.backup_directory.mkdir(parents=True, exist_ok=True)
|
|
||||||
self.lock = threading.RLock()
|
|
||||||
|
|
||||||
def _get_safe_filename_prefix(self, key: str) -> str:
|
|
||||||
return hashlib.sha256(key.encode()).hexdigest()
|
|
||||||
|
|
||||||
def get(self, key: str) -> Optional[Dict[str, Any]]:
|
|
||||||
with self.lock:
|
|
||||||
try:
|
|
||||||
safe_prefix = self._get_safe_filename_prefix(key)
|
|
||||||
pattern = str(self.directory / f"{safe_prefix}_v*.json")
|
|
||||||
files = glob.glob(pattern)
|
|
||||||
|
|
||||||
if not files:
|
|
||||||
return None
|
|
||||||
|
|
||||||
# Filter and parse valid files
|
|
||||||
valid_files = []
|
|
||||||
for f_path in files:
|
|
||||||
try:
|
|
||||||
name = Path(f_path).name
|
|
||||||
# Regex to parse: hash_v(\d+)_(\d+)_(\d+).json
|
|
||||||
match = re.match(r"^[0-9a-f]+_v(\d+)_(\d+)_(\d+)\.json$", name)
|
|
||||||
if match:
|
|
||||||
version = int(match.group(1))
|
|
||||||
ts = int(match.group(2))
|
|
||||||
exp = int(match.group(3))
|
|
||||||
valid_files.append((version, ts, exp, f_path))
|
|
||||||
except Exception:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if not valid_files:
|
|
||||||
return None
|
|
||||||
|
|
||||||
# Pick latest version
|
|
||||||
valid_files.sort(key=lambda x: (x[0], x[1]), reverse=True)
|
|
||||||
latest = valid_files[0]
|
|
||||||
version, ts, exp, f_path = latest
|
|
||||||
|
|
||||||
# Check expiry
|
|
||||||
if exp < time.time():
|
|
||||||
# logger.info(f"Cache expired for key: {key}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
# Read file
|
|
||||||
with open(f_path, 'r', encoding='utf-8') as f:
|
|
||||||
data = json.load(f)
|
|
||||||
logger.info(f"Cache hit for key: {key} (file: {Path(f_path).name})")
|
|
||||||
return data.get("result")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Error reading from file cache: {e}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
def save(self, key: str, result: Dict[str, Any], ttl_seconds: int) -> None:
|
|
||||||
with self.lock:
|
|
||||||
try:
|
|
||||||
safe_prefix = self._get_safe_filename_prefix(key)
|
|
||||||
pattern = str(self.directory / f"{safe_prefix}_v*.json")
|
|
||||||
files = glob.glob(pattern)
|
|
||||||
|
|
||||||
next_version = 1
|
|
||||||
|
|
||||||
# Handle backups and versioning
|
|
||||||
if files:
|
|
||||||
# Parse to find existing versions
|
|
||||||
current_versions = []
|
|
||||||
for f_path in files:
|
|
||||||
name = Path(f_path).name
|
|
||||||
match = re.match(r"^[0-9a-f]+_v(\d+)_(\d+)_(\d+)\.json$", name)
|
|
||||||
if match:
|
|
||||||
current_versions.append((int(match.group(1)), f_path))
|
|
||||||
|
|
||||||
if current_versions:
|
|
||||||
current_versions.sort(key=lambda x: x[0], reverse=True)
|
|
||||||
last_ver, last_path = current_versions[0]
|
|
||||||
next_version = last_ver + 1
|
|
||||||
|
|
||||||
# Backup previous files
|
|
||||||
for _, path in current_versions:
|
|
||||||
try:
|
|
||||||
dest = self.backup_directory / Path(path).name
|
|
||||||
shutil.move(path, dest)
|
|
||||||
logger.info(f"Backed up {Path(path).name}")
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Failed to backup {path}: {e}")
|
|
||||||
|
|
||||||
now_ts = int(time.time())
|
|
||||||
exp_ts = now_ts + ttl_seconds
|
|
||||||
filename = f"{safe_prefix}_v{next_version}_{now_ts}_{exp_ts}.json"
|
|
||||||
filepath = self.directory / filename
|
|
||||||
|
|
||||||
data = {
|
|
||||||
"_id": key,
|
|
||||||
"result": result,
|
|
||||||
"cached_at": datetime.utcnow().isoformat(),
|
|
||||||
"expires_at": (datetime.utcnow() + timedelta(seconds=ttl_seconds)).isoformat(),
|
|
||||||
"version": next_version
|
|
||||||
}
|
|
||||||
|
|
||||||
# Atomic write
|
|
||||||
temp_path = filepath.with_suffix('.tmp')
|
|
||||||
with open(temp_path, 'w', encoding='utf-8') as f:
|
|
||||||
json.dump(data, f, ensure_ascii=False, indent=2)
|
|
||||||
|
|
||||||
os.replace(temp_path, filepath)
|
|
||||||
logger.info(f"Transaction result cached with key: {key}, file: {filename}")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Error saving to file cache: {e}")
|
|
||||||
|
|
||||||
def clear(self) -> int:
|
|
||||||
with self.lock:
|
|
||||||
try:
|
|
||||||
pattern = str(self.directory / "*_v*_*.json")
|
|
||||||
files = glob.glob(pattern)
|
|
||||||
count = 0
|
|
||||||
for f in files:
|
|
||||||
try:
|
|
||||||
name = Path(f).name
|
|
||||||
if re.match(r"^[0-9a-f]+_v\d+_\d+_\d+\.json$", name):
|
|
||||||
os.remove(f)
|
|
||||||
count += 1
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
logger.info(f"Cleared {count} cached transactions")
|
|
||||||
return count
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Error clearing cache: {e}")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
def stats(self) -> Dict[str, Any]:
|
|
||||||
with self.lock:
|
|
||||||
try:
|
|
||||||
total = 0
|
|
||||||
expired = 0
|
|
||||||
pattern = str(self.directory / "*_v*_*.json")
|
|
||||||
files = glob.glob(pattern)
|
|
||||||
|
|
||||||
now_ts = time.time()
|
|
||||||
|
|
||||||
for f in files:
|
|
||||||
name = Path(f).name
|
|
||||||
match = re.match(r"^[0-9a-f]+_v\d+_\d+_(\d+)\.json$", name)
|
|
||||||
if match:
|
|
||||||
total += 1
|
|
||||||
exp = int(match.group(1))
|
|
||||||
if exp < now_ts:
|
|
||||||
expired += 1
|
|
||||||
|
|
||||||
return {
|
|
||||||
"total_cached_transactions": total,
|
|
||||||
"expired_transactions": expired,
|
|
||||||
"active_transactions": total - expired,
|
|
||||||
"cache_ttl_seconds": CACHE_TTL_SECONDS,
|
|
||||||
"backend": "json_file"
|
|
||||||
}
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Error getting cache stats: {e}")
|
|
||||||
return {"error": str(e), "backend": "json_file"}
|
|
||||||
|
|
||||||
# Initialize Cache Manager
|
|
||||||
cache_manager = JsonCacheManager(SHERIFF_RESPONSES_DIR)
|
|
||||||
|
|
||||||
TOKEN_CACHE_FILE = Path(".token_cache.json")
|
|
||||||
SHERIFF_TOKEN_TTL_SECONDS = int(os.getenv("SHERIFF_V2_TOKEN_TTL_SECONDS", "1800"))
|
|
||||||
|
|
||||||
# Pydantic Models
|
|
||||||
class TokenResponse(BaseModel):
|
|
||||||
token: str
|
|
||||||
cached: bool
|
|
||||||
|
|
||||||
class HealthResponse(BaseModel):
|
|
||||||
status: str
|
|
||||||
timestamp: str
|
|
||||||
version: str
|
|
||||||
|
|
||||||
class ErrorResponse(BaseModel):
|
|
||||||
error: str
|
|
||||||
detail: str
|
|
||||||
timestamp: str
|
|
||||||
|
|
||||||
# FastAPI App
|
|
||||||
app = FastAPI(
|
|
||||||
title="Sheriff v2 Provider API",
|
|
||||||
description="REST API for Sheriff v2 client endpoints",
|
|
||||||
version="1.0.0",
|
|
||||||
docs_url="/docs",
|
|
||||||
redoc_url="/redoc"
|
|
||||||
)
|
|
||||||
|
|
||||||
# CORS middleware
|
|
||||||
app.add_middleware(
|
|
||||||
CORSMiddleware,
|
|
||||||
allow_origins=["*"], # Configure appropriately for production
|
|
||||||
allow_credentials=True,
|
|
||||||
allow_methods=["*"],
|
|
||||||
allow_headers=["*"],
|
|
||||||
)
|
|
||||||
|
|
||||||
class QueryRutRequest(BaseModel):
|
|
||||||
rut: str
|
|
||||||
isMonitoring: bool = False
|
|
||||||
includeEquifax: bool = False
|
|
||||||
|
|
||||||
def generate_cache_key(env_key: str, payload: Dict[str, Any]) -> str:
|
|
||||||
payload_str = json.dumps(payload, sort_keys=True, separators=(',', ':'))
|
|
||||||
payload_hash = hashlib.sha256(payload_str.encode()).hexdigest()
|
|
||||||
return f"sheriff_v2_cache:{env_key}:{payload_hash}"
|
|
||||||
|
|
||||||
def get_cached_transaction(cache_key: str) -> Optional[Dict[str, Any]]:
|
|
||||||
return cache_manager.get(cache_key)
|
|
||||||
|
|
||||||
def save_transaction_to_cache(cache_key: str, result: Dict[str, Any]) -> None:
|
|
||||||
cache_manager.save(cache_key, result, CACHE_TTL_SECONDS)
|
|
||||||
|
|
||||||
def clear_cache() -> int:
|
|
||||||
return cache_manager.clear()
|
|
||||||
|
|
||||||
def get_cache_stats() -> Dict[str, Any]:
|
|
||||||
return cache_manager.stats()
|
|
||||||
|
|
||||||
def now() -> float:
|
|
||||||
return time.time()
|
|
||||||
|
|
||||||
def read_cache() -> Optional[dict]:
|
|
||||||
if TOKEN_CACHE_FILE.exists():
|
|
||||||
try:
|
|
||||||
with TOKEN_CACHE_FILE.open("r", encoding="utf-8") as f:
|
|
||||||
return json.load(f)
|
|
||||||
except Exception:
|
|
||||||
return None
|
|
||||||
return None
|
|
||||||
|
|
||||||
def write_cache(data: dict) -> None:
|
|
||||||
try:
|
|
||||||
with TOKEN_CACHE_FILE.open("w", encoding="utf-8") as f:
|
|
||||||
json.dump(data, f, indent=2)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def sheriff_base_url() -> str:
|
|
||||||
base_url = os.getenv("SHERIFF_V2_BASE_URL", "").strip()
|
|
||||||
if not base_url:
|
|
||||||
raise HTTPException(status_code=500, detail="Missing SHERIFF_V2_BASE_URL")
|
|
||||||
return base_url
|
|
||||||
|
|
||||||
def get_client_identifier() -> str:
|
|
||||||
return os.getenv("SHERIFF_V2_CLIENT_IDENTIFIER", "SheriffSecureClient-v1").strip()
|
|
||||||
|
|
||||||
def get_api_credentials() -> Dict[str, str]:
|
|
||||||
access_key = os.getenv("SHERIFF_V2_ACCESS_KEY", "").strip()
|
|
||||||
access_secret = os.getenv("SHERIFF_V2_ACCESS_SECRET", "").strip()
|
|
||||||
if not access_key or not access_secret:
|
|
||||||
raise HTTPException(status_code=500, detail="Missing Sheriff API credentials")
|
|
||||||
return {"accessKey": access_key, "accessSecret": access_secret}
|
|
||||||
|
|
||||||
def get_cached_token(env_key: str) -> Optional[str]:
|
|
||||||
cache = read_cache()
|
|
||||||
if not cache:
|
|
||||||
return None
|
|
||||||
item = cache.get(env_key)
|
|
||||||
if not item:
|
|
||||||
return None
|
|
||||||
token = item.get("token")
|
|
||||||
expires_at = item.get("expires_at", 0)
|
|
||||||
if not token:
|
|
||||||
return None
|
|
||||||
current_time = now()
|
|
||||||
if current_time < (expires_at - 60):
|
|
||||||
return token
|
|
||||||
return None
|
|
||||||
|
|
||||||
def save_cached_token(env_key: str, token: str, ttl_secs: int) -> None:
|
|
||||||
cache = read_cache() or {}
|
|
||||||
current_time = now()
|
|
||||||
cache[env_key] = {
|
|
||||||
"token": token,
|
|
||||||
"expires_at": current_time + int(ttl_secs),
|
|
||||||
"saved_at": current_time,
|
|
||||||
}
|
|
||||||
write_cache(cache)
|
|
||||||
|
|
||||||
def fetch_token(base_url: str, access_key: str, access_secret: str) -> str:
|
|
||||||
url = base_url + "/api/clients/v2/apiCredentials/getToken"
|
|
||||||
client_id = get_client_identifier()
|
|
||||||
headers = {"Content-Type": "application/json","x-client-identifier": client_id }
|
|
||||||
body_data = {"accessKey": access_key, "accessSecret": access_secret}
|
|
||||||
body_json = json.dumps(body_data)
|
|
||||||
|
|
||||||
# Print headers as JSON to show double quotes in logs
|
|
||||||
print(f"headers {headers}")
|
|
||||||
print(f"body {body_json}")
|
|
||||||
logger.info(f"Fetching token from {url}")
|
|
||||||
|
|
||||||
resp = requests.post(url, headers=headers, data=body_json, timeout=30)
|
|
||||||
if resp.status_code == 200:
|
|
||||||
payload = resp.json()
|
|
||||||
token = payload.get("data") or payload.get("token") or ""
|
|
||||||
if not token:
|
|
||||||
raise HTTPException(status_code=502, detail="Token response invalid")
|
|
||||||
save_cached_token(base_url, token, SHERIFF_TOKEN_TTL_SECONDS)
|
|
||||||
return token
|
|
||||||
raise HTTPException(status_code=resp.status_code, detail=resp.text)
|
|
||||||
|
|
||||||
def get_token() -> str:
|
|
||||||
base_url = sheriff_base_url()
|
|
||||||
cached = get_cached_token(base_url)
|
|
||||||
if cached:
|
|
||||||
return cached
|
|
||||||
creds = get_api_credentials()
|
|
||||||
return fetch_token(base_url, creds["accessKey"], creds["accessSecret"])
|
|
||||||
|
|
||||||
def execute_sheriff_get(path: str, query: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
||||||
base_url = sheriff_base_url()
|
|
||||||
token = get_token()
|
|
||||||
client_id = get_client_identifier()
|
|
||||||
if base_url.startswith("http"):
|
|
||||||
url = f"{base_url}{path}"
|
|
||||||
else:
|
|
||||||
url = f"https://{base_url}{path}"
|
|
||||||
params = query or {}
|
|
||||||
cache_key = generate_cache_key("sheriff_v2_get", {"url": url, "params": params})
|
|
||||||
cached = get_cached_transaction(cache_key)
|
|
||||||
if cached:
|
|
||||||
cached["_cache_info"] = {"cached": True, "cache_key": cache_key, "retrieved_at": datetime.utcnow().isoformat()}
|
|
||||||
return cached
|
|
||||||
headers = {
|
|
||||||
"accept": "application/json",
|
|
||||||
"Authorization": f"Bearer {token}",
|
|
||||||
"x-client-identifier": client_id,
|
|
||||||
}
|
|
||||||
print(f"Executing GET {url} with headers {headers} and params {params}")
|
|
||||||
resp = requests.get(url, headers=headers, params=params, timeout=60)
|
|
||||||
if resp.status_code == 200:
|
|
||||||
try:
|
|
||||||
data = resp.json()
|
|
||||||
except Exception:
|
|
||||||
data = {"raw": resp.text}
|
|
||||||
save_transaction_to_cache(cache_key, data)
|
|
||||||
data["_cache_info"] = {"cached": False, "cache_key": cache_key, "cached_at": datetime.utcnow().isoformat()}
|
|
||||||
return data
|
|
||||||
if resp.status_code == 401:
|
|
||||||
token = fetch_token(base_url, get_api_credentials()["accessKey"], get_api_credentials()["accessSecret"])
|
|
||||||
headers["Authorization"] = f"Bearer {token}"
|
|
||||||
resp = requests.get(url, headers=headers, params=params, timeout=60)
|
|
||||||
if resp.status_code == 200:
|
|
||||||
try:
|
|
||||||
data = resp.json()
|
|
||||||
except Exception:
|
|
||||||
data = {"raw": resp.text}
|
|
||||||
save_transaction_to_cache(cache_key, data)
|
|
||||||
data["_cache_info"] = {"cached": False, "cache_key": cache_key, "cached_at": datetime.utcnow().isoformat()}
|
|
||||||
return data
|
|
||||||
raise HTTPException(status_code=resp.status_code, detail=resp.text)
|
|
||||||
if resp.status_code == 403:
|
|
||||||
raise HTTPException(status_code=403, detail="Forbidden")
|
|
||||||
if resp.status_code == 404:
|
|
||||||
raise HTTPException(status_code=404, detail="Not Found")
|
|
||||||
if resp.status_code >= 500:
|
|
||||||
raise HTTPException(status_code=502, detail=f"Upstream error {resp.status_code}")
|
|
||||||
raise HTTPException(status_code=resp.status_code, detail=resp.text)
|
|
||||||
|
|
||||||
def execute_sheriff_post(path: str, body: Dict[str, Any]) -> Dict[str, Any]:
|
|
||||||
base_url = sheriff_base_url()
|
|
||||||
token = get_token()
|
|
||||||
client_id = get_client_identifier()
|
|
||||||
if base_url.startswith("http"):
|
|
||||||
url = f"{base_url}{path}"
|
|
||||||
else:
|
|
||||||
url = f"https://{base_url}{path}"
|
|
||||||
headers = {
|
|
||||||
"accept": "application/json",
|
|
||||||
"Authorization": f"Bearer {token}",
|
|
||||||
"x-client-identifier": client_id,
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
}
|
|
||||||
resp = requests.post(url, headers=headers, json=body, timeout=60)
|
|
||||||
if resp.status_code == 200:
|
|
||||||
try:
|
|
||||||
return resp.json()
|
|
||||||
except Exception:
|
|
||||||
return {"raw": resp.text}
|
|
||||||
if resp.status_code == 401:
|
|
||||||
token = fetch_token(base_url, get_api_credentials()["accessKey"], get_api_credentials()["accessSecret"])
|
|
||||||
headers["Authorization"] = f"Bearer {token}"
|
|
||||||
resp = requests.post(url, headers=headers, json=body, timeout=60)
|
|
||||||
if resp.status_code == 200:
|
|
||||||
try:
|
|
||||||
return resp.json()
|
|
||||||
except Exception:
|
|
||||||
return {"raw": resp.text}
|
|
||||||
raise HTTPException(status_code=resp.status_code, detail=resp.text)
|
|
||||||
if resp.status_code == 403:
|
|
||||||
raise HTTPException(status_code=403, detail="Forbidden")
|
|
||||||
if resp.status_code == 404:
|
|
||||||
raise HTTPException(status_code=404, detail="Not Found")
|
|
||||||
if resp.status_code >= 500:
|
|
||||||
raise HTTPException(status_code=502, detail=f"Upstream error {resp.status_code}")
|
|
||||||
raise HTTPException(status_code=resp.status_code, detail=resp.text)
|
|
||||||
|
|
||||||
def persist_response(rut: str, result: Dict[str, Any]) -> str:
|
|
||||||
dest_dir = SHERIFF_RESPONSES_DIR
|
|
||||||
dest_dir.mkdir(parents=True, exist_ok=True)
|
|
||||||
ts = datetime.utcnow().strftime("%Y%m%dT%H%M%SZ")
|
|
||||||
fp = dest_dir / f"{rut}_{ts}.json"
|
|
||||||
with fp.open("w", encoding="utf-8") as f:
|
|
||||||
json.dump(result, f, ensure_ascii=False, indent=2)
|
|
||||||
return str(fp)
|
|
||||||
|
|
||||||
|
|
||||||
# Rimuoviamo completamente flusso POST/Equifax
|
|
||||||
|
|
||||||
|
|
||||||
def _clear_cached_token(env_key: str) -> None:
|
|
||||||
try:
|
|
||||||
cache_data = read_cache()
|
|
||||||
if cache_data and env_key in cache_data:
|
|
||||||
del cache_data[env_key]
|
|
||||||
write_cache(cache_data)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# API Endpoints
|
|
||||||
@app.get("/", response_model=HealthResponse)
|
|
||||||
async def root():
|
|
||||||
return HealthResponse(status="healthy", timestamp=datetime.now().isoformat(), version="1.0.0")
|
|
||||||
|
|
||||||
@app.get("/health", response_model=HealthResponse)
|
|
||||||
async def health_check():
|
|
||||||
return HealthResponse(status="healthy", timestamp=datetime.now().isoformat(), version="1.0.0")
|
|
||||||
|
|
||||||
@app.get("/token", response_model=TokenResponse)
|
|
||||||
async def get_token_endpoint():
|
|
||||||
base_url = sheriff_base_url()
|
|
||||||
token = get_token()
|
|
||||||
cached = bool(get_cached_token(base_url))
|
|
||||||
return TokenResponse(token=token, cached=cached)
|
|
||||||
|
|
||||||
@app.get("/judicial/{rut}/civil")
|
|
||||||
async def get_civil_cases(rut: str):
|
|
||||||
rut_sanitized = str(rut).strip()
|
|
||||||
path = f"/api/clients/v2/helper/judicial/{rut_sanitized}/civil"
|
|
||||||
return execute_sheriff_get(path)
|
|
||||||
|
|
||||||
@app.get("/judicial/{rut}/cobranza")
|
|
||||||
async def get_cobranza_cases(rut: str):
|
|
||||||
rut_sanitized = str(rut).strip()
|
|
||||||
path = f"/api/clients/v2/helper/judicial/{rut_sanitized}/cobranza"
|
|
||||||
return execute_sheriff_get(path)
|
|
||||||
|
|
||||||
@app.get("/judicial/{rut}/laboral")
|
|
||||||
async def get_laboral_cases(rut: str):
|
|
||||||
rut_sanitized = str(rut).strip()
|
|
||||||
path = f"/api/clients/v2/helper/judicial/{rut_sanitized}/laboral"
|
|
||||||
return execute_sheriff_get(path)
|
|
||||||
|
|
||||||
@app.get("/cobranzaLaboral/{rut}/multaLaboral")
|
|
||||||
async def get_multa_laboral(rut: str):
|
|
||||||
rut_sanitized = str(rut).strip()
|
|
||||||
path = f"/api/clients/v2/helper/cobranzaLaboral/{rut_sanitized}/multaLaboral"
|
|
||||||
return execute_sheriff_get(path)
|
|
||||||
|
|
||||||
@app.get("/cobranzaLaboral/{rut}/moraPrevisional")
|
|
||||||
async def get_mora_previsional(rut: str):
|
|
||||||
rut_sanitized = str(rut).strip()
|
|
||||||
path = f"/api/clients/v2/helper/cobranzaLaboral/{rut_sanitized}/moraPrevisional"
|
|
||||||
return execute_sheriff_get(path)
|
|
||||||
|
|
||||||
@app.get("/compliance/{rut}")
|
|
||||||
async def get_compliance(rut: str):
|
|
||||||
rut_sanitized = str(rut).strip()
|
|
||||||
path = f"/api/clients/v2/helper/compliance/{rut_sanitized}"
|
|
||||||
return execute_sheriff_get(path)
|
|
||||||
|
|
||||||
@app.get("/legal/{rut}/mallaSocietaria")
|
|
||||||
async def get_malla_societaria(rut: str):
|
|
||||||
rut_sanitized = str(rut).strip()
|
|
||||||
path = f"/api/clients/v2/helper/legal/{rut_sanitized}/mallaSocietaria"
|
|
||||||
return execute_sheriff_get(path)
|
|
||||||
|
|
||||||
@app.get("/creditScore/{rut}")
|
|
||||||
async def get_credit_score(rut: str):
|
|
||||||
rut_sanitized = str(rut).strip()
|
|
||||||
path = f"/api/clients/v2/creditScore/{rut_sanitized}"
|
|
||||||
return execute_sheriff_get(path)
|
|
||||||
|
|
||||||
@app.post("/queryRut")
|
|
||||||
async def query_rut(payload: QueryRutRequest):
|
|
||||||
cargar_body = {
|
|
||||||
"rut": payload.rut.strip(),
|
|
||||||
"isMonitoring": False,
|
|
||||||
"includeEquifax": False,
|
|
||||||
}
|
|
||||||
cargar = execute_sheriff_post("/api/clients/v2/helper/cargarRut", cargar_body)
|
|
||||||
time.sleep(2)
|
|
||||||
rut_sanitized = payload.rut.strip()
|
|
||||||
logger.info(f"Querying resumen for RUT {rut_sanitized}")
|
|
||||||
url = f"/api/clients/v2/helper/resumen?rut={rut_sanitized}&complete=True"
|
|
||||||
resumen = execute_sheriff_get(url)
|
|
||||||
|
|
||||||
result: Dict[str, Any] = {"success": True, "cargarRut": cargar, "resumen": resumen}
|
|
||||||
ok = True
|
|
||||||
if isinstance(cargar, dict) and ("success" in cargar) and (not bool(cargar.get("success"))):
|
|
||||||
ok = False
|
|
||||||
if isinstance(resumen, dict) and ("success" in resumen) and (not bool(resumen.get("success"))):
|
|
||||||
ok = False
|
|
||||||
if ok:
|
|
||||||
judicial_civil = execute_sheriff_get(f"/api/clients/v2/helper/judicial/{rut_sanitized}/civil")
|
|
||||||
judicial_cobranza = execute_sheriff_get(f"/api/clients/v2/helper/judicial/{rut_sanitized}/cobranza")
|
|
||||||
judicial_laboral = execute_sheriff_get(f"/api/clients/v2/helper/judicial/{rut_sanitized}/laboral")
|
|
||||||
multa_laboral = execute_sheriff_get(f"/api/clients/v2/helper/cobranzaLaboral/{rut_sanitized}/multaLaboral")
|
|
||||||
mora_previsional = execute_sheriff_get(f"/api/clients/v2/helper/cobranzaLaboral/{rut_sanitized}/moraPrevisional")
|
|
||||||
compliance = execute_sheriff_get(f"/api/clients/v2/helper/compliance/{rut_sanitized}")
|
|
||||||
malla_societaria = execute_sheriff_get(f"/api/clients/v2/helper/legal/{rut_sanitized}/mallaSocietaria")
|
|
||||||
credit_score = execute_sheriff_get(f"/api/clients/v2/creditScore/{rut_sanitized}")
|
|
||||||
result.update({
|
|
||||||
"judicial": {
|
|
||||||
"civil": judicial_civil,
|
|
||||||
"cobranza": judicial_cobranza,
|
|
||||||
"laboral": judicial_laboral,
|
|
||||||
},
|
|
||||||
"cobranzaLaboral": {
|
|
||||||
"multaLaboral": multa_laboral,
|
|
||||||
"moraPrevisional": mora_previsional,
|
|
||||||
},
|
|
||||||
"compliance": compliance,
|
|
||||||
"legal": {
|
|
||||||
"mallaSocietaria": malla_societaria,
|
|
||||||
},
|
|
||||||
"creditScore": credit_score,
|
|
||||||
})
|
|
||||||
saved_path = persist_response(rut_sanitized, result)
|
|
||||||
result["_saved_file"] = saved_path
|
|
||||||
return result
|
|
||||||
|
|
||||||
@app.delete("/cache")
|
|
||||||
async def clear_sheriff_cache():
|
|
||||||
try:
|
|
||||||
deleted_count = clear_cache()
|
|
||||||
return {"message": "Cache cleared successfully", "deleted_count": deleted_count}
|
|
||||||
except Exception as e:
|
|
||||||
raise HTTPException(status_code=500, detail=f"Failed to clear cache: {str(e)}")
|
|
||||||
|
|
||||||
@app.get("/cache/stats")
|
|
||||||
async def get_sheriff_cache_stats():
|
|
||||||
try:
|
|
||||||
stats = get_cache_stats()
|
|
||||||
return stats
|
|
||||||
except Exception as e:
|
|
||||||
raise HTTPException(status_code=500, detail=f"Failed to get cache stats: {str(e)}")
|
|
||||||
|
|
||||||
# Rimuoviamo endpoints legacy transaction-cache
|
|
||||||
|
|
||||||
@app.get("/config")
|
|
||||||
async def get_environment_config():
|
|
||||||
try:
|
|
||||||
base_url = os.getenv("SHERIFF_V2_BASE_URL", "")
|
|
||||||
access_key = os.getenv("SHERIFF_V2_ACCESS_KEY", "")
|
|
||||||
access_secret = os.getenv("SHERIFF_V2_ACCESS_SECRET", "")
|
|
||||||
client_identifier = get_client_identifier()
|
|
||||||
return {
|
|
||||||
"base_url_set": bool(base_url),
|
|
||||||
"access_key_set": bool(access_key),
|
|
||||||
"access_secret_set": bool(access_secret),
|
|
||||||
"client_identifier": client_identifier,
|
|
||||||
"cache_ttl_seconds": CACHE_TTL_SECONDS,
|
|
||||||
"token_ttl_seconds": SHERIFF_TOKEN_TTL_SECONDS,
|
|
||||||
}
|
|
||||||
except Exception as e:
|
|
||||||
raise HTTPException(status_code=500, detail=f"Failed to get config: {str(e)}")
|
|
||||||
|
|
||||||
# Error handlers
|
|
||||||
@app.exception_handler(HTTPException)
|
|
||||||
async def http_exception_handler(request, exc):
|
|
||||||
return JSONResponse(
|
|
||||||
status_code=exc.status_code,
|
|
||||||
content=ErrorResponse(
|
|
||||||
error=str(exc.detail),
|
|
||||||
detail=str(exc.detail),
|
|
||||||
timestamp=datetime.now().isoformat()
|
|
||||||
).model_dump()
|
|
||||||
)
|
|
||||||
|
|
||||||
@app.exception_handler(Exception)
|
|
||||||
async def general_exception_handler(request, exc):
|
|
||||||
logger.error(f"Unhandled exception: {exc}")
|
|
||||||
return JSONResponse(
|
|
||||||
status_code=500,
|
|
||||||
content=ErrorResponse(
|
|
||||||
error="Internal server error",
|
|
||||||
detail=str(exc),
|
|
||||||
timestamp=datetime.now().isoformat()
|
|
||||||
).model_dump()
|
|
||||||
)
|
|
||||||
|
|
||||||
async def cleanup_old_responses():
|
|
||||||
"""
|
|
||||||
Periodically cleans up old response files:
|
|
||||||
1. Deletes files older than 1 week.
|
|
||||||
2. For remaining files, keeps only the most recent one for each RUT.
|
|
||||||
Runs every 10 minutes.
|
|
||||||
"""
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
logger.info("Starting cleanup of old Sheriff V2 responses...")
|
|
||||||
if not SHERIFF_RESPONSES_DIR.exists():
|
|
||||||
logger.info(f"Directory {SHERIFF_RESPONSES_DIR} does not exist. Skipping cleanup.")
|
|
||||||
else:
|
|
||||||
files_by_group: Dict[str, list] = {}
|
|
||||||
one_week_ago = datetime.utcnow() - timedelta(weeks=1)
|
|
||||||
deleted_count = 0
|
|
||||||
|
|
||||||
for file_path in SHERIFF_RESPONSES_DIR.glob("*.json"):
|
|
||||||
try:
|
|
||||||
filename = file_path.stem
|
|
||||||
# Format: {rut}_{timestamp}
|
|
||||||
parts = filename.rsplit('_', 1)
|
|
||||||
if len(parts) != 2:
|
|
||||||
continue
|
|
||||||
|
|
||||||
rut, timestamp_str = parts
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Parse timestamp: YYYYMMDDTHHMMSSZ
|
|
||||||
file_dt = datetime.strptime(timestamp_str, "%Y%m%dT%H%M%SZ")
|
|
||||||
except ValueError:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Check if older than 1 week
|
|
||||||
if file_dt < one_week_ago:
|
|
||||||
try:
|
|
||||||
file_path.unlink()
|
|
||||||
deleted_count += 1
|
|
||||||
logger.info(f"Deleted old response file (older than 1 week): {file_path.name}")
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Failed to delete {file_path}: {e}")
|
|
||||||
continue
|
|
||||||
|
|
||||||
if rut not in files_by_group:
|
|
||||||
files_by_group[rut] = []
|
|
||||||
|
|
||||||
files_by_group[rut].append((file_dt, file_path))
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning(f"Error processing file {file_path}: {e}")
|
|
||||||
|
|
||||||
# Process groups
|
|
||||||
for rut, file_list in files_by_group.items():
|
|
||||||
# Sort by timestamp descending (newest first)
|
|
||||||
file_list.sort(key=lambda x: x[0], reverse=True)
|
|
||||||
|
|
||||||
# Keep the first one, delete the rest
|
|
||||||
for _, file_to_delete in file_list[1:]:
|
|
||||||
try:
|
|
||||||
file_to_delete.unlink()
|
|
||||||
deleted_count += 1
|
|
||||||
logger.info(f"Deleted old response file (duplicate): {file_to_delete.name}")
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Failed to delete {file_to_delete}: {e}")
|
|
||||||
|
|
||||||
if deleted_count > 0:
|
|
||||||
logger.info(f"Cleanup finished. Deleted {deleted_count} old files.")
|
|
||||||
else:
|
|
||||||
logger.info("Cleanup finished. No files to delete.")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Error during cleanup: {e}")
|
|
||||||
|
|
||||||
# Wait for 10 minutes (600 seconds)
|
|
||||||
await asyncio.sleep(600)
|
|
||||||
|
|
||||||
@app.on_event("startup")
|
|
||||||
async def startup_event():
|
|
||||||
asyncio.create_task(cleanup_old_responses())
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
import uvicorn
|
|
||||||
uvicorn.run(app, host="0.0.0.0", port=8024)
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
pymongo==4.6.0
|
|
||||||
fastapi==0.104.1
|
|
||||||
uvicorn[standard]==0.24.0
|
|
||||||
pydantic==2.5.0
|
|
||||||
requests==2.31.0
|
|
||||||
python-dotenv==1.0.0
|
|
||||||
python-multipart==0.0.6
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
fastapi==0.104.1
|
|
||||||
uvicorn[standard]==0.24.0
|
|
||||||
pydantic==2.5.0
|
|
||||||
requests==2.31.0
|
|
||||||
python-dotenv==1.0.0
|
|
||||||
python-multipart==0.0.6
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
SHERIFF_V2_BASE_URL=sheriff.example.com
|
|
||||||
SHERIFF_V2_ACCESS_KEY=your_access_key
|
|
||||||
SHERIFF_V2_ACCESS_SECRET=your_access_secret
|
|
||||||
SHERIFF_V2_CLIENT_IDENTIFIER=SheriffSecureClient-v1
|
|
||||||
SHERIFF_V2_MONGODB_URI=mongodb://localhost:27017/duxiter
|
|
||||||
SHERIFF_V2_CACHE_TTL_SECONDS=604800
|
|
||||||
SHERIFF_V2_TOKEN_TTL_SECONDS=1800
|
|
||||||
|
|
@ -1,708 +0,0 @@
|
||||||
{
|
|
||||||
"info": {
|
|
||||||
"_postman_id": "db3f9c3b-5e0b-4dc4-bdb8-665fab04b4b9",
|
|
||||||
"name": "Sheriff API v2",
|
|
||||||
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
|
|
||||||
"_exporter_id": "31069565"
|
|
||||||
},
|
|
||||||
"item": [
|
|
||||||
{
|
|
||||||
"name": "🔒 Autenticación y Tokens",
|
|
||||||
"item": [
|
|
||||||
{
|
|
||||||
"name": "🔑 getToken (Obtener Token de Acceso)",
|
|
||||||
"event": [
|
|
||||||
{
|
|
||||||
"listen": "test",
|
|
||||||
"script": {
|
|
||||||
"exec": [
|
|
||||||
"var response = pm.response.json(); // Utiliza el método moderno para obtener el JSON",
|
|
||||||
"",
|
|
||||||
"if (response && response.data) {",
|
|
||||||
" // La respuesta contiene el token en la propiedad 'data'",
|
|
||||||
" pm.environment.set(\"bearer_token\", response.data);",
|
|
||||||
" ",
|
|
||||||
" // Opcional: Imprime el token en la Consola de Postman",
|
|
||||||
" console.log(\"Token guardado automáticamente en 'bearer_token': \" + response.data);",
|
|
||||||
"} else {",
|
|
||||||
" console.log(\"Error: No se pudo obtener el token de la respuesta. Verifica la estructura JSON.\");",
|
|
||||||
"}"
|
|
||||||
],
|
|
||||||
"type": "text/javascript",
|
|
||||||
"packages": {},
|
|
||||||
"requests": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"request": {
|
|
||||||
"auth": {
|
|
||||||
"type": "noauth"
|
|
||||||
},
|
|
||||||
"method": "POST",
|
|
||||||
"header": [
|
|
||||||
{
|
|
||||||
"key": "Content-Type",
|
|
||||||
"value": "application/json"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"body": {
|
|
||||||
"mode": "raw",
|
|
||||||
"raw": "{\n \"accessKey\": \"{{accessKey}}\",\n \"accessSecret\": \"{{accessSecret}}\"\n}",
|
|
||||||
"options": {
|
|
||||||
"raw": {
|
|
||||||
"language": "json"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"url": {
|
|
||||||
"raw": "https://{{base_url}}/api/clients/v2/apiCredentials/getToken",
|
|
||||||
"protocol": "https",
|
|
||||||
"host": [
|
|
||||||
"{{base_url}}"
|
|
||||||
],
|
|
||||||
"path": [
|
|
||||||
"api",
|
|
||||||
"clients",
|
|
||||||
"v2",
|
|
||||||
"apiCredentials",
|
|
||||||
"getToken"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"response": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "👤 Plataforma",
|
|
||||||
"item": [
|
|
||||||
{
|
|
||||||
"name": "Perfil - Detalle",
|
|
||||||
"item": [
|
|
||||||
{
|
|
||||||
"name": "⚖️ Judiciales",
|
|
||||||
"item": [
|
|
||||||
{
|
|
||||||
"name": "civil (Causas Civiles)",
|
|
||||||
"request": {
|
|
||||||
"method": "GET",
|
|
||||||
"header": [
|
|
||||||
{
|
|
||||||
"key": "accept",
|
|
||||||
"value": "application/json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "Authorization",
|
|
||||||
"value": "Bearer {{bearer_token}}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "x-client-identifier",
|
|
||||||
"value": "SheriffSecureClient-v1"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"url": {
|
|
||||||
"raw": "https://{{base_url}}/api/clients/v2/helper/judicial/{{rut}}/civil",
|
|
||||||
"protocol": "https",
|
|
||||||
"host": [
|
|
||||||
"{{base_url}}"
|
|
||||||
],
|
|
||||||
"path": [
|
|
||||||
"api",
|
|
||||||
"clients",
|
|
||||||
"v2",
|
|
||||||
"helper",
|
|
||||||
"judicial",
|
|
||||||
"{{rut}}",
|
|
||||||
"civil"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"response": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "cobranza (Causas Cobranza)",
|
|
||||||
"request": {
|
|
||||||
"method": "GET",
|
|
||||||
"header": [
|
|
||||||
{
|
|
||||||
"key": "accept",
|
|
||||||
"value": "application/json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "Authorization",
|
|
||||||
"value": "Bearer {{bearer_token}}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "x-client-identifier",
|
|
||||||
"value": "SheriffSecureClient-v1"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"url": {
|
|
||||||
"raw": "https://{{base_url}}/api/clients/v2/helper/judicial/{{rut}}/cobranza",
|
|
||||||
"protocol": "https",
|
|
||||||
"host": [
|
|
||||||
"{{base_url}}"
|
|
||||||
],
|
|
||||||
"path": [
|
|
||||||
"api",
|
|
||||||
"clients",
|
|
||||||
"v2",
|
|
||||||
"helper",
|
|
||||||
"judicial",
|
|
||||||
"{{rut}}",
|
|
||||||
"cobranza"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"response": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "laboral (Causas Laborales)",
|
|
||||||
"request": {
|
|
||||||
"method": "GET",
|
|
||||||
"header": [
|
|
||||||
{
|
|
||||||
"key": "accept",
|
|
||||||
"value": "application/json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "Authorization",
|
|
||||||
"value": "Bearer {{bearer_token}}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "x-client-identifier",
|
|
||||||
"value": "SheriffSecureClient-v1"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"url": {
|
|
||||||
"raw": "https://{{base_url}}/api/clients/v2/helper/judicial/{{rut}}/laboral",
|
|
||||||
"protocol": "https",
|
|
||||||
"host": [
|
|
||||||
"{{base_url}}"
|
|
||||||
],
|
|
||||||
"path": [
|
|
||||||
"api",
|
|
||||||
"clients",
|
|
||||||
"v2",
|
|
||||||
"helper",
|
|
||||||
"judicial",
|
|
||||||
"{{rut}}",
|
|
||||||
"laboral"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"response": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "💼 Cobranza Laboral",
|
|
||||||
"item": [
|
|
||||||
{
|
|
||||||
"name": "multaLaboral (Multas Laborales)",
|
|
||||||
"request": {
|
|
||||||
"method": "GET",
|
|
||||||
"header": [
|
|
||||||
{
|
|
||||||
"key": "accept",
|
|
||||||
"value": "application/json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "Authorization",
|
|
||||||
"value": "Bearer {{bearer_token}}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "x-client-identifier",
|
|
||||||
"value": "SheriffSecureClient-v1"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"url": {
|
|
||||||
"raw": "https://{{base_url}}/api/clients/v2/helper/cobranzaLaboral/{{rut}}/multaLaboral",
|
|
||||||
"protocol": "https",
|
|
||||||
"host": [
|
|
||||||
"{{base_url}}"
|
|
||||||
],
|
|
||||||
"path": [
|
|
||||||
"api",
|
|
||||||
"clients",
|
|
||||||
"v2",
|
|
||||||
"helper",
|
|
||||||
"cobranzaLaboral",
|
|
||||||
"{{rut}}",
|
|
||||||
"multaLaboral"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"response": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "moraPrevisional (Mora Previsional)",
|
|
||||||
"request": {
|
|
||||||
"method": "GET",
|
|
||||||
"header": [
|
|
||||||
{
|
|
||||||
"key": "accept",
|
|
||||||
"value": "application/json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "Authorization",
|
|
||||||
"value": "Bearer {{bearer_token}}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "x-client-identifier",
|
|
||||||
"value": "SheriffSecureClient-v1"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"url": {
|
|
||||||
"raw": "https://{{base_url}}/api/clients/v2/helper/cobranzaLaboral/{{rut}}/moraPrevisional",
|
|
||||||
"protocol": "https",
|
|
||||||
"host": [
|
|
||||||
"{{base_url}}"
|
|
||||||
],
|
|
||||||
"path": [
|
|
||||||
"api",
|
|
||||||
"clients",
|
|
||||||
"v2",
|
|
||||||
"helper",
|
|
||||||
"cobranzaLaboral",
|
|
||||||
"{{rut}}",
|
|
||||||
"moraPrevisional"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"response": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "🛡️ Compliance",
|
|
||||||
"item": [
|
|
||||||
{
|
|
||||||
"name": "compliance (Detalle de Cumplimiento)",
|
|
||||||
"request": {
|
|
||||||
"method": "GET",
|
|
||||||
"header": [
|
|
||||||
{
|
|
||||||
"key": "accept",
|
|
||||||
"value": "application/json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "Authorization",
|
|
||||||
"value": "Bearer {{bearer_token}}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "x-client-identifier",
|
|
||||||
"value": "SheriffSecureClient-v1"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"url": {
|
|
||||||
"raw": "https://{{base_url}}/api/clients/v2/helper/compliance/{{rut}}",
|
|
||||||
"protocol": "https",
|
|
||||||
"host": [
|
|
||||||
"{{base_url}}"
|
|
||||||
],
|
|
||||||
"path": [
|
|
||||||
"api",
|
|
||||||
"clients",
|
|
||||||
"v2",
|
|
||||||
"helper",
|
|
||||||
"compliance",
|
|
||||||
"{{rut}}"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"response": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "🔗 Legal",
|
|
||||||
"item": [
|
|
||||||
{
|
|
||||||
"name": "mallaSocietaria (Malla Societaria)",
|
|
||||||
"request": {
|
|
||||||
"method": "GET",
|
|
||||||
"header": [
|
|
||||||
{
|
|
||||||
"key": "accept",
|
|
||||||
"value": "application/json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "Authorization",
|
|
||||||
"value": "Bearer {{bearer_token}}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "x-client-identifier",
|
|
||||||
"value": "SheriffSecureClient-v1"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"url": {
|
|
||||||
"raw": "https://{{base_url}}/api/clients/v2/helper/legal/{{rut}}/mallaSocietaria",
|
|
||||||
"protocol": "https",
|
|
||||||
"host": [
|
|
||||||
"{{base_url}}"
|
|
||||||
],
|
|
||||||
"path": [
|
|
||||||
"api",
|
|
||||||
"clients",
|
|
||||||
"v2",
|
|
||||||
"helper",
|
|
||||||
"legal",
|
|
||||||
"{{rut}}",
|
|
||||||
"mallaSocietaria"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"response": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "💯 Score",
|
|
||||||
"item": [
|
|
||||||
{
|
|
||||||
"name": "💳 creditScore (Puntaje Crediticio)",
|
|
||||||
"request": {
|
|
||||||
"method": "GET",
|
|
||||||
"header": [
|
|
||||||
{
|
|
||||||
"key": "accept",
|
|
||||||
"value": "application/json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "Authorization",
|
|
||||||
"value": "Bearer {{bearer_token}}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "x-client-identifier",
|
|
||||||
"value": "SheriffSecureClient-v1"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"url": {
|
|
||||||
"raw": "https://{{base_url}}/api/clients/v2/creditScore/{{rut}}",
|
|
||||||
"protocol": "https",
|
|
||||||
"host": [
|
|
||||||
"{{base_url}}"
|
|
||||||
],
|
|
||||||
"path": [
|
|
||||||
"api",
|
|
||||||
"clients",
|
|
||||||
"v2",
|
|
||||||
"creditScore",
|
|
||||||
"{{rut}}"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"response": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "📈 Uso y Plan",
|
|
||||||
"item": [
|
|
||||||
{
|
|
||||||
"name": "usage (Consumo de la Cuenta)",
|
|
||||||
"request": {
|
|
||||||
"method": "GET",
|
|
||||||
"header": [
|
|
||||||
{
|
|
||||||
"key": "accept",
|
|
||||||
"value": "application/json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "Authorization",
|
|
||||||
"value": "Bearer {{bearer_token}}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "x-client-identifier",
|
|
||||||
"value": "SheriffSecureClient-v1"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"url": {
|
|
||||||
"raw": "https://{{base_url}}/api/clients/v2/usage",
|
|
||||||
"protocol": "https",
|
|
||||||
"host": [
|
|
||||||
"{{base_url}}"
|
|
||||||
],
|
|
||||||
"path": [
|
|
||||||
"api",
|
|
||||||
"clients",
|
|
||||||
"v2",
|
|
||||||
"usage"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"response": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "↩️ Webhooks/Callbacks (Asíncronos)",
|
|
||||||
"item": [
|
|
||||||
{
|
|
||||||
"name": "📋 Reportes (summaryByRut)",
|
|
||||||
"item": [
|
|
||||||
{
|
|
||||||
"name": "summaryByRut (PDF de Resumen por RUT)",
|
|
||||||
"request": {
|
|
||||||
"method": "GET",
|
|
||||||
"header": [
|
|
||||||
{
|
|
||||||
"key": "accept",
|
|
||||||
"value": "application/pdf"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "Content-Disposition",
|
|
||||||
"value": "attachment; filename={{rut}}.pdf"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "Authorization",
|
|
||||||
"value": "Bearer {{bearer_token}}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "x-client-identifier",
|
|
||||||
"value": "SheriffSecureClient-v1"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"url": {
|
|
||||||
"raw": "https://{{base_url}}/api/clients/v2/webhook/profile/{{rut}}/resumen?format=pdf",
|
|
||||||
"protocol": "https",
|
|
||||||
"host": [
|
|
||||||
"{{base_url}}"
|
|
||||||
],
|
|
||||||
"path": [
|
|
||||||
"api",
|
|
||||||
"clients",
|
|
||||||
"v2",
|
|
||||||
"webhook",
|
|
||||||
"profile",
|
|
||||||
"{{rut}}",
|
|
||||||
"resumen"
|
|
||||||
],
|
|
||||||
"query": [
|
|
||||||
{
|
|
||||||
"key": "format",
|
|
||||||
"value": "pdf"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"response": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "🛡️ Compliance (Asíncrono)",
|
|
||||||
"item": [
|
|
||||||
{
|
|
||||||
"name": "complianceByName (Cargar Perfil Compliance por Nombre)",
|
|
||||||
"request": {
|
|
||||||
"method": "POST",
|
|
||||||
"header": [
|
|
||||||
{
|
|
||||||
"key": "accept",
|
|
||||||
"value": "application/json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "Content-Type",
|
|
||||||
"value": "application/json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "Authorization",
|
|
||||||
"value": "Bearer {{bearer_token}}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "x-client-identifier",
|
|
||||||
"value": "SheriffSecureClient-v1"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"body": {
|
|
||||||
"mode": "raw",
|
|
||||||
"raw": "{\n \"fullName\": \"Nombre Empresa\",\n \"isMonitoring\": true,\n \"isJuridicalPerson\": true,\n \"searchErrors\": 0\n }",
|
|
||||||
"options": {
|
|
||||||
"raw": {
|
|
||||||
"language": "json"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"url": {
|
|
||||||
"raw": "https://{{base_url}}/api/clients/v2/webhook/compliance/cargarPerfil",
|
|
||||||
"protocol": "https",
|
|
||||||
"host": [
|
|
||||||
"{{base_url}}"
|
|
||||||
],
|
|
||||||
"path": [
|
|
||||||
"api",
|
|
||||||
"clients",
|
|
||||||
"v2",
|
|
||||||
"webhook",
|
|
||||||
"compliance",
|
|
||||||
"cargarPerfil"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"response": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "📄 resumen (Obtener Resumen del Perfil)",
|
|
||||||
"request": {
|
|
||||||
"method": "GET",
|
|
||||||
"header": [
|
|
||||||
{
|
|
||||||
"key": "accept",
|
|
||||||
"value": "application/json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "Authorization",
|
|
||||||
"value": "Bearer {{bearer_token}}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "x-client-identifier",
|
|
||||||
"value": "SheriffSecureClient-v1"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"url": {
|
|
||||||
"raw": "https://{{base_url}}/api/clients/v2/helper/resumen?rut={{rut}}",
|
|
||||||
"protocol": "https",
|
|
||||||
"host": [
|
|
||||||
"{{base_url}}"
|
|
||||||
],
|
|
||||||
"path": [
|
|
||||||
"api",
|
|
||||||
"clients",
|
|
||||||
"v2",
|
|
||||||
"helper",
|
|
||||||
"resumen"
|
|
||||||
],
|
|
||||||
"query": [
|
|
||||||
{
|
|
||||||
"key": "rut",
|
|
||||||
"value": "{{rut}}"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"response": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "📜 loadedProfiles (Perfiles Cargados)",
|
|
||||||
"request": {
|
|
||||||
"method": "GET",
|
|
||||||
"header": [
|
|
||||||
{
|
|
||||||
"key": "accept",
|
|
||||||
"value": "application/json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "Authorization",
|
|
||||||
"value": "Bearer {{bearer_token}}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "x-client-identifier",
|
|
||||||
"value": "SheriffSecureClient-v1"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"url": {
|
|
||||||
"raw": "https://{{base_url}}/api/clients/v2/profiles",
|
|
||||||
"protocol": "https",
|
|
||||||
"host": [
|
|
||||||
"{{base_url}}"
|
|
||||||
],
|
|
||||||
"path": [
|
|
||||||
"api",
|
|
||||||
"clients",
|
|
||||||
"v2",
|
|
||||||
"profiles"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"response": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "➕ cargarRut (Cargar Perfil)",
|
|
||||||
"request": {
|
|
||||||
"method": "POST",
|
|
||||||
"header": [
|
|
||||||
{
|
|
||||||
"key": "accept",
|
|
||||||
"value": "application/json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "Authorization",
|
|
||||||
"value": "Bearer {{bearer_token}}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "x-client-identifier",
|
|
||||||
"value": "SheriffSecureClient-v1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "Content-Type",
|
|
||||||
"value": "application/json"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"body": {
|
|
||||||
"mode": "raw",
|
|
||||||
"raw": "{\n \"rut\": \"{{rut}}\",\n \"isMonitoring\": false,\n \"includeEquifax\": false\n}",
|
|
||||||
"options": {
|
|
||||||
"raw": {
|
|
||||||
"language": "json"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"url": {
|
|
||||||
"raw": "https://{{base_url}}/api/clients/v2/helper/cargarRut",
|
|
||||||
"protocol": "https",
|
|
||||||
"host": [
|
|
||||||
"{{base_url}}"
|
|
||||||
],
|
|
||||||
"path": [
|
|
||||||
"api",
|
|
||||||
"clients",
|
|
||||||
"v2",
|
|
||||||
"helper",
|
|
||||||
"cargarRut"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"response": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"auth": {
|
|
||||||
"type": "bearer",
|
|
||||||
"bearer": [
|
|
||||||
{
|
|
||||||
"key": "token",
|
|
||||||
"value": "{{bearer_token}}",
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"event": [
|
|
||||||
{
|
|
||||||
"listen": "prerequest",
|
|
||||||
"script": {
|
|
||||||
"type": "text/javascript",
|
|
||||||
"exec": [
|
|
||||||
""
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"listen": "test",
|
|
||||||
"script": {
|
|
||||||
"type": "text/javascript",
|
|
||||||
"exec": [
|
|
||||||
""
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
|
|
||||||
venv/bin/python main.py
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,637 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
FastAPI Sheriff v2 Provider
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import json
|
|
||||||
import time
|
|
||||||
import asyncio
|
|
||||||
import logging
|
|
||||||
import hashlib
|
|
||||||
from pathlib import Path
|
|
||||||
from typing import Dict, Any, Optional
|
|
||||||
from datetime import datetime, timedelta
|
|
||||||
|
|
||||||
import requests
|
|
||||||
from fastapi import FastAPI, HTTPException, Header
|
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
|
||||||
from fastapi.responses import JSONResponse
|
|
||||||
from pydantic import BaseModel
|
|
||||||
from dotenv import load_dotenv
|
|
||||||
from pymongo import MongoClient
|
|
||||||
from pymongo.errors import ConnectionFailure
|
|
||||||
|
|
||||||
# Configure logging
|
|
||||||
logging.basicConfig(
|
|
||||||
level=logging.INFO,
|
|
||||||
format='%(asctime)s - %(levelname)s - %(message)s',
|
|
||||||
datefmt='%Y-%m-%d %H:%M:%S'
|
|
||||||
)
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
load_dotenv()
|
|
||||||
|
|
||||||
MONGODB_URI = os.getenv("MONGODB_URI", "mongodb://localhost:27017/duxiter")
|
|
||||||
print(MONGODB_URI)
|
|
||||||
CACHE_TTL_SECONDS = int(os.getenv("SHERIFF_V2_CACHE_TTL_SECONDS", "604800"))
|
|
||||||
|
|
||||||
try:
|
|
||||||
mongo_client = MongoClient(MONGODB_URI)
|
|
||||||
# Test connection
|
|
||||||
mongo_client.admin.command('ping')
|
|
||||||
db = mongo_client.get_database()
|
|
||||||
cache_collection = db.sheriff_v2_cache
|
|
||||||
|
|
||||||
# Create TTL index for automatic document expiration (ignore auth errors)
|
|
||||||
try:
|
|
||||||
cache_collection.create_index("expires_at", expireAfterSeconds=0)
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning(f"Could not create TTL index (possibly requires auth): {e}")
|
|
||||||
logger.info(f"MongoDB connected successfully. Cache TTL: {CACHE_TTL_SECONDS} seconds")
|
|
||||||
except ConnectionFailure as e:
|
|
||||||
logger.error(f"MongoDB connection failed: {e}")
|
|
||||||
mongo_client = None
|
|
||||||
db = None
|
|
||||||
cache_collection = None
|
|
||||||
|
|
||||||
TOKEN_CACHE_FILE = Path(".token_cache.json")
|
|
||||||
SHERIFF_TOKEN_TTL_SECONDS = int(os.getenv("SHERIFF_V2_TOKEN_TTL_SECONDS", "1800"))
|
|
||||||
SHERIFF_RESPONSES_DIR = Path(__file__).resolve().parents[2] / "sheriff_v2_response"
|
|
||||||
|
|
||||||
# Pydantic Models
|
|
||||||
class TokenResponse(BaseModel):
|
|
||||||
token: str
|
|
||||||
cached: bool
|
|
||||||
|
|
||||||
class HealthResponse(BaseModel):
|
|
||||||
status: str
|
|
||||||
timestamp: str
|
|
||||||
version: str
|
|
||||||
|
|
||||||
class ErrorResponse(BaseModel):
|
|
||||||
error: str
|
|
||||||
detail: str
|
|
||||||
timestamp: str
|
|
||||||
|
|
||||||
# FastAPI App
|
|
||||||
app = FastAPI(
|
|
||||||
title="Sheriff v2 Provider API",
|
|
||||||
description="REST API for Sheriff v2 client endpoints",
|
|
||||||
version="1.0.0",
|
|
||||||
docs_url="/docs",
|
|
||||||
redoc_url="/redoc"
|
|
||||||
)
|
|
||||||
|
|
||||||
# CORS middleware
|
|
||||||
app.add_middleware(
|
|
||||||
CORSMiddleware,
|
|
||||||
allow_origins=["*"], # Configure appropriately for production
|
|
||||||
allow_credentials=True,
|
|
||||||
allow_methods=["*"],
|
|
||||||
allow_headers=["*"],
|
|
||||||
)
|
|
||||||
|
|
||||||
class QueryRutRequest(BaseModel):
|
|
||||||
rut: str
|
|
||||||
isMonitoring: bool = False
|
|
||||||
includeEquifax: bool = False
|
|
||||||
|
|
||||||
def generate_cache_key(env_key: str, payload: Dict[str, Any]) -> str:
|
|
||||||
payload_str = json.dumps(payload, sort_keys=True, separators=(',', ':'))
|
|
||||||
payload_hash = hashlib.sha256(payload_str.encode()).hexdigest()
|
|
||||||
return f"sheriff_v2_cache:{env_key}:{payload_hash}"
|
|
||||||
|
|
||||||
def get_cached_transaction(cache_key: str) -> Optional[Dict[str, Any]]:
|
|
||||||
if cache_collection is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
try:
|
|
||||||
cached_doc = cache_collection.find_one({"_id": cache_key})
|
|
||||||
if cached_doc:
|
|
||||||
logger.info(f"Cache hit for key: {cache_key}")
|
|
||||||
return cached_doc.get("result")
|
|
||||||
else:
|
|
||||||
# logger.info(f"Cache miss for key: {cache_key}")
|
|
||||||
return None
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Error retrieving from cache: {e}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
def save_transaction_to_cache(cache_key: str, result: Dict[str, Any]) -> None:
|
|
||||||
if cache_collection is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
expires_at = datetime.utcnow() + timedelta(seconds=CACHE_TTL_SECONDS)
|
|
||||||
cache_doc = {
|
|
||||||
"_id": cache_key,
|
|
||||||
"result": result,
|
|
||||||
"cached_at": datetime.utcnow(),
|
|
||||||
"expires_at": expires_at
|
|
||||||
}
|
|
||||||
|
|
||||||
# Use upsert to replace existing cache entry
|
|
||||||
cache_collection.replace_one(
|
|
||||||
{"_id": cache_key},
|
|
||||||
cache_doc,
|
|
||||||
upsert=True
|
|
||||||
)
|
|
||||||
logger.info(f"Transaction result cached with key: {cache_key}, expires at: {expires_at}")
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Error saving to cache: {e}")
|
|
||||||
|
|
||||||
def clear_cache() -> int:
|
|
||||||
if cache_collection is None:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
try:
|
|
||||||
result = cache_collection.delete_many({})
|
|
||||||
logger.info(f"Cleared {result.deleted_count} cached transactions")
|
|
||||||
return result.deleted_count
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Error clearing cache: {e}")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
def get_cache_stats() -> Dict[str, Any]:
|
|
||||||
if cache_collection is None:
|
|
||||||
return {"error": "Cache not available"}
|
|
||||||
|
|
||||||
try:
|
|
||||||
total_docs = cache_collection.count_documents({})
|
|
||||||
expired_docs = cache_collection.count_documents({
|
|
||||||
"expires_at": {"$lt": datetime.utcnow()}
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
"total_cached_transactions": total_docs,
|
|
||||||
"expired_transactions": expired_docs,
|
|
||||||
"active_transactions": total_docs - expired_docs,
|
|
||||||
"cache_ttl_seconds": CACHE_TTL_SECONDS,
|
|
||||||
"mongodb_connected": True
|
|
||||||
}
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Error getting cache stats: {e}")
|
|
||||||
return {"error": str(e), "mongodb_connected": False}
|
|
||||||
|
|
||||||
def now() -> float:
|
|
||||||
return time.time()
|
|
||||||
|
|
||||||
def read_cache() -> Optional[dict]:
|
|
||||||
if TOKEN_CACHE_FILE.exists():
|
|
||||||
try:
|
|
||||||
with TOKEN_CACHE_FILE.open("r", encoding="utf-8") as f:
|
|
||||||
return json.load(f)
|
|
||||||
except Exception:
|
|
||||||
return None
|
|
||||||
return None
|
|
||||||
|
|
||||||
def write_cache(data: dict) -> None:
|
|
||||||
try:
|
|
||||||
with TOKEN_CACHE_FILE.open("w", encoding="utf-8") as f:
|
|
||||||
json.dump(data, f, indent=2)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def sheriff_base_url() -> str:
|
|
||||||
base_url = os.getenv("SHERIFF_V2_BASE_URL", "").strip()
|
|
||||||
if not base_url:
|
|
||||||
raise HTTPException(status_code=500, detail="Missing SHERIFF_V2_BASE_URL")
|
|
||||||
return base_url
|
|
||||||
|
|
||||||
def get_client_identifier() -> str:
|
|
||||||
return os.getenv("SHERIFF_V2_CLIENT_IDENTIFIER", "SheriffSecureClient-v1").strip()
|
|
||||||
|
|
||||||
def get_api_credentials() -> Dict[str, str]:
|
|
||||||
access_key = os.getenv("SHERIFF_V2_ACCESS_KEY", "").strip()
|
|
||||||
access_secret = os.getenv("SHERIFF_V2_ACCESS_SECRET", "").strip()
|
|
||||||
if not access_key or not access_secret:
|
|
||||||
raise HTTPException(status_code=500, detail="Missing Sheriff API credentials")
|
|
||||||
return {"accessKey": access_key, "accessSecret": access_secret}
|
|
||||||
|
|
||||||
def get_cached_token(env_key: str) -> Optional[str]:
|
|
||||||
cache = read_cache()
|
|
||||||
if not cache:
|
|
||||||
return None
|
|
||||||
item = cache.get(env_key)
|
|
||||||
if not item:
|
|
||||||
return None
|
|
||||||
token = item.get("token")
|
|
||||||
expires_at = item.get("expires_at", 0)
|
|
||||||
if not token:
|
|
||||||
return None
|
|
||||||
current_time = now()
|
|
||||||
if current_time < (expires_at - 60):
|
|
||||||
return token
|
|
||||||
return None
|
|
||||||
|
|
||||||
def save_cached_token(env_key: str, token: str, ttl_secs: int) -> None:
|
|
||||||
cache = read_cache() or {}
|
|
||||||
current_time = now()
|
|
||||||
cache[env_key] = {
|
|
||||||
"token": token,
|
|
||||||
"expires_at": current_time + int(ttl_secs),
|
|
||||||
"saved_at": current_time,
|
|
||||||
}
|
|
||||||
write_cache(cache)
|
|
||||||
|
|
||||||
def fetch_token(base_url: str, access_key: str, access_secret: str) -> str:
|
|
||||||
url = base_url + "/api/clients/v2/apiCredentials/getToken"
|
|
||||||
client_id = get_client_identifier()
|
|
||||||
headers = {"Content-Type": "application/json","x-client-identifier": client_id }
|
|
||||||
body_data = {"accessKey": access_key, "accessSecret": access_secret}
|
|
||||||
body_json = json.dumps(body_data)
|
|
||||||
|
|
||||||
# Print headers as JSON to show double quotes in logs
|
|
||||||
print(f"headers {headers}")
|
|
||||||
print(f"body {body_json}")
|
|
||||||
logger.info(f"Fetching token from {url}")
|
|
||||||
|
|
||||||
resp = requests.post(url, headers=headers, data=body_json, timeout=30)
|
|
||||||
if resp.status_code == 200:
|
|
||||||
payload = resp.json()
|
|
||||||
token = payload.get("data") or payload.get("token") or ""
|
|
||||||
if not token:
|
|
||||||
raise HTTPException(status_code=502, detail="Token response invalid")
|
|
||||||
save_cached_token(base_url, token, SHERIFF_TOKEN_TTL_SECONDS)
|
|
||||||
return token
|
|
||||||
raise HTTPException(status_code=resp.status_code, detail=resp.text)
|
|
||||||
|
|
||||||
def get_token() -> str:
|
|
||||||
base_url = sheriff_base_url()
|
|
||||||
cached = get_cached_token(base_url)
|
|
||||||
if cached:
|
|
||||||
return cached
|
|
||||||
creds = get_api_credentials()
|
|
||||||
return fetch_token(base_url, creds["accessKey"], creds["accessSecret"])
|
|
||||||
|
|
||||||
def execute_sheriff_get(path: str, query: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
||||||
base_url = sheriff_base_url()
|
|
||||||
token = get_token()
|
|
||||||
client_id = get_client_identifier()
|
|
||||||
if base_url.startswith("http"):
|
|
||||||
url = f"{base_url}{path}"
|
|
||||||
else:
|
|
||||||
url = f"https://{base_url}{path}"
|
|
||||||
params = query or {}
|
|
||||||
cache_key = generate_cache_key("sheriff_v2_get", {"url": url, "params": params})
|
|
||||||
cached = get_cached_transaction(cache_key)
|
|
||||||
if cached:
|
|
||||||
cached["_cache_info"] = {"cached": True, "cache_key": cache_key, "retrieved_at": datetime.utcnow().isoformat()}
|
|
||||||
return cached
|
|
||||||
headers = {
|
|
||||||
"accept": "application/json",
|
|
||||||
"Authorization": f"Bearer {token}",
|
|
||||||
"x-client-identifier": client_id,
|
|
||||||
}
|
|
||||||
print(f"Executing GET {url} with headers {headers} and params {params}")
|
|
||||||
resp = requests.get(url, headers=headers, params=params, timeout=60)
|
|
||||||
if resp.status_code == 200:
|
|
||||||
try:
|
|
||||||
data = resp.json()
|
|
||||||
except Exception:
|
|
||||||
data = {"raw": resp.text}
|
|
||||||
save_transaction_to_cache(cache_key, data)
|
|
||||||
data["_cache_info"] = {"cached": False, "cache_key": cache_key, "cached_at": datetime.utcnow().isoformat()}
|
|
||||||
return data
|
|
||||||
if resp.status_code == 401:
|
|
||||||
token = fetch_token(base_url, get_api_credentials()["accessKey"], get_api_credentials()["accessSecret"])
|
|
||||||
headers["Authorization"] = f"Bearer {token}"
|
|
||||||
resp = requests.get(url, headers=headers, params=params, timeout=60)
|
|
||||||
if resp.status_code == 200:
|
|
||||||
try:
|
|
||||||
data = resp.json()
|
|
||||||
except Exception:
|
|
||||||
data = {"raw": resp.text}
|
|
||||||
save_transaction_to_cache(cache_key, data)
|
|
||||||
data["_cache_info"] = {"cached": False, "cache_key": cache_key, "cached_at": datetime.utcnow().isoformat()}
|
|
||||||
return data
|
|
||||||
raise HTTPException(status_code=resp.status_code, detail=resp.text)
|
|
||||||
if resp.status_code == 403:
|
|
||||||
raise HTTPException(status_code=403, detail="Forbidden")
|
|
||||||
if resp.status_code == 404:
|
|
||||||
raise HTTPException(status_code=404, detail="Not Found")
|
|
||||||
if resp.status_code >= 500:
|
|
||||||
raise HTTPException(status_code=502, detail=f"Upstream error {resp.status_code}")
|
|
||||||
raise HTTPException(status_code=resp.status_code, detail=resp.text)
|
|
||||||
|
|
||||||
def execute_sheriff_post(path: str, body: Dict[str, Any]) -> Dict[str, Any]:
|
|
||||||
base_url = sheriff_base_url()
|
|
||||||
token = get_token()
|
|
||||||
client_id = get_client_identifier()
|
|
||||||
if base_url.startswith("http"):
|
|
||||||
url = f"{base_url}{path}"
|
|
||||||
else:
|
|
||||||
url = f"https://{base_url}{path}"
|
|
||||||
headers = {
|
|
||||||
"accept": "application/json",
|
|
||||||
"Authorization": f"Bearer {token}",
|
|
||||||
"x-client-identifier": client_id,
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
}
|
|
||||||
resp = requests.post(url, headers=headers, json=body, timeout=60)
|
|
||||||
if resp.status_code == 200:
|
|
||||||
try:
|
|
||||||
return resp.json()
|
|
||||||
except Exception:
|
|
||||||
return {"raw": resp.text}
|
|
||||||
if resp.status_code == 401:
|
|
||||||
token = fetch_token(base_url, get_api_credentials()["accessKey"], get_api_credentials()["accessSecret"])
|
|
||||||
headers["Authorization"] = f"Bearer {token}"
|
|
||||||
resp = requests.post(url, headers=headers, json=body, timeout=60)
|
|
||||||
if resp.status_code == 200:
|
|
||||||
try:
|
|
||||||
return resp.json()
|
|
||||||
except Exception:
|
|
||||||
return {"raw": resp.text}
|
|
||||||
raise HTTPException(status_code=resp.status_code, detail=resp.text)
|
|
||||||
if resp.status_code == 403:
|
|
||||||
raise HTTPException(status_code=403, detail="Forbidden")
|
|
||||||
if resp.status_code == 404:
|
|
||||||
raise HTTPException(status_code=404, detail="Not Found")
|
|
||||||
if resp.status_code >= 500:
|
|
||||||
raise HTTPException(status_code=502, detail=f"Upstream error {resp.status_code}")
|
|
||||||
raise HTTPException(status_code=resp.status_code, detail=resp.text)
|
|
||||||
|
|
||||||
def persist_response(rut: str, result: Dict[str, Any]) -> str:
|
|
||||||
server_dir = Path(__file__).resolve().parents[2]
|
|
||||||
dest_dir = server_dir / "sheriff_v2_response"
|
|
||||||
dest_dir.mkdir(parents=True, exist_ok=True)
|
|
||||||
ts = datetime.utcnow().strftime("%Y%m%dT%H%M%SZ")
|
|
||||||
fp = dest_dir / f"{rut}_{ts}.json"
|
|
||||||
with fp.open("w", encoding="utf-8") as f:
|
|
||||||
json.dump(result, f, ensure_ascii=False, indent=2)
|
|
||||||
return str(fp)
|
|
||||||
|
|
||||||
|
|
||||||
# Rimuoviamo completamente flusso POST/Equifax
|
|
||||||
|
|
||||||
|
|
||||||
def _clear_cached_token(env_key: str) -> None:
|
|
||||||
try:
|
|
||||||
cache_data = read_cache()
|
|
||||||
if cache_data and env_key in cache_data:
|
|
||||||
del cache_data[env_key]
|
|
||||||
write_cache(cache_data)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# API Endpoints
|
|
||||||
@app.get("/", response_model=HealthResponse)
|
|
||||||
async def root():
|
|
||||||
return HealthResponse(status="healthy", timestamp=datetime.now().isoformat(), version="1.0.0")
|
|
||||||
|
|
||||||
@app.get("/health", response_model=HealthResponse)
|
|
||||||
async def health_check():
|
|
||||||
return HealthResponse(status="healthy", timestamp=datetime.now().isoformat(), version="1.0.0")
|
|
||||||
|
|
||||||
@app.get("/token", response_model=TokenResponse)
|
|
||||||
async def get_token_endpoint():
|
|
||||||
base_url = sheriff_base_url()
|
|
||||||
token = get_token()
|
|
||||||
cached = bool(get_cached_token(base_url))
|
|
||||||
return TokenResponse(token=token, cached=cached)
|
|
||||||
|
|
||||||
@app.get("/judicial/{rut}/civil")
|
|
||||||
async def get_civil_cases(rut: str):
|
|
||||||
rut_sanitized = str(rut).strip()
|
|
||||||
path = f"/api/clients/v2/helper/judicial/{rut_sanitized}/civil"
|
|
||||||
return execute_sheriff_get(path)
|
|
||||||
|
|
||||||
@app.get("/judicial/{rut}/cobranza")
|
|
||||||
async def get_cobranza_cases(rut: str):
|
|
||||||
rut_sanitized = str(rut).strip()
|
|
||||||
path = f"/api/clients/v2/helper/judicial/{rut_sanitized}/cobranza"
|
|
||||||
return execute_sheriff_get(path)
|
|
||||||
|
|
||||||
@app.get("/judicial/{rut}/laboral")
|
|
||||||
async def get_laboral_cases(rut: str):
|
|
||||||
rut_sanitized = str(rut).strip()
|
|
||||||
path = f"/api/clients/v2/helper/judicial/{rut_sanitized}/laboral"
|
|
||||||
return execute_sheriff_get(path)
|
|
||||||
|
|
||||||
@app.get("/cobranzaLaboral/{rut}/multaLaboral")
|
|
||||||
async def get_multa_laboral(rut: str):
|
|
||||||
rut_sanitized = str(rut).strip()
|
|
||||||
path = f"/api/clients/v2/helper/cobranzaLaboral/{rut_sanitized}/multaLaboral"
|
|
||||||
return execute_sheriff_get(path)
|
|
||||||
|
|
||||||
@app.get("/cobranzaLaboral/{rut}/moraPrevisional")
|
|
||||||
async def get_mora_previsional(rut: str):
|
|
||||||
rut_sanitized = str(rut).strip()
|
|
||||||
path = f"/api/clients/v2/helper/cobranzaLaboral/{rut_sanitized}/moraPrevisional"
|
|
||||||
return execute_sheriff_get(path)
|
|
||||||
|
|
||||||
@app.get("/compliance/{rut}")
|
|
||||||
async def get_compliance(rut: str):
|
|
||||||
rut_sanitized = str(rut).strip()
|
|
||||||
path = f"/api/clients/v2/helper/compliance/{rut_sanitized}"
|
|
||||||
return execute_sheriff_get(path)
|
|
||||||
|
|
||||||
@app.get("/legal/{rut}/mallaSocietaria")
|
|
||||||
async def get_malla_societaria(rut: str):
|
|
||||||
rut_sanitized = str(rut).strip()
|
|
||||||
path = f"/api/clients/v2/helper/legal/{rut_sanitized}/mallaSocietaria"
|
|
||||||
return execute_sheriff_get(path)
|
|
||||||
|
|
||||||
@app.get("/creditScore/{rut}")
|
|
||||||
async def get_credit_score(rut: str):
|
|
||||||
rut_sanitized = str(rut).strip()
|
|
||||||
path = f"/api/clients/v2/creditScore/{rut_sanitized}"
|
|
||||||
return execute_sheriff_get(path)
|
|
||||||
|
|
||||||
@app.post("/queryRut")
|
|
||||||
async def query_rut(payload: QueryRutRequest):
|
|
||||||
cargar_body = {
|
|
||||||
"rut": payload.rut.strip(),
|
|
||||||
"isMonitoring": False,
|
|
||||||
"includeEquifax": False,
|
|
||||||
}
|
|
||||||
cargar = execute_sheriff_post("/api/clients/v2/helper/cargarRut", cargar_body)
|
|
||||||
time.sleep(2)
|
|
||||||
rut_sanitized = payload.rut.strip()
|
|
||||||
logger.info(f"Querying resumen for RUT {rut_sanitized}")
|
|
||||||
url = f"/api/clients/v2/helper/resumen?rut={rut_sanitized}&complete=True"
|
|
||||||
resumen = execute_sheriff_get(url)
|
|
||||||
|
|
||||||
result: Dict[str, Any] = {"success": True, "cargarRut": cargar, "resumen": resumen}
|
|
||||||
ok = True
|
|
||||||
if isinstance(cargar, dict) and ("success" in cargar) and (not bool(cargar.get("success"))):
|
|
||||||
ok = False
|
|
||||||
if isinstance(resumen, dict) and ("success" in resumen) and (not bool(resumen.get("success"))):
|
|
||||||
ok = False
|
|
||||||
if ok:
|
|
||||||
judicial_civil = execute_sheriff_get(f"/api/clients/v2/helper/judicial/{rut_sanitized}/civil")
|
|
||||||
judicial_cobranza = execute_sheriff_get(f"/api/clients/v2/helper/judicial/{rut_sanitized}/cobranza")
|
|
||||||
judicial_laboral = execute_sheriff_get(f"/api/clients/v2/helper/judicial/{rut_sanitized}/laboral")
|
|
||||||
multa_laboral = execute_sheriff_get(f"/api/clients/v2/helper/cobranzaLaboral/{rut_sanitized}/multaLaboral")
|
|
||||||
mora_previsional = execute_sheriff_get(f"/api/clients/v2/helper/cobranzaLaboral/{rut_sanitized}/moraPrevisional")
|
|
||||||
compliance = execute_sheriff_get(f"/api/clients/v2/helper/compliance/{rut_sanitized}")
|
|
||||||
malla_societaria = execute_sheriff_get(f"/api/clients/v2/helper/legal/{rut_sanitized}/mallaSocietaria")
|
|
||||||
credit_score = execute_sheriff_get(f"/api/clients/v2/creditScore/{rut_sanitized}")
|
|
||||||
result.update({
|
|
||||||
"judicial": {
|
|
||||||
"civil": judicial_civil,
|
|
||||||
"cobranza": judicial_cobranza,
|
|
||||||
"laboral": judicial_laboral,
|
|
||||||
},
|
|
||||||
"cobranzaLaboral": {
|
|
||||||
"multaLaboral": multa_laboral,
|
|
||||||
"moraPrevisional": mora_previsional,
|
|
||||||
},
|
|
||||||
"compliance": compliance,
|
|
||||||
"legal": {
|
|
||||||
"mallaSocietaria": malla_societaria,
|
|
||||||
},
|
|
||||||
"creditScore": credit_score,
|
|
||||||
})
|
|
||||||
saved_path = persist_response(rut_sanitized, result)
|
|
||||||
result["_saved_file"] = saved_path
|
|
||||||
return result
|
|
||||||
|
|
||||||
@app.delete("/cache")
|
|
||||||
async def clear_sheriff_cache():
|
|
||||||
try:
|
|
||||||
deleted_count = clear_cache()
|
|
||||||
return {"message": "Cache cleared successfully", "deleted_count": deleted_count}
|
|
||||||
except Exception as e:
|
|
||||||
raise HTTPException(status_code=500, detail=f"Failed to clear cache: {str(e)}")
|
|
||||||
|
|
||||||
@app.get("/cache/stats")
|
|
||||||
async def get_sheriff_cache_stats():
|
|
||||||
try:
|
|
||||||
stats = get_cache_stats()
|
|
||||||
return stats
|
|
||||||
except Exception as e:
|
|
||||||
raise HTTPException(status_code=500, detail=f"Failed to get cache stats: {str(e)}")
|
|
||||||
|
|
||||||
# Rimuoviamo endpoints legacy transaction-cache
|
|
||||||
|
|
||||||
@app.get("/config")
|
|
||||||
async def get_environment_config():
|
|
||||||
try:
|
|
||||||
base_url = os.getenv("SHERIFF_V2_BASE_URL", "")
|
|
||||||
access_key = os.getenv("SHERIFF_V2_ACCESS_KEY", "")
|
|
||||||
access_secret = os.getenv("SHERIFF_V2_ACCESS_SECRET", "")
|
|
||||||
client_identifier = get_client_identifier()
|
|
||||||
return {
|
|
||||||
"base_url_set": bool(base_url),
|
|
||||||
"access_key_set": bool(access_key),
|
|
||||||
"access_secret_set": bool(access_secret),
|
|
||||||
"client_identifier": client_identifier,
|
|
||||||
"cache_ttl_seconds": CACHE_TTL_SECONDS,
|
|
||||||
"token_ttl_seconds": SHERIFF_TOKEN_TTL_SECONDS,
|
|
||||||
}
|
|
||||||
except Exception as e:
|
|
||||||
raise HTTPException(status_code=500, detail=f"Failed to get config: {str(e)}")
|
|
||||||
|
|
||||||
# Error handlers
|
|
||||||
@app.exception_handler(HTTPException)
|
|
||||||
async def http_exception_handler(request, exc):
|
|
||||||
return JSONResponse(
|
|
||||||
status_code=exc.status_code,
|
|
||||||
content=ErrorResponse(
|
|
||||||
error=str(exc.detail),
|
|
||||||
detail=str(exc.detail),
|
|
||||||
timestamp=datetime.now().isoformat()
|
|
||||||
).model_dump()
|
|
||||||
)
|
|
||||||
|
|
||||||
@app.exception_handler(Exception)
|
|
||||||
async def general_exception_handler(request, exc):
|
|
||||||
logger.error(f"Unhandled exception: {exc}")
|
|
||||||
return JSONResponse(
|
|
||||||
status_code=500,
|
|
||||||
content=ErrorResponse(
|
|
||||||
error="Internal server error",
|
|
||||||
detail=str(exc),
|
|
||||||
timestamp=datetime.now().isoformat()
|
|
||||||
).model_dump()
|
|
||||||
)
|
|
||||||
|
|
||||||
async def cleanup_old_responses():
|
|
||||||
"""
|
|
||||||
Periodically cleans up old response files:
|
|
||||||
1. Deletes files older than 1 week.
|
|
||||||
2. For remaining files, keeps only the most recent one for each RUT.
|
|
||||||
Runs every 10 minutes.
|
|
||||||
"""
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
logger.info("Starting cleanup of old Sheriff V2 responses...")
|
|
||||||
if not SHERIFF_RESPONSES_DIR.exists():
|
|
||||||
logger.info(f"Directory {SHERIFF_RESPONSES_DIR} does not exist. Skipping cleanup.")
|
|
||||||
else:
|
|
||||||
files_by_group: Dict[str, list] = {}
|
|
||||||
one_week_ago = datetime.utcnow() - timedelta(weeks=1)
|
|
||||||
deleted_count = 0
|
|
||||||
|
|
||||||
for file_path in SHERIFF_RESPONSES_DIR.glob("*.json"):
|
|
||||||
try:
|
|
||||||
filename = file_path.stem
|
|
||||||
# Format: {rut}_{timestamp}
|
|
||||||
parts = filename.rsplit('_', 1)
|
|
||||||
if len(parts) != 2:
|
|
||||||
continue
|
|
||||||
|
|
||||||
rut, timestamp_str = parts
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Parse timestamp: YYYYMMDDTHHMMSSZ
|
|
||||||
file_dt = datetime.strptime(timestamp_str, "%Y%m%dT%H%M%SZ")
|
|
||||||
except ValueError:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Check if older than 1 week
|
|
||||||
if file_dt < one_week_ago:
|
|
||||||
try:
|
|
||||||
file_path.unlink()
|
|
||||||
deleted_count += 1
|
|
||||||
logger.info(f"Deleted old response file (older than 1 week): {file_path.name}")
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Failed to delete {file_path}: {e}")
|
|
||||||
continue
|
|
||||||
|
|
||||||
if rut not in files_by_group:
|
|
||||||
files_by_group[rut] = []
|
|
||||||
|
|
||||||
files_by_group[rut].append((file_dt, file_path))
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning(f"Error processing file {file_path}: {e}")
|
|
||||||
|
|
||||||
# Process groups
|
|
||||||
for rut, file_list in files_by_group.items():
|
|
||||||
# Sort by timestamp descending (newest first)
|
|
||||||
file_list.sort(key=lambda x: x[0], reverse=True)
|
|
||||||
|
|
||||||
# Keep the first one, delete the rest
|
|
||||||
for _, file_to_delete in file_list[1:]:
|
|
||||||
try:
|
|
||||||
file_to_delete.unlink()
|
|
||||||
deleted_count += 1
|
|
||||||
logger.info(f"Deleted old response file (duplicate): {file_to_delete.name}")
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Failed to delete {file_to_delete}: {e}")
|
|
||||||
|
|
||||||
if deleted_count > 0:
|
|
||||||
logger.info(f"Cleanup finished. Deleted {deleted_count} old files.")
|
|
||||||
else:
|
|
||||||
logger.info("Cleanup finished. No files to delete.")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Error during cleanup: {e}")
|
|
||||||
|
|
||||||
# Wait for 10 minutes (600 seconds)
|
|
||||||
await asyncio.sleep(600)
|
|
||||||
|
|
||||||
@app.on_event("startup")
|
|
||||||
async def startup_event():
|
|
||||||
asyncio.create_task(cleanup_old_responses())
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
import uvicorn
|
|
||||||
uvicorn.run(app, host="0.0.0.0", port=8024)
|
|
||||||
class QueryRutRequest(BaseModel):
|
|
||||||
rut: str
|
|
||||||
isMonitoring: bool = False
|
|
||||||
includeEquifax: bool = False
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
pymongo==4.6.0
|
|
||||||
fastapi==0.104.1
|
|
||||||
uvicorn[standard]==0.24.0
|
|
||||||
pydantic==2.5.0
|
|
||||||
requests==2.31.0
|
|
||||||
python-dotenv==1.0.0
|
|
||||||
python-multipart==0.0.6
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
fastapi==0.104.1
|
|
||||||
uvicorn[standard]==0.24.0
|
|
||||||
pydantic==2.5.0
|
|
||||||
requests==2.31.0
|
|
||||||
python-dotenv==1.0.0
|
|
||||||
python-multipart==0.0.6
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
venv/bin/python main.py
|
.venv/bin/python main.py
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
fastapi==0.104.1
|
|
||||||
uvicorn[standard]==0.24.0
|
|
||||||
pydantic==2.8.0
|
|
||||||
requests==2.31.0
|
|
||||||
python-dotenv==1.0.0
|
|
||||||
python-multipart==0.0.6
|
|
||||||
29
server/external_providers/sheriff_v2/setup_venv.sh
Executable file
29
server/external_providers/sheriff_v2/setup_venv.sh
Executable file
|
|
@ -0,0 +1,29 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Check if the .venv directory exists
|
||||||
|
if [ ! -d ".venv" ]; then
|
||||||
|
echo ".venv directory does not exist. Creating a virtual environment..."
|
||||||
|
|
||||||
|
# Create a virtual environment
|
||||||
|
python3 -m venv .venv
|
||||||
|
|
||||||
|
echo "Virtual environment created."
|
||||||
|
else
|
||||||
|
echo ".venv directory already exists."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Activate the virtual environment
|
||||||
|
source .venv/bin/activate
|
||||||
|
|
||||||
|
# Upgrade pip (optional)
|
||||||
|
pip install --upgrade pip
|
||||||
|
|
||||||
|
# Install requirements from requirements.txt
|
||||||
|
if [ -f "requirements.txt" ]; then
|
||||||
|
echo "Installing requirements from requirements.txt..."
|
||||||
|
pip install -r requirements.txt
|
||||||
|
else
|
||||||
|
echo "No requirements.txt file found."
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Setup complete."
|
||||||
Loading…
Reference in New Issue
Block a user