-
+
diff --git a/vitepress-docs/docs/en/guide/feature/subdomain.md b/vitepress-docs/docs/en/guide/feature/subdomain.md
index 8662185..3300639 100644
--- a/vitepress-docs/docs/en/guide/feature/subdomain.md
+++ b/vitepress-docs/docs/en/guide/feature/subdomain.md
@@ -27,7 +27,7 @@ RANDOM_SUBDOMAIN_DOMAINS = ["abc.com"]
RANDOM_SUBDOMAIN_LENGTH = 8
```
-- `RANDOM_SUBDOMAIN_DOMAINS`: base domains that allow optional random second-level subdomains
+- `RANDOM_SUBDOMAIN_DOMAINS`: base domains that allow random or manually entered subdomains
- `RANDOM_SUBDOMAIN_LENGTH`: random string length, range `1-63`, default `8`
The create-address APIs only generate a random subdomain when the request explicitly passes
@@ -47,6 +47,10 @@ the request body:
If you want to create an address under a specific subdomain such as `team.abc.com`, do not pass
`enableRandomSubdomain: true`; use the direct-subdomain flow below instead.
+For base domains in `RANDOM_SUBDOMAIN_DOMAINS`, the web and admin pages offer **Normal Domain**,
+**Use Random Subdomain**, and **Use Custom Subdomain** as single-choice modes. In custom mode,
+enter only `team`; the frontend combines it as `team.abc.com`.
+
> [!NOTE]
> This feature only appends a random second-level subdomain when the mailbox is created.
>
@@ -77,10 +81,10 @@ If you want to create an address under a specific subdomain such as `team.abc.co
>
> Reference issue: [#1035](https://github.com/dreamhunter2333/cloudflare_temp_email/issues/1035)
-## Let APIs Specify Subdomains Directly
+## Let APIs Specify Other Subdomains Directly
-If you do not want the system to generate a random subdomain, and instead want the caller to
-explicitly create addresses like `team.abc.com`, enable:
+If a base domain is not in `RANDOM_SUBDOMAIN_DOMAINS`, but API callers still need to create
+addresses like `team.abc.com` directly, enable:
```toml
ENABLE_CREATE_ADDRESS_SUBDOMAIN_MATCH = true
@@ -93,7 +97,7 @@ addresses can be created through `/api/new_address` or `/admin/new_address`:
- `name@dev.team.abc.com`
> [!NOTE]
-> This only relaxes the domain validation used by the create-address APIs. It does not change the
-> default domain dropdown, and it does not create Cloudflare-side subdomain mail routes for you.
+> This switch only relaxes create-address API domain validation. It does not change the frontend
+> domain scope or create Cloudflare-side subdomain mail routes for you.
>
> If the admin panel has already saved an override once, you can switch it back to **Follow Environment Variable** to clear the override and return to env fallback behavior.
diff --git a/vitepress-docs/docs/en/guide/worker-vars.md b/vitepress-docs/docs/en/guide/worker-vars.md
index 9a9ff24..9fc89e8 100644
--- a/vitepress-docs/docs/en/guide/worker-vars.md
+++ b/vitepress-docs/docs/en/guide/worker-vars.md
@@ -37,7 +37,7 @@
| `DEFAULT_DOMAINS` | JSON | Default domains available to users (not logged in or users without assigned roles) | `["awsl.uk", "dreamhunter2333.xyz"]` |
| `CREATE_ADDRESS_DEFAULT_DOMAIN_FIRST` | Text/JSON | Whether to prioritize default domain when creating new addresses, if set to true, will use the first domain when no domain is specified, mainly for telegram bot scenarios | `false` |
| `ENABLE_CREATE_ADDRESS_SUBDOMAIN_MATCH` | Text/JSON | Whether to allow create-address APIs to use base-domain suffix matching. When enabled, if `example.com` is allowed, `/api/new_address` and `/admin/new_address` can also accept `foo.example.com` or `a.b.example.com` | `true` |
-| `RANDOM_SUBDOMAIN_DOMAINS` | JSON | Base domains that allow optional random subdomain creation, so `name@abc.com` can become `name@.abc.com` | `["abc.com"]` |
+| `RANDOM_SUBDOMAIN_DOMAINS` | JSON | Base domains that allow random or manual subdomains; random mode can turn `name@abc.com` into `name@.abc.com` | `["abc.com"]` |
| `RANDOM_SUBDOMAIN_LENGTH` | Number | Random subdomain length, default `8`, valid range `1-63` | `8` |
| `DOMAIN_LABELS` | JSON | For Chinese domains, you can use DOMAIN_LABELS to display Chinese names | `["中文.awsl.uk", "dreamhunter2333.xyz"]` |
| `ENABLE_AUTO_REPLY` | Text/JSON | Allow automatic email replies. Sender filter (`source_prefix`) supports three modes: empty to match all senders, prefix for `startsWith` matching, or `/regex/` syntax for regex matching (e.g. `/@example\.com$/`) | `true` |
@@ -50,8 +50,8 @@
> [!NOTE]
> When `DEFAULT_DOMAINS` is unset or configured as an empty array, it falls back to `DOMAINS`.
>
-> `RANDOM_SUBDOMAIN_DOMAINS` only controls automatic random subdomain generation during mailbox
-> creation. It does not create Cloudflare-side subdomain routing for you.
+> `RANDOM_SUBDOMAIN_DOMAINS` defines the base-domain scope shared by the frontend random and manual
+> subdomain modes. It does not create Cloudflare-side subdomain routing for you.
>
> To actually receive mail on addresses like `name@.abc.com`, **you must add a wildcard
> `*` MX record under the base domain in DNS** by copying the apex's existing MX records to
@@ -65,9 +65,8 @@
> Subdomain addresses are usually best used for receiving only; for sending, prefer the main
> domain.
>
-> `ENABLE_CREATE_ADDRESS_SUBDOMAIN_MATCH` is different from random subdomain generation: it lets
-> API callers **directly specify** a subdomain such as `foo.example.com`, while random subdomain
-> generation appends one automatically during creation.
+> `ENABLE_CREATE_ADDRESS_SUBDOMAIN_MATCH` is independent from those frontend modes. It lets API
+> callers **directly specify** subdomains such as `foo.example.com` under other allowed base domains.
>
> `ENABLE_CREATE_ADDRESS_SUBDOMAIN_MATCH` precedence: if the env is explicitly set to `false`, the
> feature is globally forced off; otherwise the persisted admin setting takes precedence, and the env
diff --git a/vitepress-docs/docs/zh/guide/feature/subdomain.md b/vitepress-docs/docs/zh/guide/feature/subdomain.md
index 3ec0e3c..c2fd03e 100644
--- a/vitepress-docs/docs/zh/guide/feature/subdomain.md
+++ b/vitepress-docs/docs/zh/guide/feature/subdomain.md
@@ -26,7 +26,7 @@ RANDOM_SUBDOMAIN_DOMAINS = ["abc.com"]
RANDOM_SUBDOMAIN_LENGTH = 8
```
-- `RANDOM_SUBDOMAIN_DOMAINS`:允许启用随机二级域名的基础域名列表
+- `RANDOM_SUBDOMAIN_DOMAINS`:允许使用随机或手动子域名的基础域名列表
- `RANDOM_SUBDOMAIN_LENGTH`:随机串长度,范围 `1-63`,默认 `8`
创建地址 API 需要显式传入 `enableRandomSubdomain: true` 才会生成随机二级域名。前端勾选“启用随机二级域名”时会自动传这个字段;如果你自己调用 `/api/new_address` 或 `/admin/new_address`,也需要在请求体中传入:
@@ -41,6 +41,9 @@ RANDOM_SUBDOMAIN_LENGTH = 8
`domain` 必须传 `RANDOM_SUBDOMAIN_DOMAINS` 中配置的基础域名,例如 `abc.com`。如果要创建 `team.abc.com` 这种指定子域名地址,请不要传 `enableRandomSubdomain: true`,而是使用下方“直接指定子域名”的流程。
+对于 `RANDOM_SUBDOMAIN_DOMAINS` 中的基础域名,网页端和管理后台会提供“普通域名”、
+“启用随机子域名”和“使用自定义子域名”三种单选模式。自定义模式只需输入 `team`,前端会组合成 `team.abc.com`。
+
> [!NOTE]
> 这个功能只是在“创建地址”时自动补一个随机二级域名。
>
@@ -58,10 +61,10 @@ RANDOM_SUBDOMAIN_LENGTH = 8
>
> 参考 issue:[#1035](https://github.com/dreamhunter2333/cloudflare_temp_email/issues/1035)
-## 允许 API 直接指定子域名
+## 允许 API 直接指定其他子域名
-如果你不想让系统随机生成子域名,而是希望调用方在创建地址时直接指定 `team.abc.com` 这种子域名,
-可以开启:
+如果基础域名不在 `RANDOM_SUBDOMAIN_DOMAINS` 中,但仍希望 API 调用方直接指定
+`team.abc.com` 这种子域名,可以开启:
```toml
ENABLE_CREATE_ADDRESS_SUBDOMAIN_MATCH = true
@@ -75,7 +78,6 @@ ENABLE_CREATE_ADDRESS_SUBDOMAIN_MATCH = true
都可以通过 `/api/new_address` 或 `/admin/new_address` 创建。
> [!NOTE]
-> 这个能力只放宽“创建地址 API 的域名校验”,不会改动默认域名下拉,也不会自动创建 Cloudflare 侧的
-> 子域名邮箱路由。
+> 这个开关只放宽创建地址 API 的域名校验,不会改动前端的域名范围,也不会自动创建 Cloudflare 侧的子域名邮箱路由。
>
> 如果你在管理后台里保存过这个开关,后续也可以通过“跟随环境变量”把它恢复到未设置状态,再重新回退到 env 默认值。
diff --git a/vitepress-docs/docs/zh/guide/worker-vars.md b/vitepress-docs/docs/zh/guide/worker-vars.md
index d1fe34e..b909020 100644
--- a/vitepress-docs/docs/zh/guide/worker-vars.md
+++ b/vitepress-docs/docs/zh/guide/worker-vars.md
@@ -37,7 +37,7 @@
| `DEFAULT_DOMAINS` | JSON | 默认用户可用的域名(未登录或未分配角色的用户) | `["awsl.uk", "dreamhunter2333.xyz"]` |
| `CREATE_ADDRESS_DEFAULT_DOMAIN_FIRST` | 文本/JSON | 创建新地址时是否优先使用默认域名,如果设置为 true,当未指定域名时将使用第一个域名, 主要用于 telegram bot 场景 | `false` |
| `ENABLE_CREATE_ADDRESS_SUBDOMAIN_MATCH` | 文本/JSON | 是否允许创建邮箱 API 使用“基础域名后缀匹配”。开启后,如果允许域名里有 `example.com`,则 `/api/new_address` 与 `/admin/new_address` 可以接受 `foo.example.com`、`a.b.example.com` 这类子域名 | `true` |
-| `RANDOM_SUBDOMAIN_DOMAINS` | JSON | 允许启用随机子域名的基础域名列表,启用后可把 `name@abc.com` 创建成 `name@随机串.abc.com` | `["abc.com"]` |
+| `RANDOM_SUBDOMAIN_DOMAINS` | JSON | 允许使用随机或手动子域名的基础域名列表,随机模式可把 `name@abc.com` 创建成 `name@随机串.abc.com` | `["abc.com"]` |
| `RANDOM_SUBDOMAIN_LENGTH` | 数字 | 随机子域名长度,默认 `8`,范围 `1-63` | `8` |
| `DOMAIN_LABELS` | JSON | 对于中文域名,可以使用 DOMAIN_LABELS 显示域名的中文展示名称 | `["中文.awsl.uk", "dreamhunter2333.xyz"]` |
| `ENABLE_AUTO_REPLY` | 文本/JSON | 允许自动回复邮件。发件人过滤(`source_prefix`)支持三种模式:留空匹配所有发件人、填写前缀进行 `startsWith` 匹配、使用 `/regex/` 语法进行正则匹配(如 `/@example\.com$/`) | `true` |
@@ -50,8 +50,8 @@
> [!NOTE]
> `DEFAULT_DOMAINS` 未配置或配置为空数组时,会回退使用 `DOMAINS`。
>
-> `RANDOM_SUBDOMAIN_DOMAINS` 只负责“创建地址时自动补随机子域名”,不会自动帮你创建 Cloudflare
-> 侧的子域名路由。
+> `RANDOM_SUBDOMAIN_DOMAINS` 定义前端随机及手动子域名模式共同使用的基础域名范围,不会自动帮你
+> 创建 Cloudflare 侧的子域名路由。
>
> 要让 `name@<随机>.abc.com` 这种随机子域名地址真的能收到邮件,**必须在基础域名的 DNS 中为
> `*` 子域添加通配 MX 记录**:把基础域名上现有的每一条 MX 记录都复制到 `*` 主机名上,
@@ -62,8 +62,8 @@
>
> 子域名地址通常更适合收件;如果要发件,仍建议优先使用主域名。
>
-> `ENABLE_CREATE_ADDRESS_SUBDOMAIN_MATCH` 与随机子域名功能不同:它允许 API 调用方**直接指定**
-> `foo.example.com` 这类子域名;而随机子域名功能是系统在创建时自动补一个随机前缀。
+> `ENABLE_CREATE_ADDRESS_SUBDOMAIN_MATCH` 与上述前端模式独立:它允许 API 调用方在其他允许的
+> 基础域名下**直接指定** `foo.example.com` 这类子域名。
>
> `ENABLE_CREATE_ADDRESS_SUBDOMAIN_MATCH` 的优先级为:当 env 明确设置为 `false` 时,全局硬禁用;
> 其他情况下优先使用后台持久化设置,后台未设置时再回退到 env 值。
diff --git a/worker/src/common.ts b/worker/src/common.ts
index 25675f7..0fd4a49 100644
--- a/worker/src/common.ts
+++ b/worker/src/common.ts
@@ -2,7 +2,7 @@ import { Context } from 'hono';
import { Jwt } from 'hono/utils/jwt'
import { WorkerMailerOptions } from 'worker-mailer';
-import { getBooleanValue, getDomains, getStringArray, getStringValue, getIntValue, getUserRoles, getDefaultDomains, getJsonSetting, getAnotherWorkerList, hashPassword, getJsonObjectValue, getRandomSubdomainDomains, getDomainMapValue, normalizeDomains, trimLower } from './utils';
+import { getBooleanValue, getDomains, getStringArray, getStringValue, getIntValue, getUserRoles, getDefaultDomains, getJsonSetting, getAnotherWorkerList, hashPassword, getJsonObjectValue, getRandomSubdomainDomains, getDomainMapValue, isDomainOrSubdomain, normalizeDomains, trimLower } from './utils';
import { unbindTelegramByAddress } from './telegram_api/common';
import { CONSTANTS } from './constants';
import { AddressCreationSettings, AdminWebhookSettings, ExtractResult, WebhookMail, WebhookSettings } from './models';
@@ -410,8 +410,12 @@ export const newAddress = async (
domain = normalizeDomainValue(domain);
}
const { effectiveEnabled: enableSubdomainMatch } = await getAddressCreationSubdomainMatchStatus(c);
+ const allowManualSubdomain = domain
+ ? allowDomains.some((baseDomain) =>
+ allowRandomSubdomainForDomain(c, baseDomain) && isDomainOrSubdomain(domain, baseDomain))
+ : false;
const matchedAllowDomain = domain
- ? findMatchedAllowedDomain(domain, allowDomains, enableSubdomainMatch)
+ ? findMatchedAllowedDomain(domain, allowDomains, enableSubdomainMatch || allowManualSubdomain)
: null;
// check domain is valid
if (!domain || !matchedAllowDomain) {