Workflow: Production Workflow
Integrations: cron, emailSend, executeCommand, functionItem, htmlExtract, httpRequest, if, moveBinaryData, readBinaryFile, stopAndError, writeBinaryFile

{"nodes": [{"name": "HTML Extract", "type": "n8n-nodes-base.htmlExtract", "position": [-220, -390], "parameters": {"options": {}, "extractionValues": {"values": [{"key": "price", "cssSelector": "={{$node[\"initItem\"].json[\"selector\"]}}"}]}}, "typeVersion": 1, "id": "node-72975390"}, {"name": "Cron", "type": "n8n-nodes-base.cron", "position": [-1290, -390], "parameters": {"triggerTimes": {"item": [{"mode": "everyX", "unit": "minutes", "value": 15}]}}, "typeVersion": 1, "id": "node-67bc1b83"}, {"name": "getActualPrice", "type": "n8n-nodes-base.functionItem", "position": [-20, -390], "parameters": {"functionCode": "const globalData = getWorkflowStaticData('global');\n\nvar price = String(item.price).replace(\",\", \".\");\nprice = parseFloat(price);\n//price = price.replace(/\\D/g, '');\n//item.price = String(item.price).replace(\",\", \".\");\n//item.price = parseFloat(item.price);\n\nitem.priceExists = (price > 0 ? true : false)\nitem.price = price;\n\n// Update its data\nglobalData.actualPrice = item;\n\nreturn item;"}, "typeVersion": 1, "id": "node-65c51a08"}, {"name": "fetchWeb", "type": "n8n-nodes-base.httpRequest", "position": [-410, -390], "parameters": {"url": "{{ $env.BASE_URL }}", "options": {}, "responseFormat": "string"}, "typeVersion": 1, "id": "node-9c733189"}, {"name": "FunctionItem", "type": "n8n-nodes-base.functionItem", "position": [1020, -390], "parameters": {"functionCode": "const globalData = getWorkflowStaticData('global');\n\nglobalData.iteration = 0;\n//var thiselem = $node[\"initItem\"].json;\n\n//const s1 = {'slug': thiselem.slug, \"link\": thiselem.link, \"selector\": thiselem.selector, \"price\":$node[\"getActualPrice\"].json.price, \"currency\": thiselem.currency};\n//const s2 = {'slug': thiselem.slug+'2', \"link\": thiselem.link, \"selector\": thiselem.selector, \"price\":$node[\"getActualPrice\"].json.price, \"currency\": thiselem.currency};\n//const s3 = {'slug': thiselem.slug+'3', \"link\": thiselem.link, \"selector\": thiselem.selector, \"price\":$node[\"getActualPrice\"].json.price, \"currency\": thiselem.currency};\n\nreturn $node[\"changeME\"].json.myWatchers;\n"}, "typeVersion": 1, "id": "node-973cbffd"}, {"name": "Write Binary File1", "type": "n8n-nodes-base.writeBinaryFile", "position": [1850, -390], "parameters": {"fileName": "/data/kopacky.json", "dataPropertyName": "=price"}, "typeVersion": 1, "id": "node-09b5463d"}, {"name": "Move Binary Data1", "type": "n8n-nodes-base.moveBinaryData", "position": [1420, -390], "parameters": {"mode": "jsonToBinary", "options": {}, "destinationKey": "price"}, "typeVersion": 1, "id": "node-88afaca0"}, {"name": "IF1", "type": "n8n-nodes-base.if", "position": [550, -370], "parameters": {"conditions": {"string": [{"value1": "={{$node[\"checkifexists\"].json[\"stdout\"]}}", "value2": "Exists", "operation": "notEqual"}]}}, "typeVersion": 1, "id": "node-9d6abda6"}, {"name": "checkifexists", "type": "n8n-nodes-base.executeCommand", "position": [410, -370], "parameters": {"command": "if [ -r /data/kopacky.json ]; then echo Exists; fi"}, "typeVersion": 1, "id": "node-b10ce6e5"}, {"name": "IF3", "type": "n8n-nodes-base.if", "position": [680, 110], "parameters": {"conditions": {"string": [{"value1": "={{$node[\"checkifexists\"].json[\"stdout\"]}}", "value2": "Exists"}]}}, "typeVersion": 1, "id": "node-42955e40"}, {"name": "SaveToFile", "type": "n8n-nodes-base.writeBinaryFile", "position": [1650, 110], "parameters": {"fileName": "/data/kopacky.json", "dataPropertyName": "=price"}, "typeVersion": 1, "id": "node-1d89385c"}, {"name": "JsonToBinary", "type": "n8n-nodes-base.moveBinaryData", "position": [1500, 110], "parameters": {"mode": "jsonToBinary", "options": {}, "destinationKey": "price"}, "typeVersion": 1, "id": "node-8affa0be"}, {"name": "changeME", "type": "n8n-nodes-base.functionItem", "color": "#3BDD33", "position": [-830, -390], "parameters": {"functionCode": "const globalData = getWorkflowStaticData('global');\n\n//{'slug': 'kopacky', 'link': 'https://www.adsport.sk/kopacky-lisovky-adidas-x-19-3-ll-fg-ef0598/#1131861', 'currency': 'EUR'}[]\nvar myWatchers = [\n{'slug': 'kopacky', 'link': 'https://www.adsport.sk/kopacky-lisovky-adidas-x-19-3-ll-fg-ef0598/#1131861', 'selector':'.prices > strong:nth-child(1) > span:nth-child(1)', 'currency': 'EUR'},\n{'slug': 'kopacky2', 'link': 'https://www.adsport.sk/turfy-adidas-ace-tango-17-3-tf-by2203/', 'selector':'.col-xs-4 > strong:nth-child(1) > span:nth-child(1)', 'currency': 'EUR'},\n{'slug': 'mobilcek', 'link': 'https://mobil.bazos.sk/inzerat/112253662/predam-odolny-doogee-s60-52-4g-lte-nfc.php', 'selector':'.listadvlevo > table:nth-child(1) > tbody:nth-child(1) > tr:nth-child(5) > td:nth-child(2) > b:nth-child(2)', 'currency': 'EUR'},\n{'slug': 'ADIZERO RC 2', 'link': 'https://www.adsport.sk/panske-bezecke-topanky-adidas-adizero-rc-2-eg1187/', 'selector':'.col-xs-4 > strong:nth-child(1) > span:nth-child(1)', 'currency': 'EUR'}\n];\n\nitem.myWatchers = myWatchers;\nitem.watchersCount = myWatchers.length;\nglobalData.myWatchers = myWatchers;\n\nreturn item;"}, "typeVersion": 1, "id": "node-e766605b"}, {"name": "initItem", "type": "n8n-nodes-base.functionItem", "position": [-620, -390], "parameters": {"functionCode": "const globalData = getWorkflowStaticData('global');\n\nvar counter = globalData.iteration;\n\nitem.myWatchers[counter].watchersCount = item.watchersCount;\nitem.myWatchers[counter].canContinue = (globalData.iteration < item.watchersCount-1 ? true : false);\n//item.myWatchers[counter].canContinue = false;\n\nglobalData.iteration = counter + 1;\n\nreturn item.myWatchers[counter];"}, "typeVersion": 1, "id": "node-26f66a47"}, {"name": "savedItems", "type": "n8n-nodes-base.readBinaryFile", "position": [850, -20], "parameters": {"filePath": "/data/kopacky.json", "dataPropertyName": "savedItems"}, "typeVersion": 1, "continueOnFail": true, "alwaysOutputData": true, "id": "node-6925aade"}, {"name": "itemsToJSON", "type": "n8n-nodes-base.moveBinaryData", "position": [1020, -20], "parameters": {"options": {}, "sourceKey": "savedItems"}, "typeVersion": 1, "id": "node-f0c97aa6"}, {"name": "IF", "type": "n8n-nodes-base.if", "position": [2190, -90], "parameters": {"conditions": {"string": [], "boolean": [{"value1": "={{$node[\"initItem\"].json[\"canContinue\"]}}", "value2": true}]}}, "typeVersion": 1, "id": "node-e72fe6ec"}, {"name": "initItem1", "type": "n8n-nodes-base.functionItem", "position": [-1060, -390], "parameters": {"functionCode": "const globalData = getWorkflowStaticData('global');\n\nglobalData.iteration = 0;\n\nreturn item;"}, "typeVersion": 1, "id": "node-5cabbdd1"}, {"name": "IF2", "type": "n8n-nodes-base.if", "position": [1850, 110], "parameters": {"conditions": {"number": [{"value1": "={{$node[\"getActualPrice\"].json[\"price\"]}}", "value2": "={{$node[\"updateSavedItems1\"].json[\"oldPrice\"]}}"}], "string": []}}, "typeVersion": 1, "id": "node-aee9a0de"}, {"name": "updateSavedItems", "type": "n8n-nodes-base.functionItem", "position": [1350, 110], "parameters": {"functionCode": "const globalData = getWorkflowStaticData('global');\n\nvar myitems = [];\nvar i;\nfor (i = 0; i < item.items.length; i++) { \n  if($node[\"initItem\"].json.slug == item.items[i].slug && $node[\"getActualPrice\"].json.price < item.items[i].price) {\n    item.items[i].price = $node[\"getActualPrice\"].json.price;\n  }\n  myitems.push(item.items[i]);\n} \n\nreturn myitems;\n"}, "typeVersion": 1, "id": "node-5c779ea4"}, {"name": "updateSavedItems1", "type": "n8n-nodes-base.functionItem", "position": [1200, -20], "parameters": {"functionCode": "const globalData = getWorkflowStaticData('global');\nvar oldPrice = null;\nvar myitems = [];\nvar i;\nfor (i = 0; i < item.length; i++) {\n  if($node[\"initItem\"].json.slug == item[i].slug) {\n\n    item[i].link = $node[\"initItem\"].json.link;\n    item[i].selector = $node[\"initItem\"].json.selector;\n    item[i].currency = $node[\"initItem\"].json.currency;\n    \n    if(!item[i].price){\n      item[i].price = $node[\"getActualPrice\"].json.price;\n    }\n    \n    if($node[\"getActualPrice\"].json.price < item[i].price){\n      oldPrice = item[i].price;\n      item[i].price = $node[\"getActualPrice\"].json.price;\n    }\n    \n    \n  }\n  \n  myitems.push(item[i]);\n} \n\n//item.somar = $node[\"initItem\"].json;\n//return globalData.actualPrice;\n\nvar itemm = {};\nitemm.items = myitems;\nitemm.oldPrice = oldPrice;\nreturn itemm;\n"}, "typeVersion": 1, "id": "node-683db61d"}, {"name": "cleanData", "type": "n8n-nodes-base.executeCommand", "notes": "This will remove all storaged data.", "position": [-1290, -560], "parameters": {"command": "file=\"/data/kopacky.json\"\n[ -f $file ] && rm $file"}, "typeVersion": 1, "id": "node-630c1a0a"}, {"name": "IF4", "type": "n8n-nodes-base.if", "position": [150, -390], "parameters": {"conditions": {"string": [], "boolean": [{"value1": "={{$node[\"getActualPrice\"].json[\"priceExists\"]}}"}]}}, "typeVersion": 1, "id": "node-16a1013f"}, {"name": "NotifyBetterPrice", "type": "n8n-nodes-base.emailSend", "position": [1850, -90], "parameters": {"html": "=<h2>Nov\u00e1 cena je: {{$node[\"getActualPrice\"].json[\"price\"]}} {{$node[\"initItem\"].json[\"currency\"]}}</h2><br>\nP\u00f4vodn\u00e1 cena bola: {{$node[\"updateSavedItems1\"].json[\"oldPrice\"]}} {{$node[\"initItem\"].json[\"currency\"]}}<br>\nURL: {{$node[\"initItem\"].json[\"link\"]}}", "text": "=", "options": {}, "subject": "=Nov\u00e1 cena - {{$node[\"initItem\"].json[\"slug\"]}} - {{$node[\"getActualPrice\"].json[\"price\"]}} {{$node[\"initItem\"].json[\"currency\"]}}", "toEmail": "sthosstudio@gmail.com", "fromEmail": "hostovecky@weyou.sk"}, "credentials": {"smtp": "hostovecky@weyou.sk"}, "typeVersion": 1, "id": "node-6df8bbdd"}, {"name": "NotifyIncorrectPrice", "type": "n8n-nodes-base.emailSend", "position": [270, -690], "parameters": {"html": "=Please check the link or selector for the item with slug <strong>{{$node[\"initItem\"].json[\"slug\"]}}</strong><br>\nURL: {{$node[\"initItem\"].json[\"link\"]}}", "text": "=", "options": {}, "subject": "={{$node[\"initItem\"].json[\"slug\"]}} - Getting price issue.", "toEmail": "sthosstudio@gmail.com", "fromEmail": "hostovecky@weyou.sk"}, "credentials": {"smtp": "hostovecky@weyou.sk"}, "typeVersion": 1, "id": "node-ca4f72c2"}, {"id": "error-c42b7024", "name": "Error Handler", "type": "n8n-nodes-base.stopAndError", "typeVersion": 1, "position": [1000, 400], "parameters": {"message": "Workflow execution error", "options": {}}}], "connections": {}, "name": "Production Workflow", "settings": {"executionOrder": "v1", "saveManualExecutions": true, "callerPolicy": "workflowsFromSameOwner", "errorWorkflow": null, "timezone": "UTC", "executionTimeout": 3600, "maxExecutions": 1000, "retryOnFail": true, "retryCount": 3}, "meta": {"instanceId": "workflow-6422958b", "versionId": "1.0.0", "createdAt": "2025-09-29T07:07:45.106160", "updatedAt": "2025-09-29T07:07:45.106167", "owner": "n8n-user", "license": "MIT", "category": "automation", "status": "active", "priority": "high", "environment": "production"}, "tags": ["automation", "n8n", "production-ready", "excellent", "optimized"], "description": "Production-ready workflow: Production Workflow. This workflow has been optimized for production use with comprehensive error handling, security, and documentation."}