8
n8n 中文网amn8n.com

自动化网站正常运行时间监控与邮件警报及GitHub状态页更新

高级

这是一个DevOps领域的自动化工作流,包含 19 个节点。主要使用 If, Code, Gmail, Github, Switch 等节点。 自动化网站正常运行时间监控与邮件警报及GitHub状态页更新

前置要求
  • Google 账号和 Gmail API 凭证
  • GitHub Personal Access Token
  • 可能需要目标 API 的认证凭证
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
  "id": "PAjyHjl5YdaEp6Y2",
  "meta": {
    "instanceId": "cb69a80257e9c986533bb4191c0c85ea4947fbef18ed5faeb970927362aeebdc",
    "templateCredsSetupCompleted": true
  },
  "name": "Automated Website Uptime Monitor with Email Alerts & GitHub Status Page Update",
  "tags": [],
  "nodes": [
    {
      "id": "24b40a8b-20b2-4ead-87a4-0be04641323a",
      "name": "定时触发器",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -3568,
        -80
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 2
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "09c86eff-846d-43d1-a93f-f398f2223d67",
      "name": "HTTP 请求",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueRegularOutput",
      "position": [
        -3360,
        -80
      ],
      "parameters": {
        "url": "https://app.yourdomain.com/health",
        "options": {
          "response": {
            "response": {
              "fullResponse": true
            }
          }
        }
      },
      "typeVersion": 4.2,
      "alwaysOutputData": false
    },
    {
      "id": "7c7791f1-9174-4a60-b6bd-80f32d220952",
      "name": "便签",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3808,
        -480
      ],
      "parameters": {
        "color": 7,
        "width": 400,
        "height": 368,
        "content": "> **About 📝**\n- Runs automatically every **2 minutes**.\n- Acts as the \"heartbeat\" of the workflow.\n- You can change the interval to any value (e.g., seconds, minutes, hours, days).\n- Useful when you want continuous monitoring without manual input.\nThe workflow checks your website every **2 minutes** (interval configurable).  \n- If the website is **down (503, bad response, or error)** → it sends an email alert and updates the GitHub-hosted status page to show **Down**.  \n- If the website is **up (200)** → it updates the GitHub-hosted status page to show **Up**.  \n- The email notification includes an **HTML-formatted alert page**.  \n- You can use GitHub Pages to **host the status page** publicly.  \n"
      },
      "typeVersion": 1
    },
    {
      "id": "99b08165-3c6c-4505-94e9-a23d7bdf606f",
      "name": "便签1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3344,
        160
      ],
      "parameters": {
        "color": 7,
        "width": 320,
        "height": 272,
        "content": "> **HTML Request & Switch 📝**\n- Pings the target website (ex:(`https://app.yourdomain.com/health`).\n- Returns the **status code** and full response.\n- If website responds with **200**, site is considered **Up**.\n- If request fails (e.g., 503, ERR_BAD_RESPONSE), it will be treated as **Down**.\n- Always outputs data (even on error), ensuring the workflow doesn’t break.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "aad36f3c-6f6a-4c2a-9d9a-db60a08b54a7",
      "name": "便签说明4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3312,
        -224
      ],
      "parameters": {
        "width": 294,
        "height": 80,
        "content": "Here, upload your Website URL. EX: (`https://app.yourdomain.cc/health`)"
      },
      "typeVersion": 1
    },
    {
      "id": "b4d88251-7516-4f8d-998b-86aa4e62f201",
      "name": "便签2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2720,
        -656
      ],
      "parameters": {
        "color": 7,
        "width": 288,
        "height": 256,
        "content": "> **Email 📝**\n- Sends an **email alert** if the site is down.\n- Recipient: `example@gmail.com` (replace with your email/team DL).\n- Subject: \"Server Down\".\n- Message: Pre-styled HTML alert page with error details.\n- Can be customized for different recipients or notification styles.\n \n\n \n"
      },
      "typeVersion": 1
    },
    {
      "id": "3c09250f-9f90-48fd-8a95-0c3d28ac3c90",
      "name": "便签说明5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2608,
        -368
      ],
      "parameters": {
        "width": 214,
        "height": 80,
        "content": "Upload with your Email ID.\nEX:\nexamaple@gmail.com"
      },
      "typeVersion": 1
    },
    {
      "id": "741d5e5b-88f1-4acb-8ed8-0527f3c9448f",
      "name": "便签 6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2160,
        192
      ],
      "parameters": {
        "color": 7,
        "width": 560,
        "height": 192,
        "content": "> **Extract from File(Github)📝**\n\n-Extract the file from the Github Repository if the status is same (Extracted Status = Github Status) then make no changes/ no commit into Github.\n-If both are no same then commit the status in the Github."
      },
      "typeVersion": 1
    },
    {
      "id": "348d04d6-1b88-4cb6-80f2-93a136850a81",
      "name": "便签 7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2464,
        -144
      ],
      "parameters": {
        "height": 80,
        "content": "-Upload your GitHub Repository owner URL.\n-Upload your Repository Name.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "83ddb904-7e94-4d2d-8cbe-a02e832435a1",
      "name": "## 为什么选择 4o 模型?👆",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2800,
        224
      ],
      "parameters": {
        "color": 7,
        "width": 384,
        "height": 368,
        "content": "> **Template HTML Code & GitHub 📝**\n- Dynamically creates the **status page HTML (index.html) from the existing template**.\n- Two modes:\n  - **Up** → Green status page with \"Server Up\".\n  - **Down** → Red status page with error details (status code + error message).\n- You can fully customize the HTML & CSS here for branding.\n - Commits the newly generated `index.html` file to your GitHub repo.\n- Path: `index.html` in repository (default branch).\n- Commit message is configurable (default: \"test\").\n- If GitHub Pages is enabled, this file becomes the **live status page**."
      },
      "typeVersion": 1
    },
    {
      "id": "d4f178ad-6597-4944-aa80-7335f26be23d",
      "name": "Update Index.html file",
      "type": "n8n-nodes-base.github",
      "position": [
        -1504,
        -16
      ],
      "webhookId": "efc7d4f3-037b-4ade-aab0-e9ec870bd39d",
      "parameters": {
        "owner": {
          "__rl": true,
          "mode": "url",
          "value": "https://github.com/<OWNER_NAME>"
        },
        "filePath": "index.html",
        "resource": "file",
        "operation": "edit",
        "binaryData": true,
        "repository": {
          "__rl": true,
          "mode": "name",
          "value": "status"
        },
        "commitMessage": "test"
      },
      "typeVersion": 1.1
    },
    {
      "id": "7d86a237-36e6-4ce8-95cf-19ab8a58581d",
      "name": "Send a notification mail",
      "type": "n8n-nodes-base.gmail",
      "position": [
        -2832,
        -384
      ],
      "webhookId": "50a40372-a118-4922-9abe-ab5c3065b284",
      "parameters": {
        "sendTo": "example@gmail.com",
        "message": "=\n    <!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Status Page</title>\n  <style>\n    body {\n      font-family: Arial, sans-serif;\n      background: #f9fafb;\n      display: flex;\n      justify-content: center;\n      align-items: center;\n      height: 100vh;\n      margin: 0;\n    }\n\n    .status-container {\n      text-align: center;\n      padding: 2rem 3rem;\n    }\n\n    .status {\n      font-size: 2rem;\n      font-weight: bold;\n      margin: 1rem 0;\n    }\n\n    .up {\n      color: #fff;\n      background-color: #16a34a; /* green */\n    }\n\n    .down {\n      color: #fff;\n      background-color: #dc2626; /* red */\n    }\n\n    .label {\n      font-size: 1rem;\n      color: #fff;\n      text-transform: uppercase;\n      letter-spacing: 2px;\n    }\n  </style>\n</head>\n<body class=\"down\">\n  <div class=\"status-container down\">\n    <div class=\"label\">Server Status</div>\n    <div class=\"status down\">Down</div>\n    <div class=\"down\">503</div>\n    <div class=\"down\">ERR_BAD_RESPONSE</div>\n  </div>\n</body>\n</html>\n\t",
        "options": {},
        "subject": "Server Down"
      },
      "typeVersion": 2.1
    },
    {
      "id": "ba9b1eb6-6fcd-4ca8-b308-2b0daad7c299",
      "name": "Switch - status code",
      "type": "n8n-nodes-base.switch",
      "position": [
        -3152,
        -80
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "503",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "1f49118f-0394-43bb-aaa0-2a850da82ac7",
                    "operator": {
                      "type": "number",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.error.status }}",
                    "rightValue": 503
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "200",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "55486cce-71e5-4c93-a518-09c108a2f307",
                    "operator": {
                      "type": "number",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.statusCode }}",
                    "rightValue": 200
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.2
    },
    {
      "id": "ec136a3f-e79d-4480-8c19-893184458928",
      "name": "Extract from File - Generated HTML",
      "type": "n8n-nodes-base.extractFromFile",
      "position": [
        -2528,
        -16
      ],
      "parameters": {
        "options": {},
        "operation": "text"
      },
      "typeVersion": 1
    },
    {
      "id": "3e4c16f9-25f7-4944-958a-3f5c2a8651b9",
      "name": "Template HTML Code",
      "type": "n8n-nodes-base.code",
      "position": [
        -2720,
        -16
      ],
      "parameters": {
        "jsCode": "// n8n Code node (JavaScript)\n\nconst items = [];\n    if ($input.first().json.statusCode){\n      const htmlContent = `\n    <!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Status Page</title>\n  <style>\n    body {\n      font-family: Arial, sans-serif;\n      background: #f9fafb;\n      display: flex;\n      justify-content: center;\n      align-items: center;\n      height: 100vh;\n      margin: 0;\n    }\n\n    .status-container {\n      text-align: center;\n      padding: 2rem 3rem;\n    }\n\n    .status {\n      font-size: 2rem;\n      font-weight: bold;\n      margin: 1rem 0;\n    }\n\n    .up {\n      color: #fff;\n      background-color: #16a34a; /* green */\n    }\n\n    .down {\n      color: #fff;\n      background-color: #dc2626; /* red */\n    }\n\n    .label {\n      font-size: 1rem;\n      color: #fff;\n      text-transform: uppercase;\n      letter-spacing: 2px;\n    }\n  </style>\n</head>\n<body class=\"up\">\n  <div class=\"status-container up\">\n    <div class=\"label\">Server Status</div>\n    <div class=\"status\">Up</div>\n    <!-- If server is down, replace above with -->\n    <!-- <div class=\"status down\">Down</div> -->\n  </div>\n</body>\n</html>\n\t`;\n\n\t// Convert HTML string to Base64\n\tconst base64Data = Buffer.from(htmlContent, 'utf-8').toString('base64');\n\n\t// Push result in binary format\n\titems.push({\n\t\tbinary: {\n\t\t\tdata: {\n\t\t\t\tfileName: 'index.html',\n\t\t\t\tmimeType: 'text/html',\n\t\t\t\tdata: base64Data,\n\t\t\t}\n\t\t}\n\t});\n\nreturn items;\n    }\nelse if ($input.first().json.error.status) {\n  const htmlContent = `\n    <!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Status Page</title>\n  <style>\n    body {\n      font-family: Arial, sans-serif;\n      background: #f9fafb;\n      display: flex;\n      justify-content: center;\n      align-items: center;\n      height: 100vh;\n      margin: 0;\n    }\n\n    .status-container {\n      text-align: center;\n      padding: 2rem 3rem;\n    }\n\n    .status {\n      font-size: 2rem;\n      font-weight: bold;\n      margin: 1rem 0;\n    }\n\n    .up {\n      color: #fff;\n      background-color: #16a34a; /* green */\n    }\n\n    .down {\n      color: #fff;\n      background-color: #dc2626; /* red */\n    }\n\n    .label {\n      font-size: 1rem;\n      color: #fff;\n      text-transform: uppercase;\n      letter-spacing: 2px;\n    }\n  </style>\n</head>\n<body class=\"down\">\n  <div class=\"status-container down\">\n    <div class=\"label\">Server Status</div>\n    <div class=\"status down\">Down</div>\n    <div class=\"down\">${$input.first().json.error.status}</div>\n    <div class=\"down\">${$input.first().json.error.code}</div>\n  </div>\n</body>\n</html>\n\t`;\n\n\t// Convert HTML string to Base64\n\tconst base64Data = Buffer.from(htmlContent, 'utf-8').toString('base64');\n\n\t// Push result in binary format\n\titems.push({\n\t\tbinary: {\n\t\t\tdata: {\n\t\t\t\tfileName: 'index.html',\n\t\t\t\tmimeType: 'text/html',\n\t\t\t\tdata: base64Data,\n\t\t\t}\n\t\t}\n\t});\n\nreturn items;\n}\n\t"
      },
      "typeVersion": 2
    },
    {
      "id": "4423f742-0144-4ffb-b28b-a6d108e3b5ca",
      "name": "Get existing index.html file",
      "type": "n8n-nodes-base.github",
      "position": [
        -2160,
        -16
      ],
      "webhookId": "8c6e35c4-1460-4620-a736-732f472881bd",
      "parameters": {
        "owner": {
          "__rl": true,
          "mode": "url",
          "value": "https://github.com/<OWNER_NAME>"
        },
        "filePath": "index.html",
        "resource": "file",
        "operation": "get",
        "repository": {
          "__rl": true,
          "mode": "name",
          "value": "status"
        },
        "binaryPropertyName": "github_data",
        "additionalParameters": {}
      },
      "typeVersion": 1.1
    },
    {
      "id": "d9c2a2ee-5698-4a60-8ab9-1ff133d71505",
      "name": "Extract from existing File (github)",
      "type": "n8n-nodes-base.extractFromFile",
      "position": [
        -1968,
        -16
      ],
      "parameters": {
        "options": {},
        "operation": "text",
        "destinationKey": "github_data",
        "binaryPropertyName": "github_data"
      },
      "typeVersion": 1
    },
    {
      "id": "e310e8e7-ad13-4f9e-a4b6-12f45d33a62a",
      "name": "If - Compare existing HTML file with generated HTML",
      "type": "n8n-nodes-base.if",
      "position": [
        -1776,
        -16
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "ffd5eaf3-0a36-43ac-8529-03b71cf9f1c6",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.github_data }}",
              "rightValue": "={{ $('Extract from File - Generated HTML').item.json.data }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "0deafbe1-d715-45b0-9fe4-ea910a6d8f83",
      "name": "便签3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4880,
        -496
      ],
      "parameters": {
        "width": 976,
        "height": 2416,
        "content": "# 🖥️ Automated Website Uptime Monitor with Email Alerts & GitHub Status Page Update  \n\nThis n8n workflow continuously monitors your website’s availability, sends **email alerts** when the server goes down, and automatically updates a **status page (index.html)** in your GitHub repository to reflect the live status.  \n\n---\n\n## 📌 Good to Know  \n- The workflow checks your website every **2 minutes** (interval configurable).  \n- If the website is **down (503, bad response, or error)** → it sends an email alert and updates the GitHub-hosted status page to show **Down**.  \n- If the website is **up (200)** → it updates the GitHub-hosted status page to show **Up**.  \n- The email notification includes an **HTML-formatted alert page**.  \n- You can use GitHub Pages to **host the status page** publicly.\n\n### ℹ️ What is GitHub Pages?  \n- GitHub Pages is a free hosting service provided by GitHub that lets you publish **static websites (HTML, CSS, JS)** directly from a GitHub repository.  \n- You can use it to make your `index.html` status page publicly accessible with a URL like:    \n\n\n### ⚡ How to Set Up GitHub Pages for Your Status Page  \n1. Create a **new repository** on GitHub (recommended name: `status`).  \n2. Add a blank `index.html` file (n8n workflow will later update this file).  \n3. Go to your repository → **Settings** → **Pages**.  \n4. Under **Source**, select the branch (`main` or `master`) and folder (`/root`).  \n5. Save changes.  \n6. Your status page will now be live at: `https://<USERNAME>.github.io/status`\n\n\n## ✅ Prerequisites  \n- An **n8n instance** (self-hosted or cloud).  \n- A **GitHub account & repository** (to host the status page).  \n- A **Gmail account** (or any email service supported by n8n – example uses Gmail).  \n- Access to the target **website URL** you want to monitor.  \n\n---\n\n## ⚙️ How it Works  \n1. **Schedule Trigger** → Runs every 2 minutes.  \n2. **HTTP Request** → Pings your website URL.  \n3. **Switch Node** → Evaluates the response status (200 OK vs error/503).  \n4. **Code Node** → Generates a dynamic **HTML status page** (Up/Down).\n5. **GitHub Repo & File** → Github Repo Name Should be `https://github.com/<OWNER_NAME>/status` (recommended) & Must have(required) a blank file named as `index.html` before triggering this flow.\n5. **GitHub Node** → Updates/commits the `index.html` file in your repository.  \n6. **Gmail Node** → Sends an email alert if the site is down.  \n\n---\n\n## 🚀 How to Use  \n1. Import the workflow JSON into your **n8n instance**.  \n2. Configure credentials for:  \n   - **GitHub** (Personal Access Token with repo permissions).  \n   - **Gmail** (or your preferred email service).  \n3. Replace the following:  \n   - `https://app.yourdomain.com/health` → with your own website URL.  \n   - `example@gmail.com` → with your email address (or distribution list).  \n   - GitHub repo details → with your repository where `index.html` will live.  \n4. Deploy the workflow.  \n5. (Optional) Enable **GitHub Pages** on your repo to serve `index.html` as a live status page.  \n\n---\n\n## 🛠 Requirements  \n- n8n v1.0+  \n- GitHub personal access token  \n- Gmail API credentials (or SMTP/email service of your choice)  \n\n---\n\n## 🎨 Customising this Workflow  \n- **Interval** → Change schedule from 2 minutes to any desired frequency.  \n- **Email Content** → Modify HTML alert template in the Gmail node.  \n- **Status Page Styling** → Edit the HTML/CSS in the Code node to match your branding.  \n- **Error Handling** → Extend Switch node for other status codes (e.g., 404, 500).  \n- **Multiple Websites** → Duplicate HTTP Request + Switch nodes for multiple URLs.  \n\n---\n\n## 👤 Who Can Use It?  \n- **DevOps & SRE Engineers** → For automated uptime monitoring.  \n- **Freelancers/Developers** → To monitor client websites.  \n- **Startups & SMEs** → For a free, lightweight status page without paid tools.  \n- **Educators/Students** → As a hands-on learning project with n8n.  \n\n---\n\n## 🌟 Key Features  \n- 🔄 Automated uptime checks (configurable interval).  \n- 📧 Email notifications on downtime.  \n- 📝 Dynamic HTML status page generation.  \n- 🌍 GitHub Pages integration for public visibility.  \n- ⚡ Lightweight & cost-effective (no paid monitoring tool needed).  \n\n---\n\n## 🔗 Tools Integration  \n- **n8n** – Orchestration & automation.  \n- **GitHub** – Version control + hosting of status page.  \n- **Gmail** – Email notifications.  \n- **HTTP Request** – Website availability check.  \n\n---\n\n## 📈 Example Use Cases  \n- Personal website monitoring with public status page.  \n- Monitoring SaaS apps & notifying support teams.  \n- Internal company services uptime dashboard.  \n "
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "052ffe26-4ea6-4e74-91db-0076dc880da6",
  "connections": {
    "HTTP Request": {
      "main": [
        [
          {
            "node": "Switch - status code",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "HTTP Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Template HTML Code": {
      "main": [
        [
          {
            "node": "Extract from File - Generated HTML",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Switch - status code": {
      "main": [
        [
          {
            "node": "Template HTML Code",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send a notification mail",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Template HTML Code",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get existing index.html file": {
      "main": [
        [
          {
            "node": "Extract from existing File (github)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract from File - Generated HTML": {
      "main": [
        [
          {
            "node": "Get existing index.html file",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract from existing File (github)": {
      "main": [
        [
          {
            "node": "If - Compare existing HTML file with generated HTML",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If - Compare existing HTML file with generated HTML": {
      "main": [
        [
          {
            "node": "Update Index.html file",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
常见问题

如何使用这个工作流?

复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。

这个工作流适合什么场景?

高级 - 开发运维

需要付费吗?

本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。

工作流信息
难度等级
高级
节点数量19
分类1
节点类型9
难度说明

适合高级用户,包含 16+ 个节点的复杂工作流

外部链接
在 n8n.io 查看

分享此工作流