Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 3 additions & 27 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,6 @@ on:
required: false
default: ''
type: string
pages_project_name:
description: 'Override Pages project name (optional)'
required: false
default: ''
type: string
worker_name:
description: 'Override Worker name (optional)'
required: false
Expand All @@ -52,11 +47,6 @@ on:
required: false
default: 'false'
type: string
wait_for_tls:
description: 'Wait until https://<DOMAIN> becomes reachable (true/false)'
required: false
default: 'true'
type: string

concurrency:
group: inkrypt-deploy-${{ github.repository }}-${{ inputs.domain }}
Expand All @@ -76,14 +66,12 @@ jobs:
INKRYPT_RP_NAME: ${{ inputs.rp_name }}
INKRYPT_COOKIE_SAMESITE: ${{ inputs.cookie_samesite }}
INKRYPT_CORS_ORIGIN: ${{ inputs.cors_origin }}
INKRYPT_PAGES_PROJECT_NAME: ${{ inputs.pages_project_name }}
INKRYPT_WORKER_NAME: ${{ inputs.worker_name }}
INKRYPT_D1_NAME: ${{ inputs.d1_name }}

# Safety toggles
FORCE_TAKEOVER_DNS: ${{ inputs.force_takeover_dns }}
FORCE_TAKEOVER_ROUTES: ${{ inputs.force_takeover_routes }}
WAIT_FOR_TLS: ${{ inputs.wait_for_tls }}

steps:
- name: Checkout
Expand All @@ -110,23 +98,11 @@ jobs:
echo "CLOUDFLARE_ACCOUNT_ID=${{ steps.zone.outputs.account_id }}" >> "$GITHUB_ENV"
echo "CLOUDFLARE_ZONE_ID=${{ steps.zone.outputs.zone_id }}" >> "$GITHUB_ENV"

- name: Ensure Pages project exists
run: node deploy/cf-api.mjs ensure-pages-project --account-id "${{ steps.zone.outputs.account_id }}" --project-name "${{ steps.cfg.outputs.pages_project_name }}" --production-branch main

- name: Build web
run: npm --workspace apps/web run build

- name: Deploy Pages (Direct Upload)
uses: cloudflare/pages-action@v1
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ steps.zone.outputs.account_id }}
projectName: ${{ steps.cfg.outputs.pages_project_name }}
directory: apps/web/dist
branch: main

- name: Bind custom domain + DNS (Pages)
run: node deploy/cf-api.mjs ensure-pages-domain --domain "${{ steps.cfg.outputs.domain }}" --account-id "${{ steps.zone.outputs.account_id }}" --project-name "${{ steps.cfg.outputs.pages_project_name }}" --zone-id "${{ steps.zone.outputs.zone_id }}" --cname-target "${{ steps.cfg.outputs.cname_target }}"
- name: Ensure DNS record (Worker-only)
run: node deploy/cf-api.mjs ensure-dns-a --zone-id "${{ steps.zone.outputs.zone_id }}" --name "${{ steps.cfg.outputs.domain }}"

- name: Ensure D1 exists
id: d1
Expand Down Expand Up @@ -197,7 +173,7 @@ jobs:
run: npx wrangler deploy --name "${{ steps.cfg.outputs.worker_name }}" --cwd apps/worker --config wrangler.toml

- name: Ensure Worker routes
run: node deploy/cf-api.mjs ensure-worker-routes --zone-id "${{ steps.zone.outputs.zone_id }}" --worker-name "${{ steps.cfg.outputs.worker_name }}" --route "${{ steps.cfg.outputs.domain }}/api/*,${{ steps.cfg.outputs.domain }}/auth/*,${{ steps.cfg.outputs.domain }}/healthz*"
run: node deploy/cf-api.mjs ensure-worker-routes --zone-id "${{ steps.zone.outputs.zone_id }}" --worker-name "${{ steps.cfg.outputs.worker_name }}" --route "${{ steps.cfg.outputs.domain }}/*"

- name: Smoke test
shell: bash
Expand Down
51 changes: 13 additions & 38 deletions DEPLOYMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,22 @@ Inkrypt 是一款基于 Passkey 的端到端加密笔记应用——你的笔记

| 组件 | 技术 | 部署目标 |
|------|------|----------|
| 前端 | Vite + React | Cloudflare Pages |
| 后端 | Hono | Cloudflare Workers |
| 前端 | Vite + React | Cloudflare Workers Static Assets |
| 后端 | Hono | Cloudflare Workers(同一个 Worker 内) |
| 存储 | D1 | Cloudflare D1 |
| 限流 | Durable Objects | Cloudflare DO |

---

## 部署前必读

### 1. 推荐同域部署

前端和后端放在同一个域名下是最省心的方式:

```
https://notes.example.com/* → Pages(静态资源)
https://notes.example.com/api/* → Worker(API)
https://notes.example.com/auth/* → Worker(认证)
```

这样不需要处理跨域,Cookie 和 WebAuthn 都最稳定。

### 2. 域名定了就别改
### 1. 域名定了就别改

后端用 `RP_ID`(域名)和 `ORIGIN`(完整地址)验证 Passkey。上线后改域名会导致已有 Passkey 失效。

### 3. 不支持跨站部署
### 2. 不支持跨站部署

前端在 `*.pages.dev`、后端在 `example.com` 这种跨站组合会被 CSRF 保护拦截,不支持。
前后端不在同一站点会被 CSRF 保护拦截,不支持。

---

Expand All @@ -42,7 +30,7 @@ https://notes.example.com/auth/* → Worker(认证)
**Cloudflare 侧**:
- Cloudflare 账号
- 一个域名(已托管到 Cloudflare)
- 开通 Workers、Pages、D1、Durable Objects
- 开通 Workers、D1、Durable Objects

**本地**:
- Node.js 20+
Expand All @@ -54,12 +42,10 @@ https://notes.example.com/auth/* → Worker(认证)

本仓库内置 GitHub Actions 工作流:`.github/workflows/deploy.yml`,可在 GitHub 上一键完成:

- Pages 项目创建与部署(Direct Upload)
- Worker 部署(含 D1/DO)
- Worker 部署(包含静态资源 + API,含 D1/DO)
- D1 创建与 migrations
- Pages 自定义域名绑定
- DNS CNAME 自动配置
- Worker Routes 自动配置(`/api/*`、`/auth/*`、`/healthz*`)
- DNS 记录自动配置(Worker-only)
- Worker Routes 自动配置(`/*`,内部仅 `/api/*`、`/auth/*`、`/healthz*` 走代码)

### 你需要准备

Expand All @@ -70,7 +56,7 @@ https://notes.example.com/auth/* → Worker(认证)
### Token 权限建议(最小集)

- Zone:`Zone:Read`、`DNS:Edit`、`Workers Routes:Edit`
- Account:`Pages:Edit`、`Workers Scripts:Edit`、`D1:Edit`
- Account:`Workers Scripts:Edit`、`D1:Edit`

### Durable Objects(SQLite 后端)

Expand All @@ -91,7 +77,6 @@ https://notes.example.com/auth/* → Worker(认证)

- `force_takeover_dns=true`:当 `DOMAIN` 已存在 DNS 记录但不匹配时,允许覆盖
- `force_takeover_routes=true`:当目标路由已绑定其他 Worker 时,允许接管
- `wait_for_tls=false`:不等待证书/HTTPS 可用(默认会等待)

---

Expand Down Expand Up @@ -155,28 +140,18 @@ npx wrangler deploy

## 步骤 5:部署前端

1. 打开 Cloudflare Dashboard → Pages → 创建项目
2. 绑定你的 Git 仓库
3. 配置构建:
- **Build command**: `npm ci && npm --workspace apps/web run build`
- **Output directory**: `apps/web/dist`
- **Environment**: `NODE_VERSION=22`
前端静态资源会随 Worker 一起部署(Workers Static Assets),无需创建 Pages 项目。

---

## 步骤 6:配置路由

### 给 Pages 绑定域名

Pages 项目 → 自定义域名 → 添加 `notes.example.com`

### 给 Worker 添加路由

Worker → Triggers → Routes → 添加:
- `notes.example.com/api/*`
- `notes.example.com/auth/*`
- `notes.example.com/*`

这样 `/api``/auth` 走 Worker,其他走 Pages
这样整个站点都由同一个 Worker 提供:静态资源由 Static Assets 返回,`/api``/auth` 等路径由 Hono 代码处理

---

Expand Down
12 changes: 5 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,28 +125,26 @@
Token 最小权限建议:

- Zone:`Zone:Read`、`DNS:Edit`、`Workers Routes:Edit`
- Account:`Pages:Edit`、`Workers Scripts:Edit`、`D1:Edit`
- Account:`Workers Scripts:Edit`、`D1:Edit`

### 3) 运行部署工作流

进入仓库 → Actions → `Deploy Inkrypt` → Run workflow:

- 必填:`domain`
- 选填:`rp_name`、`cors_origin`、`pages_project_name`、`worker_name`、`d1_name`、`d1_location`
- 选填:`rp_name`、`cors_origin`、`worker_name`、`d1_name`、`d1_location`

安全开关(默认谨慎):

- `force_takeover_dns=true`:允许覆盖已存在但不匹配的 DNS 记录
- `force_takeover_routes=true`:允许接管已被其他 Worker 占用的 Routes
- `wait_for_tls=false`:不等待 HTTPS 就绪(默认会等待)

该工作流会自动完成:

- Pages 项目创建与部署(Direct Upload)
- Worker 部署(含 D1/DO)
- Worker 部署(包含静态资源 + API,含 D1/DO)
- D1 创建与 migrations
- Pages 自定义域名绑定 + DNS CNAME 自动配置
- Worker Routes 自动配置(`/api/*`、`/auth/*`、`/healthz*`)
- DNS 记录自动配置(Worker-only)
- Worker Routes 自动配置(`/*`,内部仅 `/api/*`、`/auth/*`、`/healthz*` 走代码
- Smoke test:访问 `https://<DOMAIN>/healthz`

### 4) 部署完成后
Expand Down
8 changes: 8 additions & 0 deletions apps/worker/wrangler.toml.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ compatibility_flags = ["nodejs_compat"]
workers_dev = false
preview_urls = false

[assets]
# Serve the Vite build output as Static Assets from the same Worker.
directory = "../web/dist"
# SPA mode: when a request doesn't match an asset, return index.html (for HTML navigations).
not_found_handling = "single-page-application"
# Only these routes should invoke the User Worker (Hono). Everything else is served as static assets.
run_worker_first = ["/api/*", "/auth/*", "/healthz*"]

[vars]
RP_NAME = "Inkrypt"
RP_ID = "notes.example.com"
Expand Down
Loading