mirror of
https://github.com/dreamhunter2333/cloudflare_temp_email.git
synced 2026-09-05 23:47:50 +08:00
fix(user): enforce address count limit when anonymous creation disabled (#819)
This commit is contained in:
+15
-1
@@ -6,7 +6,21 @@
|
|||||||
<a href="CHANGELOG_EN.md">🇺🇸 English</a>
|
<a href="CHANGELOG_EN.md">🇺🇸 English</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## 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
|
### Bug Fixes
|
||||||
|
|
||||||
|
|||||||
+15
-1
@@ -6,7 +6,21 @@
|
|||||||
<a href="CHANGELOG_EN.md">🇺🇸 English</a>
|
<a href="CHANGELOG_EN.md">🇺🇸 English</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## 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
|
### Bug Fixes
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Context, Hono } from 'hono'
|
import { Context, Hono } from 'hono'
|
||||||
|
|
||||||
import i18n from '../i18n';
|
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 { newAddress, handleListQuery, deleteAddressWithData, getAddressPrefix, getAllowDomains, updateAddressUpdatedAt, generateRandomName } from '../common'
|
||||||
import { CONSTANTS } from '../constants'
|
import { CONSTANTS } from '../constants'
|
||||||
import auto_reply from './auto_reply'
|
import auto_reply from './auto_reply'
|
||||||
@@ -105,14 +105,25 @@ api.get('/api/settings', async (c) => {
|
|||||||
|
|
||||||
api.post('/api/new_address', async (c) => {
|
api.post('/api/new_address', async (c) => {
|
||||||
const msgs = i18n.getMessagesbyContext(c);
|
const msgs = i18n.getMessagesbyContext(c);
|
||||||
|
const userPayload = c.get("userPayload");
|
||||||
|
|
||||||
if (getBooleanValue(c.env.DISABLE_ANONYMOUS_USER_CREATE_EMAIL)
|
if (getBooleanValue(c.env.DISABLE_ANONYMOUS_USER_CREATE_EMAIL)
|
||||||
&& !c.get("userPayload")
|
&& !userPayload
|
||||||
) {
|
) {
|
||||||
return c.text(msgs.NewAddressAnonymousDisabledMsg, 403)
|
return c.text(msgs.NewAddressAnonymousDisabledMsg, 403)
|
||||||
}
|
}
|
||||||
if (!getBooleanValue(c.env.ENABLE_USER_CREATE_EMAIL)) {
|
if (!getBooleanValue(c.env.ENABLE_USER_CREATE_EMAIL)) {
|
||||||
return c.text(msgs.NewAddressDisabledMsg, 403)
|
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
|
// eslint-disable-next-line prefer-const
|
||||||
let { name, domain, cf_token } = await c.req.json();
|
let { name, domain, cf_token } = await c.req.json();
|
||||||
// check cf turnstile
|
// check cf turnstile
|
||||||
|
|||||||
@@ -1,27 +1,11 @@
|
|||||||
import { Context } from 'hono';
|
import { Context } from 'hono';
|
||||||
import { Jwt } from 'hono/utils/jwt'
|
import { Jwt } from 'hono/utils/jwt'
|
||||||
|
|
||||||
import { UserSettings, RoleAddressConfig } from "../models";
|
import { isAddressCountLimitReached } from "../utils"
|
||||||
import { getJsonSetting } from "../utils"
|
|
||||||
import { CONSTANTS } from "../constants";
|
|
||||||
import { unbindTelegramByAddress } from '../telegram_api/common';
|
import { unbindTelegramByAddress } from '../telegram_api/common';
|
||||||
import i18n from '../i18n';
|
import i18n from '../i18n';
|
||||||
import { updateAddressUpdatedAt, commonGetUserRole } from '../common';
|
import { updateAddressUpdatedAt, commonGetUserRole } from '../common';
|
||||||
|
|
||||||
const getMaxAddressCount = async (
|
|
||||||
c: Context<HonoCustomType>,
|
|
||||||
userRole: string | null | undefined,
|
|
||||||
settings: UserSettings
|
|
||||||
): Promise<number> => {
|
|
||||||
if (!userRole) return settings.maxAddressCount;
|
|
||||||
const roleConfigs = await getJsonSetting<RoleAddressConfig>(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 = {
|
const UserBindAddressModule = {
|
||||||
bind: async (c: Context<HonoCustomType>) => {
|
bind: async (c: Context<HonoCustomType>) => {
|
||||||
const { user_id } = c.get("userPayload");
|
const { user_id } = c.get("userPayload");
|
||||||
@@ -56,19 +40,9 @@ const UserBindAddressModule = {
|
|||||||
).bind(user_id, address_id).first("user_id");
|
).bind(user_id, address_id).first("user_id");
|
||||||
if (db_user_address_id) return c.json({ success: true })
|
if (db_user_address_id) return c.json({ success: true })
|
||||||
// check if binded address count
|
// 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");
|
const userRole = c.get("userRolePayload");
|
||||||
// check role-based max address count first, fallback to global settings
|
if (await isAddressCountLimitReached(c, user_id, userRole)) {
|
||||||
const maxAddressCount = await getMaxAddressCount(c, userRole, settings);
|
return c.text(msgs.MaxAddressCountReachedMsg, 400)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// bind
|
// bind
|
||||||
try {
|
try {
|
||||||
@@ -221,19 +195,9 @@ const UserBindAddressModule = {
|
|||||||
return c.text(msgs.TargetUserNotFoundMsg, 400)
|
return c.text(msgs.TargetUserNotFoundMsg, 400)
|
||||||
}
|
}
|
||||||
// check target user binded address count
|
// 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);
|
const userRoleObj = await commonGetUserRole(c, target_user_id);
|
||||||
// check role-based max address count first, fallback to global settings
|
if (await isAddressCountLimitReached(c, target_user_id, userRoleObj?.role)) {
|
||||||
const maxAddressCount = await getMaxAddressCount(c, userRoleObj?.role, settings);
|
return c.text(msgs.MaxAddressCountReachedMsg, 400)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// check if binded
|
// check if binded
|
||||||
const db_user_address_id = await c.env.DB.prepare(
|
const db_user_address_id = await c.env.DB.prepare(
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { Context } from "hono";
|
import { Context } from "hono";
|
||||||
import { createMimeMessage } from "mimetext";
|
import { createMimeMessage } from "mimetext";
|
||||||
|
import { UserSettings, RoleAddressConfig } from "./models";
|
||||||
|
import { CONSTANTS } from "./constants";
|
||||||
|
|
||||||
export const getJsonObjectValue = <T = any>(
|
export const getJsonObjectValue = <T = any>(
|
||||||
value: string | any
|
value: string | any
|
||||||
@@ -303,6 +305,45 @@ export const hashPassword = async (password: string): Promise<string> => {
|
|||||||
return hashArray.map(byte => byte.toString(16).padStart(2, '0')).join('');
|
return hashArray.map(byte => byte.toString(16).padStart(2, '0')).join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getMaxAddressCount = async (
|
||||||
|
c: Context<HonoCustomType>,
|
||||||
|
userRole: string | null | undefined,
|
||||||
|
settings: UserSettings
|
||||||
|
): Promise<number> => {
|
||||||
|
if (!userRole) return settings.maxAddressCount;
|
||||||
|
const roleConfigs = await getJsonSetting<RoleAddressConfig>(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<HonoCustomType>,
|
||||||
|
user_id: number | string,
|
||||||
|
userRole: string | null | undefined
|
||||||
|
): Promise<boolean> => {
|
||||||
|
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 {
|
export default {
|
||||||
getJsonObjectValue,
|
getJsonObjectValue,
|
||||||
getSetting,
|
getSetting,
|
||||||
|
|||||||
Reference in New Issue
Block a user