feat: 新增一键部署到 Cloudflare Workers
This commit is contained in:
1 parent
e6a86b2050
commit
b7fc210bd1
13 files changed
+226
-147
No files matched your search
@@ -22,27 +22,17 @@ jobs:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: |
|
||||
frontend/package.json
|
||||
backend/package.json
|
||||
package.json
|
||||
|
||||
# 安装前端依赖 (Wrangler 构建需要)
|
||||
- name: Install Frontend Dependencies
|
||||
run: |
|
||||
cd frontend
|
||||
npm install
|
||||
|
||||
# 安装后端依赖
|
||||
- name: Install Backend Dependencies
|
||||
run: |
|
||||
cd backend
|
||||
npm install
|
||||
# 安装全部依赖
|
||||
- name: Install Dependencies
|
||||
run: npm install
|
||||
|
||||
# 准备配置文件
|
||||
- name: Prepare Wrangler Config
|
||||
env:
|
||||
CONFIG_FILE: ${{ env.WRANGLER_CONFIG || 'wrangler.toml' }}
|
||||
run: |
|
||||
cd backend
|
||||
cp wrangler.toml wrangler.toml.bak
|
||||
|
||||
if [ "$CONFIG_FILE" != "wrangler.toml" ]; then
|
||||
@@ -57,8 +47,8 @@ jobs:
|
||||
CLOUDFLARE_D1_DATABASE_NAME: ${{ secrets.CLOUDFLARE_D1_DATABASE_NAME || '2fauth-db' }}
|
||||
run: |
|
||||
# 仅替换第一个匹配到的 database_id (对应生产环境配置),保留 dev 环境配置
|
||||
sed -i "0,/database_id =/s/database_id = \".*\"/database_id = \"$CLOUDFLARE_D1_DATABASE_ID\"/" "backend/wrangler.toml"
|
||||
sed -i "0,/database_name =/s/database_name = \".*\"/database_name = \"$CLOUDFLARE_D1_DATABASE_NAME\"/" "backend/wrangler.toml"
|
||||
sed -i "0,/database_id =/s/database_id = \".*\"/database_id = \"$CLOUDFLARE_D1_DATABASE_ID\"/" "wrangler.toml"
|
||||
sed -i "0,/database_name =/s/database_name = \".*\"/database_name = \"$CLOUDFLARE_D1_DATABASE_NAME\"/" "wrangler.toml"
|
||||
|
||||
# 更新数据库结构
|
||||
- name: Update Database Schema
|
||||
@@ -66,8 +56,7 @@ jobs:
|
||||
with:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
workingDirectory: 'backend'
|
||||
command: d1 execute ${{ secrets.CLOUDFLARE_D1_DATABASE_NAME || '2fauth-db' }} --remote --file=schema.sql
|
||||
command: d1 execute ${{ secrets.CLOUDFLARE_D1_DATABASE_NAME || '2fauth-db' }} --remote --file=backend/schema.sql
|
||||
|
||||
# 动态生成 Secrets 列表 (仅包含已配置的变量)
|
||||
- name: Generate Secret List
|
||||
@@ -131,7 +120,6 @@ jobs:
|
||||
with:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
workingDirectory: 'backend'
|
||||
command: deploy --minify
|
||||
secrets: ${{ steps.secret_list.outputs.secrets }}
|
||||
env:
|
||||
@@ -162,7 +150,6 @@ jobs:
|
||||
- name: Restore Wrangler Config
|
||||
if: always()
|
||||
run: |
|
||||
cd backend
|
||||
if [ -f wrangler.toml.bak ]; then
|
||||
mv wrangler.toml.bak wrangler.toml
|
||||
fi
|
||||
+3
-1
@@ -5,11 +5,13 @@ logs
|
||||
.secrets
|
||||
.actrc
|
||||
.event
|
||||
.dev.vars
|
||||
.vscode/*
|
||||
.devcontainer/*
|
||||
.test/*
|
||||
.wrangler/*
|
||||
package-lock.json
|
||||
|
||||
backend/.dev.vars
|
||||
backend/.wrangler
|
||||
backend/README.md
|
||||
backend/node_modules
|
||||
|
||||
@@ -53,110 +53,140 @@
|
||||
|
||||
## 🚀 详细部署指南
|
||||
|
||||
本项目设计为通过 **GitHub Actions** 自动化一键部署到你的 Cloudflare 账号下。只需几分钟即可获得你专属的 2FA 管理器。
|
||||
本项目支持 2 种部署方式:**一键部署到 Cloudflare Workers** 和 **通过 GitHub Actions 部署**。请根据你的需要选择其中一种即可。
|
||||
|
||||
### 第一步:准备工作
|
||||
---
|
||||
|
||||
### 方式一:一键部署到 Cloudflare Workers(推荐,新手友好)
|
||||
|
||||
这是最简便的部署方式,只需点击下方按钮,按照页面向导提示操作即可快速上线。
|
||||
|
||||
[](https://deploy.workers.cloudflare.com/?url=https://github.com/nap0o/2fauth-worker)
|
||||
|
||||
**部署说明:**
|
||||
|
||||
1. 在部署之前,建议你先准备好你打算使用的**第三方登录(OAuth)配置**,例如 GitHub 的 Client ID / Secret(详见下文:[获取第三方登录密钥](#2-配置第三方登录))。
|
||||
2. Fork 本仓库
|
||||
2. 点击上方的 **Deploy to Cloudflare Workers** 按钮进入 Cloudflare 部署向导。
|
||||
3. 根据向导提示,授权 Cloudflare 读取该仓库(系统会自动在你的 GitHub 账号下创建一个该项目的私有克隆)。
|
||||
|
||||
**授权 Cloudflare 读取 Github 仓库 步骤:**
|
||||
|
||||
<img height="400" alt="0001" src="https://github.com/user-attachments/assets/cb64bc2f-6dcc-40cb-a781-3bc2c7bc5b28" />
|
||||
<img height="500" alt="0002" src="https://github.com/user-attachments/assets/3f186ea6-80f5-4d78-b90f-724af33a73ae" />
|
||||
<img height="500" alt="0003" src="https://github.com/user-attachments/assets/c1f2d5ee-2f3f-47c2-969f-00308cadff21" />
|
||||
|
||||
4. 点击`创建和部署`,完成部署!
|
||||
5. 访问专属的 `xxx.workers.dev` 域名,按照页面提示完成初始化配置。
|
||||
|
||||
---
|
||||
|
||||
### 方式二:通过 GitHub Actions 自动化部署(适合高级用户)
|
||||
|
||||
如果你需要更深入定制或打算持续集成,可以选择原生的 GitHub Actions 自动部署流程。
|
||||
|
||||
#### 1. 准备工作
|
||||
1. 注册一个 [Cloudflare](https://dash.cloudflare.com/) 账号。
|
||||
2. **Fork** 本仓库到你的 GitHub 账户。
|
||||
|
||||
### 第二步:获取 Cloudflare Worker部署令牌
|
||||
#### 2. 获取 Cloudflare Worker部署令牌
|
||||
1. 登录 Cloudflare Dashboard
|
||||
2. [前往获取](https://dash.cloudflare.com/profile/api-tokens)
|
||||
2. [前往获取](https://dash.cloudflare.com/profile/api-tokens) https://dash.cloudflare.com/profile/api-tokens
|
||||
3. 点击“创建令牌”
|
||||
4. 选择使用模版 “编辑 Cloudflare Workers”
|
||||
5. 配置”帐户资源“和”区域资源“
|
||||
6. 依次点击”继续以显示摘要“,点击”创建令牌“
|
||||
5. 配置“帐户资源”和“区域资源”
|
||||
6. 依次点击“继续以显示摘要”,点击“创建令牌”
|
||||
7. 复制生成的令牌
|
||||
|
||||
<img width="500" alt="image" src="https://github.com/user-attachments/assets/6487aa6e-e505-4980-aef4-e08172116746" /><br />
|
||||
|
||||
<img width="800" alt="image" src="https://github.com/user-attachments/assets/d4c737f7-2d9f-4cfb-a712-b1af416c8ef6" />
|
||||
|
||||
### 第三步:配置 Cloudflare D1 数据库
|
||||
#### 3. 配置 Cloudflare D1 数据库
|
||||
1. 登录 Cloudflare Dashboard。
|
||||
2. 依次点击左侧菜单的 **存储和数据库** -> **D1 SQL 数据库**。
|
||||
3. 点击 **创建数据库**,命名为 `2fauth-db` (或自定义名称)。
|
||||
4. 创建成功后,复制数据库详情页提供的 **`Database ID`**,备用。
|
||||
|
||||
### 第四步:配置第三方登录
|
||||
*注:系统支持 6 种登录方式,你**至少需要配置一种***。推荐使用 `GitHub登录`
|
||||
#### 4. 配置 GitHub Actions Secrets
|
||||
前往你 Fork 仓库的 **Settings** -> **Secrets and variables** -> **Actions** -> **New repository secret**,添加下列关键参数:
|
||||
|
||||
#### 使用 GitHub 登录
|
||||
1. 访问 GitHub `Settings` -> `Developer Settings` -> `OAuth Apps` -> **New OAuth App**。
|
||||
- **基础平台令牌必填:**
|
||||
- `CLOUDFLARE_ACCOUNT_ID`:你的 CF 账户 ID (CF 面板的概述页右下角可以找到)
|
||||
- `CLOUDFLARE_API_TOKEN`:刚刚在“步骤 2”获取的部署令牌
|
||||
- `CLOUDFLARE_D1_DATABASE_ID`:刚刚在“步骤 3”获取的 D1 数据库 ID
|
||||
- `CLOUDFLARE_D1_DATABASE_NAME`:默认为 `2fauth-db`
|
||||
- **系统安全与 OAuth 登录密钥:**
|
||||
- `ENCRYPTION_KEY`、`JWT_SECRET` 及你要使用的 OAuth 登录配置,请统一查阅下方的 **[🧩 通用环境变量配置说明](#-通用环境变量配置说明)** 进行添加。
|
||||
|
||||
2. 填写应用信息:
|
||||
- **名称**: `2FAuth`
|
||||
- **Homepage URL**: 填入你预期的站点域名,例如 `https://2fa.yourdomain.workers.dev` (部署后可绑定自定义域名)
|
||||
- **Authorization callback URL**: 重点!必须填写 `https://<Homepage URL>/oauth/callback`
|
||||
|
||||
3. 保存生成的 **`Client ID`** 和 **`Client Secret`**。
|
||||
|
||||
<img width="800" alt="image" src="https://github.com/user-attachments/assets/aa03b15f-deb2-4e48-bf4b-e57be342adbb" />
|
||||
|
||||
#### 方案二:使用 Telegram 登录
|
||||
1. 在 Telegram 中搜索并添加官方机器人 **[@BotFather](https://t.me/BotFather)**。
|
||||
2. 发送 `/newbot` 指令,按照提示为你的 2FAuth 创建一个机器人,并获取它的 **`Bot Token`**(如 `123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11`)。
|
||||
3. 获取机器人的 **`Bot Name`**(就是你刚才设置的以 `bot` 结尾的用户名,例如 `my_2fa_bot`)
|
||||
4. (非常重要)发送 `/setdomain` 指令,选择你刚才创建的机器人,然后发送你的应用域名(不需要 `https://`,例如:`2fa.yourdomain.workers.dev`)。
|
||||
5. (非常重要)配置 Webhook
|
||||
```bash
|
||||
curl "https://api.telegram.org/bot<你的BotToken>/setWebhook?url=https://<你设置的setdomain>/api/telegram/webhook"
|
||||
```
|
||||
|
||||
### 第五步:配置 GitHub Actions Secrets
|
||||
前往你 Fork 的 GitHub 仓库的 **Settings** -> **Secrets and variables** -> **Actions** -> **New repository secret**,添加以下必填环境变量:
|
||||
|
||||
#### ☁️ 必填:Cloudflare 资源授权
|
||||
| Secret Name | 说明 | 示例值 |
|
||||
| :--- | :--- | :--- |
|
||||
| `CLOUDFLARE_ACCOUNT_ID` | 你的 CF 账户 ID | `a1b2c3d4e5f6g7h8i9j0` (CF 面板的概述页右下角) |
|
||||
| `CLOUDFLARE_API_TOKEN` | CF 部署令牌 | `r4_xyz...` |
|
||||
|
||||
#### 🔐 必填:系统安全参数
|
||||
|
||||
> ⚠️ **高危警告:关于 ENCRYPTION_KEY 的修改风险**
|
||||
> `ENCRYPTION_KEY` 是用于加密数据库中 2FA 核心令牌种子的底层主密钥。**一旦系统投入生产使用并存入了数据,请绝对不要修改此环境变量的值!** 修改后将导致之前存入的所有数据瞬间无法解密,彻底变成死数据。
|
||||
|
||||
| Secret Name | 说明 | 示例值 |
|
||||
| :--- | :--- | :--- |
|
||||
| `OAUTH_ALLOWED_USERS` | 允许登录的账户白名单邮箱或Telegram 用户数字ID | `me@example.com,you@example.com,5341743823` (多个逗号分隔) |
|
||||
| `OAUTH_ALLOW_ALL` | 无视白名单放行所有人(**仅限测试**) | `true` 或 `1` (生产环境切勿配置) |
|
||||
| `JWT_SECRET` | 签发会话状态的系统密钥 | 越长越好, 建议 32 位随机字符 |
|
||||
| `ENCRYPTION_KEY` | 对数据库持久化2FA核心密码集的唯一底层加密密钥 | **必填**。建议使用 `openssl rand -hex 32` 生成|
|
||||
|
||||
#### 🔑 必填:Cloudflare D1数据库参数
|
||||
|
||||
| Secret Name | 说明 | 示例值 |
|
||||
| :--- | :--- | :--- |
|
||||
| `CLOUDFLARE_D1_DATABASE_ID` | Cloudflare D1 数据库 ID | `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` |
|
||||
| `CLOUDFLARE_D1_DATABASE_NAME` | Cloudflare D1 数据库名称 | 默认为 `2fauth-db` |
|
||||
|
||||
#### 🔑 必填:OAuth 身份信息 (按需选择对应的组合)
|
||||
> 如果你用 GitHub 登录,就填下面三个;如果你用 Google,就填 Google 的那三个。
|
||||
|
||||
| 平台 | Client ID 变量组 (示例) | Client Secret 变量组 (示例) | Redirect URI 变量组 (示例) |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **GitHub** | `OAUTH_GITHUB_CLIENT_ID`<br>`Ov23...` | `OAUTH_GITHUB_CLIENT_SECRET`<br>`a1b2...` | `OAUTH_GITHUB_REDIRECT_URI`<br>`https://xxx.workers.dev/oauth/callback` |
|
||||
| **Telegram** | `OAUTH_TELEGRAM_BOT_NAME`<br>`my_2fa_bot` | `OAUTH_TELEGRAM_BOT_TOKEN`<br>`123:ABC...` | *Telegram 无需 URI 变量* |
|
||||
| **Google** | `OAUTH_GOOGLE_CLIENT_ID`<br>`123...apps.googleusercontent.com` | `OAUTH_GOOGLE_CLIENT_SECRET`<br>`GOCSPX-...` | `OAUTH_GOOGLE_REDIRECT_URI`<br>`https://xxx.workers.dev/oauth/callback` |
|
||||
| **Cloudflare Access** | `OAUTH_CLOUDFLARE_CLIENT_ID`<br>`abc.access` | `OAUTH_CLOUDFLARE_CLIENT_SECRET`<br>`def...` | `OAUTH_CLOUDFLARE_REDIRECT_URI`<br>`https://xxx.workers.dev/oauth/callback`<br>*(该平台还需配置 `OAUTH_CLOUDFLARE_ORG_DOMAIN`,如: `https://<team>.cloudflareaccess.com`)* |
|
||||
| **Gitee** | `OAUTH_GITEE_CLIENT_ID`<br>`bcd...` | `OAUTH_GITEE_CLIENT_SECRET`<br>`efg...` | `OAUTH_GITEE_REDIRECT_URI`<br>`https://xxx.workers.dev/oauth/callback` |
|
||||
| **NodeLoc** | `OAUTH_NODELOC_CLIENT_ID`<br>`cde...` | `OAUTH_NODELOC_CLIENT_SECRET`<br>`fgh...` | `OAUTH_NODELOC_REDIRECT_URI`<br>`https://xxx.workers.dev/oauth/callback` |
|
||||
|
||||
<img width="800" alt="image" src="https://github.com/user-attachments/assets/ef907021-303d-4fd5-ba3e-913e8b0014a5" />
|
||||
|
||||
### 第五步:触发自动化部署
|
||||
#### 5. 触发自动化部署
|
||||
1. 进入你的 GitHub 仓库的 **Actions** 页面。
|
||||
2. 找到 `Deploy to Cloudflare Workers` 工作流。
|
||||
3. 点击 **Run workflow** 手动触发执行(或者随便 Push 一下 main 分支)。
|
||||
4. **喝杯咖啡 ☕,等待自动化脚本完成:**
|
||||
- 📦 自动构建 Vue 3 SPA 前端。
|
||||
- 🗄️ 自动帮你初始化部署 D1 数据库内的表结构(Schema)。
|
||||
- ⚡ 自动将带有静态前端的后端服务绑定至 Cloudflare 边缘节点。
|
||||
5. 部署成功后,控制台会输出你的 `xxx.workers.dev` 链接。登录进去,开始你的私有化 2FA 之旅!
|
||||
4. 等待自动化脚本完成,部署成功后,控制台会输出你的 `xxx.workers.dev` 访问链接。
|
||||
6. 访问专属的 `xxx.workers.dev` 域名,按照页面提示完成初始化配置。
|
||||
|
||||
**手动触发deploy**
|
||||
|
||||
<img width="800" height="1350" alt="image" src="https://github.com/user-attachments/assets/b2891365-5c1a-4a46-83c6-5cd53dd4b895" />
|
||||
|
||||
**配置Secrets and variables示例**
|
||||
|
||||
<img width="800" alt="image" src="https://github.com/user-attachments/assets/ef907021-303d-4fd5-ba3e-913e8b0014a5" />
|
||||
|
||||
---
|
||||
|
||||
### 🧩 通用环境变量配置说明
|
||||
|
||||
以下环境变量,不论是通过 **"方案一:一键部署"** 还是 **"方案二:GitHub Actions"**,都需要在Worker设置页面或仓库 Secrets 中按需配置:
|
||||
|
||||
#### 1. 核心安全参数(必填!)
|
||||
|
||||
> ⚠️ **高危警告:关于 ENCRYPTION_KEY 的修改风险**
|
||||
> `ENCRYPTION_KEY` 是用于加密数据库中 2FA 核心令牌种子的底层主密钥。**一旦系统投入生产使用并存入数据,请绝对不要再修改此环境变量的值!** 如果该密钥丢失或更改,数据库里旧的数据将无法解密!
|
||||
|
||||
| 变量 / Secret Name | 说明 | 示例值 |
|
||||
| :--- | :--- | :--- |
|
||||
| `ENCRYPTION_KEY` | 对数据库持久化2FA核心密种加密的主密钥 | **必填**。建议使用 `openssl rand -hex 32` 生成,务必自己保存好备份。 |
|
||||
| `JWT_SECRET` | 签发安全会话状态的系统密钥 | **必填**。越长越好, 建议输入 32 位随机字符。 |
|
||||
| `OAUTH_ALLOWED_USERS` | 允许登录系统的白名单:支持填入邮箱,或 Telegram数字ID | **必填**。`me@example.com,you@example.com,5341743823` (多个配置必须用逗号分隔) |
|
||||
|
||||
#### 2. 配置第三方登录
|
||||
*系统支持多种登录方式,你**至少需要选择一种进行环境配置***。我们最推荐使用 `GitHub` 作为后台登录。
|
||||
|
||||
**使用 GitHub 登录获取办法**
|
||||
1. 访问 GitHub `Settings` -> `Developer Settings` -> `OAuth Apps` -> **New OAuth App**。
|
||||
2. 填写应用信息:
|
||||
- **名称**: `2FAuth Worker`
|
||||
- **Homepage URL**: 填入你预期的站点域名,例如 `https://2fa.yourdomain.workers.dev` (部署后可绑定自定义域名)
|
||||
- **Authorization callback URL**: 必须填写 `https://<你的Homepage URL>/oauth/callback`
|
||||
3. 保存并复制生成的 **`Client ID`** 和 **`Client Secret`**。
|
||||
|
||||
<img width="800" alt="image" src="https://github.com/user-attachments/assets/aa03b15f-deb2-4e48-bf4b-e57be342adbb" />
|
||||
|
||||
**使用 Telegram 登录获取办法**
|
||||
1. 在 Telegram 中搜索并添加官方机器人 **[@BotFather](https://t.me/BotFather)**。
|
||||
2. 发送 `/newbot` 指令,按照提示为你的 2FAuth 创建机器人,记录它的 **`Bot Token`**(如 `123456:ABC-DEF1234...`)。
|
||||
3. 记录机器人的 **`Bot Name`**(也就是你刚设置的以 bot 结尾的用户名,例如 `my_2fa_bot`)
|
||||
4. (非常重要)发送 `/setdomain` 指令,选择此机器人,然后发送你的应用域名(不需要 `https://`,例如:`2fa.yourdomain.workers.dev`)。
|
||||
5. (非常重要)由于我们要使用无服务器直接验证,需要配置 Webhook,复制下方链接在浏览器中访问一次注册 Webhook:
|
||||
```url
|
||||
https://api.telegram.org/bot<你的BotToken>/setWebhook?url=https://<你的应用域名>/api/telegram/webhook
|
||||
```
|
||||
|
||||
**依据你选取的登录平台填入所需变量(任选一对即可):**
|
||||
|
||||
> 如果你使用 GitHub 登录,就在环境变量里配置 GitHub 那一行提供的所有变量选项。其它平台类似。
|
||||
|
||||
| 平台 | Client ID 变量群 | Client Secret 变量群 | Redirect URI 回调变量群 (示例:`https://xxx.dev/oauth/callback`) |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **GitHub** | `OAUTH_GITHUB_CLIENT_ID` | `OAUTH_GITHUB_CLIENT_SECRET` | `OAUTH_GITHUB_REDIRECT_URI` |
|
||||
| **Telegram** | `OAUTH_TELEGRAM_BOT_NAME`*(填你的Bot名称)* | `OAUTH_TELEGRAM_BOT_TOKEN`*(填Bot的Token密钥)* | *Telegram 无需 URI 回调变量* |
|
||||
| **Google** | `OAUTH_GOOGLE_CLIENT_ID` | `OAUTH_GOOGLE_CLIENT_SECRET` | `OAUTH_GOOGLE_REDIRECT_URI` |
|
||||
| **Cloudflare Access** | `OAUTH_CLOUDFLARE_CLIENT_ID` | `OAUTH_CLOUDFLARE_CLIENT_SECRET` | `OAUTH_CLOUDFLARE_REDIRECT_URI`<br>*(该平台还缺一不可:需额外配置 `OAUTH_CLOUDFLARE_ORG_DOMAIN`,如: `https://<team>.cloudflareaccess.com`)* |
|
||||
| **Gitee** | `OAUTH_GITEE_CLIENT_ID` | `OAUTH_GITEE_CLIENT_SECRET` | `OAUTH_GITEE_REDIRECT_URI` |
|
||||
| **NodeLoc** | `OAUTH_NODELOC_CLIENT_ID` | `OAUTH_NODELOC_CLIENT_SECRET` | `OAUTH_NODELOC_REDIRECT_URI` |
|
||||
|
||||
|
||||
---
|
||||
|
||||
@@ -169,19 +199,20 @@ git clone https://github.com/nap0o/2fauth-worker.git
|
||||
cd 2fauth-worker
|
||||
|
||||
# 1. 给前后端分别安装依赖
|
||||
cd frontend && npm install
|
||||
cd ../backend && npm install
|
||||
npm install
|
||||
|
||||
# 2. 在 backend 目录,复制 example.dev.vars 为 .dev.vars, 按说明写入开发用的测试密钥
|
||||
# 2.复制 example.dev.vars 为 .dev.vars, 按说明写入开发用的测试密钥
|
||||
cp example.dev.vars .dev.vars
|
||||
|
||||
# 3. 初始化本地的 SQLite Sandbox 数据库
|
||||
npx wrangler d1 execute 2fauth-db-dev --local --env dev --file=schema.sql
|
||||
npx wrangler d1 execute 2fauth-db-dev --local --env dev --file=backend/schema.sql
|
||||
|
||||
# 4. 终端启动应用
|
||||
npm run dev
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ 全栈项目架构
|
||||
|
||||
本项目遵循现代化的 **Feature-based 垂直模块化架构**:
|
||||
@@ -192,8 +223,6 @@ npm run dev
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
## 🛡️ 安全性声明 (Security Policy)
|
||||
|
||||
本项目涉及高度敏感的 2FA 认证种子数据的云端存储,在架构安全方面我们做出了如下强制约束:
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
{
|
||||
"name": "2FAuth Worker",
|
||||
"version": "1.0.2",
|
||||
"description": "A Secure 2FA Management Tool running on Cloudflare Workers",
|
||||
"author": "nap0o",
|
||||
"license": "AGPL-3.0",
|
||||
"name": "backend",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "wrangler dev --config wrangler.dev.toml --host 0.0.0.0 --port 8787 --test-scheduled",
|
||||
"dev": "wrangler dev --config ../wrangler.dev.toml --host 0.0.0.0 --port 8787 --test-scheduled",
|
||||
"deploy": "wrangler deploy --minify"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -7,6 +7,8 @@ export interface HealthIssue {
|
||||
level: HealthIssueLevel;
|
||||
message: string;
|
||||
suggestion: string;
|
||||
deploy_by_worker: string;
|
||||
deploy_by_gitaction: string;
|
||||
missingFields?: string[];
|
||||
}
|
||||
|
||||
@@ -28,7 +30,9 @@ export const runHealthCheck = (env: EnvBindings): HealthCheckResult => {
|
||||
field: 'ENCRYPTION_KEY',
|
||||
level: 'critical',
|
||||
message: 'encryption_key_too_short',
|
||||
suggestion: 'encryption_key_suggestion'
|
||||
suggestion: 'encryption_key_suggestion',
|
||||
deploy_by_worker: 'suggestion_deploy_by_worker',
|
||||
deploy_by_gitaction: 'suggestion_deploy_by_gitaction'
|
||||
});
|
||||
} else {
|
||||
passedChecks.push('encryption_key_passed');
|
||||
@@ -41,7 +45,9 @@ export const runHealthCheck = (env: EnvBindings): HealthCheckResult => {
|
||||
field: 'JWT_SECRET',
|
||||
level: 'critical',
|
||||
message: 'jwt_secret_too_short',
|
||||
suggestion: 'jwt_secret_suggestion'
|
||||
suggestion: 'jwt_secret_suggestion',
|
||||
deploy_by_worker: 'suggestion_deploy_by_worker',
|
||||
deploy_by_gitaction: 'suggestion_deploy_by_gitaction'
|
||||
});
|
||||
} else {
|
||||
passedChecks.push('jwt_secret_passed');
|
||||
@@ -53,7 +59,9 @@ export const runHealthCheck = (env: EnvBindings): HealthCheckResult => {
|
||||
field: 'OAUTH_ALLOW_ALL',
|
||||
level: 'critical',
|
||||
message: 'oauth_allow_all_enabled',
|
||||
suggestion: 'oauth_allow_all_suggestion'
|
||||
suggestion: 'oauth_allow_all_suggestion',
|
||||
deploy_by_worker: 'suggestion_deploy_by_worker',
|
||||
deploy_by_gitaction: 'suggestion_deploy_by_gitaction'
|
||||
});
|
||||
} else {
|
||||
passedChecks.push('oauth_allow_all_passed');
|
||||
@@ -66,7 +74,9 @@ export const runHealthCheck = (env: EnvBindings): HealthCheckResult => {
|
||||
field: 'OAUTH_ALLOWED_USERS',
|
||||
level: 'error',
|
||||
message: 'allowed_users_empty',
|
||||
suggestion: 'allowed_users_suggestion'
|
||||
suggestion: 'allowed_users_suggestion',
|
||||
deploy_by_worker: 'suggestion_deploy_by_worker',
|
||||
deploy_by_gitaction: 'suggestion_deploy_by_gitaction'
|
||||
});
|
||||
} else {
|
||||
passedChecks.push('allowed_users_passed');
|
||||
@@ -92,6 +102,8 @@ export const runHealthCheck = (env: EnvBindings): HealthCheckResult => {
|
||||
level: 'error',
|
||||
message: 'github_config_incomplete',
|
||||
suggestion: 'github_config_suggestion',
|
||||
deploy_by_worker: 'suggestion_deploy_by_worker',
|
||||
deploy_by_gitaction: 'suggestion_deploy_by_gitaction',
|
||||
missingFields: missing
|
||||
});
|
||||
} else {
|
||||
@@ -113,6 +125,8 @@ export const runHealthCheck = (env: EnvBindings): HealthCheckResult => {
|
||||
level: 'error',
|
||||
message: 'telegram_config_incomplete',
|
||||
suggestion: 'telegram_config_suggestion',
|
||||
deploy_by_worker: 'suggestion_deploy_by_worker',
|
||||
deploy_by_gitaction: 'suggestion_deploy_by_gitaction',
|
||||
missingFields: missing
|
||||
});
|
||||
} else {
|
||||
@@ -135,6 +149,8 @@ export const runHealthCheck = (env: EnvBindings): HealthCheckResult => {
|
||||
level: 'error',
|
||||
message: 'google_config_incomplete',
|
||||
suggestion: 'google_config_suggestion',
|
||||
deploy_by_worker: 'suggestion_deploy_by_worker',
|
||||
deploy_by_gitaction: 'suggestion_deploy_by_gitaction',
|
||||
missingFields: missing
|
||||
});
|
||||
} else {
|
||||
@@ -157,6 +173,8 @@ export const runHealthCheck = (env: EnvBindings): HealthCheckResult => {
|
||||
level: 'error',
|
||||
message: 'nodeloc_config_incomplete',
|
||||
suggestion: 'nodeloc_config_suggestion',
|
||||
deploy_by_worker: 'suggestion_deploy_by_worker',
|
||||
deploy_by_gitaction: 'suggestion_deploy_by_gitaction',
|
||||
missingFields: missing
|
||||
});
|
||||
} else {
|
||||
@@ -179,6 +197,8 @@ export const runHealthCheck = (env: EnvBindings): HealthCheckResult => {
|
||||
level: 'error',
|
||||
message: 'gitee_config_incomplete',
|
||||
suggestion: 'gitee_config_suggestion',
|
||||
deploy_by_worker: 'suggestion_deploy_by_worker',
|
||||
deploy_by_gitaction: 'suggestion_deploy_by_gitaction',
|
||||
missingFields: missing
|
||||
});
|
||||
} else {
|
||||
@@ -202,6 +222,8 @@ export const runHealthCheck = (env: EnvBindings): HealthCheckResult => {
|
||||
level: 'error',
|
||||
message: 'cloudflare_config_incomplete',
|
||||
suggestion: 'cloudflare_config_suggestion',
|
||||
deploy_by_worker: 'suggestion_deploy_by_worker',
|
||||
deploy_by_gitaction: 'suggestion_deploy_by_gitaction',
|
||||
missingFields: missing
|
||||
});
|
||||
} else {
|
||||
@@ -216,7 +238,9 @@ export const runHealthCheck = (env: EnvBindings): HealthCheckResult => {
|
||||
field: 'NO_OAUTH_PROVIDER',
|
||||
level: 'error',
|
||||
message: 'no_provider_configured',
|
||||
suggestion: 'no_provider_suggestion'
|
||||
suggestion: 'no_provider_suggestion',
|
||||
deploy_by_worker: 'suggestion_deploy_by_worker',
|
||||
deploy_by_gitaction: 'suggestion_deploy_by_gitaction'
|
||||
});
|
||||
} else {
|
||||
passedChecks.push('oauth_provider_configured');
|
||||
|
||||
File renamed without changes.
@@ -38,6 +38,15 @@
|
||||
<div class="suggestion-box">
|
||||
<div class="suggestion-title">{{ $t('healthCheck.how_to_fix') }}</div>
|
||||
|
||||
<div v-if="issue.deploy_by_worker || issue.deploy_by_gitaction" class="deploy-method-guide">
|
||||
<p v-if="issue.deploy_by_worker" class="deploy-item">
|
||||
<el-icon><Monitor /></el-icon> {{ $t(`healthCheck.suggestions.${issue.deploy_by_worker}`) }}
|
||||
</p>
|
||||
<p v-if="issue.deploy_by_gitaction" class="deploy-item">
|
||||
<el-icon><Setting /></el-icon> {{ $t(`healthCheck.suggestions.${issue.deploy_by_gitaction}`) }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 针对短密码的特殊处理:提供一键生成 -->
|
||||
<div v-if="issue.message === 'encryption_key_too_short' || issue.message === 'jwt_secret_too_short'" class="fix-action">
|
||||
<p>{{ $t(`healthCheck.suggestions.${issue.suggestion}`) }}</p>
|
||||
@@ -90,7 +99,7 @@
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { WarningFilled, CopyDocument, Refresh, Document, Select } from '@element-plus/icons-vue'
|
||||
import { WarningFilled, CopyDocument, Refresh, Document, Select, Monitor, Setting } from '@element-plus/icons-vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { request } from '@/shared/utils/request'
|
||||
import { useClipboard } from '@vueuse/core'
|
||||
@@ -292,6 +301,22 @@ onMounted(() => {
|
||||
color: var(--el-text-color-regular);
|
||||
}
|
||||
|
||||
.deploy-method-guide {
|
||||
margin-bottom: 12px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px dashed var(--el-border-color);
|
||||
}
|
||||
|
||||
.deploy-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
margin: 0 0 8px 0;
|
||||
font-size: 13px;
|
||||
color: var(--el-text-color-primary);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.text-danger p {
|
||||
color: var(--el-color-danger);
|
||||
font-weight: bold;
|
||||
|
||||
@@ -546,16 +546,18 @@
|
||||
}
|
||||
},
|
||||
"suggestions": {
|
||||
"encryption_key_suggestion": "Copy the secure 32-byte key we generated locally below. Paste it into your Cloudflare -> Workers -> Settings -> Variables as ENCRYPTION_KEY.",
|
||||
"jwt_secret_suggestion": "Copy the pre-generated cryptographically secure key below. Set it as your JWT_SECRET in Cloudflare.",
|
||||
"oauth_allow_all_suggestion": "Immediate Action Required: Go to your Cloudflare dashboard and delete the OAUTH_ALLOW_ALL variable, or set it to false.",
|
||||
"allowed_users_suggestion": "Add OAUTH_ALLOWED_USERS in your Cloudflare environment variables, and populate it with your GitHub (or respective platform) registered emails, separated by commas.",
|
||||
"github_config_suggestion": "Verify your GitHub OAuth App secrets and keys.",
|
||||
"telegram_config_suggestion": "Check your BotFather configurations and ensure all tokens are present.",
|
||||
"google_config_suggestion": "Visit the Google Cloud Console and verify credentials.",
|
||||
"nodeloc_config_suggestion": "Visit the NodeLoc Developer Dashboard and verify credentials.",
|
||||
"gitee_config_suggestion": "Verify your Gitee developer credentials.",
|
||||
"cloudflare_config_suggestion": "Four parameters are required: Client ID, Secret, Org Domain, and Redirect URI.",
|
||||
"suggestion_deploy_by_worker": "One-click Deploy to Worker: Go to Cloudflare Dashboard -> Workers & Pages -> 2fauth-worker -> Settings -> Variables and Secrets",
|
||||
"suggestion_deploy_by_gitaction": "Deploy via Github Action: Go to Forked Repository -> Settings -> Secrets and variables -> Actions",
|
||||
"encryption_key_suggestion": "Please copy the high-strength random key pre-generated for you below, and set it as ENCRYPTION_KEY according to your deployment method.",
|
||||
"jwt_secret_suggestion": "Please copy the high-strength random key pre-generated for you below, and set it as JWT_SECRET according to your deployment method.",
|
||||
"oauth_allow_all_suggestion": "Depending on your deployment method, remove OAUTH_ALLOW_ALL or set its value to false.",
|
||||
"allowed_users_suggestion": "Depending on your deployment method, fill in OAUTH_ALLOWED_USERS.",
|
||||
"github_config_suggestion": "Depending on your deployment method, fill in OAUTH_GITHUB_CLIENT_ID, OAUTH_GITHUB_CLIENT_SECRET, and OAUTH_GITHUB_REDIRECT_URI.",
|
||||
"telegram_config_suggestion": "Please check the bot configuration in BotFather to ensure the information is complete, and depending on your deployment method, fill in OAUTH_TELEGRAM_BOT_NAME, OAUTH_TELEGRAM_BOT_TOKEN, and OAUTH_TELEGRAM_REDIRECT_URI.",
|
||||
"google_config_suggestion": "Please check the credentials configuration in Google Cloud Console, and depending on your deployment method, fill in OAUTH_GOOGLE_CLIENT_ID, OAUTH_GOOGLE_CLIENT_SECRET, and OAUTH_GOOGLE_REDIRECT_URI.",
|
||||
"nodeloc_config_suggestion": "Please check your developer credentials in NodeLoc panel, and depending on your deployment method, fill in OAUTH_NODELOC_CLIENT_ID, OAUTH_NODELOC_CLIENT_SECRET, and OAUTH_NODELOC_REDIRECT_URI.",
|
||||
"gitee_config_suggestion": "Please verify Gitee developer credentials, and depending on your deployment method, fill in OAUTH_GITEE_CLIENT_ID, OAUTH_GITEE_CLIENT_SECRET, and OAUTH_GITEE_REDIRECT_URI.",
|
||||
"cloudflare_config_suggestion": "Requires four parameters: Client ID, Secret, Org Domain, Redirect URI. Depending on your deployment method, fill in OAUTH_CLOUDFLARE_CLIENT_ID, OAUTH_CLOUDFLARE_CLIENT_SECRET, and OAUTH_CLOUDFLARE_REDIRECT_URI.",
|
||||
"no_provider_suggestion": "Consult the README to pick the easiest provider. Retrieve the Client ID and Secret and configure them in Cloudflare's dashboard."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -546,16 +546,18 @@
|
||||
}
|
||||
},
|
||||
"suggestions": {
|
||||
"encryption_key_suggestion": "请复制下方为您预生成的高强度随机密钥。请立刻前往 Fork的Github仓库 -> Settings -> Secrets and variables -> Actions -> 将其填入 ENCRYPTION_KEY",
|
||||
"jwt_secret_suggestion": "请复制下方为您预生成的高强度随机密钥。请立刻前往 Fork的Github仓库 -> Settings -> Secrets and variables -> Actions -> 将其填入 JWT_SECRET",
|
||||
"oauth_allow_all_suggestion": "请立刻前往 Fork的Github仓库 -> Settings -> Secrets and variables -> Actions,将环境变量 OAUTH_ALLOW_ALL 删除,或将其值修改为 false。",
|
||||
"allowed_users_suggestion": "请在 Fork的Github仓库 -> Settings -> Secrets and variables -> Actions中添加 OAUTH_ALLOWED_USERS,并填入您在 GitHub(或其它平台)上对应的注册邮箱地址(多个以英文逗号分隔)。",
|
||||
"github_config_suggestion": "请核对获取的 GitHub OAuth Apps 密钥信息,然后前往 Fork的Github仓库 -> Settings -> Secrets and variables -> Actions,将环境变量 OAUTH_GITHUB_CLIENT_ID、OAUTH_GITHUB_CLIENT_SECRET、OAUTH_GITHUB_REDIRECT_URI 填入",
|
||||
"telegram_config_suggestion": "请在 BotFather 检查机器人配置,确保信息填全,然后前往 Fork的Github仓库 -> Settings -> Secrets and variables -> Actions,将环境变量 OAUTH_TELEGRAM_BOT_NAME、OAUTH_TELEGRAM_BOT_TOKEN、OAUTH_TELEGRAM_REDIRECT_URI 填入",
|
||||
"google_config_suggestion": "请前往 Google Cloud Console 检查凭据配置,然后前往 Fork的Github仓库 -> Settings -> Secrets and variables -> Actions,将环境变量 OAUTH_GOOGLE_CLIENT_ID、OAUTH_GOOGLE_CLIENT_SECRET、OAUTH_GOOGLE_REDIRECT_URI 填入",
|
||||
"nodeloc_config_suggestion": "请前往 NodeLoc 面板检查您的开发者凭据,然后前往 Fork的Github仓库 -> Settings -> Secrets and variables -> Actions,将环境变量 OAUTH_NODELOC_CLIENT_ID、OAUTH_NODELOC_CLIENT_SECRET、OAUTH_NODELOC_REDIRECT_URI 填入",
|
||||
"gitee_config_suggestion": "请核对 Gitee 开发者凭据,然后前往 Fork的Github仓库 -> Settings -> Secrets and variables -> Actions,将环境变量 OAUTH_GITEE_CLIENT_ID、OAUTH_GITEE_CLIENT_SECRET、OAUTH_GITEE_REDIRECT_URI 填入",
|
||||
"cloudflare_config_suggestion": "需要四位一体的参数:Client ID, Secret, Org Domain, Redirect URI,然后前往 Cloudflare 后台 -> Workers 和 Pages -> 2fauth-worker -> 设置 -> 变量和机密,将环境变量 OAUTH_CLOUDFLARE_CLIENT_ID、OAUTH_CLOUDFLARE_CLIENT_SECRET、OAUTH_CLOUDFLARE_REDIRECT_URI 填入",
|
||||
"suggestion_deploy_by_worker": "Worker 一键部署: 前往 Cloudflare 后台 -> Workers 和 Pages -> 2fauth-worker -> 设置 -> 变量和机密",
|
||||
"suggestion_deploy_by_gitaction": "Github Action部署: 前往 Fork 仓库 -> Settings -> Secrets and variables -> Actions",
|
||||
"encryption_key_suggestion": "请复制下方为您预生成的高强度随机密钥,根据不同部署方式填入,将其填入 ENCRYPTION_KEY 。",
|
||||
"jwt_secret_suggestion": "请复制下方为您预生成的高强度随机密钥,根据不同部署方式填入,将其填入 JWT_SECRET 。",
|
||||
"oauth_allow_all_suggestion": "根据不同部署方式,将 OAUTH_ALLOW_ALL 删除将其值修改为 false。",
|
||||
"allowed_users_suggestion": "根据不同部署方式,填入 OAUTH_ALLOWED_USERS ",
|
||||
"github_config_suggestion": "根据不同部署方式,填入 OAUTH_GITHUB_CLIENT_ID、OAUTH_GITHUB_CLIENT_SECRET、OAUTH_GITHUB_REDIRECT_URI ",
|
||||
"telegram_config_suggestion": "请在 BotFather 检查机器人配置,确保信息填全,根据不同部署方式,填入 OAUTH_TELEGRAM_BOT_NAME、OAUTH_TELEGRAM_BOT_TOKEN、OAUTH_TELEGRAM_REDIRECT_URI",
|
||||
"google_config_suggestion": "请前往 Google Cloud Console 检查凭据配置,根据不同部署方式,填入 OAUTH_GOOGLE_CLIENT_ID、OAUTH_GOOGLE_CLIENT_SECRET、OAUTH_GOOGLE_REDIRECT_URI",
|
||||
"nodeloc_config_suggestion": "请前往 NodeLoc 面板检查您的开发者凭据,根据不同部署方式,填入 OAUTH_NODELOC_CLIENT_ID、OAUTH_NODELOC_CLIENT_SECRET、OAUTH_NODELOC_REDIRECT_URI 填入",
|
||||
"gitee_config_suggestion": "请核对 Gitee 开发者凭据,,根据不同部署方式,填入 OAUTH_GITEE_CLIENT_ID、OAUTH_GITEE_CLIENT_SECRET、OAUTH_GITEE_REDIRECT_URI 填入",
|
||||
"cloudflare_config_suggestion": "需要四位一体的参数:Client ID, Secret, Org Domain, Redirect URI,根据不同部署方式,填入 OAUTH_CLOUDFLARE_CLIENT_ID、OAUTH_CLOUDFLARE_CLIENT_SECRET、OAUTH_CLOUDFLARE_REDIRECT_URI 填入",
|
||||
"no_provider_suggestion": "阅读2Fauth Worker 的 README,挑选最适合您且配置最简单的一种登录方式,按照其说明获取 Client ID 与 Secret 并在云端配置。"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "2fauth-worker",
|
||||
"version": "1.0.2",
|
||||
"description": "A Secure 2FA Management Tool running on Cloudflare Workers",
|
||||
"author": "nap0o",
|
||||
"license": "AGPL-3.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"postinstall": "npm install --prefix backend && npm install --prefix frontend",
|
||||
"dev": "npm run dev --prefix backend & npm run dev --prefix frontend"
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
name = "2fauth-worker-demo"
|
||||
main = "src/app/index.ts"
|
||||
main = "backend/src/app/index.ts"
|
||||
compatibility_date = "2024-04-01"
|
||||
compatibility_flags = ["nodejs_compat"]
|
||||
|
||||
[assets]
|
||||
binding = "ASSETS"
|
||||
directory = "../frontend/dist"
|
||||
directory = "frontend/dist"
|
||||
not_found_handling = "single-page-application"
|
||||
run_worker_first = true
|
||||
|
||||
[build]
|
||||
command = "npm run build --prefix ../frontend"
|
||||
command = "npm run build --prefix frontend"
|
||||
|
||||
[[d1_databases]]
|
||||
binding = "DB"
|
||||
@@ -1,11 +1,11 @@
|
||||
name = "2fauth-worker-dev"
|
||||
main = "src/app/index.ts"
|
||||
main = "backend/src/app/index.ts"
|
||||
compatibility_date = "2024-04-01"
|
||||
compatibility_flags = ["nodejs_compat"]
|
||||
|
||||
[assets]
|
||||
binding = "ASSETS"
|
||||
directory = "../frontend/dist"
|
||||
directory = "frontend/dist"
|
||||
not_found_handling = "single-page-application"
|
||||
run_worker_first = true
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
name = "2fauth-worker"
|
||||
main = "src/app/index.ts"
|
||||
main = "backend/src/app/index.ts"
|
||||
compatibility_date = "2024-04-01"
|
||||
compatibility_flags = ["nodejs_compat"]
|
||||
|
||||
[assets]
|
||||
binding = "ASSETS"
|
||||
directory = "../frontend/dist"
|
||||
directory = "frontend/dist"
|
||||
not_found_handling = "single-page-application"
|
||||
run_worker_first = false
|
||||
run_worker_first = true
|
||||
|
||||
[build]
|
||||
command = "npm run build --prefix ../frontend"
|
||||
command = "npm run build --prefix frontend"
|
||||
|
||||
[[d1_databases]]
|
||||
binding = "DB"
|
||||
@@ -24,4 +24,4 @@ crons = ["0 2 * * *"]
|
||||
[[env.dev.d1_databases]]
|
||||
binding = "DB"
|
||||
database_name = "2fauth-db-dev"
|
||||
database_id = "9e591736-5dbb-4f09-8769-0270d7257731"
|
||||
database_id = "9e591736-5dbb-4f09-8769-0270d7257731"
|
||||
Reference in new issue
Block a user