From ce71cedfd127b75edf4be6e4eaf0cb3f2db50e31 Mon Sep 17 00:00:00 2001 From: seaHi Date: Fri, 7 Aug 2026 13:29:14 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=BC=BA=20Cloudflare=20Acces?= =?UTF-8?q?s=20=E9=83=A8=E7=BD=B2=E6=A0=A1=E9=AA=8C=E4=B8=8E=E5=90=8E?= =?UTF-8?q?=E5=8F=B0=E8=AE=A4=E8=AF=81=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/deploy.yml | 40 +++++++++++++++++++++++++++++++------ README.md | 2 ++ src/hooks.server.ts | 25 ++++++++++++++++++++--- 3 files changed, 58 insertions(+), 9 deletions(-) diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 704a106..0b33c48 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -51,15 +51,43 @@ jobs: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} run: pnpm exec wrangler d1 migrations apply blogflare-db --remote - # 8. 使用本地安装好的 wrangler 直接推送部署 - - name: 8. 自动化推送到 Cloudflare Pages (主分支为生产发布) + # 8. 将后台认证配置同步到 Pages Production;缺失时阻止发布锁死后台 + - 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://.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: CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} run: pnpm exec wrangler pages deploy .svelte-kit/cloudflare --project-name=blogflare --branch=main - # 9. 构建与发布成功时,用 curl 直联调用 Telegram 官方发送通知接口 - - name: 9. 发送 Telegram 部署成功通知 + # 10. 构建与发布成功时,用 curl 直联调用 Telegram 官方发送通知接口 + - name: 10. 发送 Telegram 部署成功通知 if: success() env: TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }} @@ -72,8 +100,8 @@ jobs: -d "parse_mode=HTML" \ --data-urlencode "text=${MSG}" - # 10. 一旦任意步骤发生阻挠或错误,立即发警报知音 - - name: 10. 发送 Telegram 部署失败告警 + # 11. 一旦任意步骤发生阻挠或错误,立即发警报知音 + - name: 11. 发送 Telegram 部署失败告警 if: failure() env: TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }} diff --git a/README.md b/README.md index 712667a..f7763c2 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,8 @@ pnpm run dev -- --open 本地生产预览可复制 `.dev.vars.example` 为 `.dev.vars` 并填入真实值。开发服务器会保留仅限 `dev` 模式的本地认证旁路。 +Gitea 自动部署还要求在仓库的 Actions Secrets 中配置同名的 `CF_ACCESS_TEAM_DOMAIN` 和 `CF_ACCESS_AUD`。部署流程会先将它们同步到 Cloudflare Pages Production;缺少配置时会终止发布,避免后台因认证变量缺失而被锁定。 + ## 📦 构建与部署 ### 构建项目 diff --git a/src/hooks.server.ts b/src/hooks.server.ts index 1ab0960..eaec396 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -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 }) => { if (!event.url.pathname.startsWith('/admin')) { return resolve(event); @@ -28,16 +38,25 @@ export const handle: Handle = async ({ event, resolve }) => { const teamDomain = event.platform?.env.CF_ACCESS_TEAM_DOMAIN; const audience = event.platform?.env.CF_ACCESS_AUD; - if (!token || !teamDomain || !audience) { + if (!teamDomain || !audience) { console.error( JSON.stringify({ - message: 'Cloudflare Access configuration or JWT is missing', + message: 'Cloudflare Access configuration is missing', path: event.url.pathname, - hasToken: Boolean(token), hasTeamDomain: Boolean(teamDomain), hasAudience: Boolean(audience) }) ); + return accessNotConfigured(); + } + + if (!token) { + console.error( + JSON.stringify({ + message: 'Cloudflare Access JWT is missing', + path: event.url.pathname + }) + ); return unauthorized(); }