diff --git a/astro.config.mjs b/astro.config.mjs index b28e65982..e2408b9a7 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -75,7 +75,6 @@ export default defineConfig({ }), icon({ include: { - "preprocess: vitePreprocess(),": ["*"], "fa6-brands": ["*"], "fa6-regular": ["*"], "fa6-solid": ["*"], diff --git a/scripts/clean-unused-images.js b/scripts/clean-unused-images.js index a51379e3b..fe712cfdf 100644 --- a/scripts/clean-unused-images.js +++ b/scripts/clean-unused-images.js @@ -73,18 +73,43 @@ function extractImageReferences(content) { references.add(match[1] || match[2]); } - // 匹配 markdown 图片语法: ![alt](path) - const markdownImageRegex = /!\[.*?\]\(([^)]+)\)/g; - while ((match = markdownImageRegex.exec(content)) !== null) { - references.add(match[1]); - } - // 匹配 HTML img 标签: const htmlImageRegex = /]+src=["']([^"']+)["'][^>]*>/gi; while ((match = htmlImageRegex.exec(content)) !== null) { references.add(match[1]); } + // 匹配 markdown 图片语法: ![alt](path) - 更新为支持空格 + // 标准 Markdown 图片语法: ![alt](url "title") 或 ![alt](url) + // 我们主要关心 url 部分,它可能包含空格,但通常会被 <> 包裹或者 URL 编码 + // 但如果是本地文件引用,可能直接就是路径 + + // 1. 匹配标准 Markdown 图片 ![...](...) + const markdownImageRegex = /!\[.*?\]\((.*?)\)/g; + while ((match = markdownImageRegex.exec(content)) !== null) { + let url = match[1].trim(); + // 如果 URL 包含 title 部分 (例如 "path/to/image.png" "Title"),去除 title + // 简单的做法是看是否有空格后跟引号 + const titleMatch = url.match(/^(\S+)\s+["'].*["']$/); + if (titleMatch) { + url = titleMatch[1]; + } else { + // 处理可能的 URL 编码空格 (%20) + try { + url = decodeURIComponent(url); + } catch (e) { + // ignore + } + } + + // 移除可能存在的 <> 包裹 (CommonMark 标准允许 ) + if (url.startsWith('<') && url.endsWith('>')) { + url = url.slice(1, -1); + } + + references.add(url); + } + // 匹配 Astro Image 组件引用 const astroImageRegex = /import\s+.*?\s+from\s+["']([^"']+\.(jpg|jpeg|png|gif|webp|svg|avif))["']/gi; diff --git a/src/content/.obsidian/plugins/obsidian-paste-image-rename/data.json b/src/content/.obsidian/plugins/obsidian-paste-image-rename/data.json index caa7e3f3c..70514b34a 100644 --- a/src/content/.obsidian/plugins/obsidian-paste-image-rename/data.json +++ b/src/content/.obsidian/plugins/obsidian-paste-image-rename/data.json @@ -1,10 +1,10 @@ { - "imageNamePattern": "{{fileName}}", - "dupNumberAtStart": false, - "dupNumberDelimiter": "-", - "dupNumberAlways": false, - "autoRename": true, - "handleAllAttachments": false, - "excludeExtensionPattern": "", - "disableRenameNotice": false -} + "imageNamePattern": "{{fileName}}", + "dupNumberAtStart": false, + "dupNumberDelimiter": "-", + "dupNumberAlways": false, + "autoRename": true, + "handleAllAttachments": false, + "excludeExtensionPattern": "", + "disableRenameNotice": false +} \ No newline at end of file diff --git a/src/content/assets/images/1dc1a98a404db83e909c1f87e8a115cf.png b/src/content/assets/images/1dc1a98a404db83e909c1f87e8a115cf.png new file mode 100644 index 000000000..aaaf8c7ca Binary files /dev/null and b/src/content/assets/images/1dc1a98a404db83e909c1f87e8a115cf.png differ diff --git a/src/content/assets/images/Screenshot_2025-11-24-07-55-54-35_df198e732186825c8df26e3c5a10d7cd.jpg b/src/content/assets/images/Screenshot_2025-11-24-07-55-54-35_df198e732186825c8df26e3c5a10d7cd.jpg deleted file mode 100644 index a811e3a43..000000000 Binary files a/src/content/assets/images/Screenshot_2025-11-24-07-55-54-35_df198e732186825c8df26e3c5a10d7cd.jpg and /dev/null differ diff --git a/src/content/assets/images/Screenshot_2025-11-24-07-56-23-48_a2e3670364a4153bdb03dad30c8d4108.jpg b/src/content/assets/images/Screenshot_2025-11-24-07-56-23-48_a2e3670364a4153bdb03dad30c8d4108.jpg deleted file mode 100644 index f4276b70a..000000000 Binary files a/src/content/assets/images/Screenshot_2025-11-24-07-56-23-48_a2e3670364a4153bdb03dad30c8d4108.jpg and /dev/null differ diff --git a/src/content/assets/images/explorer_xY0rowaOaU 1.gif b/src/content/assets/images/explorer_xY0rowaOaU 1.gif deleted file mode 100644 index b3cc18208..000000000 Binary files a/src/content/assets/images/explorer_xY0rowaOaU 1.gif and /dev/null differ diff --git a/src/content/assets/images/msedge_VsxMMnGtBE.gif b/src/content/assets/images/msedge_VsxMMnGtBE.gif new file mode 100644 index 000000000..a6f9820bb Binary files /dev/null and b/src/content/assets/images/msedge_VsxMMnGtBE.gif differ diff --git a/src/content/assets/images/random-url-gen-1.png b/src/content/assets/images/random-url-gen-1.png deleted file mode 100644 index 20d4049b0..000000000 Binary files a/src/content/assets/images/random-url-gen-1.png and /dev/null differ diff --git a/src/content/assets/images/random-url-gen-2.png b/src/content/assets/images/random-url-gen-2.png deleted file mode 100644 index 5188d02f9..000000000 Binary files a/src/content/assets/images/random-url-gen-2.png and /dev/null differ diff --git a/src/content/assets/images/random-url-gen-3.png b/src/content/assets/images/random-url-gen-3.png deleted file mode 100644 index b31472ad6..000000000 Binary files a/src/content/assets/images/random-url-gen-3.png and /dev/null differ diff --git a/src/content/assets/images/random-url-gen-7.png b/src/content/assets/images/random-url-gen-7.png deleted file mode 100644 index 7fa7d94d1..000000000 Binary files a/src/content/assets/images/random-url-gen-7.png and /dev/null differ diff --git a/src/content/assets/images/uptimeflare-1.png b/src/content/assets/images/uptimeflare-1.png new file mode 100644 index 000000000..37e12ad81 Binary files /dev/null and b/src/content/assets/images/uptimeflare-1.png differ diff --git a/src/content/assets/images/uptimeflare-10.png b/src/content/assets/images/uptimeflare-10.png new file mode 100644 index 000000000..09ba6be91 Binary files /dev/null and b/src/content/assets/images/uptimeflare-10.png differ diff --git a/src/content/assets/images/uptimeflare-11.png b/src/content/assets/images/uptimeflare-11.png new file mode 100644 index 000000000..a6a20b6eb Binary files /dev/null and b/src/content/assets/images/uptimeflare-11.png differ diff --git a/src/content/assets/images/uptimeflare-12.png b/src/content/assets/images/uptimeflare-12.png new file mode 100644 index 000000000..88f8369b2 Binary files /dev/null and b/src/content/assets/images/uptimeflare-12.png differ diff --git a/src/content/assets/images/uptimeflare-13.png b/src/content/assets/images/uptimeflare-13.png new file mode 100644 index 000000000..d4f73c8d2 Binary files /dev/null and b/src/content/assets/images/uptimeflare-13.png differ diff --git a/src/content/assets/images/uptimeflare-2.png b/src/content/assets/images/uptimeflare-2.png new file mode 100644 index 000000000..265723e96 Binary files /dev/null and b/src/content/assets/images/uptimeflare-2.png differ diff --git a/src/content/assets/images/uptimeflare-3.png b/src/content/assets/images/uptimeflare-3.png new file mode 100644 index 000000000..4198de337 Binary files /dev/null and b/src/content/assets/images/uptimeflare-3.png differ diff --git a/src/content/assets/images/uptimeflare-4.png b/src/content/assets/images/uptimeflare-4.png new file mode 100644 index 000000000..0b0ab6ebe Binary files /dev/null and b/src/content/assets/images/uptimeflare-4.png differ diff --git a/src/content/assets/images/uptimeflare-5.png b/src/content/assets/images/uptimeflare-5.png new file mode 100644 index 000000000..ad12a4bb0 Binary files /dev/null and b/src/content/assets/images/uptimeflare-5.png differ diff --git a/src/content/assets/images/uptimeflare-6.png b/src/content/assets/images/uptimeflare-6.png new file mode 100644 index 000000000..8d8ed8f5f Binary files /dev/null and b/src/content/assets/images/uptimeflare-6.png differ diff --git a/src/content/assets/images/uptimeflare-7.png b/src/content/assets/images/uptimeflare-7.png new file mode 100644 index 000000000..46d6b9af5 Binary files /dev/null and b/src/content/assets/images/uptimeflare-7.png differ diff --git a/src/content/assets/images/uptimeflare-8.png b/src/content/assets/images/uptimeflare-8.png new file mode 100644 index 000000000..72e30321f Binary files /dev/null and b/src/content/assets/images/uptimeflare-8.png differ diff --git a/src/content/assets/images/uptimeflare-9.png b/src/content/assets/images/uptimeflare-9.png new file mode 100644 index 000000000..2d2bd0a19 Binary files /dev/null and b/src/content/assets/images/uptimeflare-9.png differ diff --git a/src/content/assets/images/uptimeflare.png b/src/content/assets/images/uptimeflare.png new file mode 100644 index 000000000..59dc58e13 Binary files /dev/null and b/src/content/assets/images/uptimeflare.png differ diff --git a/src/content/assets/images/win11-to-win10-10.png b/src/content/assets/images/win11-to-win10-10.png deleted file mode 100644 index 71119258a..000000000 Binary files a/src/content/assets/images/win11-to-win10-10.png and /dev/null differ diff --git a/src/content/assets/images/win11-to-win10-12.png b/src/content/assets/images/win11-to-win10-12.png deleted file mode 100644 index f7c44b1fa..000000000 Binary files a/src/content/assets/images/win11-to-win10-12.png and /dev/null differ diff --git a/src/content/assets/images/win11-to-win10-14.png b/src/content/assets/images/win11-to-win10-14.png deleted file mode 100644 index 20078f0d8..000000000 Binary files a/src/content/assets/images/win11-to-win10-14.png and /dev/null differ diff --git a/src/content/assets/images/win11-to-win10-4.png b/src/content/assets/images/win11-to-win10-4.png deleted file mode 100644 index dc0046eb1..000000000 Binary files a/src/content/assets/images/win11-to-win10-4.png and /dev/null differ diff --git a/src/content/assets/images/win11-to-win10-9.png b/src/content/assets/images/win11-to-win10-9.png deleted file mode 100644 index 25d7dc105..000000000 Binary files a/src/content/assets/images/win11-to-win10-9.png and /dev/null differ diff --git a/src/content/posts/uptimeflare.md b/src/content/posts/uptimeflare.md new file mode 100644 index 000000000..493bfcd86 --- /dev/null +++ b/src/content/posts/uptimeflare.md @@ -0,0 +1,293 @@ +--- +title: 超高校级的监控服务:UptimeFlare!基于CF Worker!自托管!声明式!新手友好! +published: 2026-01-03T03:10:53 +description: 谁不想拥有一个监控自己服务的服务呢? +image: ../assets/images/uptimeflare.png +draft: false +lang: "" +--- +# 前言 +本来这个教程应该是永远都不会出的,因为在此之前,我曾经给大家介绍了一个无需自托管的监控服务:[UptimeRobot](/posts/uptimerobot/) + +但是,就在最近我再次查看控制台,发现之前创建的监控全部都没了,咱也不知道是被官方删了还是号被黑客大手子肘击了,总之,我现在不得不要重建监控服务了 + +# 原理 +首先,UptimeFlare是一个基于Cloudflare Worker+KV的监控服务 + +它的原理非常简单,一共由三个部分组成 +- **前端**:放在Cloudflare Page,用于给用户展示zhandianzhuangt +- **后端**:放在Cloudflare Worker,通过 Worker 自带的 **Cron** 每分钟 检查站点状态,并将状态持久化进 **KV** +![](../assets/images/uptimeflare-1.png) +![](../assets/images/uptimeflare-2.png) + +# 正式开始 +首先我们需要 **Fork** 项目,你可以选择原项目(English),或者我的项目(中文) + +::github{repo="lyc8503/UptimeFlare"} +::github{repo="afoim/UptimeFlare"} + +首先,我们先尝试将其部署到Cloudflare + +创建一个Cloudflare API Token(编辑Workers) +![](../assets/images/msedge_VsxMMnGtBE.gif) + +接下来将该Token绑定到你的Github仓库 +![](../assets/images/uptimeflare-4.png) + +最后,来到 `Action` 页面,手动创建一个 `Deploy to Cloudflare` 的工作流 +![](../assets/images/uptimeflare-5.png) + +等待工作流运行结束,你应该可以在Cloudflare仪表板看见一个新的Page,新的Worker和新的KV +![](../assets/images/uptimeflare-6.png) +![](../assets/images/uptimeflare-7.png) + +点开 Page,注意不要点错了 +![](../assets/images/uptimeflare-8.png) + +绑定你的域名,尝试访问 +![](../assets/images/uptimeflare-9.png) + +如果你能看到一个初始的监控页面,则正常 +![](../assets/images/uptimeflare-10.png) + +接下来,我们开始自定义该监控 + +编辑根目录的 `uptime.config.ts` + +这里是默认的汉化过后的配置,拷贝过去并定制成你的版本吧~ + +```ts +/** + * ============================ + * UptimeFlare 通用配置模板 + * ============================ + * 使用前请将示例内容替换为你自己的信息 + */ + +/* ---------------------------- + * 状态页面配置 + * ---------------------------- */ +const pageConfig = { + // 状态页面标题 + title: "我的服务状态页", + + // 状态页顶部链接 + links: [ + { link: "https://example.com", label: "主页" }, + { link: "https://github.com/example", label: "GitHub" }, + ], + + // [可选] 监控分组 + // 未列入分组的监控将不会显示在状态页(但仍会被监控) + /* + group: { + "Web 服务": ["web_main", "web_backup"], + "服务器": ["server_ssh"], + }, + */ +}; + +/* ---------------------------- + * Worker 监控配置 + * ---------------------------- */ +const workerConfig = { + // 除非状态发生变化,否则最多每 N 分钟写入一次 KV + kvWriteCooldownMinutes: 3, + + // [可选] HTTP Basic Auth + // passwordProtection: "username:password", + + /* -------------------------- + * 监控列表 + * -------------------------- */ + monitors: [ + /** + * HTTP / HTTPS 监控示例 + */ + { + id: "web_main", // 唯一 ID(不要随意修改,否则历史会丢失) + name: "主站", + method: "HEAD", // GET / HEAD / POST ... + target: "https://example.com/", + statusPageLink: "https://example.com/", + expectedCodes: [200], + timeout: 10000, + hideLatencyChart: false, + }, + + /** + * TCP 端口存活监控示例(SSH / 数据库等) + */ + { + id: "server_ssh", + name: "服务器 SSH", + method: "TCP_PING", + target: "127.0.0.1:22", + timeout: 5000, + }, + ], + + /* -------------------------- + * 通知配置 + * -------------------------- */ + notification: { + // 时区 + timeZone: "Asia/Shanghai", + + // 宽限期(分钟) + // 连续失败 N 分钟后才发送告警 + gracePeriod: 5, + + // [可选] Apprise + // appriseApiServer: "https://apprise.example.com/notify", + // recipientUrl: "tgram://bottoken/chatid", + }, + + /* -------------------------- + * 回调函数 + * -------------------------- */ + callbacks: { + /** + * 监控状态变化时触发 + */ + onStatusChange: async ( + env: any, + monitor: any, + isUp: boolean, + timeIncidentStart: number, + timeNow: number, + reason: string + ) => { + const statusText = isUp ? "恢复正常 (UP)" : "服务中断 (DOWN)"; + console.log( + `[状态变更] ${monitor.name} -> ${statusText},原因:${reason}` + ); + + // 示例:在此处接入 邮件 / Webhook / 飞书 / Telegram 等 + // env 中可以读取 Worker 环境变量 + }, + + /** + * 事件持续期间(每分钟调用一次) + */ + onIncident: async ( + env: any, + monitor: any, + timeIncidentStart: number, + timeNow: number, + reason: string + ) => { + // 可用于升级告警、统计持续时间等 + }, + }, +}; + +// ⚠️ 必须导出,否则编译失败 +export { pageConfig, workerConfig }; + +``` + +如果服务故障如何做通知? + +UptimeFlare非常自由,你可以在 `callbacks` 中编写故障时要做的任何事情,这里以发送 `POST` 请求让 `Resend` 发送邮件给你举例 + +首先前往 https://resend.com/ + +添加一个域名(作为你的发信域名) +![](../assets/images/uptimeflare-11.png) + +创建一个发信API Key +![](../assets/images/uptimeflare-12.png) + +添加环境变量: `RESEND_API_KEY` 将其绑定到 **Action** + +![](../assets/images/uptimeflare-13.png) + +编辑 `uptime.config.ts` 的 `callbacks` 部分,注意更改 `resendPayload` 方法中的相关参数 + +示例代码: +```ts +  callbacks: { +    onStatusChange: async ( +      env: any, +      monitor: any, +      isUp: boolean, +      timeIncidentStart: number, +      timeNow: number, +      reason: string +    ) => { +      // 当任何监控的状态发生变化时,将调用此回调 +      // 在这里编写任何 Typescript 代码 + +      // 调用 Resend API 发送邮件通知 +      // 务必在 Cloudflare Worker 的设置 -> 变量中配置: RESEND_API_KEY +      if (env.RESEND_API_KEY) { +        try { +          const statusText = isUp ? '恢复正常 (UP)' : '服务中断 (DOWN)'; +          const color = isUp ? '#4ade80' : '#ef4444'; // green-400 : red-500 +          const subject = `[${statusText}] ${monitor.name} 状态变更通知`; +          // 尝试格式化时间 +          let timeString = new Date(timeNow * 1000).toISOString(); +          try { +            timeString = new Date(timeNow * 1000).toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' }); +          } catch (e) { /* ignore */ } + +          const htmlContent = ` +           
+             

${statusText}

+             

监控名称: ${monitor.name}

+             

时间: ${timeString}

+             

原因: ${reason}

+             
+             

来自 UptimeFlare 监控报警

+           
+          `; + +          const resendPayload = { +            from: "系统状态更新 ", +            to: ["你的@邮箱.com"], +            subject: subject, +            html: htmlContent, +          }; + +          const resp = await fetch('https://api.resend.com/emails', { +            method: 'POST', +            headers: { +              'Authorization': `Bearer ${env.RESEND_API_KEY}`, +              'Content-Type': 'application/json' +            }, +            body: JSON.stringify(resendPayload) +          }); + +          if (!resp.ok) { +            console.error(`Resend API call failed: ${resp.status} ${await resp.text()}`); +          } +        } catch (e) { +          console.error(`Error calling Resend API: ${e}`); +        } +      } + +      // 这不会遵循宽限期设置,并且在状态变化时立即调用 +      // 如果您想实现宽限期,需要手动处理 +    }, +    onIncident: async ( +      env: any, +      monitor: any, +      timeIncidentStart: number, +      timeNow: number, +      reason: string +    ) => { +      // 如果任何监控有正在进行的事件,此回调将每分钟调用一次 +      // 在这里编写任何 Typescript 代码 + + +    }, +  }, +} +``` + +接下来,当服务故障/重新上线就会通知你啦~ +![](../assets/images/1dc1a98a404db83e909c1f87e8a115cf.png) + +最终效果: +::url{href="https://ok.2x.nz"} \ No newline at end of file