{
  "name": "ezGPT - Webhook JSON Download",
  "active": false,
  "nodes": [
    {
      "id": "download-request",
      "name": "Download Request",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        200,
        300
      ],
      "parameters": {
        "httpMethod": "GET",
        "path": "ezgpt-file-download",
        "responseMode": "responseNode",
        "options": {}
      }
    },
    {
      "id": "sample-rows",
      "name": "Sample Rows",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        420,
        300
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const rows = [\n  {\"id\":\"001\",\"title\":\"Блокнот\",\"quantity\":2,\"active\":true,\"note\":null,\"tags\":[\"офис\",\"бумага\"]},\n  {\"id\":\"002\",\"title\":\"Чай «Утро»\",\"quantity\":0,\"active\":false,\"note\":\"Проверить остаток\",\"tags\":[]},\n  {\"id\":\"003\",\"title\":\"Папка A4\",\"quantity\":5,\"active\":true,\"note\":\"Строка 1\\nСтрока 2\",\"tags\":[\"архив\"]}\n];\nreturn rows.map(json => ({ json }));"
      }
    },
    {
      "id": "check-rows",
      "name": "Check Rows",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        640,
        300
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const items = $input.all();\nif (items.length < 1 || items.length > 20) throw new Error('row_count_invalid');\nconst ids = new Set();\nfor (const { json: row } of items) {\n  if (Object.keys(row).sort().join('|') !== 'active|id|note|quantity|tags|title') {\n    throw new Error('row_fields_invalid');\n  }\n  if (typeof row.id !== 'string' || !/^\\d{3}$/.test(row.id) || ids.has(row.id)) {\n    throw new Error('id_invalid_or_duplicate');\n  }\n  if (typeof row.title !== 'string' || !row.title.trim() || row.title.length > 100) {\n    throw new Error('title_invalid');\n  }\n  if (!Number.isSafeInteger(row.quantity) || row.quantity < 0) throw new Error('quantity_invalid');\n  if (typeof row.active !== 'boolean') throw new Error('active_invalid');\n  if (row.note !== null && (typeof row.note !== 'string' || row.note.length > 200)) {\n    throw new Error('note_invalid');\n  }\n  if (!Array.isArray(row.tags) || row.tags.length > 3 || row.tags.some(t => typeof t !== 'string' || !t.trim() || t.length > 30)) {\n    throw new Error('tags_invalid');\n  }\n  ids.add(row.id);\n}\nreturn items.map(item => ({ json: item.json }));"
      }
    },
    {
      "id": "make-json",
      "name": "Make JSON",
      "type": "n8n-nodes-base.convertToFile",
      "typeVersion": 1.1,
      "position": [
        860,
        300
      ],
      "parameters": {
        "operation": "toJson",
        "mode": "once",
        "binaryPropertyName": "export",
        "options": {
          "fileName": "catalog-demo.json",
          "format": true,
          "encoding": "utf8",
          "addBOM": false
        }
      }
    },
    {
      "id": "check-file",
      "name": "Check File",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1080,
        300
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const items = $input.all();\nif (items.length !== 1) throw new Error('one_file_required');\nconst item = items[0];\nif (Object.keys(item.binary ?? {}).join('|') !== 'export') throw new Error('export_binary_required');\nif (item.binary.export.fileName !== 'catalog-demo.json') throw new Error('file_name_invalid');\nconst bytes = await this.helpers.getBinaryDataBuffer(0, 'export');\nif (bytes.length < 1 || bytes.length > 16384) throw new Error('file_size_invalid');\nif (bytes[0] === 239 && bytes[1] === 187 && bytes[2] === 191) throw new Error('bom_not_expected');\nconst text = bytes.toString('utf8');\nif (!Buffer.from(text, 'utf8').equals(bytes)) throw new Error('utf8_invalid');\nconst expected = $('Check Rows').all().map(i => i.json);\nif (text !== JSON.stringify(expected, null, 2)) throw new Error('file_content_mismatch');\nreturn [{ json: {}, binary: item.binary }];"
      }
    },
    {
      "id": "send-file",
      "name": "Send File",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.4,
      "position": [
        1300,
        300
      ],
      "parameters": {
        "respondWith": "binary",
        "responseDataSource": "set",
        "inputFieldName": "export",
        "options": {
          "responseCode": 200,
          "responseHeaders": {
            "entries": [
              {
                "name": "Content-Type",
                "value": "application/json; charset=utf-8"
              },
              {
                "name": "Content-Disposition",
                "value": "attachment; filename=\"catalog-demo.json\""
              },
              {
                "name": "Cache-Control",
                "value": "no-store"
              }
            ]
          }
        }
      }
    }
  ],
  "connections": {
    "Download Request": {
      "main": [
        [
          {
            "node": "Sample Rows",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sample Rows": {
      "main": [
        [
          {
            "node": "Check Rows",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Rows": {
      "main": [
        [
          {
            "node": "Make JSON",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Make JSON": {
      "main": [
        [
          {
            "node": "Check File",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check File": {
      "main": [
        [
          {
            "node": "Send File",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "saveDataSuccessExecution": "all",
    "saveDataErrorExecution": "all"
  },
  "pinData": {}
}
