mirror of
https://github.com/dreamhunter2333/cloudflare_temp_email.git
synced 2026-05-07 06:12:50 +08:00
feat: add var DISABLE_CUSTOM_ADDRESS_NAME and CREATE_ADDRESS_DEFAULT_… (#717)
* feat: add var DISABLE_CUSTOM_ADDRESS_NAME and CREATE_ADDRESS_DEFAULT_DOMAIN_FIRST * fix: enhance input validation with trim() for address creation - Add trim() handling in newAddress() function to prevent whitespace issues - Add trim() handling for address prefixes to ensure consistent formatting - Add trim() handling in Telegram API address parsing for robustness - Prevents edge cases with whitespace-only or padded input strings 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,7 @@ api.get('/open_api/settings', async (c) => {
|
||||
const auth = c.req.raw.headers.get("x-custom-auth");
|
||||
needAuth = !auth || !passwords.includes(auth);
|
||||
}
|
||||
|
||||
return c.json({
|
||||
"title": c.env.TITLE,
|
||||
"announcement": utils.getStringValue(c.env.ANNOUNCEMENT),
|
||||
@@ -29,6 +30,7 @@ api.get('/open_api/settings', async (c) => {
|
||||
"adminContact": c.env.ADMIN_CONTACT,
|
||||
"enableUserCreateEmail": utils.getBooleanValue(c.env.ENABLE_USER_CREATE_EMAIL),
|
||||
"disableAnonymousUserCreateEmail": utils.getBooleanValue(c.env.DISABLE_ANONYMOUS_USER_CREATE_EMAIL),
|
||||
"disableCustomAddressName": utils.getBooleanValue(c.env.DISABLE_CUSTOM_ADDRESS_NAME),
|
||||
"enableUserDeleteEmail": utils.getBooleanValue(c.env.ENABLE_USER_DELETE_EMAIL),
|
||||
"enableAutoReply": utils.getBooleanValue(c.env.ENABLE_AUTO_REPLY),
|
||||
"enableIndexAbout": utils.getBooleanValue(c.env.ENABLE_INDEX_ABOUT),
|
||||
|
||||
@@ -8,6 +8,31 @@ import { AdminWebhookSettings, WebhookMail, WebhookSettings } from './models';
|
||||
|
||||
const DEFAULT_NAME_REGEX = /[^a-z0-9]/g;
|
||||
|
||||
export const generateRandomName = (c: Context<HonoCustomType>): string => {
|
||||
// name min length min 1
|
||||
const minLength = Math.max(
|
||||
getIntValue(c.env.MIN_ADDRESS_LEN, 1),
|
||||
1
|
||||
);
|
||||
// name max length min 1
|
||||
const maxLength = Math.max(
|
||||
getIntValue(c.env.MAX_ADDRESS_LEN, 30),
|
||||
1
|
||||
);
|
||||
|
||||
// Build full name recursively until minimum length is reached
|
||||
const buildName = (currentName: string = ""): string => {
|
||||
return currentName.length >= minLength
|
||||
? currentName
|
||||
: buildName(currentName + Math.random().toString(36).substring(2, 15));
|
||||
};
|
||||
|
||||
const fullName = buildName();
|
||||
|
||||
// Return truncated to max length
|
||||
return fullName.substring(0, Math.min(fullName.length, maxLength));
|
||||
};
|
||||
|
||||
const checkNameRegex = (c: Context<HonoCustomType>, name: string) => {
|
||||
let error = null;
|
||||
try {
|
||||
@@ -76,8 +101,8 @@ export const newAddress = async (
|
||||
enableCheckNameRegex?: boolean,
|
||||
}
|
||||
): Promise<{ address: string, jwt: string }> => {
|
||||
// remove special characters
|
||||
name = name.replace(getNameRegex(c), '')
|
||||
// trim whitespace and remove special characters
|
||||
name = name.trim().replace(getNameRegex(c), '')
|
||||
// check name
|
||||
if (enableCheckNameRegex) {
|
||||
await checkNameBlockList(c, name);
|
||||
@@ -102,15 +127,20 @@ export const newAddress = async (
|
||||
}
|
||||
// create address with prefix
|
||||
if (typeof addressPrefix === "string") {
|
||||
name = addressPrefix + name;
|
||||
name = addressPrefix.trim() + name;
|
||||
} else if (enablePrefix) {
|
||||
name = getStringValue(c.env.PREFIX) + name;
|
||||
name = getStringValue(c.env.PREFIX).trim() + name;
|
||||
}
|
||||
// check domain
|
||||
const allowDomains = checkAllowDomains ? await getAllowDomains(c) : getDomains(c);
|
||||
// if domain is not set, use the random domain
|
||||
// if domain is not set, select domain based on environment configuration
|
||||
if (!domain && allowDomains.length > 0) {
|
||||
domain = allowDomains[Math.floor(Math.random() * allowDomains.length)];
|
||||
const createAddressDefaultDomainFirst = getBooleanValue(c.env.CREATE_ADDRESS_DEFAULT_DOMAIN_FIRST);
|
||||
if (createAddressDefaultDomainFirst) {
|
||||
domain = allowDomains[0];
|
||||
} else {
|
||||
domain = allowDomains[Math.floor(Math.random() * allowDomains.length)];
|
||||
}
|
||||
}
|
||||
// check domain is valid
|
||||
if (!domain || !allowDomains.includes(domain)) {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Context, Hono } from 'hono'
|
||||
|
||||
import i18n from '../i18n';
|
||||
import { getBooleanValue, getJsonSetting, checkCfTurnstile, getStringValue, getSplitStringListValue } from '../utils';
|
||||
import { newAddress, handleListQuery, deleteAddressWithData, getAddressPrefix, getAllowDomains, updateAddressUpdatedAt } from '../common'
|
||||
import { newAddress, handleListQuery, deleteAddressWithData, getAddressPrefix, getAllowDomains, updateAddressUpdatedAt, generateRandomName } from '../common'
|
||||
import { CONSTANTS } from '../constants'
|
||||
import auto_reply from './auto_reply'
|
||||
import webhook_settings from './webhook_settings';
|
||||
@@ -123,9 +123,13 @@ api.post('/api/new_address', async (c) => {
|
||||
} catch (error) {
|
||||
return c.text(msgs.TurnstileCheckFailedMsg, 500)
|
||||
}
|
||||
// if no name, generate random name
|
||||
if (!name) {
|
||||
name = Math.random().toString(36).substring(2, 15);
|
||||
// Check if custom email names are disabled from environment variable
|
||||
const disableCustomAddressName = getBooleanValue(c.env.DISABLE_CUSTOM_ADDRESS_NAME);
|
||||
|
||||
// if no name or custom names are disabled, generate random name
|
||||
if (!name || disableCustomAddressName) {
|
||||
// Generate random name with context-based length configuration
|
||||
name = generateRandomName(c);
|
||||
}
|
||||
// check name block list
|
||||
try {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Context } from "hono";
|
||||
import { Jwt } from "hono/utils/jwt";
|
||||
import { CONSTANTS } from "../constants";
|
||||
import { getIntValue, getJsonSetting } from "../utils";
|
||||
import { deleteAddressWithData, newAddress } from "../common";
|
||||
import { getBooleanValue, getIntValue, getJsonSetting } from "../utils";
|
||||
import { deleteAddressWithData, newAddress, generateRandomName } from "../common";
|
||||
|
||||
export const tgUserNewAddress = async (
|
||||
c: Context<HonoCustomType>, userId: string, address: string
|
||||
@@ -15,21 +15,28 @@ export const tgUserNewAddress = async (
|
||||
throw Error("Rate limit exceeded")
|
||||
}
|
||||
}
|
||||
// @ts-ignore
|
||||
address = address || Math.random().toString(36).substring(2, 15);
|
||||
const [name, domain] = address.includes("@") ? address.split("@") : [address, null];
|
||||
// Check if custom address names are disabled
|
||||
const disableCustomAddressName = getBooleanValue(c.env.DISABLE_CUSTOM_ADDRESS_NAME);
|
||||
|
||||
// Parse address parameter - handle empty or whitespace-only address
|
||||
const trimmedAddress = address ? address.trim() : "";
|
||||
const [name, domain] = trimmedAddress.includes("@") ? trimmedAddress.split("@") : [trimmedAddress, null];
|
||||
const jwtList = await c.env.KV.get<string[]>(`${CONSTANTS.TG_KV_PREFIX}:${userId}`, 'json') || [];
|
||||
if (jwtList.length >= getIntValue(c.env.TG_MAX_ADDRESS, 5)) {
|
||||
throw Error("绑定地址数量已达上限");
|
||||
}
|
||||
// Generate name if disabled or not provided
|
||||
const finalName = (!name || disableCustomAddressName) ? generateRandomName(c) : name;
|
||||
|
||||
// check name block list
|
||||
const value = await getJsonSetting(c, CONSTANTS.ADDRESS_BLOCK_LIST_KEY);
|
||||
const blockList = (value || []) as string[];
|
||||
if (blockList.some((item) => name.includes(item))) {
|
||||
throw Error(`Name[${name}]is blocked`);
|
||||
if (blockList.some((item) => finalName.includes(item))) {
|
||||
throw Error(`Name[${finalName}]is blocked`);
|
||||
}
|
||||
|
||||
const res = await newAddress(c, {
|
||||
name: name || Math.random().toString(36).substring(2, 15),
|
||||
name: finalName,
|
||||
domain,
|
||||
enablePrefix: true
|
||||
});
|
||||
|
||||
@@ -18,7 +18,7 @@ const COMMANDS = [
|
||||
},
|
||||
{
|
||||
command: "new",
|
||||
description: "新建邮箱地址, 如果要自定义邮箱地址, 请输入 /new <name>@<domain>, name [a-z0-9] 有效"
|
||||
description: "新建邮箱地址, 如果要自定义邮箱地址, 请输入 /new, 通过 /new <name>@<domain> 可以指定, name [a-z0-9] 有效, name 为空则随机生成, @<domain> 可选"
|
||||
},
|
||||
{
|
||||
command: "address",
|
||||
|
||||
2
worker/src/types.d.ts
vendored
2
worker/src/types.d.ts
vendored
@@ -24,6 +24,8 @@ type Bindings = {
|
||||
MAX_ADDRESS_LEN: string | number | undefined
|
||||
DEFAULT_DOMAINS: string | string[] | undefined
|
||||
DOMAINS: string | string[] | undefined
|
||||
DISABLE_CUSTOM_ADDRESS_NAME: string | boolean | undefined
|
||||
CREATE_ADDRESS_DEFAULT_DOMAIN_FIRST: string | boolean | undefined
|
||||
ADMIN_USER_ROLE: string | undefined
|
||||
USER_DEFAULT_ROLE: string | UserRole | undefined
|
||||
USER_ROLES: string | UserRole[] | undefined
|
||||
|
||||
@@ -35,6 +35,8 @@ PREFIX = "tmp"
|
||||
# (min, max) length of the adderss, if not set, the default is (1, 30)
|
||||
# MIN_ADDRESS_LEN = 1
|
||||
# MAX_ADDRESS_LEN = 30
|
||||
# Disable custom email address name, if set true, users cannot input custom email name, will auto generate
|
||||
# DISABLE_CUSTOM_ADDRESS_NAME = true
|
||||
# IF YOU WANT TO MAKE YOUR SITE PRIVATE, UNCOMMENT THE FOLLOWING LINES
|
||||
# PASSWORDS = ["123", "456"]
|
||||
# For admin panel
|
||||
@@ -43,6 +45,8 @@ PREFIX = "tmp"
|
||||
# DISABLE_ADMIN_PASSWORD_CHECK = false
|
||||
# ADMIN CONTACT, CAN BE ANY STRING
|
||||
# ADMIN_CONTACT = "xx@xx.xxx"
|
||||
# Create new address with default domain first, if set true, will use first domain from DEFAULT_DOMAINS when no domain specified
|
||||
# CREATE_ADDRESS_DEFAULT_DOMAIN_FIRST = false
|
||||
DEFAULT_DOMAINS = ["xxx.xxx1" , "xxx.xxx2"] # domain name for no role users
|
||||
DOMAINS = ["xxx.xxx1" , "xxx.xxx2"] # all domain names
|
||||
# For chinese domain name, you can use DOMAIN_LABELS to show chinese domain name
|
||||
|
||||
Reference in New Issue
Block a user