2026.4.4新方案
Zexo 一键部署功能逻辑解析与实现
在基于 Cloudflare Workers 的博客管理方案 Zexo 中,“一键部署”功能允许管理员在后台直接触发关联的 Pages 部署钩子,从而无需登录 Git 平台即可完成站点的自动构建与更新。本文将深入解析此功能的完整运作链路。
具体代码分析(使用AI编辑)
该功能本质上是为 Cloudflare Workers 脚本(即 Zexo)添加了一个专用的 API 端点。当用户在前台点击触发按钮时,前端会向此端点发送请求,后端接收到请求后,会向预设的 Cloudflare Pages 的 Deploy Hook URL 发起一个 POST 请求,从而触发一次全新的构建部署流程。
- 前端触发逻辑
前端逻辑定义在工具面板的 JavaScript 中。当用户点击“立即触发”按钮时,会调用
“zexo_trigger_deploy()” 函数。该函数通过 Fetch API 向 Workers 脚本自身的特定管理 API 发送请求,并根据返回结果弹出操作成功或失败的提示。
核心代码片段 (
“tool.js” 中相关部分)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| function zexo_trigger_deploy() { // 显示加载中的提示 swal({title: "触发中...", icon: "loading.gif", button: false, closeModal: false}); // 调用后端部署触发 API fetch('/hpp/admin/api/trigger-deploy', { method: 'POST' }) .then(response => { swal.close(); // 关闭加载提示 if (response.status === 200) { sweetAlert("成功", "已成功触发 Pages 部署", "success"); } else { // 如果状态码不是200,尝试读取错误信息,否则给出通用失败提示 response.text().then(text => { sweetAlert("触发失败", `状态码: ${response.status}`, "error"); }); } }) .catch(error => { swal.close(); sweetAlert("错误", "请求发送失败: " + error, "error"); }); }
|
代码介绍:
此函数是用户交互的入口。
用户反馈:首先调用
“swal” 显示一个“触发中…”的加载提示,提升用户体验。
发起请求:使用
“fetch” 函数向
“/hpp/admin/api/trigger-deploy” 路径发起一个 POST 请求。这是一个相对路径,请求会发送到当前服务的域名下,即 Zexo Workers 本身。
处理响应:
- 无论成功与否,首先关闭加载提示 (
“swal.close()”)。 - 检查 HTTP 状态码。若为
“200”,则弹出成功提示。 - 若状态码非
“200” 或发生网络错误 (
“catch”),则弹出包含错误信息的失败提示。
后端 API 实现逻辑
后端逻辑是整个功能的核心,在 Zexo 的主程序 (
“index.js”) 中实现。它需要完成权限验证、配置读取和最终向 Cloudflare Pages 发送构建请求三个关键步骤。
核心代码片段 (
“index.js” 中
“/hpp/admin/api/trigger-deploy” 端点处理逻辑)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| if (path == “/hpp/admin/api/trigger-deploy”) { // 1. 权限校验:必须为已登录的管理员 if (hpp_logstatus != 1) { return new Response(‘Unauthorized’, { status: 401 }); } // 2. 获取配置中预设的 Deploy Hook URL const hookUrl = hpp_deploy_hook_url; // 3. 向 Cloudflare Pages 的 Deploy Hook 发送 POST 请求 const deployRes = await fetch(hookUrl, { method: ‘POST’, headers: { ‘Content-Type’: ‘application/json’ } }); // 4. 将 Cloudflare Pages 的响应状态返回给前端 const deployStatus = await deployRes.status; if (deployStatus == 200 || deployStatus == 201) { return new Response(‘Deploy Triggered’, { status: deployStatus }); } else { return new Response(‘Fail To Trigger Deploy’, { status: deployStatus }); } }
|
代码介绍:
此代码块是 Workers 脚本中用于处理部署触发请求的端点。
- 权限验证 (
“hpp_logstatus != 1”):首先检查全局变量
“hpp_logstatus”。该变量在请求初始化阶段已通过验证 Cookie 中的用户名和密码 MD5 值完成设置。只有已登录的管理员请求才会通过此项检查,否则直接返回
“401 Unauthorized” 状态码。这确保了功能的安全性。 - 读取配置 (
“hpp_deploy_hook_url”):从已解析的全局配置对象中获取
“hpp_deploy_hook_url”。这个 URL 是在 Zexo 初始化配置时,由用户填写的 Cloudflare Pages 提供的专属部署钩子地址。 - 触发构建 (
“fetch(hookUrl, …)”):这是最核心的一步。使用
“fetch” 函数,以
“POST” 方法、
“application/json” 的内容类型,向上述钩子 URL 发起一个空请求。Cloudflare Pages 服务在收到此请求后,便会开始一次新的构建和部署流程。 - 响应转发:将 Cloudflare Pages 的响应状态码(通常成功触发为
“200” 或
“201”)原样转发给前端调用者。这样,前端 JavaScript 就能根据状态码判断触发是否成功,并给用户相应的提示。
运作逻辑总结
整个一键部署功能的逻辑链路形成了一个清晰的责任链:
- 交互层:用户在后台工具面板点击按钮。
- 前端控制层:
“zexo_trigger_deploy()” 函数被调用,它负责向后端 API 发送请求并管理用户界面反馈(加载、成功/失败提示)。 - 网关与鉴权层:请求到达 Cloudflare Workers,由
“/hpp/admin/api/trigger-deploy” 端点处理。该端点首先执行严格的登录状态检查,确保只有授权用户才能触发部署。 - 配置与执行层:通过校验后,端点从存储的配置中读取关键的 Deploy Hook URL,并向其发起一个简单的 HTTP POST 请求。
- 外部服务层:Cloudflare Pages 服务接收到来自 Workers 的请求,随即开始执行其预设的构建流程(如从 Git 仓库拉取最新代码、执行构建命令、发布到全球网络)。
2026.3.22旧方案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| /** * 调用后端接口,触发 Cloudflare Pages 部署 */ async function triggerPagesDeploy() { const btn = document.getElementById('deployTriggerBtn'); const resultDiv = document.getElementById('deployResult') || document.createElement('div'); // 结果展示区域 const originalBtnHTML = btn.innerHTML;
// 防止重复点击,更新按钮状态 btn.disabled = true; btn.innerHTML = '<i class="material-icons">hourglass_empty</i> 发送中...';
// 显示加载状态 resultDiv.innerHTML = '<div class="alert alert-info">正在向服务器发送部署指令...</div>';
try { // 关键:调用您已实现的后端 API const response = await fetch('/hpp/admin/api/trigger-deploy', { method: 'POST', headers: { 'Content-Type': 'application/json', }, });
const data = await response.json();
if (response.ok && data.status === 'success') { resultDiv.innerHTML = `<div class="alert alert-success"> <i class="material-icons">check_circle</i> <strong>成功!</strong> ${data.message} </div>`; } else { resultDiv.innerHTML = `<div class="alert alert-danger"> <i class="material-icons">error</i> <strong>操作失败。</strong> ${data.message || '服务器返回错误'} </div>`; } } catch (error) { console.error('部署触发失败:', error); resultDiv.innerHTML = `<div class="alert alert-danger"> <i class="material-icons">error</i> <strong>网络请求失败。</strong> 请检查控制台或网络连接。 </div>`; } finally { // 3秒后恢复按钮状态 setTimeout(() => { btn.disabled = false; btn.innerHTML = originalBtnHTML; }, 3000); } }
|
以上是triggerPagesDeploy()的定义,为按钮的onClick参数服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| // 在 index.js 文件的 handleRequest 函数中,找到 /hpp/admin/api/ 相关的 if 判断链。
if ("/hpp/admin/api/trigger-deploy" == path) { // 1. 权限验证(复用现有逻辑) if (hpp_logstatus != 1) { return new Response('{"status": "error", "message": "Unauthorized"}', { status: 401, headers: { "content-type": "application/json" } }); }
// 2. 定义您的 Cloudflare Pages Deploy Hook URL // 【重要】请将下面的 URL 替换成您自己的 Hook URL! const DEPLOY_HOOK_URL = 'xxx';
try { // 3. 向 Cloudflare 发送部署触发请求 const deployResponse = await fetch(DEPLOY_HOOK_URL, { method: 'POST', headers: { 'Content-Type': 'application/json', }, // Cloudflare Deploy Hook 通常不需要请求体,但保持 JSON 格式是好的做法 body: JSON.stringify({}) });
// 4. 处理并返回结果 if (deployResponse.ok) { const result = { status: 'success', message: '已成功触发重新部署。Cloudflare Pages 构建任务已开始,请稍后查看部署状态。' }; return new Response(JSON.stringify(result), { status: 200, headers: { "content-type": "application/json;charset=UTF-8" } }); } else { const errorText = await deployResponse.text(); console.error('Deploy hook failed:', deployResponse.status, errorText); const result = { status: 'error', message: `触发部署失败 (状态码: ${deployResponse.status})` }; return new Response(JSON.stringify(result), { status: 502, // Bad Gateway headers: { "content-type": "application/json;charset=UTF-8" } }); } } catch (error) { // 5. 处理网络或其他异常 console.error('Error triggering deploy:', error); const result = { status: 'error', message: `请求发生错误: ${error.message}` }; return new Response(JSON.stringify(result), { status: 500, headers: { "content-type": "application/json;charset=UTF-8" } }); } }
|
以上是/api/trigger-deployapi的定义,为triggerPagesDeploy()函数服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| <!-- 以下 HTML 代码块可以添加到工具箱页面的合适位置,例如在其他工具卡片之后 --> <div class="card"> <div class="card-header card-header-info"> <h4 class="card-title">博客站点部署</h4> <p class="card-category">触发 Cloudflare Pages 重新构建与部署</p> </div> <div class="card-body"> <p>此操作将触发与 HexoPlusPlus 关联的 Cloudflare Pages 项目 (<strong>zari-blog</strong>) 重新构建。系统将从 Git 仓库拉取最新代码并执行部署流程。</p> <div class="alert alert-warning"> <i class="material-icons">warning</i> <span>部署过程需要一定时间,期间网站访问可能不稳定。请谨慎操作。</span> </div> <button type="button" class="btn btn-info" onclick="triggerPagesDeploy()" id="deployTriggerBtn"> <i class="material-icons">cloud_upload</i> 立即触发重新部署 </button> <div id="deployResult" style="margin-top: 15px;"></div> </div> </div>
<script> /** * 触发 Cloudflare Pages 重新部署 */ async function triggerPagesDeploy() { const btn = document.getElementById('deployTriggerBtn'); const resultDiv = document.getElementById('deployResult'); const originalBtnHTML = btn.innerHTML;
// 防止重复点击 btn.disabled = true; btn.innerHTML = '<i class="material-icons">hourglass_empty</i> 部署请求发送中...';
resultDiv.innerHTML = '<div class="alert alert-info">正在发送部署指令,请稍候...</div>';
try { const response = await fetch('/hpp/admin/api/trigger-deploy', { method: 'POST', headers: { 'Content-Type': 'application/json', }, });
const data = await response.json();
if (response.ok && data.status === 'success') { resultDiv.innerHTML = `<div class="alert alert-success"> <i class="material-icons">check_circle</i> <strong>成功!</strong> ${data.message} </div>`; } else { resultDiv.innerHTML = `<div class="alert alert-danger"> <i class="material-icons">error</i> <strong>操作失败。</strong> ${data.message || '未知错误'} </div>`; } } catch (error) { console.error('部署触发失败:', error); resultDiv.innerHTML = `<div class="alert alert-danger"> <i class="material-icons">error</i> <strong>网络请求失败。</strong> 请检查控制台或网络连接。 </div>`; } finally { // 3秒后恢复按钮状态 setTimeout(() => { btn.disabled = false; btn.innerHTML = originalBtnHTML; }, 3000); } } </script>
|
以上是工具页面部署按钮的片段,用于前端操作