diff --git a/CHANGELOG.md b/CHANGELOG.md index 46cfabfc..9698f91c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,21 @@ 🇺🇸 English

-## v1.2.1(main) +## v1.3.0(main) + +### Features + +- feat: |OAuth2| 新增 OAuth2 邮箱格式转换功能,支持通过正则表达式转换第三方登录返回的邮箱格式(如将 `user@domain` 转换为 `user@custom.domain`) + +### Bug Fixes + +- fix: |用户地址| 修复禁止匿名创建时,已登录用户地址数量限制检查失效的问题,新增公共函数 `isAddressCountLimitReached` 统一处理地址数量限制逻辑 + +### Improvements + +- refactor: |代码重构| 提取地址数量限制检查为公共函数,优化代码复用性 + +## v1.2.1 ### Bug Fixes diff --git a/CHANGELOG_EN.md b/CHANGELOG_EN.md index 5db79727..25860158 100644 --- a/CHANGELOG_EN.md +++ b/CHANGELOG_EN.md @@ -6,7 +6,21 @@ 🇺🇸 English

-## v1.2.1(main) +## v1.3.0(main) + +### Features + +- feat: |OAuth2| Add email format transformation support for OAuth2, allowing regex-based email format conversion from third-party login providers (e.g., transform `user@domain` to `user@custom.domain`) + +### Bug Fixes + +- fix: |User Address| Fix address count limit check failure when anonymous creation is disabled for logged-in users, add public function `isAddressCountLimitReached` to unify address count limit logic + +### Improvements + +- refactor: |Code Refactoring| Extract address count limit check as a public function to improve code reusability + +## v1.2.1 ### Bug Fixes diff --git a/worker/src/mails_api/index.ts b/worker/src/mails_api/index.ts index a7889d4e..02e641d8 100644 --- a/worker/src/mails_api/index.ts +++ b/worker/src/mails_api/index.ts @@ -1,7 +1,7 @@ import { Context, Hono } from 'hono' import i18n from '../i18n'; -import { getBooleanValue, getJsonSetting, checkCfTurnstile, getStringValue, getSplitStringListValue } from '../utils'; +import { getBooleanValue, getJsonSetting, checkCfTurnstile, getStringValue, getSplitStringListValue, isAddressCountLimitReached } from '../utils'; import { newAddress, handleListQuery, deleteAddressWithData, getAddressPrefix, getAllowDomains, updateAddressUpdatedAt, generateRandomName } from '../common' import { CONSTANTS } from '../constants' import auto_reply from './auto_reply' @@ -105,14 +105,25 @@ api.get('/api/settings', async (c) => { api.post('/api/new_address', async (c) => { const msgs = i18n.getMessagesbyContext(c); + const userPayload = c.get("userPayload"); + if (getBooleanValue(c.env.DISABLE_ANONYMOUS_USER_CREATE_EMAIL) - && !c.get("userPayload") + && !userPayload ) { return c.text(msgs.NewAddressAnonymousDisabledMsg, 403) } if (!getBooleanValue(c.env.ENABLE_USER_CREATE_EMAIL)) { return c.text(msgs.NewAddressDisabledMsg, 403) } + + // 如果启用了禁止匿名创建,且用户已登录,检查地址数量限制 + if (getBooleanValue(c.env.DISABLE_ANONYMOUS_USER_CREATE_EMAIL) && userPayload) { + const userRole = c.get("userRolePayload"); + if (await isAddressCountLimitReached(c, userPayload.user_id, userRole)) { + return c.text(msgs.MaxAddressCountReachedMsg, 400) + } + } + // eslint-disable-next-line prefer-const let { name, domain, cf_token } = await c.req.json(); // check cf turnstile diff --git a/worker/src/user_api/bind_address.ts b/worker/src/user_api/bind_address.ts index 78405f38..848ae50f 100644 --- a/worker/src/user_api/bind_address.ts +++ b/worker/src/user_api/bind_address.ts @@ -1,27 +1,11 @@ import { Context } from 'hono'; import { Jwt } from 'hono/utils/jwt' -import { UserSettings, RoleAddressConfig } from "../models"; -import { getJsonSetting } from "../utils" -import { CONSTANTS } from "../constants"; +import { isAddressCountLimitReached } from "../utils" import { unbindTelegramByAddress } from '../telegram_api/common'; import i18n from '../i18n'; import { updateAddressUpdatedAt, commonGetUserRole } from '../common'; -const getMaxAddressCount = async ( - c: Context, - userRole: string | null | undefined, - settings: UserSettings -): Promise => { - if (!userRole) return settings.maxAddressCount; - const roleConfigs = await getJsonSetting(c, CONSTANTS.ROLE_ADDRESS_CONFIG_KEY); - if (!roleConfigs) return settings.maxAddressCount; - const roleMaxCount = roleConfigs[userRole]?.maxAddressCount; - if (typeof roleMaxCount !== 'number') return settings.maxAddressCount; - if (roleMaxCount <= 0) return settings.maxAddressCount; - return roleMaxCount; -}; - const UserBindAddressModule = { bind: async (c: Context) => { const { user_id } = c.get("userPayload"); @@ -56,19 +40,9 @@ const UserBindAddressModule = { ).bind(user_id, address_id).first("user_id"); if (db_user_address_id) return c.json({ success: true }) // check if binded address count - const value = await getJsonSetting(c, CONSTANTS.USER_SETTINGS_KEY); - const settings = new UserSettings(value); - // get user role const userRole = c.get("userRolePayload"); - // check role-based max address count first, fallback to global settings - const maxAddressCount = await getMaxAddressCount(c, userRole, settings); - if (maxAddressCount > 0) { - const { count } = await c.env.DB.prepare( - `SELECT COUNT(*) as count FROM users_address where user_id = ?` - ).bind(user_id).first<{ count: number }>() || { count: 0 }; - if (count >= maxAddressCount) { - return c.text(msgs.MaxAddressCountReachedMsg, 400) - } + if (await isAddressCountLimitReached(c, user_id, userRole)) { + return c.text(msgs.MaxAddressCountReachedMsg, 400) } // bind try { @@ -221,19 +195,9 @@ const UserBindAddressModule = { return c.text(msgs.TargetUserNotFoundMsg, 400) } // check target user binded address count - const value = await getJsonSetting(c, CONSTANTS.USER_SETTINGS_KEY); - const settings = new UserSettings(value); - // get target user role const userRoleObj = await commonGetUserRole(c, target_user_id); - // check role-based max address count first, fallback to global settings - const maxAddressCount = await getMaxAddressCount(c, userRoleObj?.role, settings); - if (maxAddressCount > 0) { - const { count } = await c.env.DB.prepare( - `SELECT COUNT(*) as count FROM users_address where user_id = ?` - ).bind(target_user_id).first<{ count: number }>() || { count: 0 }; - if (count >= maxAddressCount) { - return c.text(msgs.MaxAddressCountReachedMsg, 400) - } + if (await isAddressCountLimitReached(c, target_user_id, userRoleObj?.role)) { + return c.text(msgs.MaxAddressCountReachedMsg, 400) } // check if binded const db_user_address_id = await c.env.DB.prepare( diff --git a/worker/src/utils.ts b/worker/src/utils.ts index 8d06092a..73541fba 100644 --- a/worker/src/utils.ts +++ b/worker/src/utils.ts @@ -1,5 +1,7 @@ import { Context } from "hono"; import { createMimeMessage } from "mimetext"; +import { UserSettings, RoleAddressConfig } from "./models"; +import { CONSTANTS } from "./constants"; export const getJsonObjectValue = ( value: string | any @@ -303,6 +305,45 @@ export const hashPassword = async (password: string): Promise => { return hashArray.map(byte => byte.toString(16).padStart(2, '0')).join(''); } +export const getMaxAddressCount = async ( + c: Context, + userRole: string | null | undefined, + settings: UserSettings +): Promise => { + if (!userRole) return settings.maxAddressCount; + const roleConfigs = await getJsonSetting(c, CONSTANTS.ROLE_ADDRESS_CONFIG_KEY); + if (!roleConfigs) return settings.maxAddressCount; + const roleMaxCount = roleConfigs[userRole]?.maxAddressCount; + if (typeof roleMaxCount !== 'number') return settings.maxAddressCount; + if (roleMaxCount <= 0) return settings.maxAddressCount; + return roleMaxCount; +}; + +/** + * 检查用户是否已达到地址数量限制 + * @param c - Hono Context + * @param user_id - 用户 ID + * @param userRole - 用户角色 + * @returns true 表示已超限,false 表示未超限 + */ +export const isAddressCountLimitReached = async ( + c: Context, + user_id: number | string, + userRole: string | null | undefined +): Promise => { + const value = await getJsonSetting(c, CONSTANTS.USER_SETTINGS_KEY); + const settings = new UserSettings(value); + const maxAddressCount = await getMaxAddressCount(c, userRole, settings); + + if (maxAddressCount <= 0) return false; + + const { count } = await c.env.DB.prepare( + `SELECT COUNT(*) as count FROM users_address where user_id = ?` + ).bind(user_id).first<{ count: number }>() || { count: 0 }; + + return count >= maxAddressCount; +}; + export default { getJsonObjectValue, getSetting,