FortiOS Recommended Release Tracker Into KB

Automated workflow to sync FortiOS recommended releases from Fortinet Community to Hudu KB

  • Scrapes the latest FortiOS recommended releases from Fortinet's community forum

  • Processes and normalises the data (fixes missing product family values)

  • Updates the existing Hudu knowledge base article with fresh data

  • Automatically timestamps the update

{
  "nodes": [
    {
      "parameters": {},
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        -760,
        -360
      ],
      "id": "b443427e-30aa-4087-a850-fd758f8d9ed0",
      "name": "Manual Trigger"
    },
    {
      "parameters": {
        "url": "https://community.fortinet.com/t5/FortiGate/Technical-Tip-Recommended-Release-for-FortiOS/ta-p/227178",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "User-Agent",
              "value": "Mozilla/5.0"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -540,
        -360
      ],
      "id": "5a14482f-e92a-468e-b544-b8b227a0a12f",
      "name": "Fetch Fortinet Community Page"
    },
    {
      "parameters": {
        "sourceFormat": "html",
        "targetFormat": "n8nObject",
        "inputData": "={{ $json.data }}",
        "selectorMode": "advanced",
        "elementSelector": "div.lia-message-body-content table:contains(\"Product Family\") tbody tr"
      },
      "type": "n8n-nodes-csv-json-htmltable-converter.csvJsonHtmltableConverter",
      "typeVersion": 1,
      "position": [
        -320,
        -360
      ],
      "id": "2bdcfb4d-7780-40b0-bf1f-1bedb2b2b5bc",
      "name": "Parse HTML Table Data"
    },
    {
      "parameters": {
        "jsCode": "// n8n JavaScript Code Node\n// This node processes all items and fills missing Product Family values\n\n// Get all input items\nconst items = $input.all();\nconst results = [];\nlet lastKnownProductFamily = '';\n\n// Process each item\nfor (let i = 0; i < items.length; i++) {\n    const currentItem = items[i].json;\n    \n    // Check if this row has all 4 columns (indicating it's a product family row)\n    const hasFourColumns = currentItem['Product Family'] && \n                          currentItem['Product Details'] && \n                          currentItem['Recommended Release'] && \n                          currentItem.hasOwnProperty('End of Engineering Support Passed (Y/N)');\n    \n    if (hasFourColumns) {\n        // This row has all 4 columns - it's a product family row\n        lastKnownProductFamily = currentItem['Product Family'];\n        \n        const outputItem = {\n            'Product Family': currentItem['Product Family'],\n            'Product Details': currentItem['Product Details'],\n            'Recommended Release': currentItem['Recommended Release'],\n            'End of Engineering Support Passed (Y/N)': currentItem['End of Engineering Support Passed (Y/N)']\n        };\n        results.push(outputItem);\n    } else {\n        // This row is missing the 4th column - data is shifted left\n        // \"Product Family\" field contains the actual Product Details\n        // \"Product Details\" field contains the actual Recommended Release\n        // \"Recommended Release\" field contains the actual End of Engineering Support status\n        // \"End of Engineering Support Passed (Y/N)\" field is missing\n        \n        const outputItem = {\n            'Product Family': lastKnownProductFamily,\n            'Product Details': currentItem['Product Family'] || '', // This is actually the product model\n            'Recommended Release': currentItem['Product Details'] || '', // This is actually the version\n            'End of Engineering Support Passed (Y/N)': currentItem['Recommended Release'] || '' // This is actually Y/N\n        };\n        results.push(outputItem);\n    }\n}\n\nreturn results;"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -100,
        -360
      ],
      "id": "85bce601-c053-4999-ad32-a1c0a97db712",
      "name": "Fix Data Alignment Issues"
    },
    {
      "parameters": {
        "sourceFormat": "n8nObject",
        "targetFormat": "html",
        "inputData": "={{ $('Fix Data Alignment Issues').item.json }}",
        "prettyPrint": true
      },
      "type": "n8n-nodes-csv-json-htmltable-converter.csvJsonHtmltableConverter",
      "typeVersion": 1,
      "position": [
        120,
        -360
      ],
      "id": "d664c574-8613-45ef-9917-9080f63461b9",
      "name": "Generate HTML Table"
    },
    {
      "parameters": {
        "resource": "articles",
        "limit": 1,
        "filters": {
          "name": "FortiOS Recommended Releases"
        }
      },
      "type": "n8n-nodes-hudu.hudu",
      "typeVersion": 1,
      "position": [
        340,
        -360
      ],
      "id": "50510321-b845-47a0-8515-8980db9dba27",
      "name": "Retrieve Hudu KB Article",
      "credentials": {
        "huduApi": {
          "id": "23pSojhjXIg9tX4l",
          "name": "Hudu Cloud"
        }
      }
    },
    {
      "parameters": {
        "operation": "replace",
        "sourceHtml": "={{ $json.content }}",
        "replacementContent": "={{ $('Generate HTML Table').item.json.convertedData }}",
        "tablePreset": "table-under-heading",
        "headingText": "Recommended releases"
      },
      "type": "n8n-nodes-csv-json-htmltable-converter.csvJsonHtmltableConverter",
      "typeVersion": 1,
      "position": [
        560,
        -360
      ],
      "id": "d15e74c8-7d83-4408-b449-1fcfef4bd6c7",
      "name": "Replace Table Content"
    },
    {
      "parameters": {
        "jsCode": "// n8n JavaScript Code Node\n// This node updates the \"Current as of\" date in HTML content\n\n// Get the HTML content from the previous node\nlet htmlContent = $input.first().json.convertedData;\n\n// Create current date in simplified format\nconst now = new Date();\nconst options = { \n    weekday: 'short', \n    day: 'numeric', \n    month: 'short', \n    year: 'numeric', \n    hour: '2-digit', \n    minute: '2-digit',\n    timeZone: 'Australia/Sydney'\n};\n\n// Format: Mon 1 Sept 2025 17:01 GMT+10\nconst formattedDate = now.toLocaleDateString('en-AU', options).replace(',', '') + ' GMT+10';\n\n// Regular expression to find and replace the \"Current as of\" date\n// Matches: <p><strong>Current as of</strong>: [any content]</p>\nconst regex = /(<p><strong>Current as of<\\/strong>:\\s*)[^<]*(<\\/p>)/gi;\n\n// Replace with new date\nconst updatedHtml = htmlContent.replace(regex, `$1${formattedDate}$2`);\n\n// Return the updated HTML\nreturn {\n    convertedData: updatedHtml\n};"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        780,
        -360
      ],
      "id": "363eae63-b386-4ffb-82d7-7c51aa41c941",
      "name": "Update Timestamp"
    },
    {
      "parameters": {
        "resource": "articles",
        "operation": "update",
        "articleId": "={{ $('Retrieve Hudu KB Article').item.json.id }}",
        "updateFields": {
          "content": "={{ $json.convertedData }}"
        }
      },
      "type": "n8n-nodes-hudu.hudu",
      "typeVersion": 1,
      "position": [
        1000,
        -360
      ],
      "id": "83ae3d7e-51ed-41fc-8a25-28e5d40085f0",
      "name": "Save Updated Article",
      "credentials": {
        "huduApi": {
          "id": "23pSojhjXIg9tX4l",
          "name": "Hudu Cloud"
        }
      }
    },
    {
      "parameters": {
        "content": "### WEB SCRAPING\nRetrieves the latest FortiOS recommended releases from Fortinet's community forum",
        "height": 320,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -620,
        -480
      ],
      "typeVersion": 1,
      "id": "27fa0a90-800d-4d96-a2a1-b720a7a60bd4",
      "name": "Sticky Note"
    },
    {
      "parameters": {
        "content": "### DATA PROCESSING\nExtracts table data and fixes formatting issues where product family names are missing from subsequent rows",
        "height": 320,
        "width": 420,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -360,
        -480
      ],
      "typeVersion": 1,
      "id": "6c50c7a6-e2d2-4ff4-af53-085b73cb1adc",
      "name": "Sticky Note1"
    },
    {
      "parameters": {
        "content": "### CONTENT PREPARATION\nConverts processed data back to HTML format, retrieves existing KB article, replaces table content, and updates the 'Current as of' timestamp",
        "height": 320,
        "width": 840,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        80,
        -480
      ],
      "typeVersion": 1,
      "id": "e703e797-a5d4-4157-bc0e-f1a0d40287aa",
      "name": "Sticky Note2"
    },
    {
      "parameters": {
        "content": "### FINAL UPDATE\nSaves the updated content back to the Hudu knowledge base article",
        "height": 320,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        940,
        -480
      ],
      "typeVersion": 1,
      "id": "8418f283-b516-4bba-a291-c4c4db2aa391",
      "name": "Sticky Note3"
    },
    {
      "parameters": {
        "content": "### FORTIOS RELEASE TRACKER\nAutomated workflow to sync FortiOS recommended releases from Fortinet Community to Hudu KB\n\n- Scrapes latest FortiOS recommended releases from Fortinet's community forum\n- Processes and normalises the data (fixes missing product family values)\n- Updates the existing Hudu knowledge base article with fresh data\n- Automatically timestamps the update",
        "height": 200,
        "width": 680
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -620,
        -720
      ],
      "typeVersion": 1,
      "id": "d0534980-65cf-4ab2-8f0d-44edaf7170ed",
      "name": "Sticky Note4"
    }
  ],
  "connections": {
    "Manual Trigger": {
      "main": [
        [
          {
            "node": "Fetch Fortinet Community Page",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Fortinet Community Page": {
      "main": [
        [
          {
            "node": "Parse HTML Table Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse HTML Table Data": {
      "main": [
        [
          {
            "node": "Fix Data Alignment Issues",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fix Data Alignment Issues": {
      "main": [
        [
          {
            "node": "Generate HTML Table",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate HTML Table": {
      "main": [
        [
          {
            "node": "Retrieve Hudu KB Article",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Retrieve Hudu KB Article": {
      "main": [
        [
          {
            "node": "Replace Table Content",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Replace Table Content": {
      "main": [
        [
          {
            "node": "Update Timestamp",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Timestamp": {
      "main": [
        [
          {
            "node": "Save Updated Article",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "meta": {
    "templateCredsSetupCompleted": true,
    "instanceId": "611ddd66d5de12500d1ef99054392712c274ebf5b09f9b60165457ca4792fd1c"
  }
}
3