@qubiit/lmagent 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.editorconfig +18 -0
- package/AGENTS.md +169 -0
- package/CLAUDE.md +122 -0
- package/CONTRIBUTING.md +90 -0
- package/LICENSE +21 -0
- package/README.md +195 -0
- package/config/commands.yaml +194 -0
- package/config/levels.yaml +135 -0
- package/config/models.yaml +192 -0
- package/config/settings.yaml +405 -0
- package/config/tools-extended.yaml +534 -0
- package/config/tools.yaml +437 -0
- package/docs/assets/logo.png +0 -0
- package/docs/commands.md +132 -0
- package/docs/customization-guide.md +445 -0
- package/docs/getting-started.md +154 -0
- package/docs/how-to-start.md +242 -0
- package/docs/navigation-index.md +227 -0
- package/docs/usage-guide.md +113 -0
- package/install.js +1044 -0
- package/package.json +35 -0
- package/pyproject.toml +182 -0
- package/rules/_bootstrap.md +138 -0
- package/rules/agents-ia.md +607 -0
- package/rules/api-design.md +337 -0
- package/rules/automations-n8n.md +646 -0
- package/rules/code-style.md +570 -0
- package/rules/documentation.md +98 -0
- package/rules/security.md +316 -0
- package/rules/stack.md +395 -0
- package/rules/testing.md +326 -0
- package/rules/workflow.md +353 -0
- package/scripts/create_skill.js +300 -0
- package/scripts/validate_skills.js +283 -0
- package/skills/ai-agent-engineer/SKILL.md +394 -0
- package/skills/ai-agent-engineer/references/agent-patterns.md +149 -0
- package/skills/api-designer/SKILL.md +429 -0
- package/skills/api-designer/references/api-standards.md +13 -0
- package/skills/architect/SKILL.md +285 -0
- package/skills/architect/references/c4-model.md +133 -0
- package/skills/automation-engineer/SKILL.md +352 -0
- package/skills/automation-engineer/references/n8n-patterns.md +127 -0
- package/skills/backend-engineer/SKILL.md +261 -0
- package/skills/backend-engineer/assets/fastapi-project-structure.yaml +74 -0
- package/skills/backend-engineer/references/debugging-guide.md +174 -0
- package/skills/backend-engineer/references/design-patterns.md +208 -0
- package/skills/backend-engineer/scripts/scaffold_backend.py +313 -0
- package/skills/bmad-methodology/SKILL.md +202 -0
- package/skills/bmad-methodology/references/scale-adaptive-levels.md +141 -0
- package/skills/browser-agent/SKILL.md +502 -0
- package/skills/browser-agent/scripts/playwright_setup.ts +16 -0
- package/skills/code-reviewer/SKILL.md +306 -0
- package/skills/code-reviewer/references/code-review-checklist.md +16 -0
- package/skills/data-engineer/SKILL.md +474 -0
- package/skills/data-engineer/assets/pg-monitoring-queries.sql +154 -0
- package/skills/data-engineer/references/index-strategy.md +128 -0
- package/skills/data-engineer/scripts/backup_postgres.py +221 -0
- package/skills/devops-engineer/SKILL.md +547 -0
- package/skills/devops-engineer/references/ci-cd-patterns.md +265 -0
- package/skills/devops-engineer/scripts/docker_healthcheck.py +125 -0
- package/skills/document-generator/SKILL.md +746 -0
- package/skills/document-generator/references/pdf-generation.md +22 -0
- package/skills/frontend-engineer/SKILL.md +532 -0
- package/skills/frontend-engineer/references/accessibility-guide.md +146 -0
- package/skills/frontend-engineer/scripts/audit_bundle.py +144 -0
- package/skills/git-workflow/SKILL.md +374 -0
- package/skills/git-workflow/references/git-flow.md +25 -0
- package/skills/mcp-builder/SKILL.md +471 -0
- package/skills/mcp-builder/references/mcp-server-guide.md +23 -0
- package/skills/mobile-engineer/SKILL.md +502 -0
- package/skills/mobile-engineer/references/platform-guidelines.md +160 -0
- package/skills/orchestrator/SKILL.md +246 -0
- package/skills/orchestrator/references/methodology-routing.md +117 -0
- package/skills/orchestrator/references/persona-mapping.md +85 -0
- package/skills/orchestrator/references/routing-logic.md +110 -0
- package/skills/performance-engineer/SKILL.md +549 -0
- package/skills/performance-engineer/references/caching-patterns.md +181 -0
- package/skills/performance-engineer/scripts/profile_endpoint.py +170 -0
- package/skills/product-manager/SKILL.md +488 -0
- package/skills/product-manager/references/prioritization-frameworks.md +126 -0
- package/skills/prompt-engineer/SKILL.md +433 -0
- package/skills/prompt-engineer/references/prompt-patterns.md +158 -0
- package/skills/qa-engineer/SKILL.md +441 -0
- package/skills/qa-engineer/references/testing-strategy.md +166 -0
- package/skills/qa-engineer/scripts/run_coverage.py +147 -0
- package/skills/scrum-master/SKILL.md +225 -0
- package/skills/scrum-master/references/sprint-ceremonies.md +159 -0
- package/skills/security-analyst/SKILL.md +390 -0
- package/skills/security-analyst/references/owasp-top10.md +188 -0
- package/skills/security-analyst/scripts/audit_security.py +242 -0
- package/skills/seo-auditor/SKILL.md +523 -0
- package/skills/seo-auditor/references/seo-checklist.md +17 -0
- package/skills/spec-driven-dev/SKILL.md +342 -0
- package/skills/spec-driven-dev/references/phase-gates.md +107 -0
- package/skills/supabase-expert/SKILL.md +602 -0
- package/skills/supabase-expert/references/supabase-patterns.md +19 -0
- package/skills/swe-agent/SKILL.md +311 -0
- package/skills/swe-agent/references/trajectory-format.md +134 -0
- package/skills/systematic-debugger/SKILL.md +512 -0
- package/skills/systematic-debugger/references/debugging-guide.md +12 -0
- package/skills/tech-lead/SKILL.md +409 -0
- package/skills/tech-lead/references/code-review-checklist.md +111 -0
- package/skills/technical-writer/SKILL.md +631 -0
- package/skills/technical-writer/references/doc-templates.md +218 -0
- package/skills/testing-strategist/SKILL.md +476 -0
- package/skills/testing-strategist/references/testing-pyramid.md +16 -0
- package/skills/ux-ui-designer/SKILL.md +419 -0
- package/skills/ux-ui-designer/references/design-system-foundation.md +168 -0
- package/skills_overview.txt +94 -0
- package/templates/PROJECT_KICKOFF.md +284 -0
- package/templates/SKILL_TEMPLATE.md +131 -0
- package/templates/USAGE.md +95 -0
- package/templates/agent-python/README.md +71 -0
- package/templates/agent-python/agent.py +272 -0
- package/templates/agent-python/config.yaml +76 -0
- package/templates/agent-python/prompts/system.md +109 -0
- package/templates/agent-python/requirements.txt +7 -0
- package/templates/automation-n8n/README.md +14 -0
- package/templates/automation-n8n/webhook-handler.json +57 -0
- package/templates/backend-node/Dockerfile +12 -0
- package/templates/backend-node/README.md +15 -0
- package/templates/backend-node/package.json +30 -0
- package/templates/backend-node/src/index.ts +19 -0
- package/templates/backend-node/src/routes.ts +7 -0
- package/templates/backend-node/tsconfig.json +22 -0
- package/templates/backend-python/Dockerfile +11 -0
- package/templates/backend-python/README.md +78 -0
- package/templates/backend-python/app/core/config.py +12 -0
- package/templates/backend-python/app/core/database.py +12 -0
- package/templates/backend-python/app/main.py +17 -0
- package/templates/backend-python/app/routers/__init__.py +1 -0
- package/templates/backend-python/app/routers/health.py +7 -0
- package/templates/backend-python/requirements-dev.txt +6 -0
- package/templates/backend-python/requirements.txt +4 -0
- package/templates/backend-python/tests/test_health.py +9 -0
- package/templates/checkpoint.yaml +117 -0
- package/templates/database/README.md +474 -0
- package/templates/frontend-react/README.md +446 -0
- package/templates/plan.yaml +320 -0
- package/templates/session.yaml +125 -0
- package/templates/spec.yaml +229 -0
- package/templates/tasks.yaml +330 -0
- package/workflows/bugfix-backend.md +380 -0
- package/workflows/documentation.md +232 -0
- package/workflows/generate-prd.md +320 -0
- package/workflows/ideation.md +396 -0
- package/workflows/new-agent-ia.md +497 -0
- package/workflows/new-automation.md +374 -0
- package/workflows/new-feature.md +290 -0
- package/workflows/optimize-performance.md +373 -0
- package/workflows/resolve-github-issue.md +524 -0
- package/workflows/security-review.md +291 -0
- package/workflows/spec-driven.md +476 -0
- package/workflows/testing-strategy.md +296 -0
- package/workflows/third-party-integration.md +277 -0
|
@@ -0,0 +1,646 @@
|
|
|
1
|
+
# Reglas para Integraciones con n8n - LMAgent
|
|
2
|
+
|
|
3
|
+
> **Tipo**: `rule` | **Versión**: 2.1 | **Actualización**: 2026-01
|
|
4
|
+
|
|
5
|
+
## 📌 Quick Reference
|
|
6
|
+
|
|
7
|
+
| Principio | Regla |
|
|
8
|
+
|-----------|-------|
|
|
9
|
+
| **n8n-First** | Diseñar APIs pensando en cómo n8n las consumirá. |
|
|
10
|
+
| **Respuesta < 60s** | Procesos largos en background + polling o callback. |
|
|
11
|
+
| **Payloads Estables** | Nunca cambiar estructura sin versionar la API. |
|
|
12
|
+
| **Errores Útiles** | `{ success, error, error_code }` para que n8n procese. |
|
|
13
|
+
| **Idempotencia** | Re-ejecutar el mismo webhook debe dar el mismo resultado. |
|
|
14
|
+
|
|
15
|
+
### 👥 Roles que usan esta regla
|
|
16
|
+
`automation-engineer`, `backend-engineer`, `architect`
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
Este documento define las mejores prácticas para diseñar APIs y webhooks pensando en n8n.
|
|
21
|
+
|
|
22
|
+
## Principios de Diseño
|
|
23
|
+
|
|
24
|
+
### 1. n8n-First API Design
|
|
25
|
+
Cuando diseñes endpoints que serán consumidos por n8n:
|
|
26
|
+
|
|
27
|
+
- **Respuestas predecibles**: Siempre la misma estructura
|
|
28
|
+
- **Payloads estables**: No cambiar campos sin versionar
|
|
29
|
+
- **Documentación clara**: Ejemplos de JSON in/out
|
|
30
|
+
- **Errores útiles**: Mensajes que n8n pueda procesar
|
|
31
|
+
|
|
32
|
+
### 2. Arquitectura de Integración
|
|
33
|
+
```
|
|
34
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
35
|
+
│ n8n Workflow │
|
|
36
|
+
└───────┬───────────────────────────────────────┬─────────────────┘
|
|
37
|
+
│ │
|
|
38
|
+
▼ ▼
|
|
39
|
+
┌───────────────┐ ┌───────────────┐
|
|
40
|
+
│ Webhook │ │ HTTP Request │
|
|
41
|
+
│ Trigger │ │ Node │
|
|
42
|
+
└───────┬───────┘ └───────┬───────┘
|
|
43
|
+
│ │
|
|
44
|
+
▼ ▼
|
|
45
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
46
|
+
│ Backend API (FastAPI/Express) │
|
|
47
|
+
│ │
|
|
48
|
+
│ POST /webhooks/event-name GET /api/v1/resource │
|
|
49
|
+
│ - Recibe eventos de n8n - Devuelve datos para n8n │
|
|
50
|
+
│ - Procesa en background - Paginación, filtros │
|
|
51
|
+
│ - Retorna confirmación - Formato consistente │
|
|
52
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Diseño de Webhooks
|
|
58
|
+
|
|
59
|
+
### Estructura de Endpoint
|
|
60
|
+
```python
|
|
61
|
+
from fastapi import APIRouter, BackgroundTasks, HTTPException
|
|
62
|
+
from pydantic import BaseModel, Field
|
|
63
|
+
from datetime import datetime
|
|
64
|
+
from uuid import uuid4
|
|
65
|
+
|
|
66
|
+
router = APIRouter(prefix="/webhooks", tags=["webhooks"])
|
|
67
|
+
|
|
68
|
+
# =====================================
|
|
69
|
+
# Schemas Estándar
|
|
70
|
+
# =====================================
|
|
71
|
+
|
|
72
|
+
class WebhookPayload(BaseModel):
|
|
73
|
+
"""Payload estándar que n8n debe enviar."""
|
|
74
|
+
event_type: str = Field(..., description="Tipo de evento, ej: order.created")
|
|
75
|
+
timestamp: datetime = Field(default_factory=datetime.utcnow)
|
|
76
|
+
data: dict = Field(..., description="Datos del evento")
|
|
77
|
+
metadata: dict = Field(default_factory=dict, description="Metadatos opcionales")
|
|
78
|
+
|
|
79
|
+
model_config = {
|
|
80
|
+
"json_schema_extra": {
|
|
81
|
+
"example": {
|
|
82
|
+
"event_type": "order.created",
|
|
83
|
+
"timestamp": "2024-01-15T10:30:00Z",
|
|
84
|
+
"data": {
|
|
85
|
+
"order_id": "12345",
|
|
86
|
+
"customer_email": "customer@example.com",
|
|
87
|
+
"total": 99.99
|
|
88
|
+
},
|
|
89
|
+
"metadata": {
|
|
90
|
+
"source": "n8n",
|
|
91
|
+
"workflow_id": "abc123"
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
class WebhookResponse(BaseModel):
|
|
98
|
+
"""Respuesta estándar para n8n."""
|
|
99
|
+
success: bool
|
|
100
|
+
message: str
|
|
101
|
+
request_id: str = Field(description="ID único para tracking")
|
|
102
|
+
processed_at: datetime
|
|
103
|
+
|
|
104
|
+
model_config = {
|
|
105
|
+
"json_schema_extra": {
|
|
106
|
+
"example": {
|
|
107
|
+
"success": True,
|
|
108
|
+
"message": "Event queued for processing",
|
|
109
|
+
"request_id": "550e8400-e29b-41d4-a716-446655440000",
|
|
110
|
+
"processed_at": "2024-01-15T10:30:01Z"
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
class WebhookError(BaseModel):
|
|
116
|
+
"""Error estándar para n8n."""
|
|
117
|
+
success: bool = False
|
|
118
|
+
error: str
|
|
119
|
+
error_code: str
|
|
120
|
+
details: dict = {}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Implementación de Webhook
|
|
124
|
+
```python
|
|
125
|
+
@router.post(
|
|
126
|
+
"/order-created",
|
|
127
|
+
response_model=WebhookResponse,
|
|
128
|
+
responses={
|
|
129
|
+
400: {"model": WebhookError, "description": "Invalid payload"},
|
|
130
|
+
500: {"model": WebhookError, "description": "Processing error"}
|
|
131
|
+
}
|
|
132
|
+
)
|
|
133
|
+
async def handle_order_created(
|
|
134
|
+
payload: WebhookPayload,
|
|
135
|
+
background_tasks: BackgroundTasks
|
|
136
|
+
) -> WebhookResponse:
|
|
137
|
+
"""
|
|
138
|
+
Webhook para procesar órdenes creadas.
|
|
139
|
+
|
|
140
|
+
## Uso en n8n
|
|
141
|
+
|
|
142
|
+
1. Agregar nodo **HTTP Request**
|
|
143
|
+
2. **Method**: POST
|
|
144
|
+
3. **URL**: `{{$env.BACKEND_URL}}/webhooks/order-created`
|
|
145
|
+
4. **Headers**:
|
|
146
|
+
- Content-Type: application/json
|
|
147
|
+
- X-API-Key: {{$env.API_KEY}} (si se requiere auth)
|
|
148
|
+
5. **Body**: JSON
|
|
149
|
+
|
|
150
|
+
## Ejemplo de Body
|
|
151
|
+
```json
|
|
152
|
+
{
|
|
153
|
+
"event_type": "order.created",
|
|
154
|
+
"data": {
|
|
155
|
+
"order_id": "{{ $json.order_id }}",
|
|
156
|
+
"customer_email": "{{ $json.email }}",
|
|
157
|
+
"total": {{ $json.total }}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Flujo de Procesamiento
|
|
163
|
+
|
|
164
|
+
1. Se valida el payload
|
|
165
|
+
2. Se encola para procesamiento async
|
|
166
|
+
3. Se retorna inmediatamente con request_id
|
|
167
|
+
4. El procesamiento ocurre en background
|
|
168
|
+
|
|
169
|
+
## Para verificar estado
|
|
170
|
+
|
|
171
|
+
GET /webhooks/status/{request_id}
|
|
172
|
+
"""
|
|
173
|
+
request_id = str(uuid4())
|
|
174
|
+
|
|
175
|
+
# Validar evento
|
|
176
|
+
if payload.event_type != "order.created":
|
|
177
|
+
raise HTTPException(
|
|
178
|
+
status_code=400,
|
|
179
|
+
detail={
|
|
180
|
+
"success": False,
|
|
181
|
+
"error": f"Expected event_type 'order.created', got '{payload.event_type}'",
|
|
182
|
+
"error_code": "INVALID_EVENT_TYPE"
|
|
183
|
+
}
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
# Procesar en background para respuesta rápida (< 3s para n8n)
|
|
187
|
+
background_tasks.add_task(
|
|
188
|
+
process_order_async,
|
|
189
|
+
request_id=request_id,
|
|
190
|
+
order_data=payload.data
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
return WebhookResponse(
|
|
194
|
+
success=True,
|
|
195
|
+
message="Order queued for processing",
|
|
196
|
+
request_id=request_id,
|
|
197
|
+
processed_at=datetime.utcnow()
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
@router.get("/status/{request_id}")
|
|
202
|
+
async def get_webhook_status(request_id: str) -> dict:
|
|
203
|
+
"""
|
|
204
|
+
Obtiene el estado de un webhook procesado.
|
|
205
|
+
|
|
206
|
+
Útil para workflows de n8n que necesitan polling.
|
|
207
|
+
"""
|
|
208
|
+
status = await get_processing_status(request_id)
|
|
209
|
+
|
|
210
|
+
return {
|
|
211
|
+
"request_id": request_id,
|
|
212
|
+
"status": status.state, # pending, processing, completed, failed
|
|
213
|
+
"result": status.result,
|
|
214
|
+
"error": status.error,
|
|
215
|
+
"completed_at": status.completed_at
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Diseño de APIs para n8n
|
|
222
|
+
|
|
223
|
+
### Respuestas Consistentes
|
|
224
|
+
```python
|
|
225
|
+
from typing import Generic, TypeVar, List, Optional
|
|
226
|
+
from pydantic import BaseModel
|
|
227
|
+
|
|
228
|
+
T = TypeVar('T')
|
|
229
|
+
|
|
230
|
+
class PaginatedResponse(BaseModel, Generic[T]):
|
|
231
|
+
"""Respuesta paginada estándar para n8n."""
|
|
232
|
+
items: List[T]
|
|
233
|
+
total: int
|
|
234
|
+
page: int
|
|
235
|
+
page_size: int
|
|
236
|
+
has_more: bool
|
|
237
|
+
|
|
238
|
+
class APIResponse(BaseModel, Generic[T]):
|
|
239
|
+
"""Respuesta API estándar."""
|
|
240
|
+
success: bool = True
|
|
241
|
+
data: T
|
|
242
|
+
message: Optional[str] = None
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
# Uso
|
|
246
|
+
@router.get("/orders", response_model=APIResponse[PaginatedResponse[Order]])
|
|
247
|
+
async def list_orders(
|
|
248
|
+
page: int = 1,
|
|
249
|
+
page_size: int = 20,
|
|
250
|
+
status: Optional[str] = None,
|
|
251
|
+
created_after: Optional[datetime] = None
|
|
252
|
+
) -> APIResponse[PaginatedResponse[Order]]:
|
|
253
|
+
"""
|
|
254
|
+
Lista órdenes con paginación.
|
|
255
|
+
|
|
256
|
+
## Uso en n8n
|
|
257
|
+
|
|
258
|
+
**HTTP Request Node**:
|
|
259
|
+
- Method: GET
|
|
260
|
+
- URL: {{$env.BACKEND_URL}}/api/v1/orders
|
|
261
|
+
- Query Parameters:
|
|
262
|
+
- page: {{$json.page || 1}}
|
|
263
|
+
- page_size: 50
|
|
264
|
+
- status: pending
|
|
265
|
+
|
|
266
|
+
**Para iterar sobre resultados**:
|
|
267
|
+
Usa el nodo "Split In Batches" después del HTTP Request.
|
|
268
|
+
Accede a items con: {{$json.data.items}}
|
|
269
|
+
"""
|
|
270
|
+
orders, total = await order_service.list(
|
|
271
|
+
page=page,
|
|
272
|
+
page_size=page_size,
|
|
273
|
+
status=status,
|
|
274
|
+
created_after=created_after
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
return APIResponse(
|
|
278
|
+
success=True,
|
|
279
|
+
data=PaginatedResponse(
|
|
280
|
+
items=orders,
|
|
281
|
+
total=total,
|
|
282
|
+
page=page,
|
|
283
|
+
page_size=page_size,
|
|
284
|
+
has_more=(page * page_size) < total
|
|
285
|
+
)
|
|
286
|
+
)
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Filtros Flexibles
|
|
290
|
+
```python
|
|
291
|
+
from enum import Enum
|
|
292
|
+
from typing import Optional
|
|
293
|
+
|
|
294
|
+
class OrderStatus(str, Enum):
|
|
295
|
+
PENDING = "pending"
|
|
296
|
+
PROCESSING = "processing"
|
|
297
|
+
COMPLETED = "completed"
|
|
298
|
+
CANCELLED = "cancelled"
|
|
299
|
+
|
|
300
|
+
@router.get("/orders/search")
|
|
301
|
+
async def search_orders(
|
|
302
|
+
# Filtros de igualdad
|
|
303
|
+
status: Optional[OrderStatus] = None,
|
|
304
|
+
customer_id: Optional[int] = None,
|
|
305
|
+
|
|
306
|
+
# Filtros de rango
|
|
307
|
+
min_total: Optional[float] = None,
|
|
308
|
+
max_total: Optional[float] = None,
|
|
309
|
+
created_after: Optional[datetime] = None,
|
|
310
|
+
created_before: Optional[datetime] = None,
|
|
311
|
+
|
|
312
|
+
# Búsqueda de texto
|
|
313
|
+
search: Optional[str] = None,
|
|
314
|
+
|
|
315
|
+
# Paginación
|
|
316
|
+
page: int = 1,
|
|
317
|
+
page_size: int = 20,
|
|
318
|
+
|
|
319
|
+
# Ordenamiento
|
|
320
|
+
sort_by: str = "created_at",
|
|
321
|
+
sort_order: str = "desc"
|
|
322
|
+
):
|
|
323
|
+
"""
|
|
324
|
+
Búsqueda avanzada de órdenes.
|
|
325
|
+
|
|
326
|
+
## Ejemplos de uso en n8n
|
|
327
|
+
|
|
328
|
+
**Órdenes pendientes de hoy**:
|
|
329
|
+
```
|
|
330
|
+
/orders/search?status=pending&created_after={{$today}}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
**Órdenes mayores a $100**:
|
|
334
|
+
```
|
|
335
|
+
/orders/search?min_total=100&sort_by=total&sort_order=desc
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
**Buscar por email**:
|
|
339
|
+
```
|
|
340
|
+
/orders/search?search=customer@example.com
|
|
341
|
+
```
|
|
342
|
+
"""
|
|
343
|
+
pass
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## Documentación para n8n
|
|
349
|
+
|
|
350
|
+
### Template de Documentación de Endpoint
|
|
351
|
+
```markdown
|
|
352
|
+
# [Nombre del Endpoint]
|
|
353
|
+
|
|
354
|
+
## Descripción
|
|
355
|
+
[Qué hace este endpoint]
|
|
356
|
+
|
|
357
|
+
## HTTP Request
|
|
358
|
+
|
|
359
|
+
| Propiedad | Valor |
|
|
360
|
+
|-----------|-------|
|
|
361
|
+
| Method | POST/GET/PUT/DELETE |
|
|
362
|
+
| URL | `{{$env.BACKEND_URL}}/path/to/endpoint` |
|
|
363
|
+
| Content-Type | application/json |
|
|
364
|
+
| Auth | API Key / Bearer Token / None |
|
|
365
|
+
|
|
366
|
+
## Headers
|
|
367
|
+
|
|
368
|
+
| Header | Valor | Requerido |
|
|
369
|
+
|--------|-------|-----------|
|
|
370
|
+
| Content-Type | application/json | Sí |
|
|
371
|
+
| X-API-Key | {{$env.API_KEY}} | Sí |
|
|
372
|
+
|
|
373
|
+
## Request Body (para POST/PUT)
|
|
374
|
+
|
|
375
|
+
```json
|
|
376
|
+
{
|
|
377
|
+
"field1": "value",
|
|
378
|
+
"field2": 123,
|
|
379
|
+
"nested": {
|
|
380
|
+
"subfield": "value"
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
| Campo | Tipo | Requerido | Descripción |
|
|
386
|
+
|-------|------|-----------|-------------|
|
|
387
|
+
| field1 | string | Sí | Descripción |
|
|
388
|
+
| field2 | number | No | Descripción |
|
|
389
|
+
|
|
390
|
+
## Response
|
|
391
|
+
|
|
392
|
+
### Success (200)
|
|
393
|
+
```json
|
|
394
|
+
{
|
|
395
|
+
"success": true,
|
|
396
|
+
"data": {
|
|
397
|
+
"id": "123",
|
|
398
|
+
"result": "..."
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### Error (400/500)
|
|
404
|
+
```json
|
|
405
|
+
{
|
|
406
|
+
"success": false,
|
|
407
|
+
"error": "Error message",
|
|
408
|
+
"error_code": "ERROR_CODE"
|
|
409
|
+
}
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
## Ejemplo en n8n
|
|
413
|
+
|
|
414
|
+
![Screenshot del nodo configurado]
|
|
415
|
+
|
|
416
|
+
### Configuración del nodo HTTP Request:
|
|
417
|
+
1. Agregar nodo "HTTP Request"
|
|
418
|
+
2. Method: POST
|
|
419
|
+
3. URL: `{{$env.BACKEND_URL}}/path`
|
|
420
|
+
4. Authentication: Header Auth
|
|
421
|
+
5. Header Name: X-API-Key
|
|
422
|
+
6. Header Value: {{$env.API_KEY}}
|
|
423
|
+
7. Body Content Type: JSON
|
|
424
|
+
8. JSON Parameters:
|
|
425
|
+
```
|
|
426
|
+
{
|
|
427
|
+
"field1": "{{$json.value}}"
|
|
428
|
+
}
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
## Casos de Uso
|
|
432
|
+
|
|
433
|
+
1. **[Caso 1]**: [Descripción del workflow]
|
|
434
|
+
2. **[Caso 2]**: [Descripción del workflow]
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
---
|
|
438
|
+
|
|
439
|
+
## Patrones de Integración
|
|
440
|
+
|
|
441
|
+
### 1. Webhook con Callback
|
|
442
|
+
```
|
|
443
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
444
|
+
│ n8n Workflow │
|
|
445
|
+
│ │
|
|
446
|
+
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌───────────┐ │
|
|
447
|
+
│ │ Trigger │───▶│ HTTP Req │───▶│ Wait │───▶│ Callback │ │
|
|
448
|
+
│ │ │ │ (async) │ │ │ │ Handler │ │
|
|
449
|
+
│ └──────────┘ └──────────┘ └──────────┘ └───────────┘ │
|
|
450
|
+
└──────────────────────┬───────────────────────────────┬─────────┘
|
|
451
|
+
│ │
|
|
452
|
+
▼ │
|
|
453
|
+
┌─────────────────────────────────┐ │
|
|
454
|
+
│ Backend │ │
|
|
455
|
+
│ │ │
|
|
456
|
+
│ POST /webhooks/process │ │
|
|
457
|
+
│ - Recibe request │ │
|
|
458
|
+
│ - Inicia proceso largo │ │
|
|
459
|
+
│ - Retorna request_id │ │
|
|
460
|
+
│ │ │
|
|
461
|
+
│ (proceso async...) │ │
|
|
462
|
+
│ │ │
|
|
463
|
+
│ POST callback_url │◀──────────────────┘
|
|
464
|
+
│ - Envía resultado a n8n │
|
|
465
|
+
└─────────────────────────────────┘
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
```python
|
|
469
|
+
@router.post("/process-with-callback")
|
|
470
|
+
async def process_with_callback(
|
|
471
|
+
payload: WebhookPayload,
|
|
472
|
+
callback_url: str,
|
|
473
|
+
background_tasks: BackgroundTasks
|
|
474
|
+
):
|
|
475
|
+
"""
|
|
476
|
+
Procesa y notifica a n8n cuando termina.
|
|
477
|
+
|
|
478
|
+
En n8n usar "Wait" node después de este request.
|
|
479
|
+
"""
|
|
480
|
+
request_id = str(uuid4())
|
|
481
|
+
|
|
482
|
+
# Procesar en background y hacer callback
|
|
483
|
+
background_tasks.add_task(
|
|
484
|
+
process_and_callback,
|
|
485
|
+
request_id=request_id,
|
|
486
|
+
data=payload.data,
|
|
487
|
+
callback_url=callback_url
|
|
488
|
+
)
|
|
489
|
+
|
|
490
|
+
return {"request_id": request_id, "status": "processing"}
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
async def process_and_callback(
|
|
494
|
+
request_id: str,
|
|
495
|
+
data: dict,
|
|
496
|
+
callback_url: str
|
|
497
|
+
):
|
|
498
|
+
"""Procesa y notifica resultado."""
|
|
499
|
+
try:
|
|
500
|
+
result = await heavy_processing(data)
|
|
501
|
+
|
|
502
|
+
async with httpx.AsyncClient() as client:
|
|
503
|
+
await client.post(callback_url, json={
|
|
504
|
+
"request_id": request_id,
|
|
505
|
+
"success": True,
|
|
506
|
+
"result": result
|
|
507
|
+
})
|
|
508
|
+
except Exception as e:
|
|
509
|
+
async with httpx.AsyncClient() as client:
|
|
510
|
+
await client.post(callback_url, json={
|
|
511
|
+
"request_id": request_id,
|
|
512
|
+
"success": False,
|
|
513
|
+
"error": str(e)
|
|
514
|
+
})
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
### 2. Polling Pattern
|
|
518
|
+
```python
|
|
519
|
+
@router.get("/jobs/{job_id}")
|
|
520
|
+
async def get_job_status(job_id: str):
|
|
521
|
+
"""
|
|
522
|
+
Para n8n, usar con "Wait" + "IF" + loop.
|
|
523
|
+
|
|
524
|
+
## Workflow en n8n
|
|
525
|
+
|
|
526
|
+
1. HTTP Request: POST /jobs (crear job)
|
|
527
|
+
2. Loop Start
|
|
528
|
+
3. Wait: 5 seconds
|
|
529
|
+
4. HTTP Request: GET /jobs/{job_id}
|
|
530
|
+
5. IF: {{$json.status}} == "completed"
|
|
531
|
+
- True: Salir del loop
|
|
532
|
+
- False: Volver a paso 3
|
|
533
|
+
"""
|
|
534
|
+
job = await get_job(job_id)
|
|
535
|
+
|
|
536
|
+
return {
|
|
537
|
+
"job_id": job_id,
|
|
538
|
+
"status": job.status, # pending, running, completed, failed
|
|
539
|
+
"progress": job.progress, # 0-100
|
|
540
|
+
"result": job.result if job.status == "completed" else None,
|
|
541
|
+
"error": job.error if job.status == "failed" else None
|
|
542
|
+
}
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
### 3. Batch Processing
|
|
546
|
+
```python
|
|
547
|
+
@router.post("/batch/process")
|
|
548
|
+
async def batch_process(items: List[dict]):
|
|
549
|
+
"""
|
|
550
|
+
Procesa múltiples items en una sola llamada.
|
|
551
|
+
|
|
552
|
+
Útil para n8n cuando se procesan muchos items.
|
|
553
|
+
Más eficiente que llamar endpoint individual N veces.
|
|
554
|
+
"""
|
|
555
|
+
results = []
|
|
556
|
+
errors = []
|
|
557
|
+
|
|
558
|
+
for i, item in enumerate(items):
|
|
559
|
+
try:
|
|
560
|
+
result = await process_item(item)
|
|
561
|
+
results.append({"index": i, "success": True, "data": result})
|
|
562
|
+
except Exception as e:
|
|
563
|
+
errors.append({"index": i, "success": False, "error": str(e)})
|
|
564
|
+
|
|
565
|
+
return {
|
|
566
|
+
"total": len(items),
|
|
567
|
+
"successful": len(results),
|
|
568
|
+
"failed": len(errors),
|
|
569
|
+
"results": results,
|
|
570
|
+
"errors": errors
|
|
571
|
+
}
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
---
|
|
575
|
+
|
|
576
|
+
## Seguridad
|
|
577
|
+
|
|
578
|
+
### Autenticación para n8n
|
|
579
|
+
```python
|
|
580
|
+
from fastapi import Header, HTTPException
|
|
581
|
+
|
|
582
|
+
async def verify_api_key(x_api_key: str = Header(...)):
|
|
583
|
+
"""Verifica API key de n8n."""
|
|
584
|
+
if x_api_key != settings.N8N_API_KEY:
|
|
585
|
+
raise HTTPException(status_code=401, detail="Invalid API key")
|
|
586
|
+
return x_api_key
|
|
587
|
+
|
|
588
|
+
@router.post("/webhooks/secure-endpoint")
|
|
589
|
+
async def secure_endpoint(
|
|
590
|
+
payload: WebhookPayload,
|
|
591
|
+
_: str = Depends(verify_api_key)
|
|
592
|
+
):
|
|
593
|
+
"""Endpoint protegido con API key."""
|
|
594
|
+
pass
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
### Rate Limiting
|
|
598
|
+
```python
|
|
599
|
+
from slowapi import Limiter
|
|
600
|
+
from slowapi.util import get_remote_address
|
|
601
|
+
|
|
602
|
+
limiter = Limiter(key_func=get_remote_address)
|
|
603
|
+
|
|
604
|
+
@router.post("/webhooks/rate-limited")
|
|
605
|
+
@limiter.limit("100/minute")
|
|
606
|
+
async def rate_limited_endpoint(request: Request, payload: WebhookPayload):
|
|
607
|
+
"""Endpoint con rate limiting."""
|
|
608
|
+
pass
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
---
|
|
612
|
+
|
|
613
|
+
## Checklist de Integración
|
|
614
|
+
|
|
615
|
+
### Antes de crear endpoint para n8n
|
|
616
|
+
- [ ] Definir schema de request/response
|
|
617
|
+
- [ ] Documentar ejemplos de payload
|
|
618
|
+
- [ ] Implementar manejo de errores consistente
|
|
619
|
+
- [ ] Agregar logging apropiado
|
|
620
|
+
- [ ] Considerar timeout de n8n (< 60s para sync)
|
|
621
|
+
|
|
622
|
+
### Después de implementar
|
|
623
|
+
- [ ] Probar con n8n local
|
|
624
|
+
- [ ] Documentar en `automations/docs/`
|
|
625
|
+
- [ ] Crear workflow de ejemplo
|
|
626
|
+
- [ ] Agregar a colección de Postman/Insomnia
|
|
627
|
+
|
|
628
|
+
---
|
|
629
|
+
|
|
630
|
+
## ✅ Checklist de Validación (n8n Integration)
|
|
631
|
+
|
|
632
|
+
### Webhook/Endpoint
|
|
633
|
+
- [ ] Schema de request/response definido con Pydantic
|
|
634
|
+
- [ ] Ejemplos de payload documentados
|
|
635
|
+
- [ ] Errores retornan `{ success: false, error, error_code }`
|
|
636
|
+
- [ ] Respuesta en < 60 segundos (o usar async + callback)
|
|
637
|
+
|
|
638
|
+
### Seguridad
|
|
639
|
+
- [ ] API Key o auth header configurado
|
|
640
|
+
- [ ] Rate limiting implementado
|
|
641
|
+
- [ ] Validación de inputs
|
|
642
|
+
|
|
643
|
+
### Testing
|
|
644
|
+
- [ ] Probado con n8n local (HTTP Request node)
|
|
645
|
+
- [ ] Workflow de ejemplo creado
|
|
646
|
+
- [ ] Happy path + error cases testeados
|