1932 lines
88 KiB
JSON
1932 lines
88 KiB
JSON
{
|
|
"updatedAt": "2026-07-30T22:15:55.410Z",
|
|
"createdAt": "2026-07-29T13:08:05.444Z",
|
|
"id": "RaNkDfE2YCrZgCbx",
|
|
"name": "GLM Hub - Solicitudes de acceso y aprobación",
|
|
"description": null,
|
|
"active": true,
|
|
"isArchived": false,
|
|
"nodes": [
|
|
{
|
|
"parameters": {
|
|
"httpMethod": "POST",
|
|
"path": "glm-hub-solicitar-acceso",
|
|
"responseMode": "responseNode",
|
|
"options": {
|
|
"allowedOrigins": "*"
|
|
}
|
|
},
|
|
"id": "80c8fbc9-c126-447e-a2b3-b2c9ac6e9468",
|
|
"name": "Webhook - Solicitar acceso",
|
|
"type": "n8n-nodes-base.webhook",
|
|
"typeVersion": 2.1,
|
|
"position": [
|
|
-784,
|
|
208
|
|
],
|
|
"webhookId": "ad3e98b9-baf2-400f-90e7-dcd4c5c20457"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "const source = $json.body ?? $json;\nlet payload = source;\nif (typeof payload === 'string') {\n try { payload = JSON.parse(payload); } catch { payload = {}; }\n}\nconst accessToken = String(payload?.accessToken ?? '').trim();\nconst appId = String(payload?.appId ?? '').trim();\nconst jwtPattern = /^[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+$/;\nconst uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\nlet error = '';\nif (!jwtPattern.test(accessToken)) error = 'La sesión recibida no es válida.';\nelse if (!uuidPattern.test(appId)) error = 'Selecciona una aplicación válida.';\nreturn [{ json: { valid: !error, error, authorization: `Bearer ${accessToken}`, appId } }];"
|
|
},
|
|
"id": "7d5b3911-3db7-4023-a89b-ac690bc99101",
|
|
"name": "Validar solicitud de acceso",
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
-560,
|
|
208
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"conditions": {
|
|
"options": {
|
|
"caseSensitive": true,
|
|
"leftValue": "",
|
|
"typeValidation": "strict",
|
|
"version": 2
|
|
},
|
|
"conditions": [
|
|
{
|
|
"id": "b5933422-7722-4c98-b34b-262a20d3ac05",
|
|
"leftValue": "={{ $json.valid }}",
|
|
"rightValue": true,
|
|
"operator": {
|
|
"type": "boolean",
|
|
"operation": "true",
|
|
"singleValue": true
|
|
}
|
|
}
|
|
],
|
|
"combinator": "and"
|
|
},
|
|
"options": {}
|
|
},
|
|
"id": "3c1cd649-6e3a-4751-ba1f-e775e8040f1d",
|
|
"name": "¿Solicitud de acceso válida?",
|
|
"type": "n8n-nodes-base.if",
|
|
"typeVersion": 2.2,
|
|
"position": [
|
|
-336,
|
|
208
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"respondWith": "json",
|
|
"responseBody": "={{ { ok: false, error: $json.error } }}",
|
|
"options": {
|
|
"responseCode": 400,
|
|
"responseHeaders": {
|
|
"entries": [
|
|
{
|
|
"name": "Cache-Control",
|
|
"value": "no-store"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"id": "ff32cfc2-c5ed-4e10-8804-2209cd17ef69",
|
|
"name": "Responder solicitud inválida",
|
|
"type": "n8n-nodes-base.respondToWebhook",
|
|
"typeVersion": 1.4,
|
|
"position": [
|
|
-112,
|
|
368
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"method": "POST",
|
|
"url": "https://dbit.digitalcompass.agency/rest/v1/rpc/glm_hub_prepare_access_request",
|
|
"sendHeaders": true,
|
|
"headerParameters": {
|
|
"parameters": [
|
|
{
|
|
"name": "apikey",
|
|
"value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJhbm9uIiwKICAgICJpc3MiOiAic3VwYWJhc2UtZGVtbyIsCiAgICAiaWF0IjogMTY0MTc2OTIwMCwKICAgICJleHAiOiAxNzk5NTM1NjAwCn0.dc_X5iR_VP_qT0zsiyj_I_OZ2T9FtRU2BBNWN8Bu4GE"
|
|
},
|
|
{
|
|
"name": "Authorization",
|
|
"value": "={{ $('Validar solicitud de acceso').first().json.authorization }}"
|
|
},
|
|
{
|
|
"name": "Accept",
|
|
"value": "application/json"
|
|
}
|
|
]
|
|
},
|
|
"sendBody": true,
|
|
"specifyBody": "json",
|
|
"jsonBody": "={{ {\n p_app_id: $('Validar solicitud de acceso').first().json.appId\n} }}",
|
|
"options": {
|
|
"response": {
|
|
"response": {
|
|
"neverError": true,
|
|
"responseFormat": "json"
|
|
}
|
|
},
|
|
"timeout": 30000
|
|
}
|
|
},
|
|
"id": "260ee2e0-d0d7-4395-a186-b6cec2a766db",
|
|
"name": "Crear solicitud en Supabase",
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4.2,
|
|
"position": [
|
|
-112,
|
|
128
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "const source = $input.first()?.json ?? {};\nconst parseJson = (value) => {\n if (typeof value !== 'string') return value;\n try { return JSON.parse(value); } catch { return value; }\n};\nlet payload = source.body ?? source.data ?? source.response?.body ?? source;\npayload = parseJson(payload);\nif (Array.isArray(payload)) payload = payload[0] ?? {};\nif (payload && typeof payload === 'object' && payload.body !== undefined && payload.ok === undefined) {\n payload = parseJson(payload.body);\n if (Array.isArray(payload)) payload = payload[0] ?? {};\n}\nif (payload?.ok !== true) {\n return [{ json: {\n created: false,\n publicError: 'La solicitud fue registrada, pero no se pudo enviar el correo. Intenta nuevamente en unos minutos.',\n technicalError: String(payload?.error ?? payload?.message ?? 'No se pudieron emitir los enlaces de aprobación.')\n } }];\n}\nconst escapeHtml = (value) => String(value ?? '').replace(/[&<>\"']/g, (c) => ({'&':'&','<':'<','>':'>','\"':'"',\"'\":'''}[c]));\nconst encode = encodeURIComponent;\nconst confirmBase = 'https://agenteit.digitalcompass.agency/webhook/glm-hub-confirmar-solicitud';\nconst requesterName = String(payload.requesterName ?? 'Usuario GLM');\nconst requesterEmail = String(payload.requesterEmail ?? '');\nconst appName = String(payload.appName ?? 'Aplicación GLM');\nconst appCategory = String(payload.appCategory ?? '');\nconst requestId = String(payload.requestId ?? '');\nconst approvers = Array.isArray(payload.approvers) ? payload.approvers : [];\nif (approvers.length !== 3) {\n return [{ json: {\n created: false,\n publicError: 'No se pudo completar el envío de la solicitud. Intenta nuevamente en unos minutos.',\n technicalError: `Se esperaban 3 aprobadores y se recibieron ${approvers.length}.`\n } }];\n}\nreturn approvers.map((approver) => {\n const approveUrl = `${confirmBase}?token=${encode(String(approver.token ?? ''))}&decision=approved&app=${encode(appName)}`;\n const rejectUrl = `${confirmBase}?token=${encode(String(approver.token ?? ''))}&decision=rejected&app=${encode(appName)}`;\n const html = `<!doctype html><html><body style=\"margin:0;background:#F5F5F5;font-family:Arial,sans-serif;color:#4A4A4A\"><table role=\"presentation\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\" style=\"background:#F5F5F5\"><tr><td align=\"center\" style=\"padding:32px 16px\"><table role=\"presentation\" width=\"640\" cellspacing=\"0\" cellpadding=\"0\" style=\"width:100%;max-width:640px;background:#FFFFFF;border-collapse:collapse;border-top:4px solid #6CC24A\"><tr><td style=\"padding:28px 34px 16px\"><img src=\"https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/GLM.png\" width=\"220\" alt=\"GomezLee Marketing\" style=\"display:block;width:220px;max-width:100%;height:auto\"></td></tr><tr><td style=\"padding:8px 34px 0\"><div style=\"font-size:11px;font-weight:700;letter-spacing:2px;color:#4F758B\">GLM HUB · SOLICITUD DE ACCESO</div><h1 style=\"margin:12px 0 8px;font-size:30px;line-height:1.12;color:#4F758B\">Una persona necesita acceso.</h1><p style=\"margin:0;font-size:15px;line-height:1.6;color:#4A4A4A\">Hola ${escapeHtml(approver.name)}, revisa la siguiente solicitud.</p></td></tr><tr><td style=\"padding:24px 34px\"><table role=\"presentation\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\" style=\"border-collapse:collapse;border:1px solid #D0D0D0\"><tr><td style=\"width:34%;padding:12px 14px;background:#EEF6E8;border-bottom:1px solid #D0D0D0;font-size:13px;font-weight:700;color:#4F758B\">Solicitante</td><td style=\"padding:12px 14px;border-bottom:1px solid #D0D0D0;font-size:14px\">${escapeHtml(requesterName)}</td></tr><tr><td style=\"padding:12px 14px;background:#EEF6E8;border-bottom:1px solid #D0D0D0;font-size:13px;font-weight:700;color:#4F758B\">Correo</td><td style=\"padding:12px 14px;border-bottom:1px solid #D0D0D0;font-size:14px\">${escapeHtml(requesterEmail)}</td></tr><tr><td style=\"padding:12px 14px;background:#EEF6E8;border-bottom:1px solid #D0D0D0;font-size:13px;font-weight:700;color:#4F758B\">Aplicación</td><td style=\"padding:12px 14px;border-bottom:1px solid #D0D0D0;font-size:14px;font-weight:700;color:#4F758B\">${escapeHtml(appName)}</td></tr><tr><td style=\"padding:12px 14px;background:#EEF6E8;font-size:13px;font-weight:700;color:#4F758B\">Área</td><td style=\"padding:12px 14px;font-size:14px\">${escapeHtml(appCategory)}</td></tr></table></td></tr><tr><td style=\"padding:0 34px 30px\"><p style=\"margin:0 0 16px;font-size:13px;line-height:1.55;color:#6B8FA3\">Por seguridad, cada botón abre una pantalla de confirmación. La primera decisión confirmada será definitiva para los tres destinatarios.</p><table role=\"presentation\" cellspacing=\"0\" cellpadding=\"0\"><tr><td style=\"padding-right:10px\"><a href=\"${approveUrl}\" style=\"display:inline-block;padding:13px 22px;background:#6CC24A;color:#FFFFFF;text-decoration:none;font-size:14px;font-weight:700;border-radius:3px\">Aceptar solicitud</a></td><td><a href=\"${rejectUrl}\" style=\"display:inline-block;padding:12px 22px;background:#FFFFFF;color:#E87722;text-decoration:none;font-size:14px;font-weight:700;border:1px solid #E87722;border-radius:3px\">Rechazar solicitud</a></td></tr></table></td></tr><tr><td style=\"padding:18px 34px;border-top:1px solid #D0D0D0;font-size:11px;line-height:1.5;color:#6B8FA3\">Solicitud ${escapeHtml(requestId)} · GomezLee Marketing · Uso interno</td></tr></table></td></tr></table></body></html>`;\n return { json: { created: true, requestId, sendTo: String(approver.email ?? ''), subject: `Solicitud de acceso | ${appName} | ${requesterName}`, html } };\n});"
|
|
},
|
|
"id": "c4bd59ac-3233-4d09-81af-17f08b9325f5",
|
|
"name": "Preparar correos de aprobación",
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
784,
|
|
48
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"conditions": {
|
|
"options": {
|
|
"caseSensitive": true,
|
|
"leftValue": "",
|
|
"typeValidation": "strict",
|
|
"version": 2
|
|
},
|
|
"conditions": [
|
|
{
|
|
"id": "02a636e0-63a0-4a56-b108-65029e555646",
|
|
"leftValue": "={{ $json.created }}",
|
|
"rightValue": true,
|
|
"operator": {
|
|
"type": "boolean",
|
|
"operation": "true",
|
|
"singleValue": true
|
|
}
|
|
}
|
|
],
|
|
"combinator": "and"
|
|
},
|
|
"options": {}
|
|
},
|
|
"id": "eae9bf96-c598-42e0-b4df-c58319e3c354",
|
|
"name": "¿Solicitud registrada?",
|
|
"type": "n8n-nodes-base.if",
|
|
"typeVersion": 2.2,
|
|
"position": [
|
|
1024,
|
|
48
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"sendTo": "={{ $json.sendTo }}",
|
|
"subject": "={{ $json.subject }}",
|
|
"message": "={{ $json.html }}",
|
|
"options": {
|
|
"appendAttribution": false,
|
|
"senderName": "GLM Hub"
|
|
}
|
|
},
|
|
"id": "f3f7f82d-d96e-499f-a2d6-9e26f5496bb1",
|
|
"name": "Enviar solicitud a aprobadores",
|
|
"type": "n8n-nodes-base.gmail",
|
|
"typeVersion": 2.1,
|
|
"position": [
|
|
1264,
|
|
-48
|
|
],
|
|
"webhookId": "7dc65e7b-4fb6-4a2f-b685-34171dd9ce1a",
|
|
"credentials": {
|
|
"gmailOAuth2": {
|
|
"id": "UDcO1FLJqA453V2D",
|
|
"name": "Gmail account 3"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"parameters": {
|
|
"respondWith": "json",
|
|
"responseBody": "={{ { ok: true, message: 'La solicitud fue enviada a Isaac Aracena, José Leopoldo Gómez y Máximo Gómez.', requestId: $('Preparar correos de aprobación').first().json.requestId } }}",
|
|
"options": {
|
|
"responseCode": 200,
|
|
"responseHeaders": {
|
|
"entries": [
|
|
{
|
|
"name": "Cache-Control",
|
|
"value": "no-store"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"id": "af925eb2-9fb8-4261-8652-4e162969e512",
|
|
"name": "Responder solicitud enviada",
|
|
"type": "n8n-nodes-base.respondToWebhook",
|
|
"typeVersion": 1.4,
|
|
"position": [
|
|
1504,
|
|
-48
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"respondWith": "json",
|
|
"responseBody": "={{ {\n ok: false,\n error: $json.publicError || 'No pudimos enviar la solicitud. Intenta nuevamente.'\n} }}",
|
|
"options": {
|
|
"responseCode": 409,
|
|
"responseHeaders": {
|
|
"entries": [
|
|
{
|
|
"name": "Cache-Control",
|
|
"value": "no-store"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"id": "0a104b37-5336-44ce-953b-979ce5faaa87",
|
|
"name": "Responder error al registrar",
|
|
"type": "n8n-nodes-base.respondToWebhook",
|
|
"typeVersion": 1.4,
|
|
"position": [
|
|
1264,
|
|
160
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"path": "glm-hub-confirmar-solicitud",
|
|
"responseMode": "responseNode",
|
|
"options": {}
|
|
},
|
|
"id": "569dfd6e-b94f-4db8-8c88-4a890cff1ea3",
|
|
"name": "Webhook - Confirmar decisión",
|
|
"type": "n8n-nodes-base.webhook",
|
|
"typeVersion": 2.1,
|
|
"position": [
|
|
-784,
|
|
672
|
|
],
|
|
"webhookId": "cba1c38c-37bc-4acb-bd61-48035604a5c4"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "const query = $json.query ?? {};\nconst token = String(query.token ?? '').trim();\nconst decision = String(query.decision ?? '').trim().toLowerCase();\nconst appName = String(query.app ?? 'aplicación GLM').trim();\nconst valid = /^[0-9a-f]{64}$/i.test(token) && ['approved','rejected'].includes(decision);\nconst escapeHtml = (value) => String(value ?? '').replace(/[&<>\"']/g, (c) => ({'&':'&','<':'<','>':'>','\"':'"',\"'\":'''}[c]));\nconst actionUrl = 'https://agenteit.digitalcompass.agency/webhook/glm-hub-resolver-solicitud';\nconst approved = decision === 'approved';\nconst title = approved ? 'Confirmar aprobación' : 'Confirmar rechazo';\nconst actionLabel = approved ? 'Sí, aprobar solicitud' : 'Sí, rechazar solicitud';\nconst actionColor = approved ? '#6CC24A' : '#E87722';\nconst html = valid\n ? `<!doctype html><html lang=\"es\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><title>${title} · GLM Hub</title></head><body style=\"margin:0;background:#F5F5F5;font-family:Arial,sans-serif;color:#4A4A4A\"><main style=\"min-height:100vh;display:grid;place-items:center;padding:24px\"><section style=\"width:100%;max-width:560px;background:#FFFFFF;border-top:4px solid #6CC24A;padding:36px;box-sizing:border-box;box-shadow:0 24px 70px rgba(0,0,0,.14)\"><img src=\"https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/GLM.png\" width=\"220\" alt=\"GomezLee Marketing\" style=\"display:block;width:220px;max-width:100%;height:auto\"><p style=\"margin:28px 0 8px;font-size:11px;font-weight:700;letter-spacing:2px;color:#4F758B\">GLM HUB · DECISIÓN DE ACCESO</p><h1 style=\"margin:0;color:#4F758B;font-size:34px;line-height:1.1\">${title}</h1><p style=\"margin:16px 0 24px;font-size:15px;line-height:1.6\">Vas a ${approved ? 'aprobar' : 'rechazar'} la solicitud de acceso a <strong>${escapeHtml(appName)}</strong>. La decisión será definitiva y los otros enlaces quedarán sin efecto.</p><form method=\"post\" action=\"${actionUrl}\"><input type=\"hidden\" name=\"token\" value=\"${escapeHtml(token)}\"><input type=\"hidden\" name=\"decision\" value=\"${escapeHtml(decision)}\"><button type=\"submit\" style=\"width:100%;min-height:50px;border:0;border-radius:3px;background:${actionColor};color:#FFFFFF;font-family:Arial,sans-serif;font-size:15px;font-weight:700;cursor:pointer\">${actionLabel}</button></form><p style=\"margin:18px 0 0;font-size:12px;line-height:1.5;color:#6B8FA3\">Cierra esta pestaña para cancelar sin registrar ninguna decisión.</p></section></main></body></html>`\n : `<!doctype html><html lang=\"es\"><body style=\"margin:0;background:#F5F5F5;font-family:Arial,sans-serif\"><main style=\"min-height:100vh;display:grid;place-items:center;padding:24px\"><section style=\"max-width:520px;background:#FFFFFF;border-top:4px solid #E87722;padding:36px\"><img src=\"https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/GLM.png\" width=\"220\" alt=\"GomezLee Marketing\"><h1 style=\"color:#4F758B\">Enlace no válido</h1><p style=\"color:#4A4A4A;line-height:1.6\">El enlace está incompleto o no corresponde a una solicitud de GLM Hub.</p></section></main></body></html>`;\nreturn [{ json: { html } }];"
|
|
},
|
|
"id": "66f986d2-7c8b-4c25-9a5c-4310f5c37d3a",
|
|
"name": "Construir página de confirmación",
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
-560,
|
|
672
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"respondWith": "text",
|
|
"responseBody": "={{ $json.html }}",
|
|
"options": {
|
|
"responseCode": 200,
|
|
"responseHeaders": {
|
|
"entries": [
|
|
{
|
|
"name": "Content-Type",
|
|
"value": "text/html; charset=utf-8"
|
|
},
|
|
{
|
|
"name": "Cache-Control",
|
|
"value": "no-store"
|
|
},
|
|
{
|
|
"name": "X-Frame-Options",
|
|
"value": "DENY"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"id": "abba0ac3-e1fc-4c2a-a6fc-ecba5e924cdf",
|
|
"name": "Mostrar confirmación",
|
|
"type": "n8n-nodes-base.respondToWebhook",
|
|
"typeVersion": 1.4,
|
|
"position": [
|
|
-336,
|
|
672
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"httpMethod": "POST",
|
|
"path": "glm-hub-resolver-solicitud",
|
|
"responseMode": "responseNode",
|
|
"options": {}
|
|
},
|
|
"id": "9baccfdb-9561-4e85-bf43-7993eb6bc65d",
|
|
"name": "Webhook - Resolver solicitud",
|
|
"type": "n8n-nodes-base.webhook",
|
|
"typeVersion": 2.1,
|
|
"position": [
|
|
-784,
|
|
1040
|
|
],
|
|
"webhookId": "54eeee1b-b24b-46ae-84dc-70c4103e3a3a"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "const source = $json.body ?? $json;\nlet payload = source;\nif (typeof payload === 'string') {\n try {\n payload = Object.fromEntries(new URLSearchParams(payload));\n } catch { payload = {}; }\n}\nconst token = String(payload?.token ?? '').trim();\nconst decision = String(payload?.decision ?? '').trim().toLowerCase();\nlet error = '';\nif (!/^[0-9a-f]{64}$/i.test(token)) error = 'El enlace de decisión no es válido.';\nelse if (!['approved','rejected'].includes(decision)) error = 'La decisión recibida no es válida.';\nreturn [{ json: { valid: !error, error, token, decision } }];"
|
|
},
|
|
"id": "0760c143-2039-428e-85d2-f9fdec1ed4f7",
|
|
"name": "Validar decisión",
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
-560,
|
|
1040
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"conditions": {
|
|
"options": {
|
|
"caseSensitive": true,
|
|
"leftValue": "",
|
|
"typeValidation": "strict",
|
|
"version": 2
|
|
},
|
|
"conditions": [
|
|
{
|
|
"id": "5c42c35a-903b-4a18-a7ef-aab6e6bb5c26",
|
|
"leftValue": "={{ $json.valid }}",
|
|
"rightValue": true,
|
|
"operator": {
|
|
"type": "boolean",
|
|
"operation": "true",
|
|
"singleValue": true
|
|
}
|
|
}
|
|
],
|
|
"combinator": "and"
|
|
},
|
|
"options": {}
|
|
},
|
|
"id": "dfacee52-d608-4086-b287-560ce870c980",
|
|
"name": "¿Decisión válida?",
|
|
"type": "n8n-nodes-base.if",
|
|
"typeVersion": 2.2,
|
|
"position": [
|
|
-336,
|
|
1040
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "const message = String($json.error ?? 'El enlace de decisión no es válido.');\nconst escapeHtml = (value) => String(value ?? '').replace(/[&<>\"']/g, (c) => ({'&':'&','<':'<','>':'>','\"':'"',\"'\":'''}[c]));\nconst html = `<!doctype html><html lang=\"es\"><body style=\"margin:0;background:#F5F5F5;font-family:Arial,sans-serif\"><main style=\"min-height:100vh;display:grid;place-items:center;padding:24px\"><section style=\"max-width:520px;background:#FFFFFF;border-top:4px solid #E87722;padding:36px\"><img src=\"https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/GLM.png\" width=\"220\" alt=\"GomezLee Marketing\"><h1 style=\"color:#4F758B\">No se pudo procesar</h1><p style=\"color:#4A4A4A;line-height:1.6\">${escapeHtml(message)}</p></section></main></body></html>`;\nreturn [{ json: { html } }];"
|
|
},
|
|
"id": "8bebdbfd-3ee5-4a87-a0d4-c0b3a9bd17d4",
|
|
"name": "Preparar decisión inválida",
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
-112,
|
|
1232
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"respondWith": "text",
|
|
"responseBody": "={{ $json.html }}",
|
|
"options": {
|
|
"responseCode": 400,
|
|
"responseHeaders": {
|
|
"entries": [
|
|
{
|
|
"name": "Content-Type",
|
|
"value": "text/html; charset=utf-8"
|
|
},
|
|
{
|
|
"name": "Cache-Control",
|
|
"value": "no-store"
|
|
},
|
|
{
|
|
"name": "X-Frame-Options",
|
|
"value": "DENY"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"id": "bdaa54d2-e20d-4523-963b-1cc88f4d8669",
|
|
"name": "Responder decisión inválida",
|
|
"type": "n8n-nodes-base.respondToWebhook",
|
|
"typeVersion": 1.4,
|
|
"position": [
|
|
128,
|
|
1232
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"method": "POST",
|
|
"url": "https://dbit.digitalcompass.agency/rest/v1/rpc/glm_hub_decide_access_request",
|
|
"sendHeaders": true,
|
|
"headerParameters": {
|
|
"parameters": [
|
|
{
|
|
"name": "apikey",
|
|
"value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q"
|
|
},
|
|
{
|
|
"name": "Authorization",
|
|
"value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q"
|
|
},
|
|
{
|
|
"name": "Accept",
|
|
"value": "application/json"
|
|
},
|
|
{
|
|
"name": "Content-Type",
|
|
"value": "application/json"
|
|
}
|
|
]
|
|
},
|
|
"sendBody": true,
|
|
"specifyBody": "json",
|
|
"jsonBody": "={{ {\n p_token: $('Validar decisión').first().json.token,\n p_decision: $('Validar decisión').first().json.decision\n} }}",
|
|
"options": {
|
|
"response": {
|
|
"response": {
|
|
"neverError": true,
|
|
"responseFormat": "json"
|
|
}
|
|
},
|
|
"timeout": 30000
|
|
}
|
|
},
|
|
"id": "df9c350b-10f7-494a-8c5e-e0354c1606db",
|
|
"name": "Registrar decisión en Supabase",
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4.2,
|
|
"position": [
|
|
-112,
|
|
960
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "const source = $input.first()?.json ?? {};\nlet payload = source.body ?? source;\nif (typeof payload === 'string') { try { payload = JSON.parse(payload); } catch { payload = {}; } }\nif (Array.isArray(payload)) payload = payload[0] ?? {};\nconst escapeHtml = (value) => String(value ?? '').replace(/[&<>\"']/g, (c) => ({'&':'&','<':'<','>':'>','\"':'"',\"'\":'''}[c]));\nconst status = String(payload.status ?? '');\nconst approved = status === 'approved';\nconst statusLabel = approved ? 'APROBADA' : status === 'rejected' ? 'RECHAZADA' : 'SIN PROCESAR';\nconst statusColor = approved ? '#6CC24A' : '#E87722';\nconst ok = payload.ok === true;\nconst alreadyDecided = payload.alreadyDecided === true;\nconst requesterName = String(payload.requesterName ?? '');\nconst requesterEmail = String(payload.requesterEmail ?? '');\nconst appName = String(payload.appName ?? 'Aplicación GLM');\nconst decidedByName = String(payload.decidedByName ?? '');\nconst decidedByEmail = String(payload.decidedByEmail ?? '');\nconst responseTitle = ok ? (approved ? 'Solicitud aprobada' : 'Solicitud rechazada') : alreadyDecided ? 'Solicitud ya atendida' : 'No se pudo procesar';\nconst responseMessage = ok\n ? `La decisión fue registrada. IT Support recibirá los datos para continuar con el acceso.`\n : alreadyDecided\n ? `Esta solicitud ya fue ${status === 'approved' ? 'aprobada' : 'rechazada'} por ${escapeHtml(decidedByName || decidedByEmail || 'otra persona')}. No se realizó ningún cambio adicional.`\n : escapeHtml(payload.error ?? 'El enlace no pudo ser validado.');\nconst responseHtml = `<!doctype html><html lang=\"es\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><title>${responseTitle} · GLM Hub</title></head><body style=\"margin:0;background:#F5F5F5;font-family:Arial,sans-serif;color:#4A4A4A\"><main style=\"min-height:100vh;display:grid;place-items:center;padding:24px\"><section style=\"width:100%;max-width:560px;background:#FFFFFF;border-top:4px solid ${ok ? statusColor : '#4F758B'};padding:36px;box-sizing:border-box;box-shadow:0 24px 70px rgba(0,0,0,.14)\"><img src=\"https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/GLM.png\" width=\"220\" alt=\"GomezLee Marketing\" style=\"display:block;width:220px;max-width:100%;height:auto\"><p style=\"margin:28px 0 8px;font-size:11px;font-weight:700;letter-spacing:2px;color:#4F758B\">GLM HUB · SOLICITUD DE ACCESO</p><h1 style=\"margin:0;color:#4F758B;font-size:34px;line-height:1.1\">${responseTitle}</h1><p style=\"margin:16px 0 0;font-size:15px;line-height:1.65\">${responseMessage}</p><p style=\"margin:24px 0 0;font-size:12px;color:#6B8FA3\">Ya puedes cerrar esta pestaña.</p></section></main></body></html>`;\nconst itHtml = ok ? `<!doctype html><html><body style=\"margin:0;background:#F5F5F5;font-family:Arial,sans-serif;color:#4A4A4A\"><table role=\"presentation\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\" style=\"background:#F5F5F5\"><tr><td align=\"center\" style=\"padding:32px 16px\"><table role=\"presentation\" width=\"680\" cellspacing=\"0\" cellpadding=\"0\" style=\"width:100%;max-width:680px;background:#FFFFFF;border-collapse:collapse;border-top:4px solid ${statusColor}\"><tr><td style=\"padding:28px 34px 16px\"><img src=\"https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/GLM.png\" width=\"220\" alt=\"GomezLee Marketing\" style=\"display:block;width:220px;max-width:100%;height:auto\"></td></tr><tr><td style=\"padding:8px 34px 0\"><div style=\"font-size:11px;font-weight:700;letter-spacing:2px;color:#4F758B\">GLM HUB · DECISIÓN REGISTRADA</div><h1 style=\"margin:12px 0 8px;font-size:30px;line-height:1.12;color:#4F758B\">Solicitud ${statusLabel.toLowerCase()}.</h1><p style=\"margin:0;font-size:15px;line-height:1.6\">IT Support debe continuar según la decisión indicada.</p></td></tr><tr><td style=\"padding:24px 34px\"><div style=\"display:inline-block;margin-bottom:16px;padding:7px 12px;border:1px solid ${statusColor};color:${statusColor};font-size:12px;font-weight:700;letter-spacing:1px\">${statusLabel}</div><table role=\"presentation\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\" style=\"border-collapse:collapse;border:1px solid #D0D0D0\"><tr><td style=\"width:38%;padding:12px 14px;background:#EEF6E8;border-bottom:1px solid #D0D0D0;font-size:13px;font-weight:700;color:#4F758B\">Persona solicitante</td><td style=\"padding:12px 14px;border-bottom:1px solid #D0D0D0;font-size:14px\">${escapeHtml(requesterName)}</td></tr><tr><td style=\"padding:12px 14px;background:#EEF6E8;border-bottom:1px solid #D0D0D0;font-size:13px;font-weight:700;color:#4F758B\">Correo solicitante</td><td style=\"padding:12px 14px;border-bottom:1px solid #D0D0D0;font-size:14px\">${escapeHtml(requesterEmail)}</td></tr><tr><td style=\"padding:12px 14px;background:#EEF6E8;border-bottom:1px solid #D0D0D0;font-size:13px;font-weight:700;color:#4F758B\">Aplicación</td><td style=\"padding:12px 14px;border-bottom:1px solid #D0D0D0;font-size:14px;font-weight:700;color:#4F758B\">${escapeHtml(appName)}</td></tr><tr><td style=\"padding:12px 14px;background:#EEF6E8;border-bottom:1px solid #D0D0D0;font-size:13px;font-weight:700;color:#4F758B\">Decisión</td><td style=\"padding:12px 14px;border-bottom:1px solid #D0D0D0;font-size:14px;font-weight:700;color:${statusColor}\">${statusLabel}</td></tr><tr><td style=\"padding:12px 14px;background:#EEF6E8;border-bottom:1px solid #D0D0D0;font-size:13px;font-weight:700;color:#4F758B\">Decidido por</td><td style=\"padding:12px 14px;border-bottom:1px solid #D0D0D0;font-size:14px\">${escapeHtml(decidedByName)}</td></tr><tr><td style=\"padding:12px 14px;background:#EEF6E8;font-size:13px;font-weight:700;color:#4F758B\">Correo de quien decidió</td><td style=\"padding:12px 14px;font-size:14px\">${escapeHtml(decidedByEmail)}</td></tr></table></td></tr><tr><td style=\"padding:18px 34px;border-top:1px solid #D0D0D0;font-size:11px;line-height:1.5;color:#6B8FA3\">GomezLee Marketing · GLM Hub · Uso interno</td></tr></table></td></tr></table></body></html>` : '';\nreturn [{ json: { notifyIt: ok, responseHtml, itHtml, subject: `Acceso ${approved ? 'aprobado' : 'rechazado'} | ${appName} | ${requesterName}` } }];"
|
|
},
|
|
"id": "72ee2fcd-bf95-4542-aad1-90ca4d83a2e7",
|
|
"name": "Preparar resultado de decisión",
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
128,
|
|
960
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"conditions": {
|
|
"options": {
|
|
"caseSensitive": true,
|
|
"leftValue": "",
|
|
"typeValidation": "strict",
|
|
"version": 2
|
|
},
|
|
"conditions": [
|
|
{
|
|
"id": "2ef854a5-d270-49bf-9a6a-5bb92aeab434",
|
|
"leftValue": "={{ $json.notifyIt }}",
|
|
"rightValue": true,
|
|
"operator": {
|
|
"type": "boolean",
|
|
"operation": "true",
|
|
"singleValue": true
|
|
}
|
|
}
|
|
],
|
|
"combinator": "and"
|
|
},
|
|
"options": {}
|
|
},
|
|
"id": "9f4e6707-3cb1-4c0e-833c-05be87602e07",
|
|
"name": "¿Notificar a IT Support?",
|
|
"type": "n8n-nodes-base.if",
|
|
"typeVersion": 2.2,
|
|
"position": [
|
|
368,
|
|
960
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"sendTo": "itsupport@gomezleemarketing.com",
|
|
"subject": "={{ $json.subject }}",
|
|
"message": "={{ $json.itHtml }}",
|
|
"options": {
|
|
"appendAttribution": false,
|
|
"senderName": "GLM Hub"
|
|
}
|
|
},
|
|
"id": "56d73834-fc03-43c7-9844-dd4e691439b2",
|
|
"name": "Enviar decisión a IT Support",
|
|
"type": "n8n-nodes-base.gmail",
|
|
"typeVersion": 2.1,
|
|
"position": [
|
|
608,
|
|
864
|
|
],
|
|
"webhookId": "234c8e3b-b904-41b8-8a74-9ca835f759df",
|
|
"credentials": {
|
|
"gmailOAuth2": {
|
|
"id": "UDcO1FLJqA453V2D",
|
|
"name": "Gmail account 3"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"parameters": {
|
|
"respondWith": "text",
|
|
"responseBody": "={{ $('Preparar resultado de decisión').first().json.responseHtml }}",
|
|
"options": {
|
|
"responseCode": 200,
|
|
"responseHeaders": {
|
|
"entries": [
|
|
{
|
|
"name": "Content-Type",
|
|
"value": "text/html; charset=utf-8"
|
|
},
|
|
{
|
|
"name": "Cache-Control",
|
|
"value": "no-store"
|
|
},
|
|
{
|
|
"name": "X-Frame-Options",
|
|
"value": "DENY"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"id": "e1d4a5ba-34fe-479a-a670-e07ebd87d0cf",
|
|
"name": "Mostrar resultado final",
|
|
"type": "n8n-nodes-base.respondToWebhook",
|
|
"typeVersion": 1.4,
|
|
"position": [
|
|
848,
|
|
960
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "const source = $input.first()?.json ?? {};\n\nconst parseJson = (value) => {\n if (typeof value !== 'string') {\n return value;\n }\n\n try {\n return JSON.parse(value);\n } catch {\n return {};\n }\n};\n\nlet payload = source;\n\n// Compatibilidad por si n8n todavía entrega algún envoltorio.\nif (source.body !== undefined) {\n payload = source.body;\n} else if (source.data !== undefined) {\n payload = source.data;\n}\n\npayload = parseJson(payload);\n\nif (Array.isArray(payload)) {\n payload = payload[0] ?? {};\n}\n\nconst registered = payload?.ok === true;\nconst code = String(payload?.code ?? '').toUpperCase();\nconst internalMessage = String(\n payload?.error ??\n payload?.message ??\n ''\n).trim();\n\nconst normalizedMessage = internalMessage.toLowerCase();\n\nlet publicError = 'No pudimos enviar la solicitud. Intenta nuevamente.';\n\nif (\n code === 'PENDING_EXISTS' ||\n normalizedMessage.includes('solicitud pendiente')\n) {\n publicError = 'Ya tienes una solicitud pendiente para esta aplicación.';\n} else if (\n normalizedMessage.includes('sesión') ||\n normalizedMessage.includes('jwt')\n) {\n publicError = 'Tu sesión venció. Cierra sesión e inicia nuevamente.';\n} else if (\n normalizedMessage.includes('administradores no necesitan')\n) {\n publicError =\n 'Tu cuenta administrativa no necesita solicitar acceso desde el Hub.';\n} else if (\n normalizedMessage.includes('no está autorizado')\n) {\n publicError =\n 'Tu cuenta no está habilitada para enviar solicitudes de acceso.';\n} else if (\n normalizedMessage.includes('ya no está publicada')\n) {\n publicError =\n 'La aplicación seleccionada ya no está disponible.';\n}\n\nreturn [\n {\n json: {\n registered,\n publicError,\n internalMessage,\n requestId: String(payload?.requestId ?? ''),\n requesterName: String(payload?.requesterName ?? ''),\n requesterEmail: String(payload?.requesterEmail ?? ''),\n appName: String(payload?.appName ?? ''),\n appCategory: String(payload?.appCategory ?? '')\n }\n }\n];"
|
|
},
|
|
"id": "e2cc29db-5db2-46ea-a388-054c320ecfc2",
|
|
"name": "Confirmar registro de solicitud",
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
96,
|
|
128
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"conditions": {
|
|
"options": {
|
|
"caseSensitive": true,
|
|
"leftValue": "",
|
|
"typeValidation": "strict",
|
|
"version": 2
|
|
},
|
|
"conditions": [
|
|
{
|
|
"id": "aa091e1c-09f8-4cdb-b8ac-3cbc4a7a33c8",
|
|
"leftValue": "={{ $json.registered }}",
|
|
"rightValue": true,
|
|
"operator": {
|
|
"type": "boolean",
|
|
"operation": "true",
|
|
"singleValue": true
|
|
}
|
|
}
|
|
],
|
|
"combinator": "and"
|
|
},
|
|
"options": {}
|
|
},
|
|
"id": "7f6803be-bc3a-45e1-b1a4-c04c33ca25b9",
|
|
"name": "¿Solicitud registrada en Supabase?",
|
|
"type": "n8n-nodes-base.if",
|
|
"typeVersion": 2.2,
|
|
"position": [
|
|
320,
|
|
128
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"method": "POST",
|
|
"url": "https://dbit.digitalcompass.agency/rest/v1/rpc/glm_hub_issue_access_request_tokens",
|
|
"sendHeaders": true,
|
|
"headerParameters": {
|
|
"parameters": [
|
|
{
|
|
"name": "apikey",
|
|
"value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q"
|
|
},
|
|
{
|
|
"name": "Authorization",
|
|
"value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q"
|
|
},
|
|
{
|
|
"name": "Accept",
|
|
"value": "application/json"
|
|
},
|
|
{
|
|
"name": "Content-Type",
|
|
"value": "application/json"
|
|
}
|
|
]
|
|
},
|
|
"sendBody": true,
|
|
"specifyBody": "json",
|
|
"jsonBody": "={{ {\n p_request_id: $('Confirmar registro de solicitud').first().json.requestId\n} }}",
|
|
"options": {
|
|
"response": {
|
|
"response": {
|
|
"neverError": true,
|
|
"responseFormat": "json"
|
|
}
|
|
},
|
|
"timeout": 30000
|
|
}
|
|
},
|
|
"id": "d684c268-bbd8-4a3b-8aab-b21510423c95",
|
|
"name": "Emitir enlaces de aprobación",
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4.2,
|
|
"position": [
|
|
544,
|
|
48
|
|
]
|
|
}
|
|
],
|
|
"connections": {
|
|
"Webhook - Solicitar acceso": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Validar solicitud de acceso",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Validar solicitud de acceso": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "¿Solicitud de acceso válida?",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"¿Solicitud de acceso válida?": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Crear solicitud en Supabase",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
],
|
|
[
|
|
{
|
|
"node": "Responder solicitud inválida",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Crear solicitud en Supabase": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Confirmar registro de solicitud",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Preparar correos de aprobación": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "¿Solicitud registrada?",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"¿Solicitud registrada?": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Enviar solicitud a aprobadores",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
],
|
|
[
|
|
{
|
|
"node": "Responder error al registrar",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Enviar solicitud a aprobadores": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Responder solicitud enviada",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Webhook - Confirmar decisión": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Construir página de confirmación",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Construir página de confirmación": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Mostrar confirmación",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Webhook - Resolver solicitud": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Validar decisión",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Validar decisión": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "¿Decisión válida?",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"¿Decisión válida?": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Registrar decisión en Supabase",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
],
|
|
[
|
|
{
|
|
"node": "Preparar decisión inválida",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Preparar decisión inválida": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Responder decisión inválida",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Registrar decisión en Supabase": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Preparar resultado de decisión",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Preparar resultado de decisión": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "¿Notificar a IT Support?",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"¿Notificar a IT Support?": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Enviar decisión a IT Support",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
],
|
|
[
|
|
{
|
|
"node": "Mostrar resultado final",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Enviar decisión a IT Support": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Mostrar resultado final",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Confirmar registro de solicitud": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "¿Solicitud registrada en Supabase?",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"¿Solicitud registrada en Supabase?": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Emitir enlaces de aprobación",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
],
|
|
[
|
|
{
|
|
"node": "Responder error al registrar",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Emitir enlaces de aprobación": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Preparar correos de aprobación",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
}
|
|
},
|
|
"settings": {
|
|
"executionOrder": "v1",
|
|
"binaryMode": "separate",
|
|
"availableInMCP": true
|
|
},
|
|
"staticData": null,
|
|
"meta": {
|
|
"templateCredsSetupCompleted": true
|
|
},
|
|
"versionId": "a4528c24-f544-4063-9bda-2b04c9943a97",
|
|
"activeVersionId": "a4528c24-f544-4063-9bda-2b04c9943a97",
|
|
"versionCounter": 72,
|
|
"triggerCount": 3,
|
|
"shared": [
|
|
{
|
|
"updatedAt": "2026-07-29T13:08:05.454Z",
|
|
"createdAt": "2026-07-29T13:08:05.454Z",
|
|
"role": "workflow:owner",
|
|
"workflowId": "RaNkDfE2YCrZgCbx",
|
|
"projectId": "PJpTANzTXIFibWsW",
|
|
"project": {
|
|
"updatedAt": "2026-04-22T14:25:09.686Z",
|
|
"createdAt": "2026-04-22T14:22:54.790Z",
|
|
"id": "PJpTANzTXIFibWsW",
|
|
"name": "Isaac Aracena <iaracena@gomezleemarketing.com>",
|
|
"type": "personal",
|
|
"icon": null,
|
|
"description": null,
|
|
"creatorId": "0a88c0b1-928e-4412-896e-c5d1c99b2029"
|
|
}
|
|
}
|
|
],
|
|
"tags": [],
|
|
"activeVersion": {
|
|
"updatedAt": "2026-07-29T15:29:25.000Z",
|
|
"createdAt": "2026-07-29T15:29:15.992Z",
|
|
"versionId": "a4528c24-f544-4063-9bda-2b04c9943a97",
|
|
"workflowId": "RaNkDfE2YCrZgCbx",
|
|
"nodes": [
|
|
{
|
|
"parameters": {
|
|
"httpMethod": "POST",
|
|
"path": "glm-hub-solicitar-acceso",
|
|
"responseMode": "responseNode",
|
|
"options": {
|
|
"allowedOrigins": "*"
|
|
}
|
|
},
|
|
"id": "80c8fbc9-c126-447e-a2b3-b2c9ac6e9468",
|
|
"name": "Webhook - Solicitar acceso",
|
|
"type": "n8n-nodes-base.webhook",
|
|
"typeVersion": 2.1,
|
|
"position": [
|
|
-784,
|
|
208
|
|
],
|
|
"webhookId": "ad3e98b9-baf2-400f-90e7-dcd4c5c20457"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "const source = $json.body ?? $json;\nlet payload = source;\nif (typeof payload === 'string') {\n try { payload = JSON.parse(payload); } catch { payload = {}; }\n}\nconst accessToken = String(payload?.accessToken ?? '').trim();\nconst appId = String(payload?.appId ?? '').trim();\nconst jwtPattern = /^[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+$/;\nconst uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\nlet error = '';\nif (!jwtPattern.test(accessToken)) error = 'La sesión recibida no es válida.';\nelse if (!uuidPattern.test(appId)) error = 'Selecciona una aplicación válida.';\nreturn [{ json: { valid: !error, error, authorization: `Bearer ${accessToken}`, appId } }];"
|
|
},
|
|
"id": "7d5b3911-3db7-4023-a89b-ac690bc99101",
|
|
"name": "Validar solicitud de acceso",
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
-560,
|
|
208
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"conditions": {
|
|
"options": {
|
|
"caseSensitive": true,
|
|
"leftValue": "",
|
|
"typeValidation": "strict",
|
|
"version": 2
|
|
},
|
|
"conditions": [
|
|
{
|
|
"id": "b5933422-7722-4c98-b34b-262a20d3ac05",
|
|
"leftValue": "={{ $json.valid }}",
|
|
"rightValue": true,
|
|
"operator": {
|
|
"type": "boolean",
|
|
"operation": "true",
|
|
"singleValue": true
|
|
}
|
|
}
|
|
],
|
|
"combinator": "and"
|
|
},
|
|
"options": {}
|
|
},
|
|
"id": "3c1cd649-6e3a-4751-ba1f-e775e8040f1d",
|
|
"name": "¿Solicitud de acceso válida?",
|
|
"type": "n8n-nodes-base.if",
|
|
"typeVersion": 2.2,
|
|
"position": [
|
|
-336,
|
|
208
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"respondWith": "json",
|
|
"responseBody": "={{ { ok: false, error: $json.error } }}",
|
|
"options": {
|
|
"responseCode": 400,
|
|
"responseHeaders": {
|
|
"entries": [
|
|
{
|
|
"name": "Cache-Control",
|
|
"value": "no-store"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"id": "ff32cfc2-c5ed-4e10-8804-2209cd17ef69",
|
|
"name": "Responder solicitud inválida",
|
|
"type": "n8n-nodes-base.respondToWebhook",
|
|
"typeVersion": 1.4,
|
|
"position": [
|
|
-112,
|
|
368
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"method": "POST",
|
|
"url": "https://dbit.digitalcompass.agency/rest/v1/rpc/glm_hub_prepare_access_request",
|
|
"sendHeaders": true,
|
|
"headerParameters": {
|
|
"parameters": [
|
|
{
|
|
"name": "apikey",
|
|
"value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJhbm9uIiwKICAgICJpc3MiOiAic3VwYWJhc2UtZGVtbyIsCiAgICAiaWF0IjogMTY0MTc2OTIwMCwKICAgICJleHAiOiAxNzk5NTM1NjAwCn0.dc_X5iR_VP_qT0zsiyj_I_OZ2T9FtRU2BBNWN8Bu4GE"
|
|
},
|
|
{
|
|
"name": "Authorization",
|
|
"value": "={{ $('Validar solicitud de acceso').first().json.authorization }}"
|
|
},
|
|
{
|
|
"name": "Accept",
|
|
"value": "application/json"
|
|
}
|
|
]
|
|
},
|
|
"sendBody": true,
|
|
"specifyBody": "json",
|
|
"jsonBody": "={{ {\n p_app_id: $('Validar solicitud de acceso').first().json.appId\n} }}",
|
|
"options": {
|
|
"response": {
|
|
"response": {
|
|
"neverError": true,
|
|
"responseFormat": "json"
|
|
}
|
|
},
|
|
"timeout": 30000
|
|
}
|
|
},
|
|
"id": "260ee2e0-d0d7-4395-a186-b6cec2a766db",
|
|
"name": "Crear solicitud en Supabase",
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4.2,
|
|
"position": [
|
|
-112,
|
|
128
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "const source = $input.first()?.json ?? {};\nconst parseJson = (value) => {\n if (typeof value !== 'string') return value;\n try { return JSON.parse(value); } catch { return value; }\n};\nlet payload = source.body ?? source.data ?? source.response?.body ?? source;\npayload = parseJson(payload);\nif (Array.isArray(payload)) payload = payload[0] ?? {};\nif (payload && typeof payload === 'object' && payload.body !== undefined && payload.ok === undefined) {\n payload = parseJson(payload.body);\n if (Array.isArray(payload)) payload = payload[0] ?? {};\n}\nif (payload?.ok !== true) {\n return [{ json: {\n created: false,\n publicError: 'La solicitud fue registrada, pero no se pudo enviar el correo. Intenta nuevamente en unos minutos.',\n technicalError: String(payload?.error ?? payload?.message ?? 'No se pudieron emitir los enlaces de aprobación.')\n } }];\n}\nconst escapeHtml = (value) => String(value ?? '').replace(/[&<>\"']/g, (c) => ({'&':'&','<':'<','>':'>','\"':'"',\"'\":'''}[c]));\nconst encode = encodeURIComponent;\nconst confirmBase = 'https://agenteit.digitalcompass.agency/webhook/glm-hub-confirmar-solicitud';\nconst requesterName = String(payload.requesterName ?? 'Usuario GLM');\nconst requesterEmail = String(payload.requesterEmail ?? '');\nconst appName = String(payload.appName ?? 'Aplicación GLM');\nconst appCategory = String(payload.appCategory ?? '');\nconst requestId = String(payload.requestId ?? '');\nconst approvers = Array.isArray(payload.approvers) ? payload.approvers : [];\nif (approvers.length !== 3) {\n return [{ json: {\n created: false,\n publicError: 'No se pudo completar el envío de la solicitud. Intenta nuevamente en unos minutos.',\n technicalError: `Se esperaban 3 aprobadores y se recibieron ${approvers.length}.`\n } }];\n}\nreturn approvers.map((approver) => {\n const approveUrl = `${confirmBase}?token=${encode(String(approver.token ?? ''))}&decision=approved&app=${encode(appName)}`;\n const rejectUrl = `${confirmBase}?token=${encode(String(approver.token ?? ''))}&decision=rejected&app=${encode(appName)}`;\n const html = `<!doctype html><html><body style=\"margin:0;background:#F5F5F5;font-family:Arial,sans-serif;color:#4A4A4A\"><table role=\"presentation\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\" style=\"background:#F5F5F5\"><tr><td align=\"center\" style=\"padding:32px 16px\"><table role=\"presentation\" width=\"640\" cellspacing=\"0\" cellpadding=\"0\" style=\"width:100%;max-width:640px;background:#FFFFFF;border-collapse:collapse;border-top:4px solid #6CC24A\"><tr><td style=\"padding:28px 34px 16px\"><img src=\"https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/GLM.png\" width=\"220\" alt=\"GomezLee Marketing\" style=\"display:block;width:220px;max-width:100%;height:auto\"></td></tr><tr><td style=\"padding:8px 34px 0\"><div style=\"font-size:11px;font-weight:700;letter-spacing:2px;color:#4F758B\">GLM HUB · SOLICITUD DE ACCESO</div><h1 style=\"margin:12px 0 8px;font-size:30px;line-height:1.12;color:#4F758B\">Una persona necesita acceso.</h1><p style=\"margin:0;font-size:15px;line-height:1.6;color:#4A4A4A\">Hola ${escapeHtml(approver.name)}, revisa la siguiente solicitud.</p></td></tr><tr><td style=\"padding:24px 34px\"><table role=\"presentation\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\" style=\"border-collapse:collapse;border:1px solid #D0D0D0\"><tr><td style=\"width:34%;padding:12px 14px;background:#EEF6E8;border-bottom:1px solid #D0D0D0;font-size:13px;font-weight:700;color:#4F758B\">Solicitante</td><td style=\"padding:12px 14px;border-bottom:1px solid #D0D0D0;font-size:14px\">${escapeHtml(requesterName)}</td></tr><tr><td style=\"padding:12px 14px;background:#EEF6E8;border-bottom:1px solid #D0D0D0;font-size:13px;font-weight:700;color:#4F758B\">Correo</td><td style=\"padding:12px 14px;border-bottom:1px solid #D0D0D0;font-size:14px\">${escapeHtml(requesterEmail)}</td></tr><tr><td style=\"padding:12px 14px;background:#EEF6E8;border-bottom:1px solid #D0D0D0;font-size:13px;font-weight:700;color:#4F758B\">Aplicación</td><td style=\"padding:12px 14px;border-bottom:1px solid #D0D0D0;font-size:14px;font-weight:700;color:#4F758B\">${escapeHtml(appName)}</td></tr><tr><td style=\"padding:12px 14px;background:#EEF6E8;font-size:13px;font-weight:700;color:#4F758B\">Área</td><td style=\"padding:12px 14px;font-size:14px\">${escapeHtml(appCategory)}</td></tr></table></td></tr><tr><td style=\"padding:0 34px 30px\"><p style=\"margin:0 0 16px;font-size:13px;line-height:1.55;color:#6B8FA3\">Por seguridad, cada botón abre una pantalla de confirmación. La primera decisión confirmada será definitiva para los tres destinatarios.</p><table role=\"presentation\" cellspacing=\"0\" cellpadding=\"0\"><tr><td style=\"padding-right:10px\"><a href=\"${approveUrl}\" style=\"display:inline-block;padding:13px 22px;background:#6CC24A;color:#FFFFFF;text-decoration:none;font-size:14px;font-weight:700;border-radius:3px\">Aceptar solicitud</a></td><td><a href=\"${rejectUrl}\" style=\"display:inline-block;padding:12px 22px;background:#FFFFFF;color:#E87722;text-decoration:none;font-size:14px;font-weight:700;border:1px solid #E87722;border-radius:3px\">Rechazar solicitud</a></td></tr></table></td></tr><tr><td style=\"padding:18px 34px;border-top:1px solid #D0D0D0;font-size:11px;line-height:1.5;color:#6B8FA3\">Solicitud ${escapeHtml(requestId)} · GomezLee Marketing · Uso interno</td></tr></table></td></tr></table></body></html>`;\n return { json: { created: true, requestId, sendTo: String(approver.email ?? ''), subject: `Solicitud de acceso | ${appName} | ${requesterName}`, html } };\n});"
|
|
},
|
|
"id": "c4bd59ac-3233-4d09-81af-17f08b9325f5",
|
|
"name": "Preparar correos de aprobación",
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
784,
|
|
48
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"conditions": {
|
|
"options": {
|
|
"caseSensitive": true,
|
|
"leftValue": "",
|
|
"typeValidation": "strict",
|
|
"version": 2
|
|
},
|
|
"conditions": [
|
|
{
|
|
"id": "02a636e0-63a0-4a56-b108-65029e555646",
|
|
"leftValue": "={{ $json.created }}",
|
|
"rightValue": true,
|
|
"operator": {
|
|
"type": "boolean",
|
|
"operation": "true",
|
|
"singleValue": true
|
|
}
|
|
}
|
|
],
|
|
"combinator": "and"
|
|
},
|
|
"options": {}
|
|
},
|
|
"id": "eae9bf96-c598-42e0-b4df-c58319e3c354",
|
|
"name": "¿Solicitud registrada?",
|
|
"type": "n8n-nodes-base.if",
|
|
"typeVersion": 2.2,
|
|
"position": [
|
|
1024,
|
|
48
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"sendTo": "={{ $json.sendTo }}",
|
|
"subject": "={{ $json.subject }}",
|
|
"message": "={{ $json.html }}",
|
|
"options": {
|
|
"appendAttribution": false,
|
|
"senderName": "GLM Hub"
|
|
}
|
|
},
|
|
"id": "f3f7f82d-d96e-499f-a2d6-9e26f5496bb1",
|
|
"name": "Enviar solicitud a aprobadores",
|
|
"type": "n8n-nodes-base.gmail",
|
|
"typeVersion": 2.1,
|
|
"position": [
|
|
1264,
|
|
-48
|
|
],
|
|
"webhookId": "7dc65e7b-4fb6-4a2f-b685-34171dd9ce1a",
|
|
"credentials": {
|
|
"gmailOAuth2": {
|
|
"id": "UDcO1FLJqA453V2D",
|
|
"name": "Gmail account 3"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"parameters": {
|
|
"respondWith": "json",
|
|
"responseBody": "={{ { ok: true, message: 'La solicitud fue enviada a Isaac Aracena, José Leopoldo Gómez y Máximo Gómez.', requestId: $('Preparar correos de aprobación').first().json.requestId } }}",
|
|
"options": {
|
|
"responseCode": 200,
|
|
"responseHeaders": {
|
|
"entries": [
|
|
{
|
|
"name": "Cache-Control",
|
|
"value": "no-store"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"id": "af925eb2-9fb8-4261-8652-4e162969e512",
|
|
"name": "Responder solicitud enviada",
|
|
"type": "n8n-nodes-base.respondToWebhook",
|
|
"typeVersion": 1.4,
|
|
"position": [
|
|
1504,
|
|
-48
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"respondWith": "json",
|
|
"responseBody": "={{ {\n ok: false,\n error: $json.publicError || 'No pudimos enviar la solicitud. Intenta nuevamente.'\n} }}",
|
|
"options": {
|
|
"responseCode": 409,
|
|
"responseHeaders": {
|
|
"entries": [
|
|
{
|
|
"name": "Cache-Control",
|
|
"value": "no-store"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"id": "0a104b37-5336-44ce-953b-979ce5faaa87",
|
|
"name": "Responder error al registrar",
|
|
"type": "n8n-nodes-base.respondToWebhook",
|
|
"typeVersion": 1.4,
|
|
"position": [
|
|
1264,
|
|
160
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"path": "glm-hub-confirmar-solicitud",
|
|
"responseMode": "responseNode",
|
|
"options": {}
|
|
},
|
|
"id": "569dfd6e-b94f-4db8-8c88-4a890cff1ea3",
|
|
"name": "Webhook - Confirmar decisión",
|
|
"type": "n8n-nodes-base.webhook",
|
|
"typeVersion": 2.1,
|
|
"position": [
|
|
-784,
|
|
672
|
|
],
|
|
"webhookId": "cba1c38c-37bc-4acb-bd61-48035604a5c4"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "const query = $json.query ?? {};\nconst token = String(query.token ?? '').trim();\nconst decision = String(query.decision ?? '').trim().toLowerCase();\nconst appName = String(query.app ?? 'aplicación GLM').trim();\nconst valid = /^[0-9a-f]{64}$/i.test(token) && ['approved','rejected'].includes(decision);\nconst escapeHtml = (value) => String(value ?? '').replace(/[&<>\"']/g, (c) => ({'&':'&','<':'<','>':'>','\"':'"',\"'\":'''}[c]));\nconst actionUrl = 'https://agenteit.digitalcompass.agency/webhook/glm-hub-resolver-solicitud';\nconst approved = decision === 'approved';\nconst title = approved ? 'Confirmar aprobación' : 'Confirmar rechazo';\nconst actionLabel = approved ? 'Sí, aprobar solicitud' : 'Sí, rechazar solicitud';\nconst actionColor = approved ? '#6CC24A' : '#E87722';\nconst html = valid\n ? `<!doctype html><html lang=\"es\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><title>${title} · GLM Hub</title></head><body style=\"margin:0;background:#F5F5F5;font-family:Arial,sans-serif;color:#4A4A4A\"><main style=\"min-height:100vh;display:grid;place-items:center;padding:24px\"><section style=\"width:100%;max-width:560px;background:#FFFFFF;border-top:4px solid #6CC24A;padding:36px;box-sizing:border-box;box-shadow:0 24px 70px rgba(0,0,0,.14)\"><img src=\"https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/GLM.png\" width=\"220\" alt=\"GomezLee Marketing\" style=\"display:block;width:220px;max-width:100%;height:auto\"><p style=\"margin:28px 0 8px;font-size:11px;font-weight:700;letter-spacing:2px;color:#4F758B\">GLM HUB · DECISIÓN DE ACCESO</p><h1 style=\"margin:0;color:#4F758B;font-size:34px;line-height:1.1\">${title}</h1><p style=\"margin:16px 0 24px;font-size:15px;line-height:1.6\">Vas a ${approved ? 'aprobar' : 'rechazar'} la solicitud de acceso a <strong>${escapeHtml(appName)}</strong>. La decisión será definitiva y los otros enlaces quedarán sin efecto.</p><form method=\"post\" action=\"${actionUrl}\"><input type=\"hidden\" name=\"token\" value=\"${escapeHtml(token)}\"><input type=\"hidden\" name=\"decision\" value=\"${escapeHtml(decision)}\"><button type=\"submit\" style=\"width:100%;min-height:50px;border:0;border-radius:3px;background:${actionColor};color:#FFFFFF;font-family:Arial,sans-serif;font-size:15px;font-weight:700;cursor:pointer\">${actionLabel}</button></form><p style=\"margin:18px 0 0;font-size:12px;line-height:1.5;color:#6B8FA3\">Cierra esta pestaña para cancelar sin registrar ninguna decisión.</p></section></main></body></html>`\n : `<!doctype html><html lang=\"es\"><body style=\"margin:0;background:#F5F5F5;font-family:Arial,sans-serif\"><main style=\"min-height:100vh;display:grid;place-items:center;padding:24px\"><section style=\"max-width:520px;background:#FFFFFF;border-top:4px solid #E87722;padding:36px\"><img src=\"https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/GLM.png\" width=\"220\" alt=\"GomezLee Marketing\"><h1 style=\"color:#4F758B\">Enlace no válido</h1><p style=\"color:#4A4A4A;line-height:1.6\">El enlace está incompleto o no corresponde a una solicitud de GLM Hub.</p></section></main></body></html>`;\nreturn [{ json: { html } }];"
|
|
},
|
|
"id": "66f986d2-7c8b-4c25-9a5c-4310f5c37d3a",
|
|
"name": "Construir página de confirmación",
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
-560,
|
|
672
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"respondWith": "text",
|
|
"responseBody": "={{ $json.html }}",
|
|
"options": {
|
|
"responseCode": 200,
|
|
"responseHeaders": {
|
|
"entries": [
|
|
{
|
|
"name": "Content-Type",
|
|
"value": "text/html; charset=utf-8"
|
|
},
|
|
{
|
|
"name": "Cache-Control",
|
|
"value": "no-store"
|
|
},
|
|
{
|
|
"name": "X-Frame-Options",
|
|
"value": "DENY"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"id": "abba0ac3-e1fc-4c2a-a6fc-ecba5e924cdf",
|
|
"name": "Mostrar confirmación",
|
|
"type": "n8n-nodes-base.respondToWebhook",
|
|
"typeVersion": 1.4,
|
|
"position": [
|
|
-336,
|
|
672
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"httpMethod": "POST",
|
|
"path": "glm-hub-resolver-solicitud",
|
|
"responseMode": "responseNode",
|
|
"options": {}
|
|
},
|
|
"id": "9baccfdb-9561-4e85-bf43-7993eb6bc65d",
|
|
"name": "Webhook - Resolver solicitud",
|
|
"type": "n8n-nodes-base.webhook",
|
|
"typeVersion": 2.1,
|
|
"position": [
|
|
-784,
|
|
1040
|
|
],
|
|
"webhookId": "54eeee1b-b24b-46ae-84dc-70c4103e3a3a"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "const source = $json.body ?? $json;\nlet payload = source;\nif (typeof payload === 'string') {\n try {\n payload = Object.fromEntries(new URLSearchParams(payload));\n } catch { payload = {}; }\n}\nconst token = String(payload?.token ?? '').trim();\nconst decision = String(payload?.decision ?? '').trim().toLowerCase();\nlet error = '';\nif (!/^[0-9a-f]{64}$/i.test(token)) error = 'El enlace de decisión no es válido.';\nelse if (!['approved','rejected'].includes(decision)) error = 'La decisión recibida no es válida.';\nreturn [{ json: { valid: !error, error, token, decision } }];"
|
|
},
|
|
"id": "0760c143-2039-428e-85d2-f9fdec1ed4f7",
|
|
"name": "Validar decisión",
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
-560,
|
|
1040
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"conditions": {
|
|
"options": {
|
|
"caseSensitive": true,
|
|
"leftValue": "",
|
|
"typeValidation": "strict",
|
|
"version": 2
|
|
},
|
|
"conditions": [
|
|
{
|
|
"id": "5c42c35a-903b-4a18-a7ef-aab6e6bb5c26",
|
|
"leftValue": "={{ $json.valid }}",
|
|
"rightValue": true,
|
|
"operator": {
|
|
"type": "boolean",
|
|
"operation": "true",
|
|
"singleValue": true
|
|
}
|
|
}
|
|
],
|
|
"combinator": "and"
|
|
},
|
|
"options": {}
|
|
},
|
|
"id": "dfacee52-d608-4086-b287-560ce870c980",
|
|
"name": "¿Decisión válida?",
|
|
"type": "n8n-nodes-base.if",
|
|
"typeVersion": 2.2,
|
|
"position": [
|
|
-336,
|
|
1040
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "const message = String($json.error ?? 'El enlace de decisión no es válido.');\nconst escapeHtml = (value) => String(value ?? '').replace(/[&<>\"']/g, (c) => ({'&':'&','<':'<','>':'>','\"':'"',\"'\":'''}[c]));\nconst html = `<!doctype html><html lang=\"es\"><body style=\"margin:0;background:#F5F5F5;font-family:Arial,sans-serif\"><main style=\"min-height:100vh;display:grid;place-items:center;padding:24px\"><section style=\"max-width:520px;background:#FFFFFF;border-top:4px solid #E87722;padding:36px\"><img src=\"https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/GLM.png\" width=\"220\" alt=\"GomezLee Marketing\"><h1 style=\"color:#4F758B\">No se pudo procesar</h1><p style=\"color:#4A4A4A;line-height:1.6\">${escapeHtml(message)}</p></section></main></body></html>`;\nreturn [{ json: { html } }];"
|
|
},
|
|
"id": "8bebdbfd-3ee5-4a87-a0d4-c0b3a9bd17d4",
|
|
"name": "Preparar decisión inválida",
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
-112,
|
|
1232
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"respondWith": "text",
|
|
"responseBody": "={{ $json.html }}",
|
|
"options": {
|
|
"responseCode": 400,
|
|
"responseHeaders": {
|
|
"entries": [
|
|
{
|
|
"name": "Content-Type",
|
|
"value": "text/html; charset=utf-8"
|
|
},
|
|
{
|
|
"name": "Cache-Control",
|
|
"value": "no-store"
|
|
},
|
|
{
|
|
"name": "X-Frame-Options",
|
|
"value": "DENY"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"id": "bdaa54d2-e20d-4523-963b-1cc88f4d8669",
|
|
"name": "Responder decisión inválida",
|
|
"type": "n8n-nodes-base.respondToWebhook",
|
|
"typeVersion": 1.4,
|
|
"position": [
|
|
128,
|
|
1232
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"method": "POST",
|
|
"url": "https://dbit.digitalcompass.agency/rest/v1/rpc/glm_hub_decide_access_request",
|
|
"sendHeaders": true,
|
|
"headerParameters": {
|
|
"parameters": [
|
|
{
|
|
"name": "apikey",
|
|
"value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q"
|
|
},
|
|
{
|
|
"name": "Authorization",
|
|
"value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q"
|
|
},
|
|
{
|
|
"name": "Accept",
|
|
"value": "application/json"
|
|
},
|
|
{
|
|
"name": "Content-Type",
|
|
"value": "application/json"
|
|
}
|
|
]
|
|
},
|
|
"sendBody": true,
|
|
"specifyBody": "json",
|
|
"jsonBody": "={{ {\n p_token: $('Validar decisión').first().json.token,\n p_decision: $('Validar decisión').first().json.decision\n} }}",
|
|
"options": {
|
|
"response": {
|
|
"response": {
|
|
"neverError": true,
|
|
"responseFormat": "json"
|
|
}
|
|
},
|
|
"timeout": 30000
|
|
}
|
|
},
|
|
"id": "df9c350b-10f7-494a-8c5e-e0354c1606db",
|
|
"name": "Registrar decisión en Supabase",
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4.2,
|
|
"position": [
|
|
-112,
|
|
960
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "const source = $input.first()?.json ?? {};\nlet payload = source.body ?? source;\nif (typeof payload === 'string') { try { payload = JSON.parse(payload); } catch { payload = {}; } }\nif (Array.isArray(payload)) payload = payload[0] ?? {};\nconst escapeHtml = (value) => String(value ?? '').replace(/[&<>\"']/g, (c) => ({'&':'&','<':'<','>':'>','\"':'"',\"'\":'''}[c]));\nconst status = String(payload.status ?? '');\nconst approved = status === 'approved';\nconst statusLabel = approved ? 'APROBADA' : status === 'rejected' ? 'RECHAZADA' : 'SIN PROCESAR';\nconst statusColor = approved ? '#6CC24A' : '#E87722';\nconst ok = payload.ok === true;\nconst alreadyDecided = payload.alreadyDecided === true;\nconst requesterName = String(payload.requesterName ?? '');\nconst requesterEmail = String(payload.requesterEmail ?? '');\nconst appName = String(payload.appName ?? 'Aplicación GLM');\nconst decidedByName = String(payload.decidedByName ?? '');\nconst decidedByEmail = String(payload.decidedByEmail ?? '');\nconst responseTitle = ok ? (approved ? 'Solicitud aprobada' : 'Solicitud rechazada') : alreadyDecided ? 'Solicitud ya atendida' : 'No se pudo procesar';\nconst responseMessage = ok\n ? `La decisión fue registrada. IT Support recibirá los datos para continuar con el acceso.`\n : alreadyDecided\n ? `Esta solicitud ya fue ${status === 'approved' ? 'aprobada' : 'rechazada'} por ${escapeHtml(decidedByName || decidedByEmail || 'otra persona')}. No se realizó ningún cambio adicional.`\n : escapeHtml(payload.error ?? 'El enlace no pudo ser validado.');\nconst responseHtml = `<!doctype html><html lang=\"es\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><title>${responseTitle} · GLM Hub</title></head><body style=\"margin:0;background:#F5F5F5;font-family:Arial,sans-serif;color:#4A4A4A\"><main style=\"min-height:100vh;display:grid;place-items:center;padding:24px\"><section style=\"width:100%;max-width:560px;background:#FFFFFF;border-top:4px solid ${ok ? statusColor : '#4F758B'};padding:36px;box-sizing:border-box;box-shadow:0 24px 70px rgba(0,0,0,.14)\"><img src=\"https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/GLM.png\" width=\"220\" alt=\"GomezLee Marketing\" style=\"display:block;width:220px;max-width:100%;height:auto\"><p style=\"margin:28px 0 8px;font-size:11px;font-weight:700;letter-spacing:2px;color:#4F758B\">GLM HUB · SOLICITUD DE ACCESO</p><h1 style=\"margin:0;color:#4F758B;font-size:34px;line-height:1.1\">${responseTitle}</h1><p style=\"margin:16px 0 0;font-size:15px;line-height:1.65\">${responseMessage}</p><p style=\"margin:24px 0 0;font-size:12px;color:#6B8FA3\">Ya puedes cerrar esta pestaña.</p></section></main></body></html>`;\nconst itHtml = ok ? `<!doctype html><html><body style=\"margin:0;background:#F5F5F5;font-family:Arial,sans-serif;color:#4A4A4A\"><table role=\"presentation\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\" style=\"background:#F5F5F5\"><tr><td align=\"center\" style=\"padding:32px 16px\"><table role=\"presentation\" width=\"680\" cellspacing=\"0\" cellpadding=\"0\" style=\"width:100%;max-width:680px;background:#FFFFFF;border-collapse:collapse;border-top:4px solid ${statusColor}\"><tr><td style=\"padding:28px 34px 16px\"><img src=\"https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/GLM.png\" width=\"220\" alt=\"GomezLee Marketing\" style=\"display:block;width:220px;max-width:100%;height:auto\"></td></tr><tr><td style=\"padding:8px 34px 0\"><div style=\"font-size:11px;font-weight:700;letter-spacing:2px;color:#4F758B\">GLM HUB · DECISIÓN REGISTRADA</div><h1 style=\"margin:12px 0 8px;font-size:30px;line-height:1.12;color:#4F758B\">Solicitud ${statusLabel.toLowerCase()}.</h1><p style=\"margin:0;font-size:15px;line-height:1.6\">IT Support debe continuar según la decisión indicada.</p></td></tr><tr><td style=\"padding:24px 34px\"><div style=\"display:inline-block;margin-bottom:16px;padding:7px 12px;border:1px solid ${statusColor};color:${statusColor};font-size:12px;font-weight:700;letter-spacing:1px\">${statusLabel}</div><table role=\"presentation\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\" style=\"border-collapse:collapse;border:1px solid #D0D0D0\"><tr><td style=\"width:38%;padding:12px 14px;background:#EEF6E8;border-bottom:1px solid #D0D0D0;font-size:13px;font-weight:700;color:#4F758B\">Persona solicitante</td><td style=\"padding:12px 14px;border-bottom:1px solid #D0D0D0;font-size:14px\">${escapeHtml(requesterName)}</td></tr><tr><td style=\"padding:12px 14px;background:#EEF6E8;border-bottom:1px solid #D0D0D0;font-size:13px;font-weight:700;color:#4F758B\">Correo solicitante</td><td style=\"padding:12px 14px;border-bottom:1px solid #D0D0D0;font-size:14px\">${escapeHtml(requesterEmail)}</td></tr><tr><td style=\"padding:12px 14px;background:#EEF6E8;border-bottom:1px solid #D0D0D0;font-size:13px;font-weight:700;color:#4F758B\">Aplicación</td><td style=\"padding:12px 14px;border-bottom:1px solid #D0D0D0;font-size:14px;font-weight:700;color:#4F758B\">${escapeHtml(appName)}</td></tr><tr><td style=\"padding:12px 14px;background:#EEF6E8;border-bottom:1px solid #D0D0D0;font-size:13px;font-weight:700;color:#4F758B\">Decisión</td><td style=\"padding:12px 14px;border-bottom:1px solid #D0D0D0;font-size:14px;font-weight:700;color:${statusColor}\">${statusLabel}</td></tr><tr><td style=\"padding:12px 14px;background:#EEF6E8;border-bottom:1px solid #D0D0D0;font-size:13px;font-weight:700;color:#4F758B\">Decidido por</td><td style=\"padding:12px 14px;border-bottom:1px solid #D0D0D0;font-size:14px\">${escapeHtml(decidedByName)}</td></tr><tr><td style=\"padding:12px 14px;background:#EEF6E8;font-size:13px;font-weight:700;color:#4F758B\">Correo de quien decidió</td><td style=\"padding:12px 14px;font-size:14px\">${escapeHtml(decidedByEmail)}</td></tr></table></td></tr><tr><td style=\"padding:18px 34px;border-top:1px solid #D0D0D0;font-size:11px;line-height:1.5;color:#6B8FA3\">GomezLee Marketing · GLM Hub · Uso interno</td></tr></table></td></tr></table></body></html>` : '';\nreturn [{ json: { notifyIt: ok, responseHtml, itHtml, subject: `Acceso ${approved ? 'aprobado' : 'rechazado'} | ${appName} | ${requesterName}` } }];"
|
|
},
|
|
"id": "72ee2fcd-bf95-4542-aad1-90ca4d83a2e7",
|
|
"name": "Preparar resultado de decisión",
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
128,
|
|
960
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"conditions": {
|
|
"options": {
|
|
"caseSensitive": true,
|
|
"leftValue": "",
|
|
"typeValidation": "strict",
|
|
"version": 2
|
|
},
|
|
"conditions": [
|
|
{
|
|
"id": "2ef854a5-d270-49bf-9a6a-5bb92aeab434",
|
|
"leftValue": "={{ $json.notifyIt }}",
|
|
"rightValue": true,
|
|
"operator": {
|
|
"type": "boolean",
|
|
"operation": "true",
|
|
"singleValue": true
|
|
}
|
|
}
|
|
],
|
|
"combinator": "and"
|
|
},
|
|
"options": {}
|
|
},
|
|
"id": "9f4e6707-3cb1-4c0e-833c-05be87602e07",
|
|
"name": "¿Notificar a IT Support?",
|
|
"type": "n8n-nodes-base.if",
|
|
"typeVersion": 2.2,
|
|
"position": [
|
|
368,
|
|
960
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"sendTo": "itsupport@gomezleemarketing.com",
|
|
"subject": "={{ $json.subject }}",
|
|
"message": "={{ $json.itHtml }}",
|
|
"options": {
|
|
"appendAttribution": false,
|
|
"senderName": "GLM Hub"
|
|
}
|
|
},
|
|
"id": "56d73834-fc03-43c7-9844-dd4e691439b2",
|
|
"name": "Enviar decisión a IT Support",
|
|
"type": "n8n-nodes-base.gmail",
|
|
"typeVersion": 2.1,
|
|
"position": [
|
|
608,
|
|
864
|
|
],
|
|
"webhookId": "234c8e3b-b904-41b8-8a74-9ca835f759df",
|
|
"credentials": {
|
|
"gmailOAuth2": {
|
|
"id": "UDcO1FLJqA453V2D",
|
|
"name": "Gmail account 3"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"parameters": {
|
|
"respondWith": "text",
|
|
"responseBody": "={{ $('Preparar resultado de decisión').first().json.responseHtml }}",
|
|
"options": {
|
|
"responseCode": 200,
|
|
"responseHeaders": {
|
|
"entries": [
|
|
{
|
|
"name": "Content-Type",
|
|
"value": "text/html; charset=utf-8"
|
|
},
|
|
{
|
|
"name": "Cache-Control",
|
|
"value": "no-store"
|
|
},
|
|
{
|
|
"name": "X-Frame-Options",
|
|
"value": "DENY"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"id": "e1d4a5ba-34fe-479a-a670-e07ebd87d0cf",
|
|
"name": "Mostrar resultado final",
|
|
"type": "n8n-nodes-base.respondToWebhook",
|
|
"typeVersion": 1.4,
|
|
"position": [
|
|
848,
|
|
960
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "const source = $input.first()?.json ?? {};\n\nconst parseJson = (value) => {\n if (typeof value !== 'string') {\n return value;\n }\n\n try {\n return JSON.parse(value);\n } catch {\n return {};\n }\n};\n\nlet payload = source;\n\n// Compatibilidad por si n8n todavía entrega algún envoltorio.\nif (source.body !== undefined) {\n payload = source.body;\n} else if (source.data !== undefined) {\n payload = source.data;\n}\n\npayload = parseJson(payload);\n\nif (Array.isArray(payload)) {\n payload = payload[0] ?? {};\n}\n\nconst registered = payload?.ok === true;\nconst code = String(payload?.code ?? '').toUpperCase();\nconst internalMessage = String(\n payload?.error ??\n payload?.message ??\n ''\n).trim();\n\nconst normalizedMessage = internalMessage.toLowerCase();\n\nlet publicError = 'No pudimos enviar la solicitud. Intenta nuevamente.';\n\nif (\n code === 'PENDING_EXISTS' ||\n normalizedMessage.includes('solicitud pendiente')\n) {\n publicError = 'Ya tienes una solicitud pendiente para esta aplicación.';\n} else if (\n normalizedMessage.includes('sesión') ||\n normalizedMessage.includes('jwt')\n) {\n publicError = 'Tu sesión venció. Cierra sesión e inicia nuevamente.';\n} else if (\n normalizedMessage.includes('administradores no necesitan')\n) {\n publicError =\n 'Tu cuenta administrativa no necesita solicitar acceso desde el Hub.';\n} else if (\n normalizedMessage.includes('no está autorizado')\n) {\n publicError =\n 'Tu cuenta no está habilitada para enviar solicitudes de acceso.';\n} else if (\n normalizedMessage.includes('ya no está publicada')\n) {\n publicError =\n 'La aplicación seleccionada ya no está disponible.';\n}\n\nreturn [\n {\n json: {\n registered,\n publicError,\n internalMessage,\n requestId: String(payload?.requestId ?? ''),\n requesterName: String(payload?.requesterName ?? ''),\n requesterEmail: String(payload?.requesterEmail ?? ''),\n appName: String(payload?.appName ?? ''),\n appCategory: String(payload?.appCategory ?? '')\n }\n }\n];"
|
|
},
|
|
"id": "e2cc29db-5db2-46ea-a388-054c320ecfc2",
|
|
"name": "Confirmar registro de solicitud",
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
96,
|
|
128
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"conditions": {
|
|
"options": {
|
|
"caseSensitive": true,
|
|
"leftValue": "",
|
|
"typeValidation": "strict",
|
|
"version": 2
|
|
},
|
|
"conditions": [
|
|
{
|
|
"id": "aa091e1c-09f8-4cdb-b8ac-3cbc4a7a33c8",
|
|
"leftValue": "={{ $json.registered }}",
|
|
"rightValue": true,
|
|
"operator": {
|
|
"type": "boolean",
|
|
"operation": "true",
|
|
"singleValue": true
|
|
}
|
|
}
|
|
],
|
|
"combinator": "and"
|
|
},
|
|
"options": {}
|
|
},
|
|
"id": "7f6803be-bc3a-45e1-b1a4-c04c33ca25b9",
|
|
"name": "¿Solicitud registrada en Supabase?",
|
|
"type": "n8n-nodes-base.if",
|
|
"typeVersion": 2.2,
|
|
"position": [
|
|
320,
|
|
128
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"method": "POST",
|
|
"url": "https://dbit.digitalcompass.agency/rest/v1/rpc/glm_hub_issue_access_request_tokens",
|
|
"sendHeaders": true,
|
|
"headerParameters": {
|
|
"parameters": [
|
|
{
|
|
"name": "apikey",
|
|
"value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q"
|
|
},
|
|
{
|
|
"name": "Authorization",
|
|
"value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q"
|
|
},
|
|
{
|
|
"name": "Accept",
|
|
"value": "application/json"
|
|
},
|
|
{
|
|
"name": "Content-Type",
|
|
"value": "application/json"
|
|
}
|
|
]
|
|
},
|
|
"sendBody": true,
|
|
"specifyBody": "json",
|
|
"jsonBody": "={{ {\n p_request_id: $('Confirmar registro de solicitud').first().json.requestId\n} }}",
|
|
"options": {
|
|
"response": {
|
|
"response": {
|
|
"neverError": true,
|
|
"responseFormat": "json"
|
|
}
|
|
},
|
|
"timeout": 30000
|
|
}
|
|
},
|
|
"id": "d684c268-bbd8-4a3b-8aab-b21510423c95",
|
|
"name": "Emitir enlaces de aprobación",
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4.2,
|
|
"position": [
|
|
544,
|
|
48
|
|
]
|
|
}
|
|
],
|
|
"connections": {
|
|
"Webhook - Solicitar acceso": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Validar solicitud de acceso",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Validar solicitud de acceso": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "¿Solicitud de acceso válida?",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"¿Solicitud de acceso válida?": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Crear solicitud en Supabase",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
],
|
|
[
|
|
{
|
|
"node": "Responder solicitud inválida",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Crear solicitud en Supabase": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Confirmar registro de solicitud",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Preparar correos de aprobación": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "¿Solicitud registrada?",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"¿Solicitud registrada?": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Enviar solicitud a aprobadores",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
],
|
|
[
|
|
{
|
|
"node": "Responder error al registrar",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Enviar solicitud a aprobadores": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Responder solicitud enviada",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Webhook - Confirmar decisión": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Construir página de confirmación",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Construir página de confirmación": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Mostrar confirmación",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Webhook - Resolver solicitud": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Validar decisión",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Validar decisión": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "¿Decisión válida?",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"¿Decisión válida?": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Registrar decisión en Supabase",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
],
|
|
[
|
|
{
|
|
"node": "Preparar decisión inválida",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Preparar decisión inválida": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Responder decisión inválida",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Registrar decisión en Supabase": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Preparar resultado de decisión",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Preparar resultado de decisión": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "¿Notificar a IT Support?",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"¿Notificar a IT Support?": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Enviar decisión a IT Support",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
],
|
|
[
|
|
{
|
|
"node": "Mostrar resultado final",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Enviar decisión a IT Support": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Mostrar resultado final",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Confirmar registro de solicitud": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "¿Solicitud registrada en Supabase?",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"¿Solicitud registrada en Supabase?": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Emitir enlaces de aprobación",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
],
|
|
[
|
|
{
|
|
"node": "Responder error al registrar",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Emitir enlaces de aprobación": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Preparar correos de aprobación",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
}
|
|
},
|
|
"authors": "Isaac Aracena",
|
|
"name": "Version a4528c24",
|
|
"description": "",
|
|
"autosaved": true,
|
|
"workflowPublishHistory": [
|
|
{
|
|
"createdAt": "2026-07-29T15:29:25.705Z",
|
|
"id": 3624,
|
|
"workflowId": "RaNkDfE2YCrZgCbx",
|
|
"versionId": "a4528c24-f544-4063-9bda-2b04c9943a97",
|
|
"event": "activated",
|
|
"userId": "0a88c0b1-928e-4412-896e-c5d1c99b2029"
|
|
}
|
|
]
|
|
}
|
|
} |