3241 lines
105 KiB
JSON
3241 lines
105 KiB
JSON
{
|
||
"name": "Lightning_Report_Manual",
|
||
"nodes": [
|
||
{
|
||
"parameters": {
|
||
"content": "export REPORT_SERVICE_TOKEN=\"your-long-random-secret\"\nTo run: uvicorn report_service.main:app --host 0.0.0.0 --port 8000\n\nOn another terminal: ngrok http 8000\n\nReport generation uses async polling nodes (Start Async Report -> Wait -> Poll -> Download). Update ngrok base URL in those HTTP nodes when the tunnel URL changes.\n",
|
||
"height": 128,
|
||
"width": 512
|
||
},
|
||
"type": "n8n-nodes-base.stickyNote",
|
||
"position": [
|
||
198256,
|
||
84640
|
||
],
|
||
"typeVersion": 1,
|
||
"id": "d3e74f9c-5ff4-4cdf-b189-038aa7b37b63",
|
||
"name": "Sticky Note1"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"method": "POST",
|
||
"url": "https://api-test.iklim.co/v1/auth/login",
|
||
"sendBody": true,
|
||
"bodyParameters": {
|
||
"parameters": [
|
||
{
|
||
"name": "username",
|
||
"value": "info@iklim.co"
|
||
},
|
||
{
|
||
"name": "password",
|
||
"value": "gn6aV01Z759HTw9I"
|
||
}
|
||
]
|
||
},
|
||
"options": {}
|
||
},
|
||
"id": "652fbcfb-cbb2-4fd9-a0da-fac80224bb3d",
|
||
"name": "Login to iklim.co",
|
||
"type": "n8n-nodes-base.httpRequest",
|
||
"typeVersion": 4.1,
|
||
"position": [
|
||
197056,
|
||
83728
|
||
],
|
||
"onError": "continueErrorOutput"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"assignments": {
|
||
"assignments": [
|
||
{
|
||
"id": "5c1668fb-f97b-4ec8-80f9-61bfb0b8274e",
|
||
"name": "username",
|
||
"value": "={{ $json.username }}",
|
||
"type": "string"
|
||
},
|
||
{
|
||
"id": "97c76bac-aa08-437f-ac48-a0cad9b93526",
|
||
"name": "type",
|
||
"value": "={{ $json.type }}",
|
||
"type": "string"
|
||
},
|
||
{
|
||
"id": "fc59d51f-b225-4abb-9f17-93173e650652",
|
||
"name": "accessToken",
|
||
"value": "={{ $json.accessToken }}",
|
||
"type": "string"
|
||
},
|
||
{
|
||
"id": "95e5ec41-2369-492b-9950-fc230a47f7fa",
|
||
"name": "accessTokenExpiration",
|
||
"value": "={{ $now.plus({ hours: 1 }).toMillis() }}",
|
||
"type": "string"
|
||
},
|
||
{
|
||
"id": "3d79d06a-ed55-48af-b4bc-b7fd67d80be1",
|
||
"name": "refreshToken",
|
||
"value": "={{ $json.refreshToken }}",
|
||
"type": "string"
|
||
},
|
||
{
|
||
"id": "aedf0ef4-9971-42e8-8b32-7d039342dd88",
|
||
"name": "refreshTokenExpiration",
|
||
"value": "={{ $now.plus({ days: 1 }).toMillis() }}",
|
||
"type": "string"
|
||
}
|
||
]
|
||
},
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.set",
|
||
"typeVersion": 3.4,
|
||
"position": [
|
||
197296,
|
||
83712
|
||
],
|
||
"id": "abe10af8-3702-4df7-b114-bd3141fd2a1d",
|
||
"name": "CalculateExpirations"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"assignments": {
|
||
"assignments": [
|
||
{
|
||
"id": "5c1668fb-f97b-4ec8-80f9-61bfb0b8274e",
|
||
"name": "username",
|
||
"value": "={{ $json.username }}",
|
||
"type": "string"
|
||
},
|
||
{
|
||
"id": "97c76bac-aa08-437f-ac48-a0cad9b93526",
|
||
"name": "type",
|
||
"value": "={{ $json.type }}",
|
||
"type": "string"
|
||
},
|
||
{
|
||
"id": "fc59d51f-b225-4abb-9f17-93173e650652",
|
||
"name": "accessToken",
|
||
"value": "={{ $json.accessToken }}",
|
||
"type": "string"
|
||
},
|
||
{
|
||
"id": "95e5ec41-2369-492b-9950-fc230a47f7fa",
|
||
"name": "accessTokenExpiration",
|
||
"value": "={{ $now.plus({ hours: 1 }).toMillis() }}",
|
||
"type": "string"
|
||
},
|
||
{
|
||
"id": "3d79d06a-ed55-48af-b4bc-b7fd67d80be1",
|
||
"name": "refreshToken",
|
||
"value": "={{ $json.refreshToken }}",
|
||
"type": "string"
|
||
},
|
||
{
|
||
"id": "aedf0ef4-9971-42e8-8b32-7d039342dd88",
|
||
"name": "refreshTokenExpiration",
|
||
"value": "={{ $now.plus({ days: 1 }).toMillis() }}",
|
||
"type": "string"
|
||
}
|
||
]
|
||
},
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.set",
|
||
"typeVersion": 3.4,
|
||
"position": [
|
||
197360,
|
||
83968
|
||
],
|
||
"id": "283b025a-0cd4-4bdb-95e6-a44ccd3f9aa1",
|
||
"name": "CalculateExpirations1"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"method": "POST",
|
||
"url": "https://api-test.iklim.co/v1/auth/refresh",
|
||
"sendBody": true,
|
||
"bodyParameters": {
|
||
"parameters": [
|
||
{
|
||
"name": "refreshToken",
|
||
"value": "={{ $input.auth..refreshToken }}"
|
||
}
|
||
]
|
||
},
|
||
"options": {}
|
||
},
|
||
"id": "7121fe11-5dd1-495b-8d75-0cf564e67710",
|
||
"name": "Refresh Token",
|
||
"type": "n8n-nodes-base.httpRequest",
|
||
"typeVersion": 4.1,
|
||
"position": [
|
||
197056,
|
||
83984
|
||
],
|
||
"onError": "continueErrorOutput"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"rules": {
|
||
"values": [
|
||
{
|
||
"conditions": {
|
||
"options": {
|
||
"caseSensitive": true,
|
||
"leftValue": "",
|
||
"typeValidation": "strict",
|
||
"version": 3
|
||
},
|
||
"conditions": [
|
||
{
|
||
"leftValue": "={{ DateTime.fromMillis($json.refreshTokenExpiration - 300000) }}",
|
||
"rightValue": "={{ $now }}",
|
||
"operator": {
|
||
"type": "dateTime",
|
||
"operation": "before"
|
||
},
|
||
"id": "2e2eea90-cc23-412f-a0f3-c8b9747d0fae"
|
||
}
|
||
],
|
||
"combinator": "and"
|
||
},
|
||
"renameOutput": true,
|
||
"outputKey": "Relogin"
|
||
},
|
||
{
|
||
"conditions": {
|
||
"options": {
|
||
"caseSensitive": true,
|
||
"leftValue": "",
|
||
"typeValidation": "strict",
|
||
"version": 3
|
||
},
|
||
"conditions": [
|
||
{
|
||
"id": "2c01f003-c0d1-4ed7-b41b-5e281b7353bb",
|
||
"leftValue": "={{ DateTime.fromMillis($json.accessTokenExpiration - 600000)}}",
|
||
"rightValue": "={{ $now }}",
|
||
"operator": {
|
||
"type": "dateTime",
|
||
"operation": "before"
|
||
}
|
||
}
|
||
],
|
||
"combinator": "and"
|
||
},
|
||
"renameOutput": true,
|
||
"outputKey": "Refresh"
|
||
},
|
||
{
|
||
"conditions": {
|
||
"options": {
|
||
"caseSensitive": true,
|
||
"leftValue": "",
|
||
"typeValidation": "strict",
|
||
"version": 3
|
||
},
|
||
"conditions": [
|
||
{
|
||
"id": "590bec27-6e6f-4f0b-a701-3a359032c4ed",
|
||
"leftValue": "={{ $now }}",
|
||
"rightValue": "",
|
||
"operator": {
|
||
"type": "dateTime",
|
||
"operation": "exists",
|
||
"singleValue": true
|
||
}
|
||
}
|
||
],
|
||
"combinator": "and"
|
||
},
|
||
"renameOutput": true,
|
||
"outputKey": "Valid"
|
||
}
|
||
]
|
||
},
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.switch",
|
||
"typeVersion": 3.4,
|
||
"position": [
|
||
196832,
|
||
83952
|
||
],
|
||
"id": "5f998d9b-0af9-46d2-9ace-9a7fa926ef7b",
|
||
"name": "Switch"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"jsCode": "const staticData = $getWorkflowStaticData('global');\n\nif (!staticData.hasOwnProperty('user')) {\n staticData.user = null;\n}\n\nif (!staticData.hasOwnProperty('authType')) {\n staticData.authType = 'Bearer';\n}\n\nif (!staticData.hasOwnProperty('accessToken')) {\n staticData.accessToken = null;\n}\n\nif (!staticData.hasOwnProperty('accessTokenExpiration')) {\n staticData.accessTokenExpiration = 0;\n}\n\nif (!staticData.hasOwnProperty('refreshToken')) {\n staticData.refreshToken = null;\n}\n\nif (!staticData.hasOwnProperty('refreshTokenExpiration')) {\n staticData.refreshTokenExpiration = 0;\n}\n\nconst input = $input.first()?.json || {};\nconst currentTime = input.timestamp ?? Date.now();\n\nreturn [\n {\n json: {\n user: staticData.user,\n bearer: staticData.bearer,\n accessToken: staticData.accessToken,\n accessTokenExpiration: staticData.accessTokenExpiration,\n refreshToken: staticData.refreshToken,\n refreshTokenExpiration: staticData.refreshTokenExpiration,\n timestamp: currentTime,\n },\n },\n];"
|
||
},
|
||
"type": "n8n-nodes-base.code",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
196640,
|
||
83968
|
||
],
|
||
"id": "8b56df42-1a88-4d08-baaf-2d1ed6887348",
|
||
"name": "Restore Credentials",
|
||
"retryOnFail": false
|
||
},
|
||
{
|
||
"parameters": {
|
||
"jsCode": "const staticData = $getWorkflowStaticData('global');\nstaticData.authRetryAttempt = 0;\nfor (const item of $input.all()) {\n staticData.user = item.json.username;\n staticData.authType = item.json.type;\n staticData.accessToken = item.json.accessToken;\n staticData.accessTokenExpiration = item.json.accessTokenExpiration;\n staticData.refreshToken = item.json.refreshToken;\n staticData.refreshTokenExpiration = item.json.refreshTokenExpiration;\n}\n\nreturn $input.all();"
|
||
},
|
||
"type": "n8n-nodes-base.code",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
197584,
|
||
83968
|
||
],
|
||
"id": "d58a574d-9712-4ebe-b941-9efa291ba21f",
|
||
"name": "Store Refresh Credentials"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"jsCode": "const staticData = $getWorkflowStaticData('global');\nstaticData.authRetryAttempt = 0;\nfor (const item of $input.all()) {\n staticData.user = item.json.username;\n staticData.authType = item.json.type;\n staticData.accessToken = item.json.accessToken;\n staticData.accessTokenExpiration = item.json.accessTokenExpiration;\n staticData.refreshToken = item.json.refreshToken;\n staticData.refreshTokenExpiration = item.json.refreshTokenExpiration;\n}\n\nreturn $input.all();"
|
||
},
|
||
"type": "n8n-nodes-base.code",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
197504,
|
||
83712
|
||
],
|
||
"id": "b8077750-76db-49f7-a371-4e839e126a0b",
|
||
"name": "Store Login Credentials"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"select": "channel",
|
||
"channelId": {
|
||
"__rl": true,
|
||
"value": "C0A9K1AC7SN",
|
||
"mode": "list",
|
||
"cachedResultName": "n8n-events"
|
||
},
|
||
"text": "=🛑 {{ $workflow.name }} — yetkilendirme başarısız ({{ $json.authRetryAttempt }}/3 deneme)\n*Hata:* {{ $json.last_error_message || $json.error?.message || $json.message }}",
|
||
"otherOptions": {
|
||
"includeLinkToWorkflow": false
|
||
}
|
||
},
|
||
"type": "n8n-nodes-base.slack",
|
||
"typeVersion": 2.4,
|
||
"position": [
|
||
197664,
|
||
83360
|
||
],
|
||
"id": "353eaf10-e27a-4bb9-a8f2-91787ea7027d",
|
||
"name": "Send Error Notification",
|
||
"webhookId": "0b3582cf-042e-43d6-bc02-4c9debab0566",
|
||
"credentials": {
|
||
"slackApi": {
|
||
"id": "OKgM8VkM05pJl9kU",
|
||
"name": "Tarla Slack Account"
|
||
}
|
||
}
|
||
},
|
||
{
|
||
"parameters": {},
|
||
"id": "027e6023-51a1-402f-99f7-a79b272d8e6e",
|
||
"name": "Daily Trigger",
|
||
"type": "n8n-nodes-base.manualTrigger",
|
||
"typeVersion": 1,
|
||
"position": [
|
||
195936,
|
||
83968
|
||
]
|
||
},
|
||
{
|
||
"parameters": {
|
||
"method": "POST",
|
||
"url": "https://api-test.iklim.co/v1/lightnings/within",
|
||
"sendHeaders": true,
|
||
"headerParameters": {
|
||
"parameters": [
|
||
{
|
||
"name": "Authorization",
|
||
"value": "={{ $json.headers.Authorization }}"
|
||
},
|
||
{
|
||
"name": "X-Signature",
|
||
"value": "={{ $json.headers['X-Signature'] }}"
|
||
},
|
||
{
|
||
"name": "X-Timestamp",
|
||
"value": "={{ $json.headers['X-Timestamp'] }}"
|
||
},
|
||
{
|
||
"name": "X-Nonce",
|
||
"value": "={{ $json.headers['X-Nonce'] }}"
|
||
},
|
||
{
|
||
"name": "X-Idempotency-Key",
|
||
"value": "={{ $json.headers['X-Idempotency-Key'] }}"
|
||
},
|
||
{
|
||
"name": "Content-Type",
|
||
"value": "={{ $json.headers['Content-Type'] }}"
|
||
},
|
||
{
|
||
"name": "Accept",
|
||
"value": "={{ $json.headers['Content-Type'] }}"
|
||
}
|
||
]
|
||
},
|
||
"sendBody": true,
|
||
"specifyBody": "json",
|
||
"jsonBody": "={{ $json.requestBody }}",
|
||
"options": {}
|
||
},
|
||
"id": "3d480a8d-13ed-422b-9757-66c4574b6073",
|
||
"name": "Lightning Request",
|
||
"type": "n8n-nodes-base.httpRequest",
|
||
"typeVersion": 4.1,
|
||
"position": [
|
||
198160,
|
||
84240
|
||
],
|
||
"onError": "continueErrorOutput"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"operation": "get",
|
||
"dataTableId": {
|
||
"__rl": true,
|
||
"value": "bMJhm1lTDbCj9eY1",
|
||
"mode": "list",
|
||
"cachedResultName": "customers",
|
||
"cachedResultUrl": "/projects/dWFeIPZ8ouir7Ex0/datatables/bMJhm1lTDbCj9eY1"
|
||
},
|
||
"filters": {
|
||
"conditions": [
|
||
{
|
||
"keyValue": "={{ $('Manual Input').first().json.customer_id }}"
|
||
}
|
||
]
|
||
}
|
||
},
|
||
"type": "n8n-nodes-base.dataTable",
|
||
"typeVersion": 1.1,
|
||
"position": [
|
||
196640,
|
||
84224
|
||
],
|
||
"id": "3d86be42-36ab-46ce-b731-26eb59aed434",
|
||
"name": "Iterate Customers"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"operation": "get",
|
||
"dataTableId": {
|
||
"__rl": true,
|
||
"value": "e2gQ68IGiYmCZkES",
|
||
"mode": "list",
|
||
"cachedResultName": "wind_turbine_farm",
|
||
"cachedResultUrl": "/projects/dWFeIPZ8ouir7Ex0/datatables/e2gQ68IGiYmCZkES"
|
||
},
|
||
"filters": {
|
||
"conditions": [
|
||
{
|
||
"keyName": "customer_id",
|
||
"keyValue": "={{ $json.id }}"
|
||
}
|
||
]
|
||
},
|
||
"returnAll": true
|
||
},
|
||
"type": "n8n-nodes-base.dataTable",
|
||
"typeVersion": 1.1,
|
||
"position": [
|
||
197376,
|
||
84240
|
||
],
|
||
"id": "9e1353e6-c8a2-4be2-8ead-22869274137b",
|
||
"name": "Get Customer Wind Turbines"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"select": "channel",
|
||
"channelId": {
|
||
"__rl": true,
|
||
"value": "C0A9K1AC7SN",
|
||
"mode": "list",
|
||
"cachedResultName": "n8n-events"
|
||
},
|
||
"text": "=🛑 {{ $workflow.name }} — iş akışı durduruldu ({{ $json.lightningRetryAttempt }}/3 deneme başarısız)\n*Müşteri:* {{ $json.customer_name }}\n*Türbin:* {{ $json.turbine_name }}\n*Hata:* {{ $json.last_error_message || $json.error?.message || $json.message }}",
|
||
"otherOptions": {
|
||
"includeLinkToWorkflow": false
|
||
}
|
||
},
|
||
"type": "n8n-nodes-base.slack",
|
||
"typeVersion": 2.4,
|
||
"position": [
|
||
198800,
|
||
83808
|
||
],
|
||
"id": "43d48e1c-e05b-4f8d-a38b-9137dc77eaf2",
|
||
"name": "Request Error Notification",
|
||
"webhookId": "38b3ebc5-a136-4b78-9c7a-98f40e92a9d5",
|
||
"notesInFlow": false,
|
||
"executeOnce": true,
|
||
"credentials": {
|
||
"slackApi": {
|
||
"id": "OKgM8VkM05pJl9kU",
|
||
"name": "Tarla Slack Account"
|
||
}
|
||
}
|
||
},
|
||
{
|
||
"parameters": {
|
||
"jsCode": "const MAX_ATTEMPTS = 3;\nconst farm = $('Loop init').all().pop().json;\nconst errorPayload = $input.first().json || {};\nconst errorMessage = errorPayload.error?.message || errorPayload.message || JSON.stringify(errorPayload);\n\nlet turbineName = 'N/A';\ntry {\n turbineName = $('Get Customer Wind Turbines').first().json.name || turbineName;\n} catch (e) {}\n\nlet previousAttempt = 0;\ntry {\n const prev = $('Lightning Retry Handler').all().pop().json;\n if (prev?.lightningRetryAttempt != null) {\n previousAttempt = Number(prev.lightningRetryAttempt);\n }\n} catch (e) {}\n\nconst attempt = previousAttempt + 1;\n\nif (attempt < MAX_ATTEMPTS) {\n return [{\n json: {\n ...farm,\n lightningRetryAttempt: attempt,\n retry_lightning: true,\n pageNumber: farm.pageNumber ?? 0,\n last_error_message: errorMessage,\n turbine_name: turbineName,\n },\n }];\n}\n\nreturn [{\n json: {\n ...farm,\n lightningRetryAttempt: attempt,\n retry_lightning: false,\n last_error_message: errorMessage,\n turbine_name: turbineName,\n error: errorPayload.error || errorPayload,\n },\n}];"
|
||
},
|
||
"type": "n8n-nodes-base.code",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
198384,
|
||
83808
|
||
],
|
||
"id": "ee2eeba4-3669-46e5-acd6-798e54d65818",
|
||
"name": "Lightning Retry Handler"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"conditions": {
|
||
"options": {
|
||
"caseSensitive": true,
|
||
"leftValue": "",
|
||
"typeValidation": "strict",
|
||
"version": 3
|
||
},
|
||
"conditions": [
|
||
{
|
||
"id": "f5a6b7c8-d9e0-4123-f456-667788990011",
|
||
"leftValue": "={{ $json.retry_lightning }}",
|
||
"rightValue": "",
|
||
"operator": {
|
||
"type": "boolean",
|
||
"operation": "true",
|
||
"singleValue": true
|
||
}
|
||
}
|
||
],
|
||
"combinator": "and"
|
||
},
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.if",
|
||
"typeVersion": 2.3,
|
||
"position": [
|
||
198592,
|
||
83808
|
||
],
|
||
"id": "07f95ba2-437b-45ae-9620-0fd52a7b885a",
|
||
"name": "Retry Lightning Request?"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"amount": 30
|
||
},
|
||
"type": "n8n-nodes-base.wait",
|
||
"typeVersion": 1.1,
|
||
"position": [
|
||
198800,
|
||
83984
|
||
],
|
||
"id": "24254a45-5809-4f5b-856b-94e95aac7807",
|
||
"name": "Wait Before Retry",
|
||
"webhookId": "b7c8d9e0-f1a2-4345-b678-889900112233"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"errorMessage": "=Lightning API failed after 3 attempts for {{ $json.customer_name }}: {{ $json.last_error_message || $json.error?.message || 'unknown error' }}"
|
||
},
|
||
"type": "n8n-nodes-base.stopAndError",
|
||
"typeVersion": 1,
|
||
"position": [
|
||
198976,
|
||
83808
|
||
],
|
||
"id": "253ed90d-ab8f-400c-a8c6-edbe6b3c08a9",
|
||
"name": "Stop Workflow"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"jsCode": "const MAX_ATTEMPTS = 3;\nconst errorPayload = $input.first().json || {};\nconst errorMessage = errorPayload.error?.message || errorPayload.message || JSON.stringify(errorPayload);\nconst staticData = $getWorkflowStaticData('global');\n\nlet previousAttempt = 0;\ntry {\n const prev = $('Auth Retry Handler').all().pop().json;\n if (prev?.authRetryAttempt != null) {\n previousAttempt = Number(prev.authRetryAttempt);\n }\n} catch (e) {\n previousAttempt = Number(staticData.authRetryAttempt || 0);\n}\n\nconst attempt = previousAttempt + 1;\nstaticData.authRetryAttempt = attempt;\n\nif (attempt < MAX_ATTEMPTS) {\n return [{\n json: {\n retry_auth: true,\n authRetryAttempt: attempt,\n last_error_message: errorMessage,\n },\n }];\n}\n\nreturn [{\n json: {\n retry_auth: false,\n authRetryAttempt: attempt,\n last_error_message: errorMessage,\n error: errorPayload.error || errorPayload,\n },\n}];"
|
||
},
|
||
"type": "n8n-nodes-base.code",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
197280,
|
||
83360
|
||
],
|
||
"id": "95ede16f-dcaa-4371-bd72-86f258b68d42",
|
||
"name": "Auth Retry Handler"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"conditions": {
|
||
"options": {
|
||
"caseSensitive": true,
|
||
"leftValue": "",
|
||
"typeValidation": "strict",
|
||
"version": 3
|
||
},
|
||
"conditions": [
|
||
{
|
||
"id": "a9b8c7d6-e5f4-3210-abcd-auth000002",
|
||
"leftValue": "={{ $json.retry_auth }}",
|
||
"rightValue": "",
|
||
"operator": {
|
||
"type": "boolean",
|
||
"operation": "true",
|
||
"singleValue": true
|
||
}
|
||
}
|
||
],
|
||
"combinator": "and"
|
||
},
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.if",
|
||
"typeVersion": 2.3,
|
||
"position": [
|
||
197472,
|
||
83360
|
||
],
|
||
"id": "fae58887-d64d-4b80-b26d-c5247bad23cd",
|
||
"name": "Retry Auth Request?"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"amount": 30
|
||
},
|
||
"type": "n8n-nodes-base.wait",
|
||
"typeVersion": 1.1,
|
||
"position": [
|
||
197664,
|
||
83520
|
||
],
|
||
"id": "37f1340c-3234-46e1-b4f0-b6696c5b3ee9",
|
||
"name": "Wait Before Auth Retry",
|
||
"webhookId": "c8d9e0f1-a2b3-4345-b678-889900112244"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"errorMessage": "=iklim.co authentication failed after 3 attempts: {{ $json.last_error_message || $json.error?.message || 'unknown error' }}"
|
||
},
|
||
"type": "n8n-nodes-base.stopAndError",
|
||
"typeVersion": 1,
|
||
"position": [
|
||
197840,
|
||
83360
|
||
],
|
||
"id": "6a088c93-0f6e-4d5f-84d3-fa03da9e3914",
|
||
"name": "Stop Workflow Auth"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"jsCode": "const turbines = $input.all();\n\n// 1. Safeguard: Skip if no turbines are found\nif (turbines.length === 0) {\n return { skip_customer: true };\n}\n\nconst lats = turbines.map(t => Number(t.json.latitude));\nconst lons = turbines.map(t => Number(t.json.longitude));\n\n// 2. Calculate Centroid\nconst n = lats.length;\nconst centroid_lat = lats.reduce((a, b) => a + b, 0) / n;\nconst centroid_lon = lons.reduce((a, b) => a + b, 0) / n;\n\n// 3. Haversine Distance Helper\nconst getDist = (lat1, lon1, lat2, lon2) => {\n const R = 6371e3; // Earth radius in meters\n const dLat = (lat2 - lat1) * Math.PI / 180;\n const dLon = (lon2 - lon1) * Math.PI / 180;\n const a = Math.sin(dLat/2)**2 + Math.cos(lat1*Math.PI/180)*Math.cos(lat2*Math.PI/180)*Math.sin(dLon/2)**2;\n return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));\n};\n\n// 4. Find Farthest Turbine\nlet maxDist = 0;\nfor (let i = 0; i < n; i++) {\n const d = getDist(centroid_lat, centroid_lon, lats[i], lons[i]);\n if (d > maxDist) maxDist = d;\n}\n\n// 5. Calculate Rings\n// Updated: Ensures a minimum 1000m radius even for a single turbine point\nconst r1 = Math.max(1000, Math.ceil(maxDist / 1000) * 1000);\nconst r4 = r1 + 6000;\nconst boundary = Math.min(r4 + 2000, 49999); \n\nreturn {\n skip_customer: false,\n farm_id: turbines[0].json.customer_id,\n customer_name: ($('Loop Over Items').first().json || {}).customer_name,\n language: String(($('Loop Over Items').first().json || {}).language || 'en').trim() || 'en',\n centroid_latitude: centroid_lat,\n centroid_longitude: centroid_lon,\n monitoring_boundary_m: boundary,\n rings: { r1, r2: r1+2000, r3: r1+4000, r4 }\n};"
|
||
},
|
||
"type": "n8n-nodes-base.code",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
197584,
|
||
84240
|
||
],
|
||
"id": "2238396a-397b-4c85-865f-3c7dec711501",
|
||
"name": "Centroid & Distance Ring calculation"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"jsCode": "// Merge the centroid/rings data with the new loop variables\nreturn {\n ...$input.item.json, \n pageNumber: 0,\n stopLoop: false,\n allStrikes: [],\n tStart: null,\n tLast: null,\n lightningRetryAttempt: 0\n};"
|
||
},
|
||
"type": "n8n-nodes-base.code",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
197776,
|
||
84240
|
||
],
|
||
"id": "d0c98ffc-0742-46cf-afb5-933208ee748e",
|
||
"name": "Loop init"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"jsCode": "const SILENCE_MS = 12 * 60 * 60 * 1000;\nconst response = $input.first().json;\nconst batchStrikes = response.lightnings || [];\nconst currentPage = response.pageNumber || 0;\n\nconst farm = $('Loop init').all().pop().json;\nlet allStrikes = [];\nlet stopLoop = false;\n\nif (currentPage > 0) {\n try {\n const prevState = $('Logic Gate').all().pop().json;\n allStrikes = prevState.allStrikes ? [...prevState.allStrikes] : [];\n } catch (e) { }\n}\n\nif (batchStrikes.length === 0 && currentPage === 0) {\n return { stopLoop: true, allStrikes: [], pageNumber: 0, status: \"EMPTY\", customer_name: farm.customer_name };\n}\n\nfor (let i = 0; i < batchStrikes.length; i++) {\n const currentTs = new Date(batchStrikes[i].captured).getTime();\n batchStrikes[i].timestamp = currentTs;\n allStrikes.push(batchStrikes[i]);\n\n if (batchStrikes[i + 1]) {\n const nextTs = new Date(batchStrikes[i + 1].captured).getTime();\n if (currentTs - nextTs > SILENCE_MS) {\n stopLoop = true;\n break;\n }\n }\n}\n\nif (batchStrikes.length < 100) {\n stopLoop = true;\n}\n\nconst timestamps = allStrikes.map((s) => s.timestamp).filter((ts) => Number.isFinite(ts));\nconst final_tStart = timestamps.length > 0 ? Math.min(...timestamps) : null;\nconst final_tLast = timestamps.length > 0 ? Math.max(...timestamps) : null;\n\nconst roundToMinute = (ts) => Math.floor(Number(ts || 0) / 60000);\nconst stormKey = [\n String(farm.customer_name || ''),\n roundToMinute(final_tStart),\n roundToMinute(final_tLast),\n].join('_');\n\nreturn {\n stopLoop,\n pageNumber: currentPage + 1,\n allStrikes,\n tStart: final_tStart,\n tLast: final_tLast,\n storm_start_min: roundToMinute(final_tStart),\n storm_end_min: roundToMinute(final_tLast),\n storm_key: stormKey,\n customer_name: farm.customer_name,\n status: stopLoop ? \"FINISHED\" : \"PAGING\"\n};"
|
||
},
|
||
"type": "n8n-nodes-base.code",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
198400,
|
||
84240
|
||
],
|
||
"id": "01389127-d9dd-4113-b979-e651099bed2a",
|
||
"name": "Logic Gate"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"conditions": {
|
||
"options": {
|
||
"caseSensitive": true,
|
||
"leftValue": "",
|
||
"typeValidation": "strict",
|
||
"version": 3
|
||
},
|
||
"conditions": [
|
||
{
|
||
"id": "68ba538e-8070-4470-b0fa-30106a1f65b5",
|
||
"leftValue": "={{ $node[\"Logic Gate\"].json.stopLoop }}",
|
||
"rightValue": "",
|
||
"operator": {
|
||
"type": "boolean",
|
||
"operation": "true",
|
||
"singleValue": true
|
||
}
|
||
}
|
||
],
|
||
"combinator": "and"
|
||
},
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.if",
|
||
"typeVersion": 2.3,
|
||
"position": [
|
||
198592,
|
||
84192
|
||
],
|
||
"id": "d42949ed-a501-4e0f-97c4-c755f154ea42",
|
||
"name": "Stop Loop"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"conditions": {
|
||
"options": {
|
||
"caseSensitive": true,
|
||
"leftValue": "",
|
||
"typeValidation": "strict",
|
||
"version": 3
|
||
},
|
||
"conditions": [
|
||
{
|
||
"id": "8b6c4fdd-0f94-4bc8-82ac-0996af68c534",
|
||
"leftValue": "={{ $node[\"Logic Gate\"].json.allStrikes.length }}",
|
||
"rightValue": 0,
|
||
"operator": {
|
||
"type": "number",
|
||
"operation": "gt"
|
||
}
|
||
}
|
||
],
|
||
"combinator": "and"
|
||
},
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.if",
|
||
"typeVersion": 2.3,
|
||
"position": [
|
||
198784,
|
||
84224
|
||
],
|
||
"id": "ce0b591c-5dd7-427c-a0ff-1511bacb35d6",
|
||
"name": "Lightning found?"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"operation": "get",
|
||
"dataTableId": {
|
||
"__rl": true,
|
||
"value": "PCYhmruG5hu52Alf",
|
||
"mode": "list",
|
||
"cachedResultName": "storm_logs",
|
||
"cachedResultUrl": "/projects/dWFeIPZ8ouir7Ex0/datatables/PCYhmruG5hu52Alf"
|
||
},
|
||
"matchType": "allConditions",
|
||
"filters": {
|
||
"conditions": [
|
||
{
|
||
"keyName": "storm_key",
|
||
"keyValue": "={{ $json.storm_key }}"
|
||
}
|
||
]
|
||
},
|
||
"limit": 1
|
||
},
|
||
"type": "n8n-nodes-base.dataTable",
|
||
"typeVersion": 1.1,
|
||
"position": [
|
||
199024,
|
||
84256
|
||
],
|
||
"id": "0e157cc7-a646-4cf6-a5a2-6fb842b43af8",
|
||
"name": "Get row(s)",
|
||
"alwaysOutputData": true
|
||
},
|
||
{
|
||
"parameters": {
|
||
"conditions": {
|
||
"options": {
|
||
"caseSensitive": true,
|
||
"leftValue": "",
|
||
"typeValidation": "strict",
|
||
"version": 3
|
||
},
|
||
"conditions": [
|
||
{
|
||
"id": "02a4a5c6-2645-4d72-9952-9a62c69fa19f",
|
||
"leftValue": "={{ $json.skip_insert }}",
|
||
"rightValue": "",
|
||
"operator": {
|
||
"type": "boolean",
|
||
"operation": "false",
|
||
"singleValue": true
|
||
}
|
||
}
|
||
],
|
||
"combinator": "and"
|
||
},
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.if",
|
||
"typeVersion": 2.3,
|
||
"position": [
|
||
199408,
|
||
84256
|
||
],
|
||
"id": "017fb1b5-d99f-4456-bb1d-cefd5bd43879",
|
||
"name": "If"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"dataTableId": {
|
||
"__rl": true,
|
||
"value": "PCYhmruG5hu52Alf",
|
||
"mode": "list",
|
||
"cachedResultName": "storm_logs",
|
||
"cachedResultUrl": "/projects/dWFeIPZ8ouir7Ex0/datatables/PCYhmruG5hu52Alf"
|
||
},
|
||
"columns": {
|
||
"mappingMode": "defineBelow",
|
||
"value": {
|
||
"storm_start": "={{ $json.tStart }}",
|
||
"storm_end": "={{ $json.tLast }}",
|
||
"total_strikes": "={{ $json.allStrikes.length }}",
|
||
"customer_name": "={{ $json.customer_name }}",
|
||
"status": "waiting for confirmation",
|
||
"storm_key": "={{ $json.storm_key }}",
|
||
"storm_start_min": "={{ $json.storm_start_min }}",
|
||
"storm_end_min": "={{ $json.storm_end_min }}"
|
||
},
|
||
"matchingColumns": [],
|
||
"schema": [
|
||
{
|
||
"id": "customer_name",
|
||
"displayName": "customer_name",
|
||
"required": false,
|
||
"defaultMatch": false,
|
||
"display": true,
|
||
"type": "string",
|
||
"readOnly": false,
|
||
"removed": false
|
||
},
|
||
{
|
||
"id": "total_strikes",
|
||
"displayName": "total_strikes",
|
||
"required": false,
|
||
"defaultMatch": false,
|
||
"display": true,
|
||
"type": "number",
|
||
"readOnly": false,
|
||
"removed": false
|
||
},
|
||
{
|
||
"id": "storm_start",
|
||
"displayName": "storm_start",
|
||
"required": false,
|
||
"defaultMatch": false,
|
||
"display": true,
|
||
"type": "number",
|
||
"readOnly": false,
|
||
"removed": false
|
||
},
|
||
{
|
||
"id": "storm_end",
|
||
"displayName": "storm_end",
|
||
"required": false,
|
||
"defaultMatch": false,
|
||
"display": true,
|
||
"type": "number",
|
||
"readOnly": false,
|
||
"removed": false
|
||
},
|
||
{
|
||
"id": "status",
|
||
"displayName": "status",
|
||
"required": false,
|
||
"defaultMatch": false,
|
||
"display": true,
|
||
"type": "string",
|
||
"readOnly": false,
|
||
"removed": false
|
||
},
|
||
{
|
||
"id": "storm_key",
|
||
"displayName": "storm_key",
|
||
"required": false,
|
||
"defaultMatch": false,
|
||
"display": true,
|
||
"type": "string",
|
||
"readOnly": false,
|
||
"removed": false
|
||
},
|
||
{
|
||
"id": "storm_start_min",
|
||
"displayName": "storm_start_min",
|
||
"required": false,
|
||
"defaultMatch": false,
|
||
"display": true,
|
||
"type": "number",
|
||
"readOnly": false,
|
||
"removed": false
|
||
},
|
||
{
|
||
"id": "storm_end_min",
|
||
"displayName": "storm_end_min",
|
||
"required": false,
|
||
"defaultMatch": false,
|
||
"display": true,
|
||
"type": "number",
|
||
"readOnly": false,
|
||
"removed": false
|
||
}
|
||
],
|
||
"attemptToConvertTypes": true,
|
||
"convertFieldsToString": false
|
||
},
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.dataTable",
|
||
"typeVersion": 1.1,
|
||
"position": [
|
||
199648,
|
||
84256
|
||
],
|
||
"id": "56e977ca-9aad-450a-8e34-e0c52d1039d5",
|
||
"name": "Insert row"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.splitInBatches",
|
||
"typeVersion": 3,
|
||
"position": [
|
||
197136,
|
||
84224
|
||
],
|
||
"id": "7d12e1cf-c145-4b9a-a24b-9ab5e91eb906",
|
||
"name": "Loop Over Items"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"jsCode": "const crypto = require('crypto');\nconst HMAC_SECRET = 'c88f845bd6d520ded507ef6b02efc223019ccf68f41d9070705712d480ba5166';\nconst URI = '/v1/thunderstorms/within';\n\nconst storm = $('Logic Gate').all().pop().json;\nconst farm = $('Loop init').all().pop().json;\nconst auth = $('Restore Credentials').all().pop().json;\n\nif (!storm.tLast || !storm.tStart) {\n throw new Error(\"Missing storm timestamps. Skipping Thunderstorm query.\");\n}\n\nconst durationSeconds = Math.floor((storm.tLast - storm.tStart) / 1000);\nconst timestamp = Date.now().toString();\n\nconst bodyPayload = {\n latitude: Number(Number(farm.centroid_latitude).toFixed(6)),\n longitude: Number(Number(farm.centroid_longitude).toFixed(6)),\n radius: Math.max(50000, farm.monitoring_boundary_m), // Ensure coverage[cite: 2]\n backwardInterval: Math.max(3600, durationSeconds + 600), // Dynamic padding[cite: 2]\n endTimeEpoch: Number(storm.tLast), // Use actual storm end[cite: 2]\n intersectsWith: \"THREAT_POLYGON\",\n pageNumber: 0,\n pageSize: 10\n};\n\nconst bodyString = JSON.stringify(bodyPayload);\nconst dataToSign = `POST|${URI}|${timestamp}|${bodyString}`;\nconst signature = crypto.createHmac('sha256', HMAC_SECRET).update(dataToSign).digest('hex').toLowerCase();\n\nreturn {\n requestBody: bodyPayload,\n headers: {\n \"X-Signature\": signature,\n \"X-Timestamp\": timestamp,\n \"X-Nonce\": crypto.randomUUID(),\n \"X-Idempotency-Key\": crypto.randomUUID(),\n \"Authorization\": \"Bearer \" + auth.accessToken,\n \"Content-Type\": \"application/json\"\n }\n};"
|
||
},
|
||
"type": "n8n-nodes-base.code",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
199872,
|
||
84256
|
||
],
|
||
"id": "62e5f434-bf10-459e-ae44-b82524238e01",
|
||
"name": "Calculate Thunderstorm Headers"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"method": "POST",
|
||
"url": "https://api-test.iklim.co/v1/thunderstorms/within",
|
||
"sendHeaders": true,
|
||
"headerParameters": {
|
||
"parameters": [
|
||
{
|
||
"name": "Authorization",
|
||
"value": "={{ $json.headers.Authorization }}"
|
||
},
|
||
{
|
||
"name": "X-Signature",
|
||
"value": "={{ $json.headers['X-Signature'] }}"
|
||
},
|
||
{
|
||
"name": "X-Timestamp",
|
||
"value": "={{ $json.headers['X-Timestamp'] }}"
|
||
},
|
||
{
|
||
"name": "X-Nonce",
|
||
"value": "={{ $json.headers['X-Nonce'] }}"
|
||
},
|
||
{
|
||
"name": "X-Idempotency-Key",
|
||
"value": "={{ $json.headers['X-Idempotency-Key'] }}"
|
||
},
|
||
{
|
||
"name": "Content-Type",
|
||
"value": "={{ $json.headers['Content-Type'] }}"
|
||
},
|
||
{
|
||
"name": "Accept",
|
||
"value": "={{ $json.headers['Content-Type'] }}"
|
||
}
|
||
]
|
||
},
|
||
"sendBody": true,
|
||
"specifyBody": "json",
|
||
"jsonBody": "={{ $json.requestBody }}",
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.httpRequest",
|
||
"typeVersion": 4.3,
|
||
"position": [
|
||
200064,
|
||
84256
|
||
],
|
||
"id": "f3c657e2-b4ab-44c7-a98e-d8c5c427e017",
|
||
"name": "Thunderstorm Request",
|
||
"onError": "continueErrorOutput"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"jsCode": "const item = $input.first();\nconst error = item.json.error || {};\n\nconst extractErrorMessage = (payload) => {\n const err = payload.error || {};\n const candidates = [\n payload.last_error_message,\n payload.errorMessage,\n err.message,\n payload.message,\n ];\n for (const candidate of candidates) {\n if (!candidate) continue;\n const text = String(candidate);\n const jsonMatch = text.match(/\\{[\\s\\S]*\\}$/);\n if (jsonMatch) {\n try {\n const parsed = JSON.parse(jsonMatch[0]);\n if (parsed.message) return String(parsed.message);\n } catch (e) {}\n }\n return text;\n }\n return 'Thunderstorm API request failed';\n};\n\nconst rawMessage = extractErrorMessage(item.json);\nconst isAccountMissing = /account is not found/i.test(String(rawMessage));\nconst reason = isAccountMissing\n ? 'Thunderstorm API account is not provisioned for this user on api-test.iklim.co'\n : String(rawMessage);\n\nreturn [{\n json: {\n thunderstorms: [],\n thunderstorm_fetch_skipped: true,\n thunderstorm_fetch_error: reason,\n thunderstorm_retry_attempts: item.json.thunderstormRetryAttempt || null,\n },\n}];"
|
||
},
|
||
"type": "n8n-nodes-base.code",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
200688,
|
||
84288
|
||
],
|
||
"id": "5d797ff1-cee4-45cc-ad7e-5b49d6528d04",
|
||
"name": "Thunderstorm Error Fallback"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"jsCode": "const MAX_ATTEMPTS = 3;\nconst errorPayload = $input.first().json || {};\nconst error = errorPayload.error || {};\n\nconst extractErrorMessage = (payload) => {\n const err = payload.error || {};\n const candidates = [\n payload.last_error_message,\n payload.errorMessage,\n err.message,\n payload.message,\n ];\n for (const candidate of candidates) {\n if (!candidate) continue;\n const text = String(candidate);\n const jsonMatch = text.match(/\\{[\\s\\S]*\\}$/);\n if (jsonMatch) {\n try {\n const parsed = JSON.parse(jsonMatch[0]);\n if (parsed.message) return String(parsed.message);\n } catch (e) {}\n }\n return text;\n }\n return 'Thunderstorm API request failed';\n};\n\nconst rawMessage = extractErrorMessage(errorPayload);\n\nlet previousAttempt = 0;\ntry {\n const prev = $('Thunderstorm Retry Handler').all().pop().json;\n if (prev?.thunderstormRetryAttempt != null) {\n previousAttempt = Number(prev.thunderstormRetryAttempt);\n }\n} catch (e) {}\n\nconst attempt = previousAttempt + 1;\n\nif (attempt < MAX_ATTEMPTS) {\n return [{\n json: {\n thunderstormRetryAttempt: attempt,\n retry_thunderstorm: true,\n last_error_message: rawMessage,\n },\n }];\n}\n\nreturn [{\n json: {\n thunderstormRetryAttempt: attempt,\n retry_thunderstorm: false,\n last_error_message: rawMessage,\n error: errorPayload.error || errorPayload,\n },\n}];"
|
||
},
|
||
"type": "n8n-nodes-base.code",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
200288,
|
||
84064
|
||
],
|
||
"id": "2638b5c6-7957-4f09-b9e8-27c156c6f138",
|
||
"name": "Thunderstorm Retry Handler"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"conditions": {
|
||
"options": {
|
||
"caseSensitive": true,
|
||
"leftValue": "",
|
||
"typeValidation": "strict",
|
||
"version": 3
|
||
},
|
||
"conditions": [
|
||
{
|
||
"id": "e5f6a7b8-c9d0-4123-e456-7890abcdef01",
|
||
"leftValue": "={{ $json.retry_thunderstorm }}",
|
||
"rightValue": "",
|
||
"operator": {
|
||
"type": "boolean",
|
||
"operation": "true",
|
||
"singleValue": true
|
||
}
|
||
}
|
||
],
|
||
"combinator": "and"
|
||
},
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.if",
|
||
"typeVersion": 2.3,
|
||
"position": [
|
||
200480,
|
||
84064
|
||
],
|
||
"id": "af6372af-3e4e-4607-9b8e-5a203ffe60ac",
|
||
"name": "Retry Thunderstorm Request?"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"amount": 30
|
||
},
|
||
"type": "n8n-nodes-base.wait",
|
||
"typeVersion": 1.1,
|
||
"position": [
|
||
200704,
|
||
84064
|
||
],
|
||
"id": "f01c06e0-5681-45db-be09-fe62a7b6cf91",
|
||
"name": "Wait Before Thunderstorm Retry",
|
||
"webhookId": "c8d9e0f1-a2b3-4456-c789-0123456789ab"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"jsCode": "const crypto = require('crypto');\nconst HMAC_SECRET = 'c88f845bd6d520ded507ef6b02efc223019ccf68f41d9070705712d480ba5166';\nconst METHOD = 'POST';\nconst URI = '/v1/lightnings/within';\n\nconst manualInput = $('Manual Input').first().json;\nconst endTime = Number(manualInput.end_time_epoch_ms);\nconst startTime = Number(manualInput.start_time_epoch_ms);\n\nif (!Number.isFinite(startTime) || !Number.isFinite(endTime) || endTime <= startTime) {\n throw new Error('Manual Input must include valid start_time_epoch_ms and end_time_epoch_ms (end > start).');\n}\n\nconst backwardInterval = Math.max(60, Math.floor((endTime - startTime) / 1000));\n\nconst staticData = $getWorkflowStaticData('global');\nconst accessToken = staticData.accessToken;\nif (!accessToken) {\n throw new Error('Missing accessToken in workflow static data');\n}\n\nconst farmBase = $('Loop init').all().pop().json;\nconst allItems = $input.all();\nconst processedItems = [];\n\nfor (const item of allItems) {\n const farmData = { ...farmBase, ...item.json };\n const pageNumber = farmData.pageNumber ?? 0;\n const timestamp = Date.now().toString();\n\n const bodyPayload = {\n latitude: farmData.centroid_latitude,\n longitude: farmData.centroid_longitude,\n radius: farmData.monitoring_boundary_m,\n backwardInterval,\n endTimeEpoch: endTime,\n pageNumber,\n pageSize: 100,\n };\n\n const bodyString = JSON.stringify(bodyPayload);\n const dataToSign = `${METHOD.toUpperCase()}|${URI}|${timestamp}|${bodyString}`;\n const signature = crypto.createHmac('sha256', HMAC_SECRET).update(dataToSign).digest('hex').toLowerCase();\n\n processedItems.push({\n json: {\n requestBody: bodyPayload,\n headers: {\n 'X-Signature': signature,\n 'X-Timestamp': timestamp,\n 'X-Nonce': crypto.randomUUID(),\n 'X-Idempotency-Key': crypto.randomUUID(),\n Authorization: 'Bearer ' + accessToken,\n 'Content-Type': 'application/json',\n },\n },\n });\n}\n\nreturn processedItems;"
|
||
},
|
||
"type": "n8n-nodes-base.code",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
197968,
|
||
84240
|
||
],
|
||
"id": "acf71540-a1ca-44e0-a404-379236841b92",
|
||
"name": "Calculate Lightning Headers"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"jsCode": "const turbines = $('Get Customer Wind Turbines').all().map((t) => t.json);\nconst storm = $('Logic Gate').all().pop().json;\nconst farm = $('Loop init').all().pop().json;\n\nconst unwrapThunderstorms = (items) => {\n const records = [];\n for (const item of items) {\n if (!item || item.thunderstorm_fetch_skipped) {\n continue;\n }\n if (Array.isArray(item.thunderstorms)) {\n records.push(...item.thunderstorms);\n continue;\n }\n if (Array.isArray(item.cells)) {\n records.push(...item.cells);\n continue;\n }\n if (Array.isArray(item.data)) {\n records.push(...item.data);\n continue;\n }\n if (Array.isArray(item.items)) {\n records.push(...item.items);\n continue;\n }\n records.push(item);\n }\n return records;\n};\n\nconst thunder = unwrapThunderstorms($input.all().map((t) => t.json));\n\nconst getNodeRowsSafely = (nodeName) => {\n try {\n return $(nodeName)\n .all()\n .map((i) => i.json)\n .filter((r) => r && r.id !== undefined && r.id !== null);\n } catch (error) {\n return [];\n }\n};\n\nconst existingRows = getNodeRowsSafely('Get row(s)');\nconst insertedRows = getNodeRowsSafely('Insert row');\nconst stormLogId = insertedRows[0]?.id ?? existingRows[0]?.id ?? null;\n\nlet customer = {};\ntry {\n customer = $('Loop Over Items').first().json || {};\n} catch (e) {\n customer = {};\n}\nconst language = String(customer.language || farm.language || 'en').trim() || 'en';\n\nconst manual = $('Manual Input').first().json;\nconst startMs = Number(manual.start_time_epoch_ms);\nconst endMs = Number(manual.end_time_epoch_ms);\nlet strikes = storm.allStrikes || [];\n\nif (Number.isFinite(startMs) && Number.isFinite(endMs)) {\n strikes = strikes.filter((strike) => {\n const ts = Number(strike.timestamp ?? new Date(strike.captured).getTime());\n return Number.isFinite(ts) && ts >= startMs && ts <= endMs;\n });\n}\n\nconst tStart = Number.isFinite(startMs) ? startMs : storm.tStart;\nconst tEnd = Number.isFinite(endMs) ? endMs : storm.tLast;\n\nreturn {\n customer_name: storm.customer_name,\n storm_log_id: stormLogId,\n language,\n centroid_lat: farm.centroid_latitude,\n centroid_lon: farm.centroid_longitude,\n boundary_m: farm.monitoring_boundary_m,\n rings: farm.rings,\n timezone: 'Europe/Istanbul',\n t_start: tStart,\n t_end: tEnd,\n n_strikes: strikes.length,\n turbines: turbines,\n strikes: strikes,\n storm_records: thunder,\n};"
|
||
},
|
||
"type": "n8n-nodes-base.code",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
200880,
|
||
84400
|
||
],
|
||
"id": "a3a4f196-882c-408b-8c82-dd110a0116dc",
|
||
"name": "Build Report Payload"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"method": "POST",
|
||
"url": "https://patrol-plural-gooey.ngrok-free.dev/generate/async",
|
||
"sendHeaders": true,
|
||
"headerParameters": {
|
||
"parameters": [
|
||
{
|
||
"name": "X-Report-Token",
|
||
"value": "test-secret-123"
|
||
},
|
||
{
|
||
"name": "Content-Type",
|
||
"value": "application/json"
|
||
},
|
||
{
|
||
"name": "ngrok-skip-browser-warning",
|
||
"value": "1"
|
||
}
|
||
]
|
||
},
|
||
"sendBody": true,
|
||
"specifyBody": "json",
|
||
"jsonBody": "={{ $json }}",
|
||
"options": {
|
||
"timeout": 120000
|
||
}
|
||
},
|
||
"type": "n8n-nodes-base.httpRequest",
|
||
"typeVersion": 4.3,
|
||
"position": [
|
||
201088,
|
||
84256
|
||
],
|
||
"id": "8b003fb1-7ce3-4b55-a18f-9e121cca7f41",
|
||
"name": "Start Async Report",
|
||
"onError": "continueErrorOutput"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"jsCode": "const payload = $('Build Report Payload').first().json;\nconst start = $input.first().json || {};\nif (!start.job_id) {\n throw new Error('Async report start did not return job_id');\n}\nreturn [{\n json: {\n ...payload,\n report_job_id: start.job_id,\n poll_attempt: 0,\n },\n}];"
|
||
},
|
||
"type": "n8n-nodes-base.code",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
201296,
|
||
84256
|
||
],
|
||
"id": "47a8b777-5552-405f-a54b-f82448478aeb",
|
||
"name": "Attach Report Job"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"amount": 15
|
||
},
|
||
"type": "n8n-nodes-base.wait",
|
||
"typeVersion": 1.1,
|
||
"position": [
|
||
201504,
|
||
84256
|
||
],
|
||
"id": "ac498279-b84f-45b2-9ed7-a0b504524d88",
|
||
"name": "Wait for Report",
|
||
"webhookId": "d4e5f6a7-b8c9-4012-d345-6789abcdef01"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"url": "=https://patrol-plural-gooey.ngrok-free.dev/generate/async/{{ $json.report_job_id }}",
|
||
"sendHeaders": true,
|
||
"headerParameters": {
|
||
"parameters": [
|
||
{
|
||
"name": "X-Report-Token",
|
||
"value": "test-secret-123"
|
||
},
|
||
{
|
||
"name": "ngrok-skip-browser-warning",
|
||
"value": "1"
|
||
}
|
||
]
|
||
},
|
||
"options": {
|
||
"timeout": 120000
|
||
}
|
||
},
|
||
"type": "n8n-nodes-base.httpRequest",
|
||
"typeVersion": 4.3,
|
||
"position": [
|
||
201712,
|
||
84256
|
||
],
|
||
"id": "cfee8f9a-fdbf-45b6-a6cc-fb5eca777a8a",
|
||
"name": "Poll Report Status",
|
||
"onError": "continueErrorOutput"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"jsCode": "const ctx = $('Wait for Report').item.json;\nconst poll = $input.first().json || {};\nreturn [{\n json: {\n ...ctx,\n poll_status: poll.status,\n poll_error: poll.error || null,\n poll_filename: poll.filename || null,\n poll_attempt: Number(ctx.poll_attempt || 0) + 1,\n },\n}];"
|
||
},
|
||
"type": "n8n-nodes-base.code",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
201920,
|
||
84256
|
||
],
|
||
"id": "0826f896-13f5-47a0-a451-9bbf5a2e2666",
|
||
"name": "Merge Poll Status"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"conditions": {
|
||
"options": {
|
||
"caseSensitive": true,
|
||
"leftValue": "",
|
||
"typeValidation": "strict",
|
||
"version": 3
|
||
},
|
||
"conditions": [
|
||
{
|
||
"id": "f5a6b7c8-d9e0-4123-a456-6677889900aa",
|
||
"leftValue": "={{ $json.poll_status }}",
|
||
"rightValue": "complete",
|
||
"operator": {
|
||
"type": "string",
|
||
"operation": "equals"
|
||
}
|
||
}
|
||
],
|
||
"combinator": "and"
|
||
},
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.if",
|
||
"typeVersion": 2.3,
|
||
"position": [
|
||
202128,
|
||
84256
|
||
],
|
||
"id": "bd3d377e-3252-42b5-b1cf-3c137168fab8",
|
||
"name": "Report Complete?"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"conditions": {
|
||
"options": {
|
||
"caseSensitive": true,
|
||
"leftValue": "",
|
||
"typeValidation": "strict",
|
||
"version": 3
|
||
},
|
||
"conditions": [
|
||
{
|
||
"id": "f5a6b7c8-d9e0-4123-a456-6677889900ac",
|
||
"leftValue": "={{ $json.poll_status }}",
|
||
"rightValue": "failed",
|
||
"operator": {
|
||
"type": "string",
|
||
"operation": "equals"
|
||
}
|
||
}
|
||
],
|
||
"combinator": "and"
|
||
},
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.if",
|
||
"typeVersion": 2.3,
|
||
"position": [
|
||
202320,
|
||
83920
|
||
],
|
||
"id": "91b3aabb-4a7d-458f-9d56-d1229f729bc3",
|
||
"name": "Report Failed?"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"conditions": {
|
||
"options": {
|
||
"caseSensitive": true,
|
||
"leftValue": "",
|
||
"typeValidation": "strict",
|
||
"version": 3
|
||
},
|
||
"conditions": [
|
||
{
|
||
"id": "f5a6b7c8-d9e0-4123-a456-6677889900ae",
|
||
"leftValue": "={{ $json.poll_attempt }}",
|
||
"rightValue": 60,
|
||
"operator": {
|
||
"type": "number",
|
||
"operation": "lt"
|
||
}
|
||
}
|
||
],
|
||
"combinator": "and"
|
||
},
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.if",
|
||
"typeVersion": 2.3,
|
||
"position": [
|
||
202544,
|
||
83968
|
||
],
|
||
"id": "52ed1273-0a93-4fdf-ab70-f33f9cd70462",
|
||
"name": "Continue Polling?"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"errorMessage": "=Report generation failed: {{ $json.poll_error || 'unknown error' }}"
|
||
},
|
||
"type": "n8n-nodes-base.stopAndError",
|
||
"typeVersion": 1,
|
||
"position": [
|
||
202528,
|
||
83776
|
||
],
|
||
"id": "916b6e49-e8ee-4319-b9b1-54624e743922",
|
||
"name": "Stop Report Failed"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"errorMessage": "=Report generation timed out after {{ $json.poll_attempt || 60 }} poll attempts"
|
||
},
|
||
"type": "n8n-nodes-base.stopAndError",
|
||
"typeVersion": 1,
|
||
"position": [
|
||
202752,
|
||
83984
|
||
],
|
||
"id": "927e0d98-2985-4dde-be31-872ffe72ebd5",
|
||
"name": "Stop Report Timeout"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"url": "=https://patrol-plural-gooey.ngrok-free.dev/generate/async/{{ $json.report_job_id }}/download",
|
||
"sendHeaders": true,
|
||
"headerParameters": {
|
||
"parameters": [
|
||
{
|
||
"name": "X-Report-Token",
|
||
"value": "test-secret-123"
|
||
},
|
||
{
|
||
"name": "ngrok-skip-browser-warning",
|
||
"value": "1"
|
||
}
|
||
]
|
||
},
|
||
"options": {
|
||
"response": {
|
||
"response": {
|
||
"responseFormat": "file"
|
||
}
|
||
},
|
||
"timeout": 180000
|
||
}
|
||
},
|
||
"type": "n8n-nodes-base.httpRequest",
|
||
"typeVersion": 4.3,
|
||
"position": [
|
||
202352,
|
||
84240
|
||
],
|
||
"id": "99a22feb-89a2-483a-af5b-eaeda7811954",
|
||
"name": "Download Report",
|
||
"onError": "continueErrorOutput"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"jsCode": "const customerName = String($node['Build Report Payload'].json.customer_name || 'customer').trim().replace(/\\s+/g, '_');\nconst now = new Date();\nconst year = String(now.getFullYear()).slice(-2);\nconst month = String(now.getMonth() + 1).padStart(2, '0');\nconst day = String(now.getDate()).padStart(2, '0');\nconst hour = String(now.getHours()).padStart(2, '0');\nconst minute = String(now.getMinutes()).padStart(2, '0');\nconst generatedAt = `${year}-${month}-${day}_${hour}-${minute}`;\nconst fileName = `${generatedAt}_${customerName}_iklimco_lightning_report.docx`;\n\nfor (const item of $input.all()) {\n if (!item.binary || !item.binary.data) {\n continue;\n }\n\n item.binary.data.fileName = fileName;\n item.binary.data.fileExtension = 'docx';\n item.binary.data.mimeType = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';\n}\n\nreturn $input.all();"
|
||
},
|
||
"type": "n8n-nodes-base.code",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
202592,
|
||
84224
|
||
],
|
||
"id": "0a28b731-3d0d-4b2c-b745-4c9d44c4c301",
|
||
"name": "Prepare DOCX Metadata"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"operation": "get",
|
||
"dataTableId": {
|
||
"__rl": true,
|
||
"value": "PCYhmruG5hu52Alf",
|
||
"mode": "list",
|
||
"cachedResultName": "storm_logs",
|
||
"cachedResultUrl": "/projects/dWFeIPZ8ouir7Ex0/datatables/PCYhmruG5hu52Alf"
|
||
},
|
||
"matchType": "allConditions",
|
||
"filters": {
|
||
"conditions": []
|
||
},
|
||
"limit": 1000
|
||
},
|
||
"type": "n8n-nodes-base.dataTable",
|
||
"typeVersion": 1.1,
|
||
"position": [
|
||
202800,
|
||
84224
|
||
],
|
||
"id": "e9dff831-748d-403a-ad98-7988a3b0222c",
|
||
"name": "Get Storm Logs For Next Id",
|
||
"alwaysOutputData": true
|
||
},
|
||
{
|
||
"parameters": {
|
||
"jsCode": "const payload = $node['Build Report Payload'].json || {};\nconst reportFileItem = $('Prepare DOCX Metadata').all()[0] || {};\nconst rows = $input.all().map((item) => item.json || {});\n\nconst normalizeId = (row) => {\n const raw = row?.id ?? row?.ID ?? row?._id ?? null;\n\n if (raw === null || raw === undefined) {\n return null;\n }\n\n if (typeof raw === 'string' && raw.trim() === '') {\n return null;\n }\n\n const num = Number(raw);\n if (Number.isFinite(num) && num > 0) {\n return num;\n }\n\n return raw;\n};\n\nconst hasValue = (v) => !(v === null || v === undefined || v === '');\n\nlet resolvedStormLogId = hasValue(payload.storm_log_id) ? payload.storm_log_id : null;\n\nif (!hasValue(resolvedStormLogId)) {\n const byKey = rows.find((row) => String(row.storm_key || '') === String(payload.storm_key || ''));\n if (byKey) {\n resolvedStormLogId = normalizeId(byKey);\n }\n}\n\nif (!hasValue(resolvedStormLogId)) {\n const ids = rows\n .map(normalizeId)\n .map((id) => Number(id))\n .filter((id) => Number.isFinite(id) && id > 0);\n\n if (ids.length > 0) {\n resolvedStormLogId = Math.max(...ids);\n }\n}\n\nreturn [{\n json: {\n ...(reportFileItem.json || {}),\n ...payload,\n storm_log_id: hasValue(resolvedStormLogId) ? resolvedStormLogId : 'unknown',\n },\n binary: reportFileItem.binary,\n}];"
|
||
},
|
||
"type": "n8n-nodes-base.code",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
203008,
|
||
84224
|
||
],
|
||
"id": "f54e8e2d-a107-449e-9959-59febc8b9c97",
|
||
"name": "Resolve Storm Log Id"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"resource": "file",
|
||
"options": {
|
||
"channelId": "C0A9K1AC7SN",
|
||
"initialComment": "=✅ Lightning report uploaded for *{{ $json.customer_name }}* (log id: *{{ $json.storm_log_id }}*)."
|
||
}
|
||
},
|
||
"type": "n8n-nodes-base.slack",
|
||
"typeVersion": 2.4,
|
||
"position": [
|
||
203584,
|
||
84224
|
||
],
|
||
"id": "f5beaab8-d524-4072-87bb-156b0d5b5ff3",
|
||
"name": "Upload Report to Slack",
|
||
"webhookId": "c4d5e6f7-a8b9-4012-cdef-345678901234",
|
||
"credentials": {
|
||
"slackApi": {
|
||
"id": "S5M0i1zlyebqU6oP",
|
||
"name": "n8n_lightning_report_bot"
|
||
}
|
||
}
|
||
},
|
||
{
|
||
"parameters": {
|
||
"select": "channel",
|
||
"channelId": {
|
||
"__rl": true,
|
||
"value": "C0A9K1AC7SN",
|
||
"mode": "list",
|
||
"cachedResultName": "n8n-events"
|
||
},
|
||
"messageType": "block",
|
||
"blocksUi": "={{ {\n \"blocks\": [\n {\n \"type\": \"section\",\n \"text\": {\n \"type\": \"mrkdwn\",\n \"text\": `Approve or reject this report for *${$('Resolve Storm Log Id').item.json.customer_name ?? ''}* (log id: \\`${$('Resolve Storm Log Id').item.json.storm_log_id}\\`).`\n }\n },\n {\n \"type\": \"actions\",\n \"elements\": [\n {\n \"type\": \"button\",\n \"text\": { \"type\": \"plain_text\", \"text\": \"Approve\", \"emoji\": true },\n \"style\": \"primary\",\n \"action_id\": \"approve_storm_log\",\n \"value\": String($('Resolve Storm Log Id').item.json.storm_log_id)\n },\n {\n \"type\": \"button\",\n \"text\": { \"type\": \"plain_text\", \"text\": \"Reject\", \"emoji\": true },\n \"style\": \"danger\",\n \"action_id\": \"reject_storm_log\",\n \"value\": String($('Resolve Storm Log Id').item.json.storm_log_id)\n }\n ]\n }\n ]\n} }}",
|
||
"text": "Lightning storm report approval",
|
||
"otherOptions": {}
|
||
},
|
||
"type": "n8n-nodes-base.slack",
|
||
"typeVersion": 2.4,
|
||
"position": [
|
||
204160,
|
||
84368
|
||
],
|
||
"id": "d0222810-1f47-41a5-9af2-31f14fdee53a",
|
||
"name": "Send Approval Buttons",
|
||
"webhookId": "d4e5f6a7-b8c9-0123-def0-456789abcdef",
|
||
"credentials": {
|
||
"slackApi": {
|
||
"id": "S5M0i1zlyebqU6oP",
|
||
"name": "n8n_lightning_report_bot"
|
||
}
|
||
}
|
||
},
|
||
{
|
||
"parameters": {
|
||
"httpMethod": "POST",
|
||
"path": "slack-storm-log-approval",
|
||
"responseMode": "responseNode",
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.webhook",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
196304,
|
||
84688
|
||
],
|
||
"id": "797d3223-c057-4c4d-b806-1b3bf5c451ec",
|
||
"name": "Slack Interaction Webhook",
|
||
"webhookId": "c9d8e7f6-a5b4-4321-fedc-ba9876543211"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"jsCode": "const raw = $input.first().json;\nlet payloadRaw = raw.payload;\nif (payloadRaw === undefined && raw.body !== undefined) {\n const b = raw.body;\n if (typeof b === 'string' && b.includes('payload=')) {\n try {\n payloadRaw = new URLSearchParams(b).get('payload');\n } catch {\n payloadRaw = undefined;\n }\n } else if (b && typeof b === 'object' && b.payload !== undefined) {\n payloadRaw = b.payload;\n }\n}\nlet payload;\nif (payloadRaw !== null && payloadRaw !== undefined && typeof payloadRaw === 'object') {\n payload = payloadRaw;\n} else if (typeof payloadRaw === 'string') {\n try {\n payload = JSON.parse(payloadRaw);\n } catch {\n return [{ json: { valid: false } }];\n }\n} else {\n return [{ json: { valid: false } }];\n}\nif (payload.type !== 'block_actions') {\n return [{ json: { valid: false } }];\n}\nconst action = payload.actions?.[0];\nconst rawId = String(action?.value ?? '').trim();\nif (!rawId || !action?.action_id) {\n return [{ json: { valid: false } }];\n}\nlet status = null;\nif (action.action_id === 'approve_storm_log') {\n status = 'confirmed';\n} else if (action.action_id === 'reject_storm_log') {\n status = 'rejected';\n}\nif (!status) {\n return [{ json: { valid: false } }];\n}\n\nconst sectionText = payload?.message?.blocks?.find((block) => block?.type === 'section')?.text?.text || '';\nconst fallbackText = payload?.message?.text || '';\nconst sourceText = sectionText || fallbackText;\nconst customerMatch = sourceText.match(/for \\*(.*?)\\* \\(log id:/i) || sourceText.match(/for (.*?) \\(log id:/i);\nconst customer_name = customerMatch?.[1]?.trim() || 'unknown customer';\n\nconst numId = Number(rawId);\nconst id = Number.isFinite(numId) ? numId : rawId;\nconst slackResponseUrl = typeof payload.response_url === 'string' ? payload.response_url.trim() : '';\nreturn [{ json: { valid: true, id, status, customer_name, slack_response_url: slackResponseUrl } }];"
|
||
},
|
||
"type": "n8n-nodes-base.code",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
196544,
|
||
84688
|
||
],
|
||
"id": "8af87bcb-8da1-4951-a01f-3dc66cd812ec",
|
||
"name": "Parse Slack Button Payload"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"conditions": {
|
||
"options": {
|
||
"caseSensitive": true,
|
||
"leftValue": "",
|
||
"typeValidation": "strict",
|
||
"version": 3
|
||
},
|
||
"conditions": [
|
||
{
|
||
"id": "b8c9d0e1-f2a3-4455-b667-c78890123456",
|
||
"leftValue": "={{ $json.valid }}",
|
||
"rightValue": "",
|
||
"operator": {
|
||
"type": "boolean",
|
||
"operation": "true",
|
||
"singleValue": true
|
||
}
|
||
}
|
||
],
|
||
"combinator": "and"
|
||
},
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.if",
|
||
"typeVersion": 2.3,
|
||
"position": [
|
||
196784,
|
||
84688
|
||
],
|
||
"id": "b79d2a86-1145-4722-8743-df5e695a821a",
|
||
"name": "Valid Storm Approval Button?"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"operation": "update",
|
||
"dataTableId": {
|
||
"__rl": true,
|
||
"value": "PCYhmruG5hu52Alf",
|
||
"mode": "list",
|
||
"cachedResultName": "storm_logs",
|
||
"cachedResultUrl": "/projects/dWFeIPZ8ouir7Ex0/datatables/PCYhmruG5hu52Alf"
|
||
},
|
||
"matchType": "allConditions",
|
||
"filters": {
|
||
"conditions": [
|
||
{
|
||
"keyValue": "={{ $json.id }}"
|
||
}
|
||
]
|
||
},
|
||
"columns": {
|
||
"mappingMode": "defineBelow",
|
||
"value": {
|
||
"status": "={{ $json.status }}"
|
||
},
|
||
"matchingColumns": [],
|
||
"schema": [
|
||
{
|
||
"id": "status",
|
||
"displayName": "status",
|
||
"required": false,
|
||
"defaultMatch": false,
|
||
"display": true,
|
||
"type": "string",
|
||
"readOnly": false,
|
||
"removed": false
|
||
}
|
||
],
|
||
"attemptToConvertTypes": false,
|
||
"convertFieldsToString": false
|
||
},
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.dataTable",
|
||
"typeVersion": 1.1,
|
||
"position": [
|
||
197536,
|
||
84688
|
||
],
|
||
"id": "d38a6984-6fdc-4134-b82d-581280b2f5a2",
|
||
"name": "Approve Pending Row"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"method": "POST",
|
||
"url": "={{ $('Still Valid After Ack?').first().json.slack_response_url }}",
|
||
"sendHeaders": true,
|
||
"headerParameters": {
|
||
"parameters": [
|
||
{
|
||
"name": "Content-Type",
|
||
"value": "application/json"
|
||
}
|
||
]
|
||
},
|
||
"sendBody": true,
|
||
"specifyBody": "json",
|
||
"jsonBody": "={{ (() => {\n const ack = $('Still Valid After Ack?').first().json;\n const isConfirmed = ack.status === 'confirmed';\n const id = String(ack.id ?? '');\n const customer = String(ack.customer_name || 'unknown customer');\n const verb = isConfirmed ? 'approved' : 'rejected';\n return {\n response_type: 'in_channel',\n replace_original: false,\n text: `Storm log #${id} for ${customer} ${verb} and recorded in storm_logs.`,\n };\n})() }}",
|
||
"options": {}
|
||
},
|
||
"id": "f44c0b2e-6b33-40ca-8208-447f8a7b6039",
|
||
"name": "Notify Slack Recording Result",
|
||
"type": "n8n-nodes-base.httpRequest",
|
||
"typeVersion": 4.1,
|
||
"position": [
|
||
200592,
|
||
85072
|
||
]
|
||
},
|
||
{
|
||
"parameters": {
|
||
"respondWith": "json",
|
||
"responseBody": "{}",
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.respondToWebhook",
|
||
"typeVersion": 1.1,
|
||
"position": [
|
||
197040,
|
||
84688
|
||
],
|
||
"id": "4455b8ce-d7d7-4ea7-9291-eb889f6b11a3",
|
||
"name": "Respond Slack Interaction"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"conditions": {
|
||
"options": {
|
||
"caseSensitive": true,
|
||
"leftValue": "",
|
||
"typeValidation": "strict",
|
||
"version": 3
|
||
},
|
||
"conditions": [
|
||
{
|
||
"id": "f7e8d9c0-a1b2-4333-c445-d56677889901",
|
||
"leftValue": "={{ $json.valid }}",
|
||
"rightValue": "",
|
||
"operator": {
|
||
"type": "boolean",
|
||
"operation": "true",
|
||
"singleValue": true
|
||
}
|
||
}
|
||
],
|
||
"combinator": "and"
|
||
},
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.if",
|
||
"typeVersion": 2.3,
|
||
"position": [
|
||
197232,
|
||
84688
|
||
],
|
||
"id": "35b27958-e840-4ae8-9b3f-bde79485000a",
|
||
"name": "Still Valid After Ack?"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"jsCode": "\n\n// Grab the data from the Logic Gate node explicitly\nconst stormData = $('Logic Gate').item.json;\n\n// Check if \"Get row(s)\" found anything\nconst existing = $input.all().filter(i => i.json && i.json.id !== undefined && i.json.id !== null);\n\nif (existing.length > 0) {\n // If found, merge and skip\n return [{ json: { ...stormData, skip_insert: true } }];\n}\n\n// If not found, merge and allow insert\nreturn [{ json: { ...stormData, skip_insert: false } }];"
|
||
},
|
||
"type": "n8n-nodes-base.code",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
199200,
|
||
84256
|
||
],
|
||
"id": "f9ac0c55-3e90-44da-99f1-3e409edf4673",
|
||
"name": "Prepare Insert Decision"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"jsCode": "const reportItem = $input.first();\nlet customer = {};\ntry {\n customer = $('Loop Over Items').first().json || {};\n} catch (e) {\n customer = {};\n}\nconst contactEmail = String(customer.contact_email || '').trim();\nconst fileName = reportItem.binary?.data?.fileName || 'lightning_report.docx';\nconst customerName = reportItem.json.customer_name || customer.customer_name || 'Customer';\n\nreturn [{\n json: {\n ...reportItem.json,\n contact_email: contactEmail,\n customer_id: customer.id,\n email_file_name: fileName,\n customer_name: customerName,\n },\n binary: reportItem.binary,\n}];"
|
||
},
|
||
"type": "n8n-nodes-base.code",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
203200,
|
||
84224
|
||
],
|
||
"id": "ce5cf428-f1b5-48de-bf2d-4d69901d319c",
|
||
"name": "Prepare Customer Email Context"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"content": "Add these columns to the storm_logs datatable:\n- pending_contact_email (string)\n- pending_email_file_name (string)\n- pending_report_base64 (long text)\n- pending_report_mime_type (string)\n\nReports persist here until approval email is sent or rejected.",
|
||
"width": 420
|
||
},
|
||
"type": "n8n-nodes-base.stickyNote",
|
||
"position": [
|
||
197488,
|
||
85264
|
||
],
|
||
"typeVersion": 1,
|
||
"id": "7c034fb3-300b-43ea-9e4b-d84541ac2978",
|
||
"name": "Sticky Note Storm Logs Email Columns"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"jsCode": "const item = $input.first();\nconst stormLogId = String(item.json.storm_log_id ?? '').trim();\nconst binaryMeta = item.binary?.data || {};\nlet reportBase64 = '';\n\ntry {\n const buffer = await this.helpers.getBinaryDataBuffer(0, 'data');\n reportBase64 = buffer.toString('base64');\n} catch {\n const raw = binaryMeta.data;\n if (typeof raw === 'string') {\n reportBase64 = raw.replace(/^data:[^;]+;base64,/, '').replace(/\\s+/g, '');\n } else if (raw) {\n reportBase64 = Buffer.from(raw).toString('base64');\n }\n}\n\nconst persistSkipped = !stormLogId || stormLogId === 'unknown' || !reportBase64;\n\nreturn [{\n json: {\n ...item.json,\n storm_log_id: stormLogId,\n pending_contact_email: item.json.contact_email || '',\n pending_email_file_name: item.json.email_file_name || 'lightning_report.docx',\n pending_report_base64: reportBase64,\n pending_report_mime_type: binaryMeta.mimeType || 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n persist_skipped: persistSkipped,\n },\n binary: item.binary,\n}];"
|
||
},
|
||
"type": "n8n-nodes-base.code",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
203392,
|
||
84224
|
||
],
|
||
"id": "8f370884-72f6-444e-a429-7229eff9d0ca",
|
||
"name": "Build Pending Email Record"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"conditions": {
|
||
"options": {
|
||
"caseSensitive": true,
|
||
"leftValue": "",
|
||
"typeValidation": "strict",
|
||
"version": 3
|
||
},
|
||
"conditions": [
|
||
{
|
||
"id": "a9b8c7d6-e5f4-3210-abcd-ef0987654321",
|
||
"leftValue": "={{ $('Build Pending Email Record').first().json.persist_skipped }}",
|
||
"rightValue": "",
|
||
"operator": {
|
||
"type": "boolean",
|
||
"operation": "false",
|
||
"singleValue": true
|
||
}
|
||
}
|
||
],
|
||
"combinator": "and"
|
||
},
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.if",
|
||
"typeVersion": 2.3,
|
||
"position": [
|
||
203776,
|
||
84224
|
||
],
|
||
"id": "9b070b06-30e7-4d6d-97c1-1b178f70c9e8",
|
||
"name": "Can Persist Report?"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"operation": "update",
|
||
"dataTableId": {
|
||
"__rl": true,
|
||
"value": "PCYhmruG5hu52Alf",
|
||
"mode": "list",
|
||
"cachedResultName": "storm_logs",
|
||
"cachedResultUrl": "/projects/dWFeIPZ8ouir7Ex0/datatables/PCYhmruG5hu52Alf"
|
||
},
|
||
"matchType": "allConditions",
|
||
"filters": {
|
||
"conditions": [
|
||
{
|
||
"keyValue": "={{ $('Build Pending Email Record').first().json.storm_log_id }}"
|
||
}
|
||
]
|
||
},
|
||
"columns": {
|
||
"mappingMode": "defineBelow",
|
||
"value": {
|
||
"pending_contact_email": "={{ $('Build Pending Email Record').first().json.pending_contact_email }}",
|
||
"pending_email_file_name": "={{ $('Build Pending Email Record').first().json.pending_email_file_name }}",
|
||
"pending_report_base64": "={{ $('Build Pending Email Record').first().json.pending_report_base64 }}",
|
||
"pending_report_mime_type": "={{ $('Build Pending Email Record').first().json.pending_report_mime_type }}"
|
||
},
|
||
"matchingColumns": [],
|
||
"schema": [
|
||
{
|
||
"id": "pending_contact_email",
|
||
"displayName": "pending_contact_email",
|
||
"required": false,
|
||
"defaultMatch": false,
|
||
"display": true,
|
||
"type": "string",
|
||
"readOnly": false,
|
||
"removed": false
|
||
},
|
||
{
|
||
"id": "pending_email_file_name",
|
||
"displayName": "pending_email_file_name",
|
||
"required": false,
|
||
"defaultMatch": false,
|
||
"display": true,
|
||
"type": "string",
|
||
"readOnly": false,
|
||
"removed": false
|
||
},
|
||
{
|
||
"id": "pending_report_base64",
|
||
"displayName": "pending_report_base64",
|
||
"required": false,
|
||
"defaultMatch": false,
|
||
"display": true,
|
||
"type": "string",
|
||
"readOnly": false,
|
||
"removed": false
|
||
},
|
||
{
|
||
"id": "pending_report_mime_type",
|
||
"displayName": "pending_report_mime_type",
|
||
"required": false,
|
||
"defaultMatch": false,
|
||
"display": true,
|
||
"type": "string",
|
||
"readOnly": false,
|
||
"removed": false
|
||
}
|
||
],
|
||
"attemptToConvertTypes": false,
|
||
"convertFieldsToString": false
|
||
},
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.dataTable",
|
||
"typeVersion": 1.1,
|
||
"position": [
|
||
204016,
|
||
84208
|
||
],
|
||
"id": "c5477916-2b80-48bc-af90-8516c6990513",
|
||
"name": "Persist Report To Storm Logs"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"conditions": {
|
||
"options": {
|
||
"caseSensitive": true,
|
||
"leftValue": "",
|
||
"typeValidation": "strict",
|
||
"version": 3
|
||
},
|
||
"conditions": [
|
||
{
|
||
"id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
|
||
"leftValue": "={{ $('Still Valid After Ack?').first().json.status }}",
|
||
"rightValue": "confirmed",
|
||
"operator": {
|
||
"type": "string",
|
||
"operation": "equals"
|
||
}
|
||
}
|
||
],
|
||
"combinator": "and"
|
||
},
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.if",
|
||
"typeVersion": 2.3,
|
||
"position": [
|
||
197728,
|
||
85024
|
||
],
|
||
"id": "49819232-e8f6-4ecd-9119-6627c0708b4d",
|
||
"name": "Report Approved?"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"operation": "get",
|
||
"dataTableId": {
|
||
"__rl": true,
|
||
"value": "PCYhmruG5hu52Alf",
|
||
"mode": "list",
|
||
"cachedResultName": "storm_logs",
|
||
"cachedResultUrl": "/projects/dWFeIPZ8ouir7Ex0/datatables/PCYhmruG5hu52Alf"
|
||
},
|
||
"matchType": "allConditions",
|
||
"filters": {
|
||
"conditions": [
|
||
{
|
||
"keyValue": "={{ $('Still Valid After Ack?').first().json.id }}"
|
||
}
|
||
]
|
||
},
|
||
"limit": 1
|
||
},
|
||
"type": "n8n-nodes-base.dataTable",
|
||
"typeVersion": 1.1,
|
||
"position": [
|
||
198000,
|
||
84880
|
||
],
|
||
"id": "4b2668bf-983c-49f4-9082-7ea90fafde9e",
|
||
"name": "Get Pending Report From Storm Logs",
|
||
"alwaysOutputData": true
|
||
},
|
||
{
|
||
"parameters": {
|
||
"jsCode": "const ack = $('Still Valid After Ack?').first().json;\nconst stormLogId = String(ack.id ?? '').trim();\nconst rows = $('Get Pending Report From Storm Logs').all()\n .map((item) => item.json)\n .filter((row) => row && row.id !== undefined && row.id !== null);\nconst row = rows[0] || {};\nlet reportBase64 = String(row.pending_report_base64 || '').trim();\nreportBase64 = reportBase64.replace(/^data:[^;]+;base64,/, '').replace(/\\s+/g, '');\nconst hasQueued = reportBase64.length > 0;\n\nreturn [{\n json: {\n ...ack,\n has_queued_report: hasQueued,\n contact_email: String(row.pending_contact_email || '').trim(),\n customer_name: row.customer_name || ack.customer_name,\n email_file_name: row.pending_email_file_name || 'lightning_report.docx',\n pending_report_base64: reportBase64,\n pending_report_mime_type: row.pending_report_mime_type || 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n storm_log_id: stormLogId,\n },\n}];"
|
||
},
|
||
"type": "n8n-nodes-base.code",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
198224,
|
||
84880
|
||
],
|
||
"id": "e55bf292-c738-4213-a5c7-746f44edd468",
|
||
"name": "Load Queued Customer Report"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"operation": "get",
|
||
"dataTableId": {
|
||
"__rl": true,
|
||
"value": "bMJhm1lTDbCj9eY1",
|
||
"mode": "list",
|
||
"cachedResultName": "customers",
|
||
"cachedResultUrl": "/projects/dWFeIPZ8ouir7Ex0/datatables/bMJhm1lTDbCj9eY1"
|
||
},
|
||
"matchType": "allConditions",
|
||
"filters": {
|
||
"conditions": [
|
||
{
|
||
"keyName": "customer_name",
|
||
"keyValue": "={{ $('Load Queued Customer Report').item.json.customer_name }}"
|
||
}
|
||
]
|
||
},
|
||
"limit": 1
|
||
},
|
||
"type": "n8n-nodes-base.dataTable",
|
||
"typeVersion": 1.1,
|
||
"position": [
|
||
198464,
|
||
84880
|
||
],
|
||
"id": "bef1a639-c07e-4211-86b0-8cb2ffedc70f",
|
||
"name": "Lookup Customer Contact Email",
|
||
"alwaysOutputData": true
|
||
},
|
||
{
|
||
"parameters": {
|
||
"jsCode": "const ack = $('Load Queued Customer Report').first().json;\nconst lookupRows = $input.all().map((i) => i.json).filter((r) => r && (r.contact_email || r.id));\nconst lookupEmail = lookupRows.length > 0 ? String(lookupRows[0].contact_email || '').trim() : '';\nconst contactEmail = String(ack.contact_email || '').trim() || lookupEmail;\n\nreturn [{\n json: {\n ...ack,\n contact_email: contactEmail,\n },\n}];"
|
||
},
|
||
"type": "n8n-nodes-base.code",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
198672,
|
||
84880
|
||
],
|
||
"id": "8cf75ae0-b9b8-480d-b7ae-ce1fc1c79672",
|
||
"name": "Merge Customer Contact Email"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"conditions": {
|
||
"options": {
|
||
"caseSensitive": true,
|
||
"leftValue": "",
|
||
"typeValidation": "strict",
|
||
"version": 3
|
||
},
|
||
"conditions": [
|
||
{
|
||
"id": "e5f6a7b8-c9d0-1234-ef01-345678901234",
|
||
"leftValue": "={{ $json.contact_email }}",
|
||
"rightValue": "",
|
||
"operator": {
|
||
"type": "string",
|
||
"operation": "notEmpty"
|
||
}
|
||
},
|
||
{
|
||
"id": "f6a7b8c9-d0e1-2345-f012-456789012346",
|
||
"leftValue": "={{ $json.has_queued_report }}",
|
||
"rightValue": "",
|
||
"operator": {
|
||
"type": "boolean",
|
||
"operation": "true",
|
||
"singleValue": true
|
||
}
|
||
}
|
||
],
|
||
"combinator": "and"
|
||
},
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.if",
|
||
"typeVersion": 2.3,
|
||
"position": [
|
||
198880,
|
||
84880
|
||
],
|
||
"id": "586e567f-9127-4f2c-9ab7-4049c58ec36d",
|
||
"name": "Ready To Email Customer?"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"jsCode": "const item = $input.first();\nconst toEmail = String(item.json.contact_email || '').trim();\nconst fromEmail = 'info@iklim.co';\nconst customerName = String(item.json.customer_name || 'Customer');\nconst fileName = String(item.json.email_file_name || 'lightning_report.docx');\nconst mimeType = item.json.pending_report_mime_type || 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';\n\nlet content = String(item.json.pending_report_base64 || '').trim();\ncontent = content.replace(/^data:[^;]+;base64,/, '').replace(/\\s+/g, '');\n\nif (!toEmail) {\n throw new Error('Missing contact_email for SendGrid');\n}\nif (!content) {\n throw new Error('Missing pending_report_base64 for SendGrid attachment');\n}\n\ntry {\n Buffer.from(content, 'base64');\n} catch {\n throw new Error('pending_report_base64 is not valid base64');\n}\n\nreturn [{\n json: {\n ...item.json,\n sendgrid_payload: {\n personalizations: [{ to: [{ email: toEmail }] }],\n from: { email: fromEmail, name: 'iklim.co' },\n subject: `Yıldırım Aktivite Raporu – ${customerName}`,\n content: [{\n type: 'text/html',\n value: `<p>Sayın yetkili,</p><p><strong>${customerName}</strong> için yıldırım aktivite raporunuz ektedir.</p><p>İyi çalışmalar,<br>İklim.co</p>`,\n }],\n attachments: [{\n content,\n filename: fileName,\n type: mimeType,\n disposition: 'attachment',\n }],\n },\n },\n}];"
|
||
},
|
||
"type": "n8n-nodes-base.code",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
199120,
|
||
84864
|
||
],
|
||
"id": "5d6c2086-811e-4df7-89a4-9bb40619bd50",
|
||
"name": "Prepare SendGrid Email Payload"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"method": "POST",
|
||
"url": "https://api.sendgrid.com/v3/mail/send",
|
||
"authentication": "predefinedCredentialType",
|
||
"nodeCredentialType": "sendGridApi",
|
||
"sendHeaders": true,
|
||
"headerParameters": {
|
||
"parameters": [
|
||
{
|
||
"name": "Content-Type",
|
||
"value": "application/json"
|
||
}
|
||
]
|
||
},
|
||
"sendBody": true,
|
||
"specifyBody": "json",
|
||
"jsonBody": "={{ $json.sendgrid_payload }}",
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.httpRequest",
|
||
"typeVersion": 4.3,
|
||
"position": [
|
||
199360,
|
||
84864
|
||
],
|
||
"id": "62f7d108-d3fd-48ba-b338-e3339746c30e",
|
||
"name": "Send Customer Report Email",
|
||
"credentials": {
|
||
"sendGridApi": {
|
||
"id": "fhinNHPH2Wnm5fUh",
|
||
"name": "SendGrid account"
|
||
}
|
||
},
|
||
"onError": "continueErrorOutput"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"operation": "update",
|
||
"dataTableId": {
|
||
"__rl": true,
|
||
"value": "PCYhmruG5hu52Alf",
|
||
"mode": "list",
|
||
"cachedResultName": "storm_logs",
|
||
"cachedResultUrl": "/projects/dWFeIPZ8ouir7Ex0/datatables/PCYhmruG5hu52Alf"
|
||
},
|
||
"matchType": "allConditions",
|
||
"filters": {
|
||
"conditions": [
|
||
{
|
||
"keyValue": "={{ $('Still Valid After Ack?').first().json.id }}"
|
||
}
|
||
]
|
||
},
|
||
"columns": {
|
||
"mappingMode": "defineBelow",
|
||
"value": {
|
||
"pending_contact_email": "",
|
||
"pending_email_file_name": "",
|
||
"pending_report_base64": "",
|
||
"pending_report_mime_type": ""
|
||
},
|
||
"matchingColumns": [],
|
||
"schema": [
|
||
{
|
||
"id": "pending_contact_email",
|
||
"displayName": "pending_contact_email",
|
||
"required": false,
|
||
"defaultMatch": false,
|
||
"display": true,
|
||
"type": "string",
|
||
"readOnly": false,
|
||
"removed": false
|
||
},
|
||
{
|
||
"id": "pending_email_file_name",
|
||
"displayName": "pending_email_file_name",
|
||
"required": false,
|
||
"defaultMatch": false,
|
||
"display": true,
|
||
"type": "string",
|
||
"readOnly": false,
|
||
"removed": false
|
||
},
|
||
{
|
||
"id": "pending_report_base64",
|
||
"displayName": "pending_report_base64",
|
||
"required": false,
|
||
"defaultMatch": false,
|
||
"display": true,
|
||
"type": "string",
|
||
"readOnly": false,
|
||
"removed": false
|
||
},
|
||
{
|
||
"id": "pending_report_mime_type",
|
||
"displayName": "pending_report_mime_type",
|
||
"required": false,
|
||
"defaultMatch": false,
|
||
"display": true,
|
||
"type": "string",
|
||
"readOnly": false,
|
||
"removed": false
|
||
}
|
||
],
|
||
"attemptToConvertTypes": false,
|
||
"convertFieldsToString": false
|
||
},
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.dataTable",
|
||
"typeVersion": 1.1,
|
||
"position": [
|
||
199984,
|
||
85072
|
||
],
|
||
"id": "24720d02-c609-43ae-87d7-d9ec62b3f351",
|
||
"name": "Clear Pending Report In Storm Logs"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"jsCode": "const ack = $('Still Valid After Ack?').first().json;\nconst stormLogId = String($json.storm_log_id ?? $json.id ?? ack.id ?? '').trim();\nreturn [{ json: { ...ack, ...$json, storm_log_id: stormLogId || ack.id } }];"
|
||
},
|
||
"type": "n8n-nodes-base.code",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
200176,
|
||
85072
|
||
],
|
||
"id": "34c47201-3f8a-449b-a327-7c6c76f6aec8",
|
||
"name": "Restore Approval Context"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"conditions": {
|
||
"options": {
|
||
"caseSensitive": true,
|
||
"leftValue": "",
|
||
"typeValidation": "strict",
|
||
"version": 3
|
||
},
|
||
"conditions": [
|
||
{
|
||
"id": "d0e1f2a3-b4c5-6789-abcd-ef0123456780",
|
||
"leftValue": "={{ $('Still Valid After Ack?').first().json.slack_response_url }}",
|
||
"rightValue": "",
|
||
"operator": {
|
||
"type": "string",
|
||
"operation": "notEmpty"
|
||
}
|
||
}
|
||
],
|
||
"combinator": "and"
|
||
},
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.if",
|
||
"typeVersion": 2.3,
|
||
"position": [
|
||
200368,
|
||
85072
|
||
],
|
||
"id": "e54d3ac1-57da-4ca7-a7fb-cc3e90367178",
|
||
"name": "Has Slack Response URL?"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"select": "channel",
|
||
"channelId": {
|
||
"__rl": true,
|
||
"value": "C0A9K1AC7SN",
|
||
"mode": "list",
|
||
"cachedResultName": "n8n-events"
|
||
},
|
||
"text": "=📧 Lightning report emailed to {{ $('Merge Customer Contact Email').item.json.contact_email }} for *{{ $('Merge Customer Contact Email').item.json.customer_name }}*.",
|
||
"otherOptions": {
|
||
"includeLinkToWorkflow": false
|
||
}
|
||
},
|
||
"type": "n8n-nodes-base.slack",
|
||
"typeVersion": 2.4,
|
||
"position": [
|
||
199632,
|
||
84864
|
||
],
|
||
"id": "8389e862-384a-40ff-ae64-40b9adc777df",
|
||
"name": "Email Sent Notification",
|
||
"webhookId": "e1f2a3b4-c5d6-7890-abcd-ef0123456789",
|
||
"credentials": {
|
||
"slackApi": {
|
||
"id": "OKgM8VkM05pJl9kU",
|
||
"name": "Tarla Slack Account"
|
||
}
|
||
}
|
||
},
|
||
{
|
||
"parameters": {
|
||
"assignments": {
|
||
"assignments": [
|
||
{
|
||
"id": "8a6510f7-a33d-4c87-9472-44329c24e08e",
|
||
"name": "customer_id",
|
||
"value": "=9",
|
||
"type": "number"
|
||
},
|
||
{
|
||
"id": "e3af6b6b-2360-4900-8d39-8a5fe0fd2f3e",
|
||
"name": "start_time_epoch_ms",
|
||
"value": "=1781315646000",
|
||
"type": "number"
|
||
},
|
||
{
|
||
"id": "164778e6-cb52-4f1f-8207-4ec37d404a8f",
|
||
"name": "end_time_epoch_ms",
|
||
"value": "=1781363826000",
|
||
"type": "number"
|
||
}
|
||
]
|
||
},
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.set",
|
||
"typeVersion": 3.4,
|
||
"position": [
|
||
196208,
|
||
83968
|
||
],
|
||
"id": "252ea1d9-f46d-4b05-b894-6b637547122a",
|
||
"name": "Manual Input"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"content": "Manual Input fields:\n- customer_id: customer to load from datatable (location, turbines, name, language)\n- start_time_epoch_ms / end_time_epoch_ms: report time window (required)",
|
||
"height": 120,
|
||
"width": 520
|
||
},
|
||
"type": "n8n-nodes-base.stickyNote",
|
||
"position": [
|
||
195488,
|
||
83760
|
||
],
|
||
"typeVersion": 1,
|
||
"id": "6e2958a5-80c6-4c8a-9011-ab882073c897",
|
||
"name": "Sticky Note Manual Input"
|
||
}
|
||
],
|
||
"pinData": {},
|
||
"connections": {
|
||
"Login to iklim.co": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "CalculateExpirations",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
],
|
||
[
|
||
{
|
||
"node": "Auth Retry Handler",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"CalculateExpirations": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Store Login Credentials",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"CalculateExpirations1": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Store Refresh Credentials",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Refresh Token": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "CalculateExpirations1",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
],
|
||
[
|
||
{
|
||
"node": "Auth Retry Handler",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Restore Credentials": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Switch",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Switch": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Login to iklim.co",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
],
|
||
[
|
||
{
|
||
"node": "Refresh Token",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
],
|
||
[
|
||
{
|
||
"node": "Iterate Customers",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Store Login Credentials": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Restore Credentials",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Store Refresh Credentials": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Restore Credentials",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Daily Trigger": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Manual Input",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Lightning Request": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Logic Gate",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
],
|
||
[
|
||
{
|
||
"node": "Lightning Retry Handler",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Lightning Retry Handler": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Retry Lightning Request?",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Retry Lightning Request?": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Wait Before Retry",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
],
|
||
[
|
||
{
|
||
"node": "Request Error Notification",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Wait Before Retry": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Calculate Lightning Headers",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Request Error Notification": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Stop Workflow",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Auth Retry Handler": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Retry Auth Request?",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Retry Auth Request?": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Wait Before Auth Retry",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
],
|
||
[
|
||
{
|
||
"node": "Send Error Notification",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Wait Before Auth Retry": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Login to iklim.co",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Send Error Notification": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Stop Workflow Auth",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Iterate Customers": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Loop Over Items",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Get Customer Wind Turbines": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Centroid & Distance Ring calculation",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Centroid & Distance Ring calculation": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Loop init",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Loop init": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Calculate Lightning Headers",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Logic Gate": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Stop Loop",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Stop Loop": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Lightning found?",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
],
|
||
[
|
||
{
|
||
"node": "Calculate Lightning Headers",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Lightning found?": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Get row(s)",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
],
|
||
[
|
||
{
|
||
"node": "Loop Over Items",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Get row(s)": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Prepare Insert Decision",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"If": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Insert row",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
],
|
||
[
|
||
{
|
||
"node": "Loop Over Items",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Insert row": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Calculate Thunderstorm Headers",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Loop Over Items": {
|
||
"main": [
|
||
[],
|
||
[
|
||
{
|
||
"node": "Get Customer Wind Turbines",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Calculate Thunderstorm Headers": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Thunderstorm Request",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Calculate Lightning Headers": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Lightning Request",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Thunderstorm Request": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Build Report Payload",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
],
|
||
[
|
||
{
|
||
"node": "Thunderstorm Retry Handler",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Thunderstorm Retry Handler": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Retry Thunderstorm Request?",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Retry Thunderstorm Request?": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Wait Before Thunderstorm Retry",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
],
|
||
[
|
||
{
|
||
"node": "Thunderstorm Error Fallback",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Wait Before Thunderstorm Retry": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Calculate Thunderstorm Headers",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Thunderstorm Error Fallback": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Build Report Payload",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Build Report Payload": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Start Async Report",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Start Async Report": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Attach Report Job",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Attach Report Job": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Wait for Report",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Wait for Report": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Poll Report Status",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Poll Report Status": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Merge Poll Status",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Merge Poll Status": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Report Complete?",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Report Complete?": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Download Report",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
],
|
||
[
|
||
{
|
||
"node": "Report Failed?",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Report Failed?": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Stop Report Failed",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
],
|
||
[
|
||
{
|
||
"node": "Continue Polling?",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Continue Polling?": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Wait for Report",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
],
|
||
[
|
||
{
|
||
"node": "Stop Report Timeout",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Download Report": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Prepare DOCX Metadata",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Prepare DOCX Metadata": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Get Storm Logs For Next Id",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Get Storm Logs For Next Id": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Resolve Storm Log Id",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Resolve Storm Log Id": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Prepare Customer Email Context",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Prepare Customer Email Context": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Build Pending Email Record",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Build Pending Email Record": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Upload Report to Slack",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Upload Report to Slack": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Can Persist Report?",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Can Persist Report?": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Persist Report To Storm Logs",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
],
|
||
[
|
||
{
|
||
"node": "Send Approval Buttons",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Persist Report To Storm Logs": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Send Approval Buttons",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Send Approval Buttons": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Loop Over Items",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Slack Interaction Webhook": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Parse Slack Button Payload",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Parse Slack Button Payload": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Valid Storm Approval Button?",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Valid Storm Approval Button?": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Respond Slack Interaction",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
],
|
||
[
|
||
{
|
||
"node": "Respond Slack Interaction",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Respond Slack Interaction": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Still Valid After Ack?",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Still Valid After Ack?": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Approve Pending Row",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Approve Pending Row": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Report Approved?",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Report Approved?": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Get Pending Report From Storm Logs",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
],
|
||
[
|
||
{
|
||
"node": "Clear Pending Report In Storm Logs",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Get Pending Report From Storm Logs": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Load Queued Customer Report",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Load Queued Customer Report": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Lookup Customer Contact Email",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Lookup Customer Contact Email": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Merge Customer Contact Email",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Merge Customer Contact Email": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Ready To Email Customer?",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Ready To Email Customer?": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Prepare SendGrid Email Payload",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
],
|
||
[
|
||
{
|
||
"node": "Clear Pending Report In Storm Logs",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Prepare SendGrid Email Payload": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Send Customer Report Email",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Send Customer Report Email": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Email Sent Notification",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
],
|
||
[
|
||
{
|
||
"node": "Clear Pending Report In Storm Logs",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Email Sent Notification": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Clear Pending Report In Storm Logs",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Clear Pending Report In Storm Logs": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Restore Approval Context",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Restore Approval Context": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Has Slack Response URL?",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Has Slack Response URL?": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Notify Slack Recording Result",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Prepare Insert Decision": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "If",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Manual Input": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Restore Credentials",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
}
|
||
},
|
||
"active": false,
|
||
"settings": {
|
||
"executionOrder": "v1",
|
||
"binaryMode": "separate"
|
||
},
|
||
"versionId": "6f66fdd8-206e-4243-a9b2-fd962cecfbf6",
|
||
"meta": {
|
||
"instanceId": "15c4ff3a74619031c77894fe5fb8c0fd585362ef637b1873abd56a139f543e12"
|
||
},
|
||
"id": "cEYNZsrEiJjLLBtD",
|
||
"tags": []
|
||
} |