diff --git a/glm-hub-generar-icono-con-gemini.json b/glm-hub-generar-icono-con-gemini.json new file mode 100644 index 0000000..c4e455a --- /dev/null +++ b/glm-hub-generar-icono-con-gemini.json @@ -0,0 +1,960 @@ +{ + "updatedAt": "2026-07-29T18:48:09.844Z", + "createdAt": "2026-07-28T16:18:07.824Z", + "id": "F2rMlVS9mBypNNn7", + "name": "GLM Hub - Generar ícono con Gemini", + "description": null, + "active": true, + "isArchived": false, + "nodes": [ + { + "parameters": { + "httpMethod": "POST", + "path": "glm-hub-generar-icono", + "responseMode": "responseNode", + "options": { + "allowedOrigins": "*" + } + }, + "id": "e9002643-523d-436d-8c57-3d37f80f7ec3", + "name": "Webhook - Generar ícono", + "type": "n8n-nodes-base.webhook", + "typeVersion": 2.1, + "position": [ + -2992, + 464 + ], + "webhookId": "bc20a398-5ecf-42a2-9d17-9396aa278708" + }, + { + "parameters": { + "jsCode": "const request = $input.first().json ?? {};\nconst rawBody = request.body ?? request;\nlet body = rawBody;\n\nif (typeof rawBody === 'string') {\n try {\n body = JSON.parse(rawBody);\n } catch {\n body = {};\n }\n}\n\nconst headers = request.headers ?? {};\nconst headerAuthorization = String(headers.authorization ?? headers.Authorization ?? '').trim();\nconst accessToken = String(body.accessToken ?? '').trim();\nconst authorization = headerAuthorization.toLowerCase().startsWith('bearer ')\n ? headerAuthorization\n : accessToken\n ? `Bearer ${accessToken}`\n : '';\nconst name = String(body.name ?? '').trim();\nconst description = String(body.description ?? '').trim();\n\nlet error = '';\nif (!authorization.toLowerCase().startsWith('bearer ')) {\n error = 'La sesión de Supabase no fue enviada o no es válida.';\n} else if (name.length < 2 || name.length > 60) {\n error = 'El nombre debe contener entre 2 y 60 caracteres.';\n} else if (!description || description.length > 180) {\n error = 'La descripción debe contener entre 1 y 180 caracteres.';\n}\n\nreturn [{ json: { valid: !error, error, authorization, name, description } }];" + }, + "id": "f7b0a22e-2651-4479-86c8-25262faa38c4", + "name": "Validar solicitud", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + -2736, + 464 + ] + }, + { + "parameters": { + "conditions": { + "options": { + "caseSensitive": true, + "leftValue": "", + "typeValidation": "strict", + "version": 2 + }, + "conditions": [ + { + "id": "308c498d-7c3f-4414-a21f-571276aee28b", + "leftValue": "={{ $json.valid }}", + "rightValue": true, + "operator": { + "type": "boolean", + "operation": "true", + "singleValue": true + } + } + ], + "combinator": "and" + }, + "options": {} + }, + "id": "0313d3ca-0439-4c2d-bd34-4da5ef6c3950", + "name": "¿Solicitud válida?", + "type": "n8n-nodes-base.if", + "typeVersion": 2.2, + "position": [ + -2512, + 464 + ] + }, + { + "parameters": { + "respondWith": "json", + "responseBody": "={{ { ok: false, error: $json.error } }}", + "options": { + "responseCode": 400, + "responseHeaders": { + "entries": [ + { + "name": "Cache-Control", + "value": "no-store" + } + ] + } + } + }, + "id": "bcfcc3ea-bbb3-4346-a5b4-1681d226a40c", + "name": "Responder 400", + "type": "n8n-nodes-base.respondToWebhook", + "typeVersion": 1.4, + "position": [ + -2272, + 640 + ] + }, + { + "parameters": { + "method": "POST", + "url": "https://dbit.digitalcompass.agency/rest/v1/rpc/glm_hub_icon_generation_access", + "sendHeaders": true, + "headerParameters": { + "parameters": [ + { + "name": "apikey", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q" + }, + { + "name": "Authorization", + "value": "={{ $('Validar solicitud').first().json.authorization }}" + }, + { + "name": "Content-Type", + "value": "application/json" + } + ] + }, + "sendBody": true, + "specifyBody": "json", + "jsonBody": "{}", + "options": { + "response": { + "response": { + "fullResponse": true, + "neverError": true, + "responseFormat": "json" + } + }, + "timeout": 30000 + } + }, + "id": "bf1ac078-9362-445b-99d4-cd884ba5a8e1", + "name": "Validar administrador en Supabase", + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.2, + "position": [ + -2272, + 384 + ] + }, + { + "parameters": { + "jsCode": "const source = $input.first()?.json ?? {};\nconst request = $('Validar solicitud').first().json ?? {};\n\nconst statusCode = Number(source.statusCode ?? 200);\n\nlet permission = source.body ?? source;\n\nif (typeof permission === 'string') {\n try {\n permission = JSON.parse(permission);\n } catch {\n permission = {};\n }\n}\n\nif (Array.isArray(permission)) {\n permission = permission[0] ?? {};\n}\n\nif (\n permission &&\n typeof permission === 'object' &&\n permission.authorized === undefined &&\n permission.body !== undefined\n) {\n permission = permission.body;\n\n if (typeof permission === 'string') {\n try {\n permission = JSON.parse(permission);\n } catch {\n permission = {};\n }\n }\n}\n\nconst authorized =\n statusCode >= 200 &&\n statusCode < 300 &&\n (\n permission?.authorized === true ||\n String(permission?.authorized ?? '').toLowerCase() === 'true'\n );\n\nlet error = '';\n\nif (statusCode === 401) {\n error =\n 'La sesión de Supabase expiró. Cierra sesión e inicia nuevamente.';\n} else if (statusCode === 403) {\n error =\n 'Supabase rechazó la validación administrativa.';\n} else if (statusCode >= 400) {\n error =\n String(permission?.message ?? permission?.error ?? '') ||\n `Supabase respondió con el código ${statusCode}.`;\n} else if (!authorized) {\n error =\n 'Tu sesión no tiene permisos de administrador para generar íconos.';\n}\n\nreturn [\n {\n json: {\n authorized,\n error,\n name: String(request.name ?? ''),\n description: String(request.description ?? ''),\n email: String(permission?.email ?? ''),\n userId: String(permission?.userId ?? ''),\n supabaseStatus: statusCode\n }\n }\n];" + }, + "id": "0f6af309-436b-49df-8a1b-2120a0290474", + "name": "Confirmar autorización", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + -2048, + 384 + ] + }, + { + "parameters": { + "conditions": { + "options": { + "caseSensitive": true, + "leftValue": "", + "typeValidation": "strict", + "version": 2 + }, + "conditions": [ + { + "id": "c9275da4-0bca-4ce9-bd75-cc7d06e4cf56", + "leftValue": "={{ $json.authorized }}", + "rightValue": true, + "operator": { + "type": "boolean", + "operation": "true", + "singleValue": true + } + } + ], + "combinator": "and" + }, + "options": {} + }, + "id": "c115098c-9289-46ad-902b-d79aa8168bc0", + "name": "¿Es administrador?", + "type": "n8n-nodes-base.if", + "typeVersion": 2.2, + "position": [ + -1840, + 384 + ] + }, + { + "parameters": { + "respondWith": "json", + "responseBody": "={{ { ok: false, error: $json.error } }}", + "options": { + "responseCode": 403, + "responseHeaders": { + "entries": [ + { + "name": "Cache-Control", + "value": "no-store" + } + ] + } + } + }, + "id": "c44dee89-da47-43d0-9134-a723f80f7e39", + "name": "Responder 403", + "type": "n8n-nodes-base.respondToWebhook", + "typeVersion": 1.4, + "position": [ + -1600, + 560 + ] + }, + { + "parameters": { + "jsCode": "const { name, description } = $input.first().json;\nconst prompt = `Crea un ícono cuadrado 1:1, profesional, minimalista y fácil de reconocer para una aplicación interna de GomezLee Marketing.\n\nNombre de la aplicación: ${name}\nDescripción funcional: ${description}\n\nRequisitos visuales obligatorios:\n- Representa la función principal con un único símbolo simple, claro y reconocible.\n- Estética corporativa moderna, limpia y tecnológica.\n- Paleta inspirada en GLM: azul grisáceo, verde claro y verde azulado.\n- Fondo blanco o transparente.\n- Composición centrada, con buen espacio de respiración.\n- Sin texto, sin letras, sin siglas, sin números, sin marcas comerciales y sin logotipos existentes.\n- Sin mockups, sin fotografías, sin personas, sin sombras exageradas y sin fondos complejos.\n- Debe seguir siendo legible al reducirse a 32 x 32 píxeles.\n- Entrega solamente el ícono final.`;\nreturn [{ json: { prompt } }];" + }, + "id": "4a092f12-4966-4d3e-ba45-f82626f8aff8", + "name": "Construir prompt del ícono", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + -1600, + 272 + ] + }, + { + "parameters": { + "resource": "image", + "modelId": { + "__rl": true, + "value": "models/gemini-3-pro-image", + "mode": "list", + "cachedResultName": "models/gemini-3-pro-image (Nano Banana Pro)" + }, + "prompt": "={{ $json.prompt }}", + "options": { + "binaryPropertyOutput": "data" + } + }, + "id": "0daf0a06-a280-4500-95f6-f76b8945ed63", + "name": "Generate an image", + "type": "@n8n/n8n-nodes-langchain.googleGemini", + "typeVersion": 1.2, + "position": [ + -1360, + 272 + ], + "credentials": { + "googlePalmApi": { + "id": "jvsXYwL6IOoY2DBU", + "name": "Isaac - Gemini Api Pago" + } + }, + "onError": "continueErrorOutput" + }, + { + "parameters": { + "respondWith": "binary", + "responseDataSource": "set", + "options": { + "responseCode": 200, + "responseHeaders": { + "entries": [ + { + "name": "Cache-Control", + "value": "no-store" + }, + { + "name": "Content-Disposition", + "value": "inline; filename=\"glm-hub-icon.png\"" + }, + { + "name": "Content-Type", + "value": "image/png" + }, + { + "name": "Cache-Control", + "value": "no-store" + } + ] + } + } + }, + "id": "8594ec32-5d88-4435-aa8f-7d95d819eada", + "name": "Responder con ícono", + "type": "n8n-nodes-base.respondToWebhook", + "typeVersion": 1.4, + "position": [ + -1072, + 192 + ] + }, + { + "parameters": { + "jsCode": "const input = $input.first().json ?? {};\nconst rawError = input.error ?? input.message ?? input.description ?? input;\nlet message = 'Gemini no pudo generar el ícono. Revisa la credencial, el modelo y la cuota disponible.';\n\nif (typeof rawError === 'string' && rawError.trim()) {\n message = rawError.trim();\n} else if (rawError && typeof rawError === 'object') {\n message = String(rawError.message ?? rawError.description ?? rawError.status ?? message);\n}\n\nreturn [{ json: { ok: false, error: message } }];" + }, + "id": "ef98ab6b-3341-441b-83bc-41eabfe1d839", + "name": "Preparar error de Gemini", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + -1072, + 384 + ] + }, + { + "parameters": { + "respondWith": "json", + "responseBody": "={{ { ok: false, error: $json.error } }}", + "options": { + "responseCode": 502, + "responseHeaders": { + "entries": [ + { + "name": "Cache-Control", + "value": "no-store" + } + ] + } + } + }, + "id": "273ca42a-ef45-426d-a5c1-e4a3f9ea704d", + "name": "Responder error de Gemini", + "type": "n8n-nodes-base.respondToWebhook", + "typeVersion": 1.4, + "position": [ + -832, + 384 + ] + } + ], + "connections": { + "Webhook - Generar ícono": { + "main": [ + [ + { + "node": "Validar solicitud", + "type": "main", + "index": 0 + } + ] + ] + }, + "Validar solicitud": { + "main": [ + [ + { + "node": "¿Solicitud válida?", + "type": "main", + "index": 0 + } + ] + ] + }, + "¿Solicitud válida?": { + "main": [ + [ + { + "node": "Validar administrador en Supabase", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "Responder 400", + "type": "main", + "index": 0 + } + ] + ] + }, + "Validar administrador en Supabase": { + "main": [ + [ + { + "node": "Confirmar autorización", + "type": "main", + "index": 0 + } + ] + ] + }, + "Confirmar autorización": { + "main": [ + [ + { + "node": "¿Es administrador?", + "type": "main", + "index": 0 + } + ] + ] + }, + "¿Es administrador?": { + "main": [ + [ + { + "node": "Construir prompt del ícono", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "Responder 403", + "type": "main", + "index": 0 + } + ] + ] + }, + "Construir prompt del ícono": { + "main": [ + [ + { + "node": "Generate an image", + "type": "main", + "index": 0 + } + ] + ] + }, + "Generate an image": { + "main": [ + [ + { + "node": "Responder con ícono", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "Preparar error de Gemini", + "type": "main", + "index": 0 + } + ] + ] + }, + "Preparar error de Gemini": { + "main": [ + [ + { + "node": "Responder error de Gemini", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "settings": { + "executionOrder": "v1", + "binaryMode": "separate", + "availableInMCP": true + }, + "staticData": null, + "meta": { + "templateCredsSetupCompleted": true + }, + "versionId": "ce03fd5f-2d70-44f1-b1e2-b31fc92ddf62", + "activeVersionId": "ce03fd5f-2d70-44f1-b1e2-b31fc92ddf62", + "versionCounter": 75, + "triggerCount": 1, + "shared": [ + { + "updatedAt": "2026-07-28T16:18:07.831Z", + "createdAt": "2026-07-28T16:18:07.831Z", + "role": "workflow:owner", + "workflowId": "F2rMlVS9mBypNNn7", + "projectId": "PJpTANzTXIFibWsW", + "project": { + "updatedAt": "2026-04-22T14:25:09.686Z", + "createdAt": "2026-04-22T14:22:54.790Z", + "id": "PJpTANzTXIFibWsW", + "name": "Isaac Aracena ", + "type": "personal", + "icon": null, + "description": null, + "creatorId": "0a88c0b1-928e-4412-896e-c5d1c99b2029" + } + } + ], + "tags": [], + "activeVersion": { + "updatedAt": "2026-07-28T20:24:18.000Z", + "createdAt": "2026-07-28T20:23:55.454Z", + "versionId": "ce03fd5f-2d70-44f1-b1e2-b31fc92ddf62", + "workflowId": "F2rMlVS9mBypNNn7", + "nodes": [ + { + "parameters": { + "httpMethod": "POST", + "path": "glm-hub-generar-icono", + "responseMode": "responseNode", + "options": { + "allowedOrigins": "*" + } + }, + "id": "e9002643-523d-436d-8c57-3d37f80f7ec3", + "name": "Webhook - Generar ícono", + "type": "n8n-nodes-base.webhook", + "typeVersion": 2.1, + "position": [ + -2992, + 464 + ], + "webhookId": "bc20a398-5ecf-42a2-9d17-9396aa278708" + }, + { + "parameters": { + "jsCode": "const request = $input.first().json ?? {};\nconst rawBody = request.body ?? request;\nlet body = rawBody;\n\nif (typeof rawBody === 'string') {\n try {\n body = JSON.parse(rawBody);\n } catch {\n body = {};\n }\n}\n\nconst headers = request.headers ?? {};\nconst headerAuthorization = String(headers.authorization ?? headers.Authorization ?? '').trim();\nconst accessToken = String(body.accessToken ?? '').trim();\nconst authorization = headerAuthorization.toLowerCase().startsWith('bearer ')\n ? headerAuthorization\n : accessToken\n ? `Bearer ${accessToken}`\n : '';\nconst name = String(body.name ?? '').trim();\nconst description = String(body.description ?? '').trim();\n\nlet error = '';\nif (!authorization.toLowerCase().startsWith('bearer ')) {\n error = 'La sesión de Supabase no fue enviada o no es válida.';\n} else if (name.length < 2 || name.length > 60) {\n error = 'El nombre debe contener entre 2 y 60 caracteres.';\n} else if (!description || description.length > 180) {\n error = 'La descripción debe contener entre 1 y 180 caracteres.';\n}\n\nreturn [{ json: { valid: !error, error, authorization, name, description } }];" + }, + "id": "f7b0a22e-2651-4479-86c8-25262faa38c4", + "name": "Validar solicitud", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + -2736, + 464 + ] + }, + { + "parameters": { + "conditions": { + "options": { + "caseSensitive": true, + "leftValue": "", + "typeValidation": "strict", + "version": 2 + }, + "conditions": [ + { + "id": "308c498d-7c3f-4414-a21f-571276aee28b", + "leftValue": "={{ $json.valid }}", + "rightValue": true, + "operator": { + "type": "boolean", + "operation": "true", + "singleValue": true + } + } + ], + "combinator": "and" + }, + "options": {} + }, + "id": "0313d3ca-0439-4c2d-bd34-4da5ef6c3950", + "name": "¿Solicitud válida?", + "type": "n8n-nodes-base.if", + "typeVersion": 2.2, + "position": [ + -2512, + 464 + ] + }, + { + "parameters": { + "respondWith": "json", + "responseBody": "={{ { ok: false, error: $json.error } }}", + "options": { + "responseCode": 400, + "responseHeaders": { + "entries": [ + { + "name": "Cache-Control", + "value": "no-store" + } + ] + } + } + }, + "id": "bcfcc3ea-bbb3-4346-a5b4-1681d226a40c", + "name": "Responder 400", + "type": "n8n-nodes-base.respondToWebhook", + "typeVersion": 1.4, + "position": [ + -2272, + 640 + ] + }, + { + "parameters": { + "method": "POST", + "url": "https://dbit.digitalcompass.agency/rest/v1/rpc/glm_hub_icon_generation_access", + "sendHeaders": true, + "headerParameters": { + "parameters": [ + { + "name": "apikey", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q" + }, + { + "name": "Authorization", + "value": "={{ $('Validar solicitud').first().json.authorization }}" + }, + { + "name": "Content-Type", + "value": "application/json" + } + ] + }, + "sendBody": true, + "specifyBody": "json", + "jsonBody": "{}", + "options": { + "response": { + "response": { + "fullResponse": true, + "neverError": true, + "responseFormat": "json" + } + }, + "timeout": 30000 + } + }, + "id": "bf1ac078-9362-445b-99d4-cd884ba5a8e1", + "name": "Validar administrador en Supabase", + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.2, + "position": [ + -2272, + 384 + ] + }, + { + "parameters": { + "jsCode": "const source = $input.first()?.json ?? {};\nconst request = $('Validar solicitud').first().json ?? {};\n\nconst statusCode = Number(source.statusCode ?? 200);\n\nlet permission = source.body ?? source;\n\nif (typeof permission === 'string') {\n try {\n permission = JSON.parse(permission);\n } catch {\n permission = {};\n }\n}\n\nif (Array.isArray(permission)) {\n permission = permission[0] ?? {};\n}\n\nif (\n permission &&\n typeof permission === 'object' &&\n permission.authorized === undefined &&\n permission.body !== undefined\n) {\n permission = permission.body;\n\n if (typeof permission === 'string') {\n try {\n permission = JSON.parse(permission);\n } catch {\n permission = {};\n }\n }\n}\n\nconst authorized =\n statusCode >= 200 &&\n statusCode < 300 &&\n (\n permission?.authorized === true ||\n String(permission?.authorized ?? '').toLowerCase() === 'true'\n );\n\nlet error = '';\n\nif (statusCode === 401) {\n error =\n 'La sesión de Supabase expiró. Cierra sesión e inicia nuevamente.';\n} else if (statusCode === 403) {\n error =\n 'Supabase rechazó la validación administrativa.';\n} else if (statusCode >= 400) {\n error =\n String(permission?.message ?? permission?.error ?? '') ||\n `Supabase respondió con el código ${statusCode}.`;\n} else if (!authorized) {\n error =\n 'Tu sesión no tiene permisos de administrador para generar íconos.';\n}\n\nreturn [\n {\n json: {\n authorized,\n error,\n name: String(request.name ?? ''),\n description: String(request.description ?? ''),\n email: String(permission?.email ?? ''),\n userId: String(permission?.userId ?? ''),\n supabaseStatus: statusCode\n }\n }\n];" + }, + "id": "0f6af309-436b-49df-8a1b-2120a0290474", + "name": "Confirmar autorización", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + -2048, + 384 + ] + }, + { + "parameters": { + "conditions": { + "options": { + "caseSensitive": true, + "leftValue": "", + "typeValidation": "strict", + "version": 2 + }, + "conditions": [ + { + "id": "c9275da4-0bca-4ce9-bd75-cc7d06e4cf56", + "leftValue": "={{ $json.authorized }}", + "rightValue": true, + "operator": { + "type": "boolean", + "operation": "true", + "singleValue": true + } + } + ], + "combinator": "and" + }, + "options": {} + }, + "id": "c115098c-9289-46ad-902b-d79aa8168bc0", + "name": "¿Es administrador?", + "type": "n8n-nodes-base.if", + "typeVersion": 2.2, + "position": [ + -1840, + 384 + ] + }, + { + "parameters": { + "respondWith": "json", + "responseBody": "={{ { ok: false, error: $json.error } }}", + "options": { + "responseCode": 403, + "responseHeaders": { + "entries": [ + { + "name": "Cache-Control", + "value": "no-store" + } + ] + } + } + }, + "id": "c44dee89-da47-43d0-9134-a723f80f7e39", + "name": "Responder 403", + "type": "n8n-nodes-base.respondToWebhook", + "typeVersion": 1.4, + "position": [ + -1600, + 560 + ] + }, + { + "parameters": { + "jsCode": "const { name, description } = $input.first().json;\nconst prompt = `Crea un ícono cuadrado 1:1, profesional, minimalista y fácil de reconocer para una aplicación interna de GomezLee Marketing.\n\nNombre de la aplicación: ${name}\nDescripción funcional: ${description}\n\nRequisitos visuales obligatorios:\n- Representa la función principal con un único símbolo simple, claro y reconocible.\n- Estética corporativa moderna, limpia y tecnológica.\n- Paleta inspirada en GLM: azul grisáceo, verde claro y verde azulado.\n- Fondo blanco o transparente.\n- Composición centrada, con buen espacio de respiración.\n- Sin texto, sin letras, sin siglas, sin números, sin marcas comerciales y sin logotipos existentes.\n- Sin mockups, sin fotografías, sin personas, sin sombras exageradas y sin fondos complejos.\n- Debe seguir siendo legible al reducirse a 32 x 32 píxeles.\n- Entrega solamente el ícono final.`;\nreturn [{ json: { prompt } }];" + }, + "id": "4a092f12-4966-4d3e-ba45-f82626f8aff8", + "name": "Construir prompt del ícono", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + -1600, + 272 + ] + }, + { + "parameters": { + "resource": "image", + "modelId": { + "__rl": true, + "value": "models/gemini-3-pro-image", + "mode": "list", + "cachedResultName": "models/gemini-3-pro-image (Nano Banana Pro)" + }, + "prompt": "={{ $json.prompt }}", + "options": { + "binaryPropertyOutput": "data" + } + }, + "id": "0daf0a06-a280-4500-95f6-f76b8945ed63", + "name": "Generate an image", + "type": "@n8n/n8n-nodes-langchain.googleGemini", + "typeVersion": 1.2, + "position": [ + -1360, + 272 + ], + "credentials": { + "googlePalmApi": { + "id": "jvsXYwL6IOoY2DBU", + "name": "Isaac - Gemini Api Pago" + } + }, + "onError": "continueErrorOutput" + }, + { + "parameters": { + "respondWith": "binary", + "responseDataSource": "set", + "options": { + "responseCode": 200, + "responseHeaders": { + "entries": [ + { + "name": "Cache-Control", + "value": "no-store" + }, + { + "name": "Content-Disposition", + "value": "inline; filename=\"glm-hub-icon.png\"" + }, + { + "name": "Content-Type", + "value": "image/png" + }, + { + "name": "Cache-Control", + "value": "no-store" + } + ] + } + } + }, + "id": "8594ec32-5d88-4435-aa8f-7d95d819eada", + "name": "Responder con ícono", + "type": "n8n-nodes-base.respondToWebhook", + "typeVersion": 1.4, + "position": [ + -1072, + 192 + ] + }, + { + "parameters": { + "jsCode": "const input = $input.first().json ?? {};\nconst rawError = input.error ?? input.message ?? input.description ?? input;\nlet message = 'Gemini no pudo generar el ícono. Revisa la credencial, el modelo y la cuota disponible.';\n\nif (typeof rawError === 'string' && rawError.trim()) {\n message = rawError.trim();\n} else if (rawError && typeof rawError === 'object') {\n message = String(rawError.message ?? rawError.description ?? rawError.status ?? message);\n}\n\nreturn [{ json: { ok: false, error: message } }];" + }, + "id": "ef98ab6b-3341-441b-83bc-41eabfe1d839", + "name": "Preparar error de Gemini", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + -1072, + 384 + ] + }, + { + "parameters": { + "respondWith": "json", + "responseBody": "={{ { ok: false, error: $json.error } }}", + "options": { + "responseCode": 502, + "responseHeaders": { + "entries": [ + { + "name": "Cache-Control", + "value": "no-store" + } + ] + } + } + }, + "id": "273ca42a-ef45-426d-a5c1-e4a3f9ea704d", + "name": "Responder error de Gemini", + "type": "n8n-nodes-base.respondToWebhook", + "typeVersion": 1.4, + "position": [ + -832, + 384 + ] + } + ], + "connections": { + "Webhook - Generar ícono": { + "main": [ + [ + { + "node": "Validar solicitud", + "type": "main", + "index": 0 + } + ] + ] + }, + "Validar solicitud": { + "main": [ + [ + { + "node": "¿Solicitud válida?", + "type": "main", + "index": 0 + } + ] + ] + }, + "¿Solicitud válida?": { + "main": [ + [ + { + "node": "Validar administrador en Supabase", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "Responder 400", + "type": "main", + "index": 0 + } + ] + ] + }, + "Validar administrador en Supabase": { + "main": [ + [ + { + "node": "Confirmar autorización", + "type": "main", + "index": 0 + } + ] + ] + }, + "Confirmar autorización": { + "main": [ + [ + { + "node": "¿Es administrador?", + "type": "main", + "index": 0 + } + ] + ] + }, + "¿Es administrador?": { + "main": [ + [ + { + "node": "Construir prompt del ícono", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "Responder 403", + "type": "main", + "index": 0 + } + ] + ] + }, + "Construir prompt del ícono": { + "main": [ + [ + { + "node": "Generate an image", + "type": "main", + "index": 0 + } + ] + ] + }, + "Generate an image": { + "main": [ + [ + { + "node": "Responder con ícono", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "Preparar error de Gemini", + "type": "main", + "index": 0 + } + ] + ] + }, + "Preparar error de Gemini": { + "main": [ + [ + { + "node": "Responder error de Gemini", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "authors": "Isaac Aracena", + "name": "Version ce03fd5f", + "description": "", + "autosaved": true, + "workflowPublishHistory": [ + { + "createdAt": "2026-07-28T20:24:18.060Z", + "id": 3595, + "workflowId": "F2rMlVS9mBypNNn7", + "versionId": "ce03fd5f-2d70-44f1-b1e2-b31fc92ddf62", + "event": "activated", + "userId": "0a88c0b1-928e-4412-896e-c5d1c99b2029" + } + ] + } +} \ No newline at end of file