feat: replace provider-specific AI clients with unified callProvider()

- Remove callGemini() / callOpenAI() and all Gemini/OpenAI-specific env
    vars (GoogleAPIKey, GeminiModel, OpenAIAPIKey, OpenAIBaseUrl, OpenAIModel)
  - Add callProvider() supporting three API formats:
      openai      → POST /v1/chat/completions
      responses   → POST /v1/responses
      anthropic   → POST /v1/messages (x-api-key, anthropic-version header)
  - New env vars: AI_BASE_URL, AI_API_KEY, AI_API_FORMAT, AI_MODEL
  - Optional fallback provider via AI_FALLBACK_* (all four or none);
    primary retries 3×, fallback attempts 1×
  - Update wrangler.toml.example* to new var schema (default: Gemini OpenAI-compat)
  - Update README, README_CN, AGENTS.md, CLAUDE.md accordingly
This commit is contained in:
TooonyChen committed 2026-03-15 03:29:29 +13:00
1 parent 4835e758a4
commit c0e838b780
7 files changed
+471 -547

No files matched your search

+24
View File
@@ -42,6 +42,30 @@ pnpm run dev
- **`ASSETS` binding name must be `ASSETS`** (matched in `src/index.ts` as `env.ASSETS`).
- **Base64 MIME parts** are decoded with `atob()` in `extractMailBodies()`. Cloudflare Workers runtime supports `atob`/`btoa`.
## AI Provider Configuration
The AI layer uses a single unified function `callProvider(config: ProviderConfig, prompt)` in `src/index.ts`. All provider-specific logic is contained there — do not add new per-provider methods.
**Env vars (set in `wrangler.toml` `[vars]` or as Cloudflare Secrets):**
| Variable | Required | Description |
|---|---|---|
| `AI_BASE_URL` | ✅ | Provider base URL, no trailing slash |
| `AI_API_KEY` | ✅ | API key (use Secret in production) |
| `AI_API_FORMAT` | ✅ | `openai` \| `responses` \| `anthropic` |
| `AI_MODEL` | ✅ | Model ID |
| `AI_FALLBACK_BASE_URL` | optional | Fallback provider base URL |
| `AI_FALLBACK_API_KEY` | optional | Fallback API key |
| `AI_FALLBACK_API_FORMAT` | optional | Fallback format |
| `AI_FALLBACK_MODEL` | optional | Fallback model ID |
Fallback is only active when all four `AI_FALLBACK_*` vars are set. Primary retries 3× before fallback is attempted (1×).
**Format → endpoint mapping:**
- `openai` → `POST /v1/chat/completions` (OpenAI, Gemini OpenAI-compat, DeepSeek, Groq, …)
- `responses` → `POST /v1/responses` (OpenAI Responses API)
- `anthropic` → `POST /v1/messages` (Anthropic Claude direct; sends `anthropic-version: 2023-06-01`, `max_tokens: 1024`)
## Coding Style
- Tabs, LF, UTF-8, no trailing whitespace (see `.editorconfig`).
+6
View File
@@ -23,6 +23,12 @@ pnpm run test
3. `src/index.html` is a live fallback. Do not delete.
4. DOMPurify + sandboxed iframe on all email HTML rendering. Do not relax.
5. Basic Auth gate is first in `WorkerEntrypoint.fetch()`. Do not move or bypass.
6. All AI calls go through `callProvider()`. Do not add per-provider methods alongside it.
## AI Provider Env Vars
Required: `AI_BASE_URL`, `AI_API_KEY`, `AI_API_FORMAT` (`openai`|`responses`|`anthropic`), `AI_MODEL`.
Optional fallback: `AI_FALLBACK_BASE_URL`, `AI_FALLBACK_API_KEY`, `AI_FALLBACK_API_FORMAT`, `AI_FALLBACK_MODEL` (all four or none).
## Frontend Component Rules
+127 -157
View File
@@ -2,10 +2,9 @@
[English](https://github.com/TooonyChen/AuthInbox/blob/main/README.md) | [简体中文](https://github.com/TooonyChen/AuthInbox/blob/main/README_CN.md)
**Auth Inbox** is an open-source project that securely receive and views authentication emails using [Cloudflare](https://cloudflare.com/)'s free serverless services. It automatically processes incoming emails, extracts verification codes or links, and stores them in a database. A user-friendly web interface is provided for administrators to easily review the extracted information. AuthInbox also supports real-time notifications via Bark, making it a comprehensive and hassle-free solution for email authentication management.
Don't wanna receive ads and spams on your main email? Want a bunch of alternative email for register services and websites? Try this **secure**, **serverless**, **light** service!
**Auth Inbox** is an open-source, self-hosted email verification code platform built on [Cloudflare](https://cloudflare.com/)'s free serverless services. It automatically processes incoming emails, filters out promotional mail before hitting the AI, extracts verification codes or links, and stores them in a database. A modern React dashboard lets administrators review extracted codes, inspect raw emails, and render HTML email previews — all protected by Basic Auth.
Don't want ads and spam in your main inbox? Need a bunch of alternative addresses for signups? Try this **secure**, **serverless**, **lightweight** service!
[![Deploy to Cloudflare Workers](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/TooonyChen/AuthInbox)
@@ -15,205 +14,177 @@ Don't wanna receive ads and spams on your main email? Want a bunch of alternativ
## Table of Contents 📑
- [Features](#features)
- [Technologies Used](#technologies-used)
- [Installation](#installation)
- [License](#license)
- [Screenshots](#Screenshots)
- [Features](#features-)
- [Technologies Used](#technologies-used-)
- [Installation](#installation-)
- [License](#license-)
- [Screenshots](#screenshots-)
---
## Features ✨
- **Email Processing**: Automatically captures and stores incoming emails.
- **Code Extraction**: Utilizes AI to extract verification codes, links, and organization names from emails.
- **Secure Front-End**: Provides a web interface protected by Basic Access Authentication for viewing extracted codes.
- **Real-Time Notifications**: Optionally sends notifications via Bark when new codes are extracted.
- **Database Integration**: Stores raw and processed email data in a Cloudflare D1 Database.
- **Promotional Filter**: Detects and skips bulk/marketing emails via headers (`List-Unsubscribe`, `Precedence: bulk`, etc.) before calling the AI — saves tokens.
- **AI Code Extraction**: Uses Google Gemini (with OpenAI as fallback) to extract verification codes, links, and organization names.
- **Modern Dashboard**: React 18 + shadcn/ui interface with mail list, detail panel, and three tabs — Extracted, Raw Email, Rendered HTML preview.
- **Safe HTML Preview**: Email HTML is sanitized with DOMPurify and rendered in a sandboxed iframe.
- **One-click Copy**: Verification codes and links have copy buttons with toast confirmation.
- **Real-Time Notifications**: Optionally sends Bark push notifications when new codes arrive.
- **Database**: Stores all raw emails and AI-extracted results in Cloudflare D1.
---
## Technologies Used 🛠️
- **Cloudflare Workers**: Serverless platform for handling email processing and web requests.
- **Cloudflare D1**: Cloudflare's serverless SQL database for storing email data.
- **TypeScript**: Strongly typed programming language for robust and maintainable code.
- **Google AI Studio API**: Utilized for extracting relevant information from emails with optimized AI prompts to enhance data accuracy and reliability.
- **AI Prompt Optimization**: Custom-crafted prompts ensure precise extraction of titles, codes, and topics from varied email formats.
- **Bark API**: Optional integration for sending real-time notifications.
- **HTML/CSS**: Front-end interface with responsive and modern design.
---
## AI Prompt Optimization 🧠
To ensure accurate extraction of information from incoming emails, we've implemented AI prompt optimization using the Google AI Studio API. By crafting precise and context-aware prompts, the AI can reliably identify and extract key elements such as:
- **Organization Name (Title)**: Identifies the sender's organization or company.
- **Verification Code/Link**: Extracts codes, links, or passwords necessary for account verification.
- **Email Topic**: Summarizes the main purpose of the email, such as 'account verification' or 'password reset'.
**Prompt:**
```plaintext
Email content: [Insert raw email content here].
Please read the email and extract the following information:
1. Code/Link/Password from the email (if available).
2. Organization name (title) from which the email is sent.
3. A brief summary of the email's topic (e.g., 'account verification').
Format the output as JSON with this structure:
{
"title": "The organization or company that sent the verification code (e.g., 'Netflix')",
"code": "The extracted verification code, link, or password (e.g., '123456' or 'https://example.com/verify?code=123456')",
"topic": "A brief summary of the email's topic (e.g., 'account verification')",
"codeExist": 1
}
If both a code and a link are present, include both in the 'code' field like this:
"code": "code, link"
If there is no code, clickable link, or this is an advertisement email, return:
{
"codeExist": 0
}
```
- **Cloudflare Workers** — Serverless runtime for email handling and API.
- **Cloudflare D1** — SQLite-compatible serverless database.
- **Cloudflare Email Routing** — Routes incoming emails to the Worker.
- **React 18 + Vite + Tailwind CSS + shadcn/ui** — Frontend dashboard.
- **Any OpenAI-compatible / Anthropic AI provider** — Configurable via env vars; Gemini, OpenAI, DeepSeek, Groq, Anthropic, and more.
- **Bark API** — Optional iOS push notifications.
- **TypeScript** — End-to-end type safety.
---
## Installation ⚙️
0. **Prerequisites**
- Create a [Google AI Studio API](https://aistudio.google.com/)
### Prerequisites
- Bind a domain to your [Cloudflare](https://dash.cloudflare.com/) account
- A [Google AI Studio API key](https://aistudio.google.com/)
- A domain bound to your [Cloudflare](https://dash.cloudflare.com/) account
- Cloudflare Account ID and API Token from [here](https://dash.cloudflare.com/profile/api-tokens)
- *(Optional)* [Bark App](https://bark.day.app/) for iOS push notifications
- Get Your Cloudflare Account ID from [here](https://dash.cloudflare.com/profile)
---
- Generate a Cloudflare Workers API Token from [here](https://dash.cloudflare.com/profile/api-tokens)
### Option 1 — Deploy via GitHub Actions
- (Optional) Download the [Bark App](https://bark.day.app/) and get a Bark Token from the App
1. **Create D1 Database**
1. **Install using Github Pages**
Go to [Cloudflare Dashboard](https://dash.cloudflare.com/) → `Workers & Pages` → `D1 SQL Database` → `Create`. Name it `inbox-d1`.
1. **Creating D1 Database**
Open the database → `Console`, paste and run the contents of [`db/schema.sql`](https://github.com/TooonyChen/AuthInbox/blob/main/db/schema.sql).
1. Go to [Cloudflare Dashboard](https://dash.cloudflare.com/) -> `Workers & Pages` -> `D1 SQL Database` -> `Create`
Copy the `database_id` for the next step.
2. Input Name `inbox-d1` and click `Create`
2. **Fork & Deploy**
3. After Creating `inbox-d1`, click in to it and find `Console`
[![Deploy to Cloudflare Workers](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/TooonyChen/AuthInbox)
4. Execute the following SQL command from [db/schema.sql](https://github.com/TooonyChen/AuthInbox/blob/main/db/schema.sql) in the console, just copy, paste and execute it.
In your forked repository, go to `Settings` → `Secrets and variables` → `Actions` and add:
- `CLOUDFLARE_ACCOUNT_ID`
- `CLOUDFLARE_API_TOKEN`
- `TOML` — use the [comment-free template](https://github.com/TooonyChen/AuthInbox/blob/main/wrangler.toml.example.clear) to avoid parse errors.
5. Copy the `database_id` and `database_name` for the next step when you configure the `TOML` file
Then go to `Actions` → `Deploy Auth Inbox to Cloudflare Workers` → `Run workflow`.
2. **Deploy the Cloudflare Worker**
After success, **delete the workflow logs** to avoid leaking your config.
[![Deploy to Cloudflare Workers](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/TooonyChen/AuthInbox)
3. Jump to [Set Email Forwarding](#3-set-email-forwarding-).
1. Click the button on the top to fork this repository or directly fork this repository.
---
2. Open the repository that you fork, find the `Actions` page, find `Deploy Auth Inbox to Cloudflare Workers`, and click `enable workflow` to activate the workflows.
### Option 2 — Deploy via CLI
3. Then, in the repository page, navigate to `Settings` -> `Secrets and variables` -> `Actions` -> `Repository secrets` and add the following secrets:
- `CLOUDFLARE_ACCOUNT_ID`: Cloudflare account ID.
- `CLOUDFLARE_API_TOKEN`: Cloudflare API Token.
- `TOML`: Configuration file, refer to [wrangler.toml with comments](https://github.com/TooonyChen/AuthInbox/blob/main/wrangler.toml.example). **Please use the [version without comments](https://github.com/TooonyChen/AuthInbox/blob/main/wrangler.toml.example.clear) in adding the secret in case of unknown errors.**
1. **Clone and install**
4. Back to `Action` Page in your repository, find `Deploy Auth Inbox to Cloudflare Workers` and press `Run workflow` to deploy the worker. If you can't find the `Run workflow` button, you can trigger GitHub Actions automatically by making a small edit to the `readme.md` file in your repository.
```bash
git clone https://github.com/TooonyChen/AuthInbox.git
cd AuthInbox
pnpm install
```
5. After the deployment is successful, you can find the URL of your worker in the `Deploy Auth Inbox to Cloudflare Workers` workflow log.
2. **Create D1 database**
6. Find the `delete all logs` button in the upper-right corner of the workflow logs page. Delete the logs to avoid data leakage.
```bash
pnpm wrangler d1 create inbox-d1
pnpm wrangler d1 execute inbox-d1 --remote --file=./db/schema.sql
```
7. Done! ✅ Jump to step 3: Set Email Forwarding.
Copy the `database_id` from the output.
3. **Configure**
2. **Install using command-line**
```bash
cp wrangler.toml.example wrangler.toml
```
1. **Initialization**
Edit `wrangler.toml` — at minimum fill in:
```bash
npm install wrangler -g # install wrangler
git clone https://github.com/TooonyChen/AuthInbox.git # clone the repository
cd AuthInbox # change directory
npm install # install dependencies
npm --prefix web install # install dashboard dependencies
```
```toml
[vars]
FrontEndAdminID = "your-username"
FrontEndAdminPassword = "your-password"
UseBark = "false"
2. **create d1 database**
# AI provider — choose any compatible service
AI_BASE_URL = "https://generativelanguage.googleapis.com/v1beta/openai"
AI_API_KEY = "your-api-key"
AI_API_FORMAT = "openai"
AI_MODEL = "gemini-2.0-flash"
When you execute the [Wrangler](https://developers.cloudflare.com/workers/wrangler/get-started/) login command for the first time, you will be prompted to log in. Just follow the prompts.
[[d1_databases]]
binding = "DB"
database_name = "inbox-d1"
database_id = "<your-database-id>"
```
```bash
npx wrangler d1 create inbox-d1 # creating a d1 database called 'inbox-d1'
npx wrangler d1 execute inbox-d1 --remote --file=./db/schema.sql # write the schema.sql to the database
```
you will get the result like this:
```bash
✅ Successfully created DB 'inbox-d1'
**`AI_API_FORMAT`** options:
[[d1_databases]]
binding = "DB" # available in your Worker on env.DB
database_name = "inbox-d1"
database_id = "<unique-ID-for-your-database>"
```
please copy the result from your terminal, you will use them in the next step
| Value | Endpoint | Compatible providers |
|---|---|---|
| `openai` | `/v1/chat/completions` | OpenAI, Gemini (OpenAI-compat), DeepSeek, Groq, Mistral, … |
| `responses` | `/v1/responses` | OpenAI Responses API |
| `anthropic` | `/v1/messages` | Anthropic Claude |
3. **Configure Environment Variables**
**Common `AI_BASE_URL` values:**
```
OpenAI: https://api.openai.com
Gemini (OAI-compat): https://generativelanguage.googleapis.com/v1beta/openai
Anthropic: https://api.anthropic.com
DeepSeek: https://api.deepseek.com
Groq: https://api.groq.com/openai
```
Use `wrangler.toml` file in the project root with the necessary environment variables:
**Optional fallback provider** (triggered if primary fails after 3 retries):
```toml
# AI_FALLBACK_BASE_URL = "https://api.openai.com"
# AI_FALLBACK_API_KEY = "your-fallback-key"
# AI_FALLBACK_API_FORMAT = "openai"
# AI_FALLBACK_MODEL = "gpt-4o-mini"
```
```toml
name = "auth-inbox"
type = "typescript"
Optional Bark vars: `barkTokens`, `barkUrl`.
[vars]
UseBark = "true" # set 'true' to use or 'false' to not use
barkUrl = "https://api.day.app"
barkTokens = "[token1, token2]" # set to your bark tokens on your iOS device, download it from https://bark.day.app/, you can use multiple tokens, if you only use one, then set it to '[token1]'
FrontEndAdminID = "admin" # your login
FrontEndAdminPassword = "password" # your password
GoogleAPIKey = "xxxxxxxxxxx" # your google api, go to https://aistudio.google.com/ to generate one if u dont have
4. **Build and deploy**
[[d1_databases]] # Copy the lines obtained from step 2 from your terminal.
binding = "DB"
database_name = "inbox-d1" # Copy from step 2
database_id = "<unique-ID-for-your-database>" # Copy from step 2
```
```bash
pnpm run deploy
```
4. **Deploy your own worker** 🌐
Deploy your Worker to make your project accessible on the Internet. Run:
```bash
npm run build:web
npx wrangler deploy
```
You will get output like this:
```
Outputs: https://auth-inbox.<YOUR_SUBDOMAIN>.workers.dev
```
You can now visit the URL for your newly depolyed Auth Inbox for checking the email results.
Output: `https://auth-inbox.<your-subdomain>.workers.dev`
3. **Set Email Forwarding** ✉️
---
Go to [Cloudflare Dashboard](https://dash.cloudflare.com/) -> `Websites` -> `<your-domain>` -> `Email` -> `Email-Routing` -> `Routing Rules`
### 3. Set Email Forwarding ✉️
if you want to use `catch-all address`:
![image](https://github.com/user-attachments/assets/53e5a939-6b03-4ca6-826a-7a5f02f361ac)
Go to [Cloudflare Dashboard](https://dash.cloudflare.com/) → `Websites` → `<your-domain>` → `Email` → `Email Routing` → `Routing Rules`.
if you want to use `custom address`:
![image](https://github.com/user-attachments/assets/b0d0ab94-c2ad-4870-ac08-d53e64b2c880)
**Catch-all** (forwards all addresses to the Worker):
![image](https://github.com/user-attachments/assets/53e5a939-6b03-4ca6-826a-7a5f02f361ac)
4. **Enjoy!** 🎉
**Custom address** (forwards a specific address):
![image](https://github.com/user-attachments/assets/b0d0ab94-c2ad-4870-ac08-d53e64b2c880)
All set! ✅ You can now receive and view your authentication emails securely and efficiently using Auth Inbox!
### 4. Done! 🎉
Visit your Worker URL, log in with the credentials you set, and start receiving verification emails.
---
## License 📜
This project is licensed under the [MIT License](LICENSE).
[MIT License](LICENSE)
---
@@ -221,27 +192,26 @@ This project is licensed under the [MIT License](LICENSE).
![image](https://github.com/user-attachments/assets/ec14d226-ae82-4689-b44d-22850002c34c)
---
## Acknowledgements 🙏
- **Cloudflare Workers** for providing a powerful serverless platform.
- **Google Gemini AI** for enabling intelligent email content extraction.
- **Bark** for real-time notification capabilities.
- **Open Source Community** for inspiring and supporting projects like AuthInbox.
- **ChatGPT** for helping me writing some of the code.
- **Cloudflare Workers** for the serverless platform.
- **Google Gemini AI** for intelligent email extraction.
- **Bark** for real-time notifications.
- **shadcn/ui** for the component library.
- **Open Source Community** for the inspiration.
---
## TODO 📝
- [x] **Github Pages Deployment**: Automatically deploy to Cloudflare Workers via Github Actions.
- [ ] **Regular Expressions**: Using regular expressions instead of Google Gemini AI for privacy, make Gemini AI to an optional function.
- [ ] **Multi-User Support**: Add functionality to manage multiple users for increased flexibility and broader usage.
- [ ] **Enhance Front-End Design**: Improve the UI/UX of the web interface for a more modern and user-friendly experience.
- [ ] **API**: Add API functionality.
- [ ] **Sending Email**: Add the ability to send emails.
- [ ] **More Notification Methods**: Support additional notification methods such as email, Slack, etc.
---
- [x] GitHub Actions deployment
- [x] OpenAI fallback support
- [x] React dashboard with shadcn/ui
- [x] Promotional email filter (skip ads before LLM)
- [x] Raw email viewer + sandboxed HTML preview
- [ ] Regex-based extraction as an AI-free option
- [ ] Multi-user support
- [ ] More notification methods (Slack, webhook, etc.)
- [ ] Sending email support
+132 -159
View File
@@ -2,9 +2,11 @@
[English](https://github.com/TooonyChen/AuthInbox/blob/main/README.md) | [简体中文](https://github.com/TooonyChen/AuthInbox/blob/main/README_CN.md)
**Auth Inbox** 是一个自建的开源多邮箱验证码的接码平台,基于 [Cloudflare](https://cloudflare.com/) 的免费服务。它可以自动处理收到的邮件,提取验证码或链接,并将其存储在数据库中。管理员可以通过一个用户友好的网页界面轻松查看提取的信息。AuthInbox 还支持通过 Bark 进行实时通知,使其成为一个全面且省心的邮件认证管理解决方案。
**Auth Inbox** 是一个开源的自建验证码接码平台,基于 [Cloudflare](https://cloudflare.com/) 的免费无服务器服务。它会自动处理收到的邮件,在调用 AI 之前过滤掉广告邮件,提取验证码或链接并存入数据库。现代化的 React 仪表盘让管理员可以查看提取的验证码、查看原始邮件内容、渲染 HTML 邮件预览,全程受 Basic Auth 保护。
不想在主邮箱中收到广告和垃圾邮件?想要多个备用邮箱用于注册服务和网站?试试这个吧!
不想在主邮箱中收到广告和垃圾邮件?需要多个备用邮箱用于注册各类服务?试试这个**安全**、**无服务器**、**轻量**的解决方案吧!
[![Deploy to Cloudflare Workers](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/TooonyChen/AuthInbox)
![框架](https://github.com/user-attachments/assets/f2ea1ef1-b500-4e47-88ba-a00d19f99595)
@@ -12,208 +14,177 @@
## 目录 📑
- [功能](#features)
- [使用的技术](#technologies-used)
- [安装](#installation)
- [许可证](#license)
- [截图](#Screenshots)
- [功能](#功能-)
- [使用的技术](#使用的技术-)
- [安装](#安装-)
- [许可证](#许可证-)
- [截图](#截图-)
---
## 功能 ✨
- **邮件处理**:自动捕获和存储收到的邮件。
- **验证码提取**:利用 AI 从邮件中提取验证码、链接和组织名称。
- **安全前端**:提供受 Basic Access Authentication 保护的网页界面,用于查看提取的验证码。
- **实时通知**:当提取到新的验证码时,可选通过 Bark 发送通知。
- **数据库集成**:将原始和处理过的邮件数据存储在 D1Database 中。
- **广告过滤**:通过邮件头(`List-Unsubscribe`、`Precedence: bulk` 等)识别并跳过营销邮件,不调用 AI,节省 token。
- **AI 验证码提取**:使用 Google Gemini(OpenAI 作为备用)提取验证码、链接和组织名称。
- **现代化仪表盘**:React 18 + shadcn/ui 界面,包含邮件列表、详情面板和三个 Tab —— 提取内容、原始邮件、HTML 渲染预览。
- **安全 HTML 预览**:邮件 HTML 经 DOMPurify 净化后在沙箱 iframe 中渲染。
- **一键复制**:验证码和链接均有复制按钮,复制后有 Toast 提示。
- **实时通知**:可选接入 Bark,收到新验证码时推送到 iOS 设备。
- **数据库存储**:所有原始邮件和 AI 提取结果均存入 Cloudflare D1。
---
## 使用的技术 🛠️
- **Cloudflare Workers**: 无服务器平台,用于处理邮件处理和Web请求。
- **Cloudflare D1**: Cloudflare的无服务器SQL数据库,用于存储邮件数据。
- **TypeScript**: 强类型编程语言,确保代码的稳健性和可维护性。
- **AI 提示词优化**: 定制的提示确保从多种邮件格式中精确提取标题、代码和主题。
- **Google AI Studio API**: 利用优化的AI提示从邮件中提取相关信息,以提升数据的准确性和可靠性。
- **Bark API**: 可选集成,用于发送实时通知。
- **HTML/CSS**: 前端界面,具有响应式和现代化设计。
- **Google Fonts**: 通过一致的排版增强Web界面的视觉吸引力。
---
## AI 提示词优化 🧠
为了确保从收到的电子邮件中准确提取信息,我们使用Google AI Studio API实施了AI提示优化。通过设计精确且具有上下文意识的提示,AI可以可靠地识别和提取关键要素,如:
- **组织名称(标题)**: 识别发件人的组织或公司。
- **验证码/链接**: 提取账户验证所需的代码、链接或密码。
- **电子邮件主题**: 总结电子邮件的主要目的,例如“账户验证”或“密码重置”。
**提示词如下:**
```plaintext
Email content: [Insert raw email content here].
Please read the email and extract the following information:
1. Code/Link/Password from the email (if available).
2. Organization name (title) from which the email is sent.
3. A brief summary of the email's topic (e.g., 'account verification').
Format the output as JSON with this structure:
{
"title": "The organization or company that sent the verification code (e.g., 'Netflix')",
"code": "The extracted verification code, link, or password (e.g., '123456' or 'https://example.com/verify?code=123456')",
"topic": "A brief summary of the email's topic (e.g., 'account verification')",
"codeExist": 1
}
If both a code and a link are present, include both in the 'code' field like this:
"code": "code, link"
If there is no code, clickable link, or this is an advertisement email, return:
{
"codeExist": 0
}
```
- **Cloudflare Workers** — 处理邮件和 API 的无服务器运行时。
- **Cloudflare D1** — 兼容 SQLite 的无服务器数据库。
- **Cloudflare Email Routing** — 将收到的邮件路由到 Worker。
- **React 18 + Vite + Tailwind CSS + shadcn/ui** — 前端仪表盘。
- **任意 OpenAI 兼容 / Anthropic AI 提供商** — 通过环境变量配置,支持 Gemini、OpenAI、DeepSeek、Groq、Anthropic 等。
- **Bark API** — iOS 实时推送通知(可选)。
- **TypeScript** — 端到端类型安全。
---
## 安装 ⚙️
0. **先决条件**
- 创建一个 [Google AI Studio API](https://aistudio.google.com/)
### 先决条件
- 在你的 [Cloudflare](https://dash.cloudflare.com/) 账户上绑定一个域名
- 一个 [Google AI Studio API Key](https://aistudio.google.com/)
- 在 [Cloudflare](https://dash.cloudflare.com/) 账户上绑定一个域名
- Cloudflare 账户 ID 和 API Token,可在 [此处](https://dash.cloudflare.com/profile/api-tokens) 获取
- *(可选)* [Bark App](https://bark.day.app/),用于 iOS 推送通知
- 获取你的Cloudflare 账户 ID, 可在 [Cloudflare API Tokens](https://dash.cloudflare.com/profile/api-tokens) 中找到
---
- 获取你的 Cloudflare API Token,可在 [Cloudflare API Tokens](https://dash.cloudflare.com/profile/api-tokens) 中找到
### 方式一 — 通过 GitHub Actions 部署
- (可选)下载[Bark App](https://bark.day.app/),在App中获得一个Bark Token
1. **创建 D1 数据库**
1. **使用 Github Pages 进行安装**
进入 [Cloudflare 仪表盘](https://dash.cloudflare.com/) → `Workers & Pages` → `D1 SQL Database` → `Create`,名称填 `inbox-d1`。
1. **创建 D1 数据库**
点击进入数据库 → `Console`,粘贴并执行 [`db/schema.sql`](https://github.com/TooonyChen/AuthInbox/blob/main/db/schema.sql) 中的内容。
1. 进入 [Cloudflare 仪表盘](https://dash.cloudflare.com/) -> `Workers & Pages` -> `D1 SQL Database` -> `Create`
复制 `database_id`,下一步会用到。
2. 输入名称 `inbox-d1` 并点击 `Create`
2. **Fork 并部署**
3. 创建 `inbox-d1` 后,点击进入并找到 `Console`
[![Deploy to Cloudflare Workers](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/TooonyChen/AuthInbox)
4. 在控制台中执行 [db/schema.sql](https://github.com/TooonyChen/AuthInbox/blob/main/db/schema.sql) 中的 SQL 命令,直接复制、粘贴并执行它。
在你 Fork 的仓库中,进入 `Settings` → `Secrets and variables` → `Actions`,添加以下 Secrets:
- `CLOUDFLARE_ACCOUNT_ID`
- `CLOUDFLARE_API_TOKEN`
- `TOML` — 使用[不带注释的模板](https://github.com/TooonyChen/AuthInbox/blob/main/wrangler.toml.example.clear),避免解析报错。
5. 复制 `database_id` 和 `database_name`,用于下一步配置 `TOML` 文件时使用
然后进入 `Actions` → `Deploy Auth Inbox to Cloudflare Workers` → `Run workflow`。
2. **部署 Cloudflare Worker**
部署成功后,**务必删除 workflow 日志**,防止配置信息泄露。
[![Deploy to Cloudflare Workers](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/TooonyChen/AuthInbox)
3. 跳转到[设置邮件转发](#3-设置邮件转发-)。
1. 点击上方按钮 fork 此仓库,或直接 fork 此仓库。
---
2. 打开你 fork 的仓库,找到 `Actions` 页面,找到 `Deploy Auth Inbox to Cloudflare Workers`,并点击 `enable workflow` 激活 workflow。
### 方式二 — 通过命令行部署
3. 然后,在仓库页面中,导航到 `Settings` -> `Secrets and variables` -> `Actions` -> `Repository secrets`,并添加以下 secrets:
- `CLOUDFLARE_ACCOUNT_ID`: Cloudflare 账户 ID。
- `CLOUDFLARE_API_TOKEN`: Cloudflare API Token。
- `TOML`: 配置文件,参考 [带有注释的wrangler.toml](https://github.com/TooonyChen/AuthInbox/blob/main/wrangler.toml.example)。**添加至secrets时,请使用[不带注释的版本](https://github.com/TooonyChen/AuthInbox/blob/main/wrangler.toml.example.clear)来避免奇怪的报错。**
1. **克隆并安装依赖**
4. 返回你仓库的 `Actions` 页面,找到 `Deploy Auth Inbox to Cloudflare Workers`,并按 `Run workflow` 来部署 worker。如果没找到 `Run workflow` 按钮,请你随便修改一下repo中的`readme.md`文件,让Github Actions自动运行。
```bash
git clone https://github.com/TooonyChen/AuthInbox.git
cd AuthInbox
pnpm install
```
5. 部署成功后,你可以在 `Deploy Auth Inbox to Cloudflare Workers` 的 workflow 日志中找到你的 worker URL。
2. **创建 D1 数据库**
6. 在 workflow 日志页面的右上角找到 `delete all logs` 来删除日志,删除它来避免数据外泄。
```bash
pnpm wrangler d1 create inbox-d1
pnpm wrangler d1 execute inbox-d1 --remote --file=./db/schema.sql
```
7. 完成!✅ 请前往第三步:设置邮件转发。
复制输出中的 `database_id`。
2. **使用 Wrangler 命令行部署到 Cloudflare Workers**
3. **配置**
1. **初始化**
```bash
cp wrangler.toml.example wrangler.toml
```
```bash
npm install wrangler -g # 安装 wrangler
git clone https://github.com/TooonyChen/AuthInbox.git # 克隆仓库
cd AuthInbox # 切换目录
npm install # 安装依赖
npm --prefix web install # 安装前端仪表盘依赖
```
2. **创建 d1 数据库**
当你第一次执行 [Wrangler](https://developers.cloudflare.com/workers/wrangler/get-started/) 登录命令时,系统会提示你登录。按提示操作即可。
```bash
npx wrangler d1 create inbox-d1 # 创建名为 'inbox-d1' 的 d1 数据库
npx wrangler d1 execute inbox-d1 --remote --file=./db/schema.sql # 执行 schema.sql 文件
```
你将会看到如下结果:
```bash
✅ Successfully created DB 'inbox-d1'
[[d1_databases]]
binding = "DB" # 在你的 Worker 中通过 env.DB 访问
database_name = "inbox-d1"
database_id = "<你的数据库的唯一ID>"
```
请从终端复制结果,你将在下一步中使用它们。
3. **配置环境变量**
使用项目根目录下的 `wrangler.toml` 文件,并添加所需的环境变量:
编辑 `wrangler.toml`,至少填写以下内容:
```toml
name = "auth-inbox"
type = "typescript"
[vars]
UseBark = 'true' # 设置为 'true' 启用 Bark,设置为 'false' 禁用
barkUrl = "https://api.day.app"
barkTokens = "[token1, token2]" # 填写你 iOS 设备上的 Bark tokens,可从 https://bark.day.app/ 下载应用获取,可以填写多个。如果你只想用一个,那么填写 '[token1]'
FrontEndAdminID = "admin" # 你的登录 ID
FrontEndAdminPassword = "password" # 你的登录密码
GoogleAPIKey = "xxxxxxxxxxx" # 你的 Google API key,如果没有可以前往 https://aistudio.google.com/ 生成一个
FrontEndAdminID = "你的登录账号"
FrontEndAdminPassword = "你的登录密码"
UseBark = "false"
[[d1_databases]] # 从步骤 2 的终端结果中复制这些行。
binding = "DB"
database_name = "inbox-d1" # 从步骤 2 中复制
database_id = "<你的数据库的唯一ID>" # 从步骤 2 中复制
# AI 提供商配置
AI_BASE_URL = "https://generativelanguage.googleapis.com/v1beta/openai"
AI_API_KEY = "你的 API Key"
AI_API_FORMAT = "openai"
AI_MODEL = "gemini-2.0-flash"
[[d1_databases]]
binding = "DB"
database_name = "inbox-d1"
database_id = "<你的数据库ID>"
```
4. **部署你的 worker** 🌐
**`AI_API_FORMAT`** 三选一:
部署你的 Worker 以使项目在互联网上可访问。运行以下命令:
```bash
npm run build:web
npx wrangler deploy
```
你将看到如下输出:
| 值 | 实际请求路径 | 适用提供商 |
|---|---|---|
| `openai` | `/v1/chat/completions` | OpenAI、Gemini(OpenAI 兼容)、DeepSeek、Groq、Mistral 等 |
| `responses` | `/v1/responses` | OpenAI Responses API |
| `anthropic` | `/v1/messages` | Anthropic Claude 直连 |
**常用 `AI_BASE_URL`:**
```
output: https://auth-inbox.<你的子域名>.workers.dev
OpenAI: https://api.openai.com
Gemini(OpenAI 兼容): https://generativelanguage.googleapis.com/v1beta/openai
Anthropic: https://api.anthropic.com
DeepSeek: https://api.deepseek.com
Groq: https://api.groq.com/openai
```
你现在可以访问该 URL 来查看你部署的 Auth Inbox 的邮件面板。
3. **设置邮件转发** ✉️
**备用模型(可选)**,主模型失败重试 3 次后触发:
```toml
# AI_FALLBACK_BASE_URL = "https://api.openai.com"
# AI_FALLBACK_API_KEY = "备用 API Key"
# AI_FALLBACK_API_FORMAT = "openai"
# AI_FALLBACK_MODEL = "gpt-4o-mini"
```
前往 [Cloudflare Dashboard](https://dash.cloudflare.com/) -> `Websites` -> `<你的域名>` -> `Email` -> `Email-Routing` -> `Routing Rules`
可选 Bark 配置:`barkTokens`、`barkUrl`。
如果你想使用“接收所有地址”:
![image](https://github.com/user-attachments/assets/53e5a939-6b03-4ca6-826a-7a5f02f361ac)
4. **构建并部署**
如果你想使用“自定义地址”:
![image](https://github.com/user-attachments/assets/b0d0ab94-c2ad-4870-ac08-d53e64b2c880)
```bash
pnpm run deploy
```
4. **完成!** ✅
输出:`https://auth-inbox.<你的子域名>.workers.dev`
现在你可以访问你的新部署的 Auth Inbox URL,查看电子邮件结果。
---
### 3. 设置邮件转发 ✉️
进入 [Cloudflare 仪表盘](https://dash.cloudflare.com/) → `Websites` → `<你的域名>` → `Email` → `Email Routing` → `Routing Rules`。
**接收所有地址**(将所有邮件转发给 Worker):
![image](https://github.com/user-attachments/assets/53e5a939-6b03-4ca6-826a-7a5f02f361ac)
**自定义地址**(转发特定地址):
![image](https://github.com/user-attachments/assets/b0d0ab94-c2ad-4870-ac08-d53e64b2c880)
### 4. 完成!🎉
访问你的 Worker URL,用你设置的账号密码登录,即可开始接收验证邮件。
---
## 许可证 📜
本项目基于 [MIT License](LICENSE) 许可证。
[MIT License](LICENSE)
---
@@ -221,24 +192,26 @@ If there is no code, clickable link, or this is an advertisement email, return:
![image](https://github.com/user-attachments/assets/41db550c-8340-4315-ba87-85330abc5dfb)
---
## 鸣谢 🙏
- 感谢 **Cloudflare Workers** 提供强大的无服务器平台。
- 感谢 **Google Gemini AI** 提供智能的邮件内容提取功能。
- 感谢 **Bark** 提供实时通知能力。
- 感谢 **开源社区** 为像 Auth Inbox 这样的项目提供灵感和支持。
- 感谢 **ChatGPT** 协助我写代码
- **Cloudflare Workers** 提供强大的无服务器平台。
- **Google Gemini AI** 提供智能邮件内容提取能力。
- **Bark** 提供实时推送通知。
- **shadcn/ui** 提供组件库。
- **开源社区** 提供灵感与支持。
---
## TODO 📝
- [x] **Github Pages 部署**:通过 Github Actions 自动部署到 Cloudflare Workers。
- [ ] **正则表达式**:使用正则表达式替代 Google Gemini AI 以保护隐私,并将使用 AI 作为一个可选功能。
- [ ] **多用户支持**:增加管理多用户的功能,以提高灵活性并扩展使用范围。
- [ ] **增强前端设计**:改进网页界面的UI/UX设计,使其更加现代化和用户友好。
- [ ] **API**: 添加API接口。
- [ ] **发送邮件**:添加发送邮件功能。
- [ ] **更多通知方式**:支持更多通知方式,如邮件、Slack等。
- [x] GitHub Actions 自动部署
- [x] OpenAI 备用模型支持
- [x] React 仪表盘(shadcn/ui)
- [x] 广告邮件过滤(调用 AI 前拦截)
- [x] 原始邮件查看 + 沙箱 HTML 预览
- [ ] 正则表达式提取(无 AI 选项)
- [ ] 多用户支持
- [ ] 更多通知方式(Slack、Webhook 等)
- [ ] 发送邮件功能
+148 -206
View File
@@ -10,34 +10,35 @@ import { RPCEmailMessage } from "./rpcEmail";
import indexHtml from "./index.html";
type ApiFormat = "openai" | "responses" | "anthropic";
interface ProviderConfig {
baseUrl: string;
apiKey: string;
format: ApiFormat;
model: string;
}
interface Env {
// If you set another name in wrangler.toml as the value for 'binding',
// replace "DB" with the variable name you defined.
DB: D1Database;
ASSETS?: Fetcher;
FrontEndAdminID: string;
FrontEndAdminPassword: string;
UseBark: string;
barkTokens: string;
barkUrl: string;
GoogleAPIKey: string;
UseBark: string;
GeminiModel?: string;
OpenAIAPIKey?: string;
OpenAIBaseUrl?: string;
OpenAIModel?: string;
// Primary AI provider
AI_BASE_URL: string;
AI_API_KEY: string;
AI_API_FORMAT: ApiFormat;
AI_MODEL: string;
// Fallback AI provider (all four must be set to enable fallback)
AI_FALLBACK_BASE_URL?: string;
AI_FALLBACK_API_KEY?: string;
AI_FALLBACK_API_FORMAT?: ApiFormat;
AI_FALLBACK_MODEL?: string;
}
interface ModelResponse {
ok: boolean;
status: number;
statusText: string;
payload: unknown;
}
const DEFAULT_GEMINI_MODEL = "gemini-2.0-flash";
const DEFAULT_OPENAI_BASE_URL = "https://api.openai.com";
const DEFAULT_OPENAI_MODEL = "gpt-4o-mini";
// Normalize model output into JSON text // 将模型输出规范化为可解析的 JSON 文本
function extractJsonFromText(rawText: string): Record<string, unknown> | null {
let candidate = rawText.trim();
@@ -58,49 +59,98 @@ function extractJsonFromText(rawText: string): Record<string, unknown> | null {
}
}
// Read structured content from Gemini response // 读取 Gemini 响应中的结构化文本
function extractGeminiText(payload: unknown): string | null {
const candidate = (payload as any)?.candidates?.[0]?.content?.parts?.[0];
if (!candidate || typeof candidate.text !== "string") {
console.error("Gemini response is missing expected data structure");
return null;
}
return candidate.text;
}
// Read structured content from OpenAI response // 读取 OpenAI 响应中的结构化文本
function extractOpenAIText(payload: unknown): string | null {
const message = (payload as any)?.choices?.[0]?.message;
if (!message) {
console.error("OpenAI response is missing choices");
return null;
}
const content = message.content;
if (typeof content === "string") {
return content;
}
if (Array.isArray(content)) {
const textPart = content.find((part: any) => part?.type === "text" && typeof part.text === "string");
if (textPart) {
return textPart.text;
}
}
if (typeof message?.content?.[0]?.text === "string") {
return message.content[0].text;
}
console.error("OpenAI response is missing text content");
return null;
}
// Remove trailing slashes to avoid double separators // 移除结尾斜杠防止重复拼接
function normaliseBaseUrl(baseUrl: string): string {
return baseUrl.replace(/\/+$/, "");
}
// Unified AI provider caller supporting openai / responses / anthropic formats
async function callProvider(config: ProviderConfig, prompt: string): Promise<string | null> {
const base = normaliseBaseUrl(config.baseUrl);
let endpoint: string;
let body: unknown;
const headers: Record<string, string> = { "Content-Type": "application/json" };
if (config.format === "openai") {
endpoint = `${base}/v1/chat/completions`;
headers["Authorization"] = `Bearer ${config.apiKey}`;
body = {
model: config.model,
temperature: 0,
response_format: { type: "json_object" },
messages: [
{ role: "system", content: "You return only valid JSON that matches the requested schema." },
{ role: "user", content: prompt },
],
};
} else if (config.format === "responses") {
endpoint = `${base}/v1/responses`;
headers["Authorization"] = `Bearer ${config.apiKey}`;
body = {
model: config.model,
input: [{ role: "user", content: prompt }],
text: { format: { type: "json_object" } },
};
} else {
// anthropic
endpoint = `${base}/v1/messages`;
headers["x-api-key"] = config.apiKey;
headers["anthropic-version"] = "2023-06-01";
body = {
model: config.model,
max_tokens: 1024,
messages: [{ role: "user", content: prompt }],
};
}
let response: Response;
try {
response = await fetch(endpoint, { method: "POST", headers, body: JSON.stringify(body) });
} catch (err) {
console.error(`[callProvider:${config.format}] fetch error:`, err);
return null;
}
if (!response.ok) {
console.error(`[callProvider:${config.format}] HTTP ${response.status} ${response.statusText}`);
return null;
}
let payload: unknown;
try {
payload = await response.json();
} catch {
console.error(`[callProvider:${config.format}] failed to parse JSON response`);
return null;
}
// Extract text from response based on format
if (config.format === "openai") {
const content = (payload as any)?.choices?.[0]?.message?.content;
if (typeof content === "string") return content;
if (Array.isArray(content)) {
const part = content.find((p: any) => p?.type === "text" && typeof p.text === "string");
return part?.text ?? null;
}
console.error("[callProvider:openai] unexpected response shape");
return null;
}
if (config.format === "responses") {
const text = (payload as any)?.output?.[0]?.content?.[0]?.text
?? (payload as any)?.output?.[0]?.text;
if (typeof text === "string") return text;
console.error("[callProvider:responses] unexpected response shape");
return null;
}
// anthropic
const text = (payload as any)?.content?.[0]?.text;
if (typeof text === "string") return text;
console.error("[callProvider:anthropic] unexpected response shape");
return null;
}
function jsonResponse(data: unknown, status = 200): Response {
return new Response(JSON.stringify(data), {
status,
@@ -406,113 +456,27 @@ export default class extends WorkerEntrypoint<Env> {
}
}
// Call Gemini for primary extraction // 调用 Gemini 执行主解析
private async callGemini(prompt: string, apiKey: string, model: string): Promise<ModelResponse> {
const endpoint = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${apiKey}`;
const init: RequestInit = {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
contents: [
{
parts: [{ text: prompt }],
},
],
}),
};
try {
const response = await fetch(endpoint, init);
let payload: unknown = null;
try {
payload = await response.json();
} catch (parseError) {
console.error("Gemini JSON parse error:", parseError);
}
return {
ok: response.ok,
status: response.status,
statusText: response.statusText,
payload,
};
} catch (error) {
console.error("Gemini request threw:", error);
return {
ok: false,
status: 0,
statusText: "FETCH_ERROR",
payload: null,
};
}
}
// Call OpenAI as the fallback extractor // 调用 OpenAI 作为兜底解析
private async callOpenAI(
prompt: string,
baseUrl: string,
apiKey: string,
model: string
): Promise<ModelResponse> {
const endpoint = `${normaliseBaseUrl(baseUrl)}/v1/chat/completions`;
const init: RequestInit = {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${apiKey}`,
},
body: JSON.stringify({
model,
temperature: 0,
response_format: { type: "json_object" },
messages: [
{
role: "system",
content: "You return only valid JSON that matches the requested schema.",
},
{
role: "user",
content: prompt,
},
],
}),
};
try {
const response = await fetch(endpoint, init);
let payload: unknown = null;
try {
payload = await response.json();
} catch (parseError) {
console.error("OpenAI JSON parse error:", parseError);
}
return {
ok: response.ok,
status: response.status,
statusText: response.statusText,
payload,
};
} catch (error) {
console.error("OpenAI request threw:", error);
return {
ok: false,
status: 0,
statusText: "FETCH_ERROR",
payload: null,
};
}
}
// Primary email handler // 主要邮件处理入口
async email(message: ForwardableEmailMessage): Promise<void> {
const env: Env = this.env;
const useBark = env.UseBark.toLowerCase() === "true";
const googleApiKey = env.GoogleAPIKey;
const geminiModel = env.GeminiModel || DEFAULT_GEMINI_MODEL;
const openAIKey = env.OpenAIAPIKey;
const openAIBaseUrl = env.OpenAIBaseUrl || DEFAULT_OPENAI_BASE_URL;
const openAIModel = env.OpenAIModel || DEFAULT_OPENAI_MODEL;
const primary: ProviderConfig = {
baseUrl: env.AI_BASE_URL,
apiKey: env.AI_API_KEY,
format: env.AI_API_FORMAT ?? "openai",
model: env.AI_MODEL,
};
const fallback: ProviderConfig | null =
env.AI_FALLBACK_BASE_URL && env.AI_FALLBACK_API_KEY && env.AI_FALLBACK_MODEL
? {
baseUrl: env.AI_FALLBACK_BASE_URL,
apiKey: env.AI_FALLBACK_API_KEY,
format: env.AI_FALLBACK_API_FORMAT ?? "openai",
model: env.AI_FALLBACK_MODEL,
}
: null;
// Pull raw email content // 获取原始邮件内容
const rawEmail =
@@ -569,63 +533,41 @@ export default class extends WorkerEntrypoint<Env> {
try {
const maxRetries = 3;
let retryCount = 0;
let extractedData: Record<string, unknown> | null = null;
let geminiStatusError = false;
// First attempt Gemini with simple retries // 先使用 Gemini 并进行简单重试
while (retryCount < maxRetries && !extractedData && !geminiStatusError) {
const geminiResult = await this.callGemini(aiPrompt, googleApiKey, geminiModel);
console.log(`Gemini response attempt ${retryCount + 1}:`, geminiResult.payload);
if (!geminiResult.ok) {
geminiStatusError = true;
console.error(
`Gemini request failed with status ${geminiResult.status} ${geminiResult.statusText}`
);
break;
}
const extractedText = extractGeminiText(geminiResult.payload);
if (extractedText) {
console.log(`Extracted Text before parsing: "${extractedText}"`);
const parsed = extractJsonFromText(extractedText);
// Primary provider: up to 3 attempts
for (let attempt = 1; attempt <= maxRetries; attempt++) {
console.log(`[primary:${primary.format}] attempt ${attempt}`);
const text = await callProvider(primary, aiPrompt);
if (text) {
const parsed = extractJsonFromText(text);
if (parsed) {
extractedData = parsed;
console.log("Parsed Extracted Data:", extractedData);
console.log("[primary] extracted data:", extractedData);
break;
}
}
retryCount += 1;
if (retryCount < maxRetries) {
console.log("Retrying AI request...");
} else {
console.error("Max retries reached. Unable to get valid AI response.");
}
if (attempt < maxRetries) console.log("[primary] retrying...");
else console.error("[primary] max retries reached");
}
if (!extractedData && geminiStatusError && openAIKey) {
// Gemini failed with non-200, switch to OpenAI fallback // 当 Gemini 返回非 200 时切换到 OpenAI 兜底
const openAIResult = await this.callOpenAI(aiPrompt, openAIBaseUrl, openAIKey, openAIModel);
console.log("OpenAI fallback response:", openAIResult.payload);
if (!openAIResult.ok) {
console.error(
`OpenAI fallback failed with status ${openAIResult.status} ${openAIResult.statusText}`
);
} else {
const openAIText = extractOpenAIText(openAIResult.payload);
if (openAIText) {
console.log(`OpenAI text before parsing: "${openAIText}"`);
const parsed = extractJsonFromText(openAIText);
if (parsed) {
extractedData = parsed;
console.log("Parsed Extracted Data from OpenAI:", extractedData);
}
// Fallback provider: one attempt if primary produced nothing and fallback is configured
if (!extractedData && fallback) {
console.log(`[fallback:${fallback.format}] attempting`);
const text = await callProvider(fallback, aiPrompt);
if (text) {
const parsed = extractJsonFromText(text);
if (parsed) {
extractedData = parsed;
console.log("[fallback] extracted data:", extractedData);
} else {
console.error("[fallback] failed to parse response");
}
} else {
console.error("[fallback] provider returned nothing");
}
} else if (!extractedData && geminiStatusError) {
console.error("Gemini request failed and OpenAI fallback is not configured.");
} else if (!extractedData) {
console.error("[primary] failed and no fallback configured");
}
if (extractedData) {
+26 -20
View File
@@ -57,30 +57,36 @@ FrontEndAdminPassword = "password"
# 你的登录密码 **请更改为你自己的登录密码**
GoogleAPIKey = "xxxxxxxxxxx"
# GoogleAPIKey: String
# your google AI API, you can have one from https://aistudio.google.com
# 你的谷歌AI API,你可以从 https://aistudio.google.com 获取
# --- Primary AI provider ---
AI_BASE_URL = "https://generativelanguage.googleapis.com/v1beta/openai"
# Base URL of your AI provider (no trailing slash).
# Examples:
# OpenAI: https://api.openai.com
# Gemini (OAI-compat): https://generativelanguage.googleapis.com/v1beta/openai
# Anthropic: https://api.anthropic.com
# DeepSeek: https://api.deepseek.com
# Groq: https://api.groq.com/openai
GeminiModel = "gemini-2.0-flash"
# GeminiModel: String
# optional override for the Gemini model, defaults to "gemini-2.0-flash"
# 可选配置,覆盖默认的 Gemini 模型名称,默认值为 "gemini-2.0-flash"
AI_API_KEY = "your-api-key"
# API key for the primary provider. Use a Cloudflare Secret for production.
# 主模型的 API Key,生产环境请使用 Cloudflare Secret 存储
OpenAIAPIKey = ""
# OpenAIAPIKey: String
# optional key for OpenAI fallback when Gemini fails with a non-200 response
# 可选配置,当 Gemini 返回非 200 时使用 OpenAI 作为兜底
AI_API_FORMAT = "openai"
# Request/response format. One of:
# openai — POST /v1/chat/completions (OpenAI, Gemini compat, DeepSeek, Groq, etc.)
# responses — POST /v1/responses (OpenAI Responses API)
# anthropic — POST /v1/messages (Anthropic Claude direct)
# 请求格式,三选一:openai | responses | anthropic
OpenAIBaseUrl = "https://api.openai.com"
# OpenAIBaseUrl: String
# optional, change only if you proxy the OpenAI API
# 可选配置,仅在自建代理时更改
AI_MODEL = "gemini-2.0-flash"
# Model ID to use, e.g. "gpt-4o-mini", "gemini-2.0-flash", "claude-3-5-haiku-20241022"
# 模型 ID
OpenAIModel = "gpt-4o-mini"
# OpenAIModel: String
# optional override for the OpenAI model, defaults to "gpt-4o-mini"
# 可选配置,覆盖默认的 OpenAI 模型
# --- Fallback AI provider (optional — remove all four lines to disable) ---
# AI_FALLBACK_BASE_URL = "https://api.openai.com"
# AI_FALLBACK_API_KEY = "your-fallback-api-key"
# AI_FALLBACK_API_FORMAT = "openai"
# AI_FALLBACK_MODEL = "gpt-4o-mini"
# Bind a D1 database. D1 is Cloudflare’s native serverless SQL database.
# Docs: https://developers.cloudflare.com/workers/wrangler/configuration/#d1-databases
+8 -5
View File
@@ -16,11 +16,14 @@ barkUrl = "https://api.day.app"
barkTokens = "[token1, token2]"
FrontEndAdminID = "admin"
FrontEndAdminPassword = "password"
GoogleAPIKey = "xxxxxxxxxxx"
GeminiModel = "gemini-2.0-flash"
OpenAIAPIKey = ""
OpenAIBaseUrl = "https://api.openai.com"
OpenAIModel = "gpt-4o-mini"
AI_BASE_URL = "https://generativelanguage.googleapis.com/v1beta/openai"
AI_API_KEY = "your-api-key"
AI_API_FORMAT = "openai"
AI_MODEL = "gemini-2.0-flash"
# AI_FALLBACK_BASE_URL = "https://api.openai.com"
# AI_FALLBACK_API_KEY = "your-fallback-api-key"
# AI_FALLBACK_API_FORMAT = "openai"
# AI_FALLBACK_MODEL = "gpt-4o-mini"
[[d1_databases]]
binding = "DB"