{ "name": "GLM Hub - Solicitudes de acceso y aprobación", "nodes": [ { "parameters": { "httpMethod": "POST", "path": "glm-hub-solicitar-acceso", "responseMode": "responseNode", "options": { "allowedOrigins": "*" } }, "id": "c1e25778-7556-4c87-9069-560c848cf185", "name": "Webhook - Solicitar acceso", "type": "n8n-nodes-base.webhook", "typeVersion": 2.1, "position": [ -240, 368 ], "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": "382d40fd-c718-4ca5-be37-33fe80ad0483", "name": "Validar solicitud de acceso", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -16, 368 ] }, { "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": "edbdd398-aa28-46b5-8fdd-d359e9d8dcbd", "name": "¿Solicitud de acceso válida?", "type": "n8n-nodes-base.if", "typeVersion": 2.2, "position": [ 208, 368 ] }, { "parameters": { "respondWith": "json", "responseBody": "={{ { ok: false, error: $json.error } }}", "options": { "responseCode": 400, "responseHeaders": { "entries": [ { "name": "Cache-Control", "value": "no-store" } ] } } }, "id": "81e1ad6e-ca84-4c3f-9897-bdfcab58e965", "name": "Responder solicitud inválida", "type": "n8n-nodes-base.respondToWebhook", "typeVersion": 1.4, "position": [ 432, 528 ] }, { "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": "Content-Type", "value": "application/json" } ] }, "sendBody": true, "contentType": "raw", "rawContentType": "application/json", "body": "={{ JSON.stringify({ p_app_id: $json.appId }) }}", "options": { "response": { "response": { "neverError": true, "responseFormat": "json", "fullResponse": true } }, "timeout": 30000 } }, "id": "513d8635-0e4b-49bf-beeb-1fcd76555b9d", "name": "Crear solicitud en Supabase", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [ 432, 288 ] }, { "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 = `
\"GomezLee
GLM HUB · SOLICITUD DE ACCESO

Una persona necesita acceso.

Hola ${escapeHtml(approver.name)}, revisa la siguiente solicitud.

Solicitante${escapeHtml(requesterName)}
Correo${escapeHtml(requesterEmail)}
Aplicación${escapeHtml(appName)}
Área${escapeHtml(appCategory)}

Por seguridad, cada botón abre una pantalla de confirmación. La primera decisión confirmada será definitiva para los tres destinatarios.

Aceptar solicitudRechazar solicitud
Solicitud ${escapeHtml(requestId)} · GomezLee Marketing · Uso interno
`;\n return { json: { created: true, requestId, sendTo: String(approver.email ?? ''), subject: `Solicitud de acceso | ${appName} | ${requesterName}`, html } };\n});" }, "id": "5cf9121e-d2e8-4975-a833-a426c77d9389", "name": "Preparar correos de aprobación", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 1328, 208 ] }, { "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": "ee5ecbd9-95ab-42f2-a013-33eff66be5ea", "name": "¿Solicitud registrada?", "type": "n8n-nodes-base.if", "typeVersion": 2.2, "position": [ 1568, 208 ] }, { "parameters": { "sendTo": "=iaracena@gomezleemarketing.com", "subject": "={{ $json.subject }}", "message": "={{ $json.html }}", "options": { "appendAttribution": false, "senderName": "GLM Hub" } }, "id": "6fafb4fa-b7bc-46ed-b435-63cbd6c85538", "name": "Enviar solicitud a aprobadores", "type": "n8n-nodes-base.gmail", "typeVersion": 2.1, "position": [ 1808, 112 ], "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": "2aea248a-8970-4439-9c5b-18800257d835", "name": "Responder solicitud enviada", "type": "n8n-nodes-base.respondToWebhook", "typeVersion": 1.4, "position": [ 2048, 112 ] }, { "parameters": { "respondWith": "json", "responseBody": "={{ { ok: false, error: $json.publicError || 'No pudimos enviar la solicitud. Intenta nuevamente.' } }}", "options": { "responseCode": 409, "responseHeaders": { "entries": [ { "name": "Cache-Control", "value": "no-store" } ] } } }, "id": "b2affc40-34dd-47a5-ae2c-c2f9e8bb4fe6", "name": "Responder error al registrar", "type": "n8n-nodes-base.respondToWebhook", "typeVersion": 1.4, "position": [ 1808, 320 ] }, { "parameters": { "path": "glm-hub-confirmar-solicitud", "responseMode": "responseNode", "options": {} }, "id": "d1276943-026e-414e-92eb-d9665fce1aa4", "name": "Webhook - Confirmar decisión", "type": "n8n-nodes-base.webhook", "typeVersion": 2.1, "position": [ -240, 832 ], "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 ? `${title} · GLM Hub
\"GomezLee

GLM HUB · DECISIÓN DE ACCESO

${title}

Vas a ${approved ? 'aprobar' : 'rechazar'} la solicitud de acceso a ${escapeHtml(appName)}. La decisión será definitiva y los otros enlaces quedarán sin efecto.

Cierra esta pestaña para cancelar sin registrar ninguna decisión.

`\n : `
\"GomezLee

Enlace no válido

El enlace está incompleto o no corresponde a una solicitud de GLM Hub.

`;\nreturn [{ json: { html } }];" }, "id": "84f519ec-c313-42ae-ba6c-7fb612b7b510", "name": "Construir página de confirmación", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -16, 832 ] }, { "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": "9fab6a61-b360-4bb9-9b07-6c8244ba40db", "name": "Mostrar confirmación", "type": "n8n-nodes-base.respondToWebhook", "typeVersion": 1.4, "position": [ 208, 832 ] }, { "parameters": { "httpMethod": "POST", "path": "glm-hub-resolver-solicitud", "responseMode": "responseNode", "options": {} }, "id": "95a17231-e0aa-4004-9d0e-dd581488b524", "name": "Webhook - Resolver solicitud", "type": "n8n-nodes-base.webhook", "typeVersion": 2.1, "position": [ -240, 1200 ], "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": "87155b04-5944-43ae-beff-9310138b2863", "name": "Validar decisión", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -16, 1200 ] }, { "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": "876e85ab-fbcd-4090-a5a4-31daf40711f5", "name": "¿Decisión válida?", "type": "n8n-nodes-base.if", "typeVersion": 2.2, "position": [ 208, 1200 ] }, { "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 = `
\"GomezLee

No se pudo procesar

${escapeHtml(message)}

`;\nreturn [{ json: { html } }];" }, "id": "c0527ef0-615e-463d-9e54-f7a942e02421", "name": "Preparar decisión inválida", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 432, 1392 ] }, { "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": "88bb2617-4e75-4f99-9aca-7c06bb28c86e", "name": "Responder decisión inválida", "type": "n8n-nodes-base.respondToWebhook", "typeVersion": 1.4, "position": [ 672, 1392 ] }, { "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" } ] }, "sendBody": true, "contentType": "raw", "rawContentType": "application/json", "body": "={{ JSON.stringify({ p_token: $json.token, p_decision: $json.decision }) }}", "options": { "response": { "response": { "neverError": true, "responseFormat": "json" } }, "timeout": 30000 } }, "id": "675cfecd-8a18-4c75-8e3f-aebb79390f34", "name": "Registrar decisión en Supabase", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [ 432, 1120 ] }, { "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 = `${responseTitle} · GLM Hub
\"GomezLee

GLM HUB · SOLICITUD DE ACCESO

${responseTitle}

${responseMessage}

Ya puedes cerrar esta pestaña.

`;\nconst itHtml = ok ? `
\"GomezLee
GLM HUB · DECISIÓN REGISTRADA

Solicitud ${statusLabel.toLowerCase()}.

IT Support debe continuar según la decisión indicada.

${statusLabel}
Persona solicitante${escapeHtml(requesterName)}
Correo solicitante${escapeHtml(requesterEmail)}
Aplicación${escapeHtml(appName)}
Decisión${statusLabel}
Decidido por${escapeHtml(decidedByName)}
Correo de quien decidió${escapeHtml(decidedByEmail)}
GomezLee Marketing · GLM Hub · Uso interno
` : '';\nreturn [{ json: { notifyIt: ok, responseHtml, itHtml, subject: `Acceso ${approved ? 'aprobado' : 'rechazado'} | ${appName} | ${requesterName}` } }];" }, "id": "2c8fb533-fcc0-4958-a210-9498c55d98d2", "name": "Preparar resultado de decisión", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 672, 1120 ] }, { "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": "993d5ec8-1187-4e92-adce-8c9b41c08a91", "name": "¿Notificar a IT Support?", "type": "n8n-nodes-base.if", "typeVersion": 2.2, "position": [ 912, 1120 ] }, { "parameters": { "sendTo": "iaracena@gomezleemarketing.com", "subject": "={{ $json.subject }}", "message": "={{ $json.itHtml }}", "options": { "appendAttribution": false, "senderName": "GLM Hub" } }, "id": "c9fbf481-045c-4f2b-80bd-2ad006b0af56", "name": "Enviar decisión a IT Support", "type": "n8n-nodes-base.gmail", "typeVersion": 2.1, "position": [ 1152, 1024 ], "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": "d16bcca2-1d7c-44e0-be65-658478a2623f", "name": "Mostrar resultado final", "type": "n8n-nodes-base.respondToWebhook", "typeVersion": 1.4, "position": [ 1392, 1120 ] }, { "parameters": { "jsCode": "const source = $input.first()?.json ?? {};\n\nconst parseJson = (value) => {\n if (typeof value !== 'string') return value;\n try { return JSON.parse(value); } catch { return value; }\n};\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}\n\nconst statusCode = Number(source.statusCode ?? source.status ?? source.response?.statusCode ?? 200);\nconst rawError = String(payload?.error ?? payload?.message ?? source?.message ?? '').trim();\nconst normalized = rawError.toLowerCase();\nconst code = String(payload?.code ?? '').toUpperCase();\n\nlet publicError = 'No pudimos registrar la solicitud. Intenta nuevamente.';\nif (code === 'PENDING_EXISTS' || normalized.includes('pendiente')) {\n publicError = 'Ya tienes una solicitud pendiente para esta aplicación.';\n} else if (normalized.includes('sesión') || statusCode === 401) {\n publicError = 'Tu sesión venció. Cierra sesión e inicia nuevamente.';\n} else if (normalized.includes('ya no está publicada') || normalized.includes('no está publicada')) {\n publicError = 'La aplicación seleccionada ya no está disponible.';\n} else if (normalized.includes('no está autorizado') || statusCode === 403) {\n publicError = 'Tu cuenta no está habilitada para enviar esta solicitud.';\n} else if (normalized.includes('administradores no necesitan')) {\n publicError = 'Tu cuenta administrativa no necesita solicitar acceso desde el Hub.';\n}\n\nreturn [{ json: {\n registered: payload?.ok === true && statusCode >= 200 && statusCode < 300,\n publicError,\n technicalError: rawError || `Respuesta HTTP ${statusCode} sin detalle`,\n statusCode,\n requestId: String(payload?.requestId ?? '')\n} }];" }, "id": "1f46de7c-f14a-44b1-8852-cf1a9d4d6bd5", "name": "Confirmar registro de solicitud", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 640, 288 ] }, { "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": "0b0d9f6a-2d91-4d8f-b4f8-b20111a4d136", "name": "¿Solicitud registrada en Supabase?", "type": "n8n-nodes-base.if", "typeVersion": 2.2, "position": [ 864, 288 ] }, { "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" } ] }, "sendBody": true, "contentType": "raw", "rawContentType": "application/json", "body": "={{ JSON.stringify({ p_request_id: $json.requestId }) }}", "options": { "response": { "response": { "neverError": true, "responseFormat": "json", "fullResponse": true } }, "timeout": 30000 } }, "id": "9203157e-c9e7-4ad3-bb35-ae683c594f6b", "name": "Emitir enlaces de aprobación", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [ 1088, 208 ] } ], "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 } ] ] } }, "active": true, "settings": { "executionOrder": "v1", "binaryMode": "separate" }, "tags": [] }