后台手动部署网站功能

Zarijaden

2026.4.4新方案

Zexo 一键部署功能逻辑解析与实现

在基于 Cloudflare Workers 的博客管理方案 Zexo 中,“一键部署”功能允许管理员在后台直接触发关联的 Pages 部署钩子,从而无需登录 Git 平台即可完成站点的自动构建与更新。本文将深入解析此功能的完整运作链路。

具体代码分析(使用AI编辑)

该功能本质上是为 Cloudflare Workers 脚本(即 Zexo)添加了一个专用的 API 端点。当用户在前台点击触发按钮时,前端会向此端点发送请求,后端接收到请求后,会向预设的 Cloudflare Pages 的 Deploy Hook URL 发起一个 POST 请求,从而触发一次全新的构建部署流程。

  1. 前端触发逻辑

前端逻辑定义在工具面板的 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");
});
}

代码介绍:

此函数是用户交互的入口。

  1. 用户反馈:首先调用
    “swal” 显示一个“触发中…”的加载提示,提升用户体验。

  2. 发起请求:使用
    “fetch” 函数向
    “/hpp/admin/api/trigger-deploy” 路径发起一个 POST 请求。这是一个相对路径,请求会发送到当前服务的域名下,即 Zexo Workers 本身。

  3. 处理响应:

    • 无论成功与否,首先关闭加载提示 (
      “swal.close()”)。
    • 检查 HTTP 状态码。若为
      “200”,则弹出成功提示。
    • 若状态码非
      “200” 或发生网络错误 (
      “catch”),则弹出包含错误信息的失败提示。
  4. 后端 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 脚本中用于处理部署触发请求的端点。

  1. 权限验证 (
    “hpp_logstatus != 1”):首先检查全局变量
    “hpp_logstatus”。该变量在请求初始化阶段已通过验证 Cookie 中的用户名和密码 MD5 值完成设置。只有已登录的管理员请求才会通过此项检查,否则直接返回
    “401 Unauthorized” 状态码。这确保了功能的安全性。
  2. 读取配置 (
    “hpp_deploy_hook_url”):从已解析的全局配置对象中获取
    “hpp_deploy_hook_url”。这个 URL 是在 Zexo 初始化配置时,由用户填写的 Cloudflare Pages 提供的专属部署钩子地址。
  3. 触发构建 (
    “fetch(hookUrl, …)”):这是最核心的一步。使用
    “fetch” 函数,以
    “POST” 方法、
    “application/json” 的内容类型,向上述钩子 URL 发起一个空请求。Cloudflare Pages 服务在收到此请求后,便会开始一次新的构建和部署流程。
  4. 响应转发:将 Cloudflare Pages 的响应状态码(通常成功触发为
    “200” 或
    “201”)原样转发给前端调用者。这样,前端 JavaScript 就能根据状态码判断触发是否成功,并给用户相应的提示。

运作逻辑总结

整个一键部署功能的逻辑链路形成了一个清晰的责任链:

  1. 交互层:用户在后台工具面板点击按钮。
  2. 前端控制层:
    “zexo_trigger_deploy()” 函数被调用,它负责向后端 API 发送请求并管理用户界面反馈(加载、成功/失败提示)。
  3. 网关与鉴权层:请求到达 Cloudflare Workers,由
    “/hpp/admin/api/trigger-deploy” 端点处理。该端点首先执行严格的登录状态检查,确保只有授权用户才能触发部署。
  4. 配置与执行层:通过校验后,端点从存储的配置中读取关键的 Deploy Hook URL,并向其发起一个简单的 HTTP POST 请求。
  5. 外部服务层: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>

以上是工具页面部署按钮的片段,用于前端操作

  • 标题: 后台手动部署网站功能
  • 作者: Zarijaden
  • 创建于 : 2026-03-22 00:00:00
  • 更新于 : 2026-04-12 12:10:27
  • 链接: https://zari-blog.pages.dev/2026/03/21/2026-03-22-后台部署功能/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论