Skip to content
Merged
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
13 changes: 7 additions & 6 deletions .github/workflows/backend_deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -67,16 +67,17 @@ jobs:
if [ "$debug_mode" = "true" ]; then
pnpm run deploy
else
output=$(pnpm run deploy 2>&1)
if [ $? -ne 0 ]; then
code=$?
echo "Command failed with exit code $code"
exit $code
if pnpm run deploy >/dev/null 2>&1; then
echo "Deploy succeeded"
else
code=$?
echo "Command failed with exit code $code"
exit "$code"
fi
fi
echo "Deployed for tag ${{ github.ref_name }}"
env:
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
# ✅ 将 secret 映射到环境变量中
WRANGLER_TOML_CONTENT: ${{ secrets.BACKEND_TOML }}
WRANGLER_TOML_CONTENT: ${{ secrets.BACKEND_TOML }}
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

### Features

- feat: |Admin API| `/admin/new_address` 接口返回值新增 `address_id` 字段,避免创建后需再次查询地址 ID(#912)
- feat: |自动回复| 发件人过滤支持正则表达式匹配,使用 `/pattern/` 语法(如 `/@example\.com$/`),同时保持前缀匹配的向后兼容
- feat: |Turnstile| 新增全局登录表单 Turnstile 人机验证,通过 `ENABLE_GLOBAL_TURNSTILE_CHECK` 环境变量控制(#767)
- feat: |Telegram| Telegram 推送支持发送邮件附件(单文件限制 50MB),多附件通过 `sendMediaGroup` 批量发送,通过 `ENABLE_TG_PUSH_ATTACHMENT` 环境变量开启(#894)
Expand All @@ -26,6 +27,7 @@

### Docs

- docs: |API| 新增地址 JWT 与用户 JWT 的区分说明,避免混淆两种认证方式;调整文档菜单结构,将 API 接口文档归类到独立分组(#910)
- docs: |Telegram| 新增每用户邮件推送和全局推送功能说明文档(#769)
- docs: |Webhook| 新增 Telegram Bot、企业微信、Discord 等常用推送平台的 Webhook 模板示例
- feat: |Webhook| 前端预设模板新增 Telegram Bot、企业微信、Discord 三个模板
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG_EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

### Features

- feat: |Admin API| `/admin/new_address` endpoint now returns `address_id` field, avoiding additional query after address creation (#912)
- feat: |Auto Reply| Add regex matching support for sender filter using `/pattern/` syntax (e.g. `/@example\.com$/`), backward compatible with prefix matching
- feat: |Turnstile| Add global Turnstile CAPTCHA for all login forms via `ENABLE_GLOBAL_TURNSTILE_CHECK` env var (#767)
- feat: |Telegram| Support sending email attachments in Telegram push (50MB per file limit), multiple attachments sent via `sendMediaGroup`, controlled by `ENABLE_TG_PUSH_ATTACHMENT` env var (#894)
Expand All @@ -26,6 +27,7 @@

### Docs

- docs: |API| Add clarification between Address JWT and User JWT to avoid confusion; reorganize documentation menu structure with dedicated API Endpoints section (#910)
- docs: |Telegram| Add per-user mail push and global push documentation (#769)
- docs: |Webhook| Add webhook template examples for Telegram Bot, WeChat Work, Discord and other common push platforms
- feat: |Webhook| Add Telegram Bot, WeChat Work, Discord preset templates to frontend webhook settings
Expand Down
4 changes: 2 additions & 2 deletions e2e/fixtures/test-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export async function createTestAddress(
ctx: APIRequestContext,
name: string,
domain: string = TEST_DOMAIN
): Promise<{ jwt: string; address: string }> {
): Promise<{ jwt: string; address: string; address_id: number }> {
const uniqueName = `${name}${Date.now()}`;
const res = await ctx.post(`${WORKER_URL}/api/new_address`, {
data: { name: uniqueName, domain },
Expand All @@ -25,7 +25,7 @@ export async function createTestAddress(
throw new Error(`Failed to create address: ${res.status()} ${await res.text()}`);
}
const body = await res.json();
return { jwt: body.jwt, address: body.address };
return { jwt: body.jwt, address: body.address, address_id: body.address_id };
}

/**
Expand Down
18 changes: 9 additions & 9 deletions e2e/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions e2e/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"ws": "^8.18.0"
},
"dependencies": {
"imapflow": "^1.2.12",
"nodemailer": "^8.0.1"
"imapflow": "^1.2.18",
"nodemailer": "^8.0.4"
}
}
3 changes: 2 additions & 1 deletion e2e/tests/api/address-lifecycle.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import { WORKER_URL, TEST_DOMAIN, createTestAddress, deleteAddress, requestSendA
test.describe('Address Lifecycle', () => {
test('create address, request send access, fetch settings, then delete', async ({ request }) => {
// Create address
const { jwt, address } = await createTestAddress(request, 'lifecycle-test');
const { jwt, address, address_id } = await createTestAddress(request, 'lifecycle-test');
expect(address).toContain('@' + TEST_DOMAIN);
expect(jwt).toBeTruthy();
expect(address_id).toBeGreaterThan(0);

// Request send access (creates address_sender row with DEFAULT_SEND_BALANCE)
await requestSendAccess(request, jwt);
Expand Down
19 changes: 19 additions & 0 deletions e2e/tests/api/admin-new-address.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { test, expect } from '@playwright/test';
import { WORKER_URL, TEST_DOMAIN } from '../../fixtures/test-helpers';

test.describe('Admin New Address', () => {
test('should return address_id in response', async ({ request }) => {
const uniqueName = `admin-test${Date.now()}`;
const res = await request.post(`${WORKER_URL}/admin/new_address`, {
data: { name: uniqueName, domain: TEST_DOMAIN },
});

expect(res.ok()).toBe(true);
const body = await res.json();

expect(body.address).toContain('@' + TEST_DOMAIN);
expect(body.jwt).toBeTruthy();
expect(body.address_id).toBeGreaterThan(0);
expect(typeof body.address_id).toBe('number');
});
});
12 changes: 9 additions & 3 deletions vitepress-docs/docs/.vitepress/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,19 +149,25 @@ function sidebarGuide(): DefaultTheme.SidebarItem[] {
items: [
{ text: 'AI Email Recognition', link: 'feature/ai-extract' },
{ text: 'Configure SMTP IMAP Proxy', link: 'feature/config-smtp-proxy' },
{ text: 'Send Email API', link: 'feature/send-mail-api' },
{ text: 'View Email API', link: 'feature/mail-api' },
{ text: 'Configure Subdomain Email', link: 'feature/subdomain' },
{ text: 'Configure Telegram Bot', link: 'feature/telegram' },
{ text: 'Configure S3 Attachments', link: 'feature/s3-attachment' },
{ text: 'Configure WASM Email Parser', link: 'feature/mail_parser_wasm_worker' },
{ text: 'Configure Webhook', link: 'feature/webhook' },
{ text: 'New Address API', link: 'feature/new-address-api' },
{ text: 'OAuth2 Third-party Login', link: 'feature/user-oauth2' },
{ text: 'Enhance with Other Workers', link: 'feature/another-worker-enhanced' },
{ text: 'Add Google Ads', link: 'feature/google-ads.md' },
]
},
{
text: 'API Endpoints',
collapsed: false,
items: [
{ text: 'New Address API', link: 'feature/new-address-api' },
{ text: 'View Email API', link: 'feature/mail-api' },
{ text: 'Send Email API', link: 'feature/send-mail-api' },
]
},
{
text: 'Feature Overview',
collapsed: false,
Expand Down
12 changes: 9 additions & 3 deletions vitepress-docs/docs/.vitepress/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,19 +149,25 @@ function sidebarGuide(): DefaultTheme.SidebarItem[] {
items: [
{ text: 'AI 邮件识别', link: 'feature/ai-extract' },
{ text: '配置 SMTP IMAP 代理服务', link: 'feature/config-smtp-proxy' },
{ text: '发送邮件 API', link: 'feature/send-mail-api' },
{ text: '查看邮件 API', link: 'feature/mail-api' },
{ text: '配置子域名邮箱', link: 'feature/subdomain' },
{ text: '配置 Telegram Bot', link: 'feature/telegram' },
{ text: '配置 S3 附件', link: 'feature/s3-attachment' },
{ text: '配置 worker 使用 wasm 解析邮件', link: 'feature/mail_parser_wasm_worker' },
{ text: '配置 webhook', link: 'feature/webhook' },
{ text: '新建邮箱地址 API', link: 'feature/new-address-api' },
{ text: 'Oauth2 第三方登录', link: 'feature/user-oauth2' },
{ text: '配置其他worker增强', link: 'feature/another-worker-enhanced' },
{ text: '给网页增加 Google Ads', link: 'feature/google-ads.md' },
]
},
{
text: 'API 接口',
collapsed: false,
items: [
{ text: '新建邮箱地址 API', link: 'feature/new-address-api' },
{ text: '查看邮件 API', link: 'feature/mail-api' },
{ text: '发送邮件 API', link: 'feature/send-mail-api' },
]
},
{
text: '功能简介',
collapsed: false,
Expand Down
8 changes: 8 additions & 0 deletions vitepress-docs/docs/en/guide/feature/mail-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,14 @@ print(response.json())

## User Mail API

::: warning Note: User JWT vs Address JWT
This endpoint uses **User JWT** (obtained via `/user_api/login` or `/user_api/register`), with `x-user-token` header.

**Do not confuse with Address JWT**:
- Address JWT uses `Authorization: Bearer <jwt>` to access `/api/*` endpoints
- User JWT uses `x-user-token: <jwt>` to access `/user_api/*` endpoints
:::

Supports `address` filter

```python
Expand Down
14 changes: 14 additions & 0 deletions vitepress-docs/docs/en/guide/feature/new-address-api.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Create New Email Address API

::: warning Note: Address JWT vs User JWT
This page describes **Address JWT**, which is different from **User JWT**:

- **Address JWT**: Returned when creating a mailbox via `/api/new_address` or `/admin/new_address`
- Use `Authorization: Bearer <jwt>` header
- Access `/api/*` endpoints (view mails, delete mails, etc.)

- **User JWT**: Obtained via `/user_api/login` or `/user_api/register`
- Use `x-user-token: <jwt>` header
- Access `/user_api/*` endpoints (user account management)

**Do not confuse these two JWT types!**
:::

## Create Email Address via Admin API

This is a `python` example using the `requests` library to send emails.
Expand Down
8 changes: 8 additions & 0 deletions vitepress-docs/docs/zh/guide/feature/mail-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,14 @@ print(response.json())

## user 邮件 API

::: warning 注意:用户 JWT vs 地址 JWT
此接口使用**用户 JWT**(通过 `/user_api/login` 或 `/user_api/register` 获得),使用 `x-user-token` header。

**请勿与地址 JWT 混淆**:
- 地址 JWT 使用 `Authorization: Bearer <jwt>` 访问 `/api/*` 接口
- 用户 JWT 使用 `x-user-token: <jwt>` 访问 `/user_api/*` 接口
:::

支持 `address` 过滤

```python
Expand Down
14 changes: 14 additions & 0 deletions vitepress-docs/docs/zh/guide/feature/new-address-api.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# 新建邮箱地址 API

::: warning 注意:地址 JWT vs 用户 JWT
本页面介绍的是**地址 JWT**,与**用户 JWT** 是两种不同的认证方式:

- **地址 JWT**:通过 `/api/new_address` 或 `/admin/new_address` 创建邮箱时返回
- 使用 `Authorization: Bearer <jwt>` header
- 用于访问 `/api/*` 接口(查看邮件、删除邮件等)

- **用户 JWT**:通过 `/user_api/login` 或 `/user_api/register` 获得
- 使用 `x-user-token: <jwt>` header
- 用于访问 `/user_api/*` 接口(用户账户管理)

**请勿混淆两种 JWT 的使用方式!**
:::

## 通过 admin API 新建邮箱地址

这是一个 `python` 的例子,使用 `requests` 库发送邮件。
Expand Down
7 changes: 6 additions & 1 deletion worker/src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ export const newAddress = async (
enableCheckNameRegex?: boolean,
sourceMeta?: string | undefined | null,
}
): Promise<{ address: string, jwt: string, password?: string | null }> => {
): Promise<{ address: string, jwt: string, password?: string | null, address_id: number }> => {
const msgs = i18n.getMessagesbyContext(c);
// trim whitespace and remove special characters
name = name.trim().replace(getNameRegex(c), '')
Expand Down Expand Up @@ -247,6 +247,10 @@ export const newAddress = async (
`SELECT id FROM address where name = ?`
).bind(name).first<number>("id");

if (!address_id) {
throw new Error(msgs.FailedCreateAddressMsg);
}

// 如果启用地址密码功能,自动生成密码
const generatedPassword = await generatePasswordForAddress(c, name);

Expand All @@ -259,6 +263,7 @@ export const newAddress = async (
jwt: jwt,
address: name,
password: generatedPassword,
address_id: address_id,
}
}

Expand Down
Loading