{
  "name": "Hostilo: publish a page (create or update)",
  "nodes": [
    {
      "parameters": {
        "content": "## Hostilo: publish a page from n8n\n\n**1. API key:** Hostilo dashboard → API & tools → generate a key (`hk_...`).\n**2. Credential:** open both Hostilo nodes → Credential for Header Auth → Create new: Name `Authorization`, Value `Bearer hk_...`. One credential for both nodes. Never paste the key into a node field.\n**3. Test:** click *Test workflow*, then send:\n```\ncurl -X POST <test URL of the Webhook node> \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"site\":\"my-page\",\"title\":\"Hello\",\"text\":\"First version\"}'\n```\nFirst run creates `my-page.hostilo.page`, every next run updates it (old versions stay in history: `list_versions`, `restore_version`).\n\n**Google Sheets instead of a webhook:** replace the Webhook node with *Google Sheets Trigger* (Row added or updated) and keep column names `site`, `title`, `text`.\n\nDocs: https://hostilo.app/docs#rest · API: https://hostilo.app/openapi.json · Limit: 60 requests/min per key.",
        "height": 520,
        "width": 520,
        "color": 5
      },
      "id": "1f0c2a60-0001-4c1e-9a10-6f1e0a000001",
      "name": "Setup",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -620,
        -260
      ]
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "hostilo-publish",
        "responseMode": "lastNode",
        "options": {}
      },
      "id": "1f0c2a60-0002-4c1e-9a10-6f1e0a000002",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        0,
        0
      ],
      "webhookId": "6a1f3c0e-5b7d-4e2a-9c41-0a1b2c3d4e5f"
    },
    {
      "parameters": {
        "jsCode": "// Hostilo: builds the page from the incoming data.\n// Expected JSON body: {\"site\": \"my-page\", \"title\": \"...\", \"text\": \"...\"}\n// or {\"site\": \"my-page\", \"html\": \"<!DOCTYPE html>...\"} to publish your own HTML.\nconst esc = (s) => String(s ?? '').replace(/[&<>\"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' }[c]));\nreturn $input.all().map((item) => {\n  const b = item.json.body ?? item.json;\n  const site = String(b.site ?? '').trim().toLowerCase();\n  if (!/^[a-z0-9-]{3,30}$/.test(site)) {\n    throw new Error('Field \"site\": 3-30 characters, lowercase letters, digits and hyphens (e.g. my-page).');\n  }\n  const html = typeof b.html === 'string' && b.html.trim() ? b.html : `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>${esc(b.title)}</title>\n<style>body{font-family:system-ui,sans-serif;max-width:720px;margin:48px auto;padding:0 20px;line-height:1.6;color:#1f2937}h1{line-height:1.2}</style>\n</head>\n<body>\n<h1>${esc(b.title)}</h1>\n<p style=\"white-space:pre-line\">${esc(b.text)}</p>\n<p><small>Updated ${new Date().toISOString().slice(0, 10)}</small></p>\n</body>\n</html>`;\n  return { json: { site, html } };\n});"
      },
      "id": "1f0c2a60-0003-4c1e-9a10-6f1e0a000003",
      "name": "Build HTML",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        220,
        0
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://hostilo.app/api/v1/update_site",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ site_id: $json.site, html: $json.html }) }}",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      },
      "id": "1f0c2a60-0004-4c1e-9a10-6f1e0a000004",
      "name": "Hostilo: update_site",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        440,
        0
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "1f0c2a60-0005-4c1e-9a10-6f1e0a0000c1",
              "leftValue": "={{ String($json.error ?? '') }}",
              "rightValue": "nie istnieje",
              "operator": {
                "type": "string",
                "operation": "contains"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "1f0c2a60-0005-4c1e-9a10-6f1e0a000005",
      "name": "Site does not exist yet?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        660,
        0
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://hostilo.app/api/v1/create_site",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ site_name: $('Build HTML').item.json.site, html: $('Build HTML').item.json.html }) }}",
        "options": {}
      },
      "id": "1f0c2a60-0006-4c1e-9a10-6f1e0a000006",
      "name": "Hostilo: create_site",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        880,
        -100
      ]
    }
  ],
  "connections": {
    "Webhook": {
      "main": [
        [
          {
            "node": "Build HTML",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build HTML": {
      "main": [
        [
          {
            "node": "Hostilo: update_site",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Hostilo: update_site": {
      "main": [
        [
          {
            "node": "Site does not exist yet?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Site does not exist yet?": {
      "main": [
        [
          {
            "node": "Hostilo: create_site",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    }
  },
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "tags": []
}
