feat: 增强 Cloudflare Access 部署校验与后台认证逻辑
This commit is contained in:
1 parent
669a68a7c9
commit
ce71cedfd1
3 files changed
+58
-9
No files matched your search
@@ -51,15 +51,43 @@ jobs:
|
|||||||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
run: pnpm exec wrangler d1 migrations apply blogflare-db --remote
|
run: pnpm exec wrangler d1 migrations apply blogflare-db --remote
|
||||||
|
|
||||||
# 8. 使用本地安装好的 wrangler 直接推送部署
|
# 8. 将后台认证配置同步到 Pages Production;缺失时阻止发布锁死后台
|
||||||
- name: 8. 自动化推送到 Cloudflare Pages (主分支为生产发布)
|
- name: 8. 同步 Cloudflare Access 生产环境配置
|
||||||
|
env:
|
||||||
|
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
|
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
|
CF_ACCESS_TEAM_DOMAIN: ${{ secrets.CF_ACCESS_TEAM_DOMAIN }}
|
||||||
|
CF_ACCESS_AUD: ${{ secrets.CF_ACCESS_AUD }}
|
||||||
|
run: |
|
||||||
|
if [[ -z "$CF_ACCESS_TEAM_DOMAIN" || -z "$CF_ACCESS_AUD" ]]; then
|
||||||
|
echo "::error::缺少 Gitea Secrets:CF_ACCESS_TEAM_DOMAIN 或 CF_ACCESS_AUD"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$CF_ACCESS_TEAM_DOMAIN" in
|
||||||
|
https://*.cloudflareaccess.com) ;;
|
||||||
|
*)
|
||||||
|
echo "::error::CF_ACCESS_TEAM_DOMAIN 必须是 https://<team>.cloudflareaccess.com"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
access_secret_file="$(mktemp)"
|
||||||
|
trap 'rm -f "$access_secret_file"' EXIT
|
||||||
|
chmod 600 "$access_secret_file"
|
||||||
|
printf 'CF_ACCESS_TEAM_DOMAIN=%s\nCF_ACCESS_AUD=%s\n' \
|
||||||
|
"$CF_ACCESS_TEAM_DOMAIN" "$CF_ACCESS_AUD" > "$access_secret_file"
|
||||||
|
pnpm exec wrangler pages secret bulk "$access_secret_file" --project-name=blogflare
|
||||||
|
|
||||||
|
# 9. 使用本地安装好的 wrangler 直接推送部署
|
||||||
|
- name: 9. 自动化推送到 Cloudflare Pages (主分支为生产发布)
|
||||||
env:
|
env:
|
||||||
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
run: pnpm exec wrangler pages deploy .svelte-kit/cloudflare --project-name=blogflare --branch=main
|
run: pnpm exec wrangler pages deploy .svelte-kit/cloudflare --project-name=blogflare --branch=main
|
||||||
|
|
||||||
# 9. 构建与发布成功时,用 curl 直联调用 Telegram 官方发送通知接口
|
# 10. 构建与发布成功时,用 curl 直联调用 Telegram 官方发送通知接口
|
||||||
- name: 9. 发送 Telegram 部署成功通知
|
- name: 10. 发送 Telegram 部署成功通知
|
||||||
if: success()
|
if: success()
|
||||||
env:
|
env:
|
||||||
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
||||||
@@ -72,8 +100,8 @@ jobs:
|
|||||||
-d "parse_mode=HTML" \
|
-d "parse_mode=HTML" \
|
||||||
--data-urlencode "text=${MSG}"
|
--data-urlencode "text=${MSG}"
|
||||||
|
|
||||||
# 10. 一旦任意步骤发生阻挠或错误,立即发警报知音
|
# 11. 一旦任意步骤发生阻挠或错误,立即发警报知音
|
||||||
- name: 10. 发送 Telegram 部署失败告警
|
- name: 11. 发送 Telegram 部署失败告警
|
||||||
if: failure()
|
if: failure()
|
||||||
env:
|
env:
|
||||||
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
||||||
|
|||||||
@@ -69,6 +69,8 @@ pnpm run dev -- --open
|
|||||||
|
|
||||||
本地生产预览可复制 `.dev.vars.example` 为 `.dev.vars` 并填入真实值。开发服务器会保留仅限 `dev` 模式的本地认证旁路。
|
本地生产预览可复制 `.dev.vars.example` 为 `.dev.vars` 并填入真实值。开发服务器会保留仅限 `dev` 模式的本地认证旁路。
|
||||||
|
|
||||||
|
Gitea 自动部署还要求在仓库的 Actions Secrets 中配置同名的 `CF_ACCESS_TEAM_DOMAIN` 和 `CF_ACCESS_AUD`。部署流程会先将它们同步到 Cloudflare Pages Production;缺少配置时会终止发布,避免后台因认证变量缺失而被锁定。
|
||||||
|
|
||||||
## 📦 构建与部署
|
## 📦 构建与部署
|
||||||
|
|
||||||
### 构建项目
|
### 构建项目
|
||||||
|
|||||||
+22
-3
@@ -12,6 +12,16 @@ function unauthorized(): Response {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function accessNotConfigured(): Response {
|
||||||
|
return new Response('Admin authentication is not configured', {
|
||||||
|
status: 503,
|
||||||
|
headers: {
|
||||||
|
'Cache-Control': 'no-store',
|
||||||
|
'Content-Type': 'text/plain; charset=utf-8'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export const handle: Handle = async ({ event, resolve }) => {
|
export const handle: Handle = async ({ event, resolve }) => {
|
||||||
if (!event.url.pathname.startsWith('/admin')) {
|
if (!event.url.pathname.startsWith('/admin')) {
|
||||||
return resolve(event);
|
return resolve(event);
|
||||||
@@ -28,16 +38,25 @@ export const handle: Handle = async ({ event, resolve }) => {
|
|||||||
const teamDomain = event.platform?.env.CF_ACCESS_TEAM_DOMAIN;
|
const teamDomain = event.platform?.env.CF_ACCESS_TEAM_DOMAIN;
|
||||||
const audience = event.platform?.env.CF_ACCESS_AUD;
|
const audience = event.platform?.env.CF_ACCESS_AUD;
|
||||||
|
|
||||||
if (!token || !teamDomain || !audience) {
|
if (!teamDomain || !audience) {
|
||||||
console.error(
|
console.error(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
message: 'Cloudflare Access configuration or JWT is missing',
|
message: 'Cloudflare Access configuration is missing',
|
||||||
path: event.url.pathname,
|
path: event.url.pathname,
|
||||||
hasToken: Boolean(token),
|
|
||||||
hasTeamDomain: Boolean(teamDomain),
|
hasTeamDomain: Boolean(teamDomain),
|
||||||
hasAudience: Boolean(audience)
|
hasAudience: Boolean(audience)
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
return accessNotConfigured();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!token) {
|
||||||
|
console.error(
|
||||||
|
JSON.stringify({
|
||||||
|
message: 'Cloudflare Access JWT is missing',
|
||||||
|
path: event.url.pathname
|
||||||
|
})
|
||||||
|
);
|
||||||
return unauthorized();
|
return unauthorized();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user