新增注册码、草稿箱、权限拦截邮件、发件菜单隐藏
This commit is contained in:
@@ -48,7 +48,7 @@ const accountService = {
|
||||
|
||||
if (roleRow.accountCount && userRow.email !== c.env.admin) {
|
||||
const userAccountCount = await accountService.countUserAccount(c, userId)
|
||||
if(userAccountCount >= roleRow.accountCount) throw new BizError(`添加邮箱数量限制${roleRow.accountCount}个`, 403);
|
||||
if(userAccountCount >= roleRow.accountCount) throw new BizError(`添加邮箱数量到达限制`, 403);
|
||||
}
|
||||
|
||||
if (await settingService.isAddEmailVerify(c)) {
|
||||
@@ -181,6 +181,9 @@ const accountService = {
|
||||
|
||||
async setName(c, params, userId) {
|
||||
const { name, accountId } = params
|
||||
if (name.length > 30) {
|
||||
throw new BizError('用户名长度超出限制');
|
||||
}
|
||||
await orm(c).update(account).set({name}).where(and(eq(account.userId, userId),eq(account.accountId, accountId))).run();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -5,10 +5,7 @@ import { desc, count, eq, and, ne, isNotNull } from 'drizzle-orm';
|
||||
import { emailConst } from '../const/entity-const';
|
||||
import kvConst from '../const/kv-const';
|
||||
import dayjs from 'dayjs';
|
||||
import timezone from 'dayjs/plugin/timezone'
|
||||
import utc from 'dayjs/plugin/utc'
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(timezone)
|
||||
import { toUtc } from '../utils/date-uitil';
|
||||
const analysisService = {
|
||||
|
||||
async echarts(c) {
|
||||
@@ -62,7 +59,7 @@ const analysisService = {
|
||||
},
|
||||
|
||||
filterEmptyDay(data) {
|
||||
const today = dayjs().tz('Asia/Shanghai').subtract(1, 'day');
|
||||
const today = toUtc().tz('Asia/Shanghai').subtract(1, 'day');
|
||||
const previousDays = Array.from({ length: 15 }, (_, i) => {
|
||||
return today.subtract(i, 'day').format('YYYY-MM-DD');
|
||||
}).reverse();
|
||||
|
||||
@@ -150,7 +150,7 @@ const emailService = {
|
||||
}
|
||||
|
||||
if (send === settingConst.send.CLOSE) {
|
||||
throw new BizError('邮箱发送功能已停用', 403);
|
||||
throw new BizError('邮件发送功能已停用', 403);
|
||||
}
|
||||
|
||||
if (attachments.length > 0 && manyType === 'divide') {
|
||||
@@ -163,13 +163,17 @@ const emailService = {
|
||||
|
||||
if (c.env.admin !== userRow.email && roleRow.sendCount) {
|
||||
|
||||
if (roleRow.sendCount < 0) {
|
||||
throw new BizError('用户无发送次数', 403);
|
||||
}
|
||||
|
||||
if (userRow.sendCount >= roleRow.sendCount) {
|
||||
if (roleRow.sendType === 'day') throw new BizError('已到达每日发送次数限制', 403);
|
||||
if (roleRow.sendType === 'count') throw new BizError('已到达发送次数限制', 403);
|
||||
if (roleRow.sendType === 'day') throw new BizError('发送次数已到达每日限制', 403);
|
||||
if (roleRow.sendType === 'count') throw new BizError('发送次数已到达限制', 403);
|
||||
}
|
||||
|
||||
if (userRow.sendCount + receiveEmail.length > roleRow.sendCount) {
|
||||
if (roleRow.sendType === 'day') throw new BizError('剩余每日发送次数不足', 403);
|
||||
if (roleRow.sendType === 'day') throw new BizError('当日剩余发送次数不足', 403);
|
||||
if (roleRow.sendType === 'count') throw new BizError('剩余发送次数不足', 403);
|
||||
}
|
||||
|
||||
@@ -178,18 +182,20 @@ const emailService = {
|
||||
|
||||
const accountRow = await accountService.selectById(c, accountId);
|
||||
|
||||
if (!accountRow) {
|
||||
throw new BizError('发件人邮箱不存在');
|
||||
}
|
||||
|
||||
const domain = emailUtils.getDomain(accountRow.email);
|
||||
const resendToken = resendTokens[domain];
|
||||
|
||||
if (!resendToken) {
|
||||
throw new BizError('resend密钥未配置');
|
||||
}
|
||||
|
||||
if (!accountRow) {
|
||||
throw new BizError('邮箱不存在');
|
||||
}
|
||||
|
||||
if (accountRow.userId !== userId) {
|
||||
throw new BizError('非当前用户所属邮箱');
|
||||
throw new BizError('发件人邮箱非当前用户所有');
|
||||
}
|
||||
|
||||
if (!name) {
|
||||
@@ -488,7 +494,7 @@ const emailService = {
|
||||
|
||||
async allList(c, params) {
|
||||
|
||||
let { emailId, size, name, subject, accountEmail, sendEmail, userEmail, type, timeSort } = params;
|
||||
let { emailId, size, name, subject, accountEmail, userEmail, type, timeSort } = params;
|
||||
|
||||
size = Number(size);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import BizError from '../error/biz-error';
|
||||
import userService from './user-service';
|
||||
import emailUtils from '../utils/email-utils';
|
||||
import { isDel, userConst } from '../const/entity-const';
|
||||
import { isDel, settingConst, userConst } from '../const/entity-const';
|
||||
import JwtUtils from '../utils/jwt-utils';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import KvConst from '../const/kv-const';
|
||||
@@ -14,15 +14,19 @@ import saltHashUtils from '../utils/crypto-utils';
|
||||
import cryptoUtils from '../utils/crypto-utils';
|
||||
import turnstileService from './turnstile-service';
|
||||
import roleService from './role-service';
|
||||
import regKeyService from './reg-key-service';
|
||||
import dayjs from 'dayjs';
|
||||
import { formatDetailDate, toUtc } from '../utils/date-uitil';
|
||||
|
||||
const loginService = {
|
||||
|
||||
async register(c, params) {
|
||||
|
||||
const { email, password, token } = params;
|
||||
const { email, password, token, code } = params;
|
||||
|
||||
if (!await settingService.isRegister(c)) {
|
||||
const {regKey, register} = await settingService.query(c)
|
||||
|
||||
if (register === settingConst.register.CLOSE) {
|
||||
throw new BizError('注册功能已关闭');
|
||||
}
|
||||
|
||||
@@ -30,7 +34,15 @@ const loginService = {
|
||||
throw new BizError('非法邮箱');
|
||||
}
|
||||
|
||||
if (password.length < 6) {
|
||||
if (password.length > 30) {
|
||||
throw new BizError('密码长度超出限制');
|
||||
}
|
||||
|
||||
if (emailUtils.getName(email).length > 30) {
|
||||
throw new BizError('邮箱长度超出限制');
|
||||
}
|
||||
|
||||
if (password.length > 6) {
|
||||
throw new BizError('密码必须大于6位');
|
||||
}
|
||||
|
||||
@@ -38,6 +50,21 @@ const loginService = {
|
||||
throw new BizError('非法邮箱域名');
|
||||
}
|
||||
|
||||
let type = null;
|
||||
let regKeyId = 0
|
||||
|
||||
if (regKey === settingConst.regKey.OPEN) {
|
||||
const result = await this.handleOpenRegKey(c, regKey, code)
|
||||
type = result.type
|
||||
regKeyId = result.regKeyId
|
||||
}
|
||||
|
||||
if (regKey === settingConst.regKey.OPTIONAL) {
|
||||
const result = await this.handleOpenOptional(c, regKey, code)
|
||||
type = result.type
|
||||
regKeyId = result.regKeyId
|
||||
}
|
||||
|
||||
const accountRow = await accountService.selectByEmailIncludeDelNoCase(c, email);
|
||||
|
||||
if (accountRow && accountRow.isDel === isDel.DELETE) {
|
||||
@@ -45,7 +72,7 @@ const loginService = {
|
||||
}
|
||||
|
||||
if (accountRow) {
|
||||
throw new BizError('该邮箱已被其他用户绑定');
|
||||
throw new BizError('该邮箱已被注册');
|
||||
}
|
||||
|
||||
if (await settingService.isRegisterVerify(c)) {
|
||||
@@ -54,13 +81,71 @@ const loginService = {
|
||||
|
||||
const { salt, hash } = await saltHashUtils.hashPassword(password);
|
||||
|
||||
const roleRow = await roleService.selectDefaultRole(c);
|
||||
let defType = null
|
||||
|
||||
const userId = await userService.insert(c, { email, password: hash, salt, type: roleRow.roleId });
|
||||
if (!type) {
|
||||
const roleRow = await roleService.selectDefaultRole(c);
|
||||
defType = roleRow.roleId
|
||||
}
|
||||
|
||||
const userId = await userService.insert(c, { email, regKeyId,password: hash, salt, type: type || defType });
|
||||
|
||||
await userService.updateUserInfo(c, userId, true);
|
||||
|
||||
await accountService.insert(c, { userId: userId, email, name: emailUtils.getName(email) });
|
||||
|
||||
if (regKey !== settingConst.regKey.CLOSE && type) {
|
||||
await regKeyService.reduceCount(c, code, 1);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
async handleOpenRegKey(c, regKey, code) {
|
||||
|
||||
if (!code) {
|
||||
throw new BizError('注册码不能为空');
|
||||
}
|
||||
|
||||
const regKeyRow = await regKeyService.selectByCode(c, code);
|
||||
|
||||
if (!regKeyRow) {
|
||||
throw new BizError('注册码不存在');
|
||||
}
|
||||
|
||||
if (regKeyRow.count <= 0) {
|
||||
throw new BizError('注册码使用次数已耗尽');
|
||||
}
|
||||
|
||||
const today = toUtc().tz('Asia/Shanghai').startOf('day')
|
||||
const expireTime = toUtc(regKeyRow.expireTime).tz('Asia/Shanghai').startOf('day');
|
||||
|
||||
if (expireTime.isBefore(today)) {
|
||||
throw new BizError('注册码已过期');
|
||||
}
|
||||
|
||||
return { type: regKeyRow.roleId, regKeyId: regKeyRow.regKeyId };
|
||||
},
|
||||
|
||||
async handleOpenOptional(c, regKey, code) {
|
||||
|
||||
if (!code) {
|
||||
return null
|
||||
}
|
||||
|
||||
const regKeyRow = await regKeyService.selectByCode(c, code);
|
||||
|
||||
if (!regKeyRow) {
|
||||
return null
|
||||
}
|
||||
|
||||
const today = toUtc().tz('Asia/Shanghai').startOf('day')
|
||||
const expireTime = toUtc(regKeyRow.expireTime).tz('Asia/Shanghai').startOf('day');
|
||||
|
||||
if (regKeyRow.count <= 0 || expireTime.isBefore(today)) {
|
||||
return null
|
||||
}
|
||||
|
||||
return { type: regKeyRow.roleId, regKeyId: regKeyRow.regKeyId };
|
||||
},
|
||||
|
||||
async login(c, params) {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import attService from './att-service';
|
||||
import constant from '../const/constant';
|
||||
|
||||
const r2Service = {
|
||||
async putObj(c, key, content, metadata) {
|
||||
await c.env.r2.put(key, content, {
|
||||
|
||||
100
mail-worker/src/service/reg-key-service.js
Normal file
100
mail-worker/src/service/reg-key-service.js
Normal file
@@ -0,0 +1,100 @@
|
||||
import orm from '../entity/orm';
|
||||
import regKey from '../entity/reg-key';
|
||||
import { inArray, like, eq, desc, sql, or } from 'drizzle-orm';
|
||||
import roleService from './role-service';
|
||||
import BizError from '../error/biz-error';
|
||||
import { formatDetailDate, toUtc } from '../utils/date-uitil';
|
||||
import userService from './user-service';
|
||||
const regKeyService = {
|
||||
|
||||
async add(c, params, userId) {
|
||||
|
||||
let {code,roleId,count,expireTime} = params;
|
||||
|
||||
if (!code) {
|
||||
throw new BizError('注册码不能为空');
|
||||
}
|
||||
|
||||
if (!count) {
|
||||
throw new BizError('使用次数不能为空');
|
||||
}
|
||||
|
||||
if (!expireTime) {
|
||||
throw new BizError('有效时间不能为空');
|
||||
}
|
||||
|
||||
const regKeyRow = await orm(c).select().from(regKey).where(eq(regKey.code, code)).get();
|
||||
|
||||
if (regKeyRow) {
|
||||
throw new BizError('注册码已存在');
|
||||
}
|
||||
|
||||
const roleRow = roleService.selectById(c, roleId);
|
||||
if (!roleRow) {
|
||||
throw new BizError('权限身份不存在');
|
||||
}
|
||||
|
||||
expireTime = formatDetailDate(expireTime)
|
||||
|
||||
await orm(c).insert(regKey).values({code,roleId,count,userId,expireTime}).run();
|
||||
},
|
||||
|
||||
async delete(c, params) {
|
||||
let {regKeyIds} = params;
|
||||
regKeyIds = regKeyIds.split(',').map(id => Number(id));
|
||||
await orm(c).delete(regKey).where(inArray(regKey.regKeyId,regKeyIds)).run();
|
||||
},
|
||||
|
||||
async clearNotUse(c) {
|
||||
let now = formatDetailDate(toUtc().tz('Asia/Shanghai').startOf('day'))
|
||||
await orm(c).delete(regKey).where(or(eq(regKey.count, 0),sql`datetime(${regKey.expireTime}, '+8 hours') < datetime(${now})`)).run();
|
||||
},
|
||||
|
||||
selectByCode(c, code) {
|
||||
return orm(c).select().from(regKey).where(eq(regKey.code, code)).get();
|
||||
},
|
||||
|
||||
async list(c, params) {
|
||||
|
||||
const {code} = params
|
||||
let query = orm(c).select().from(regKey)
|
||||
|
||||
if (code) {
|
||||
query = query.where(like(regKey.code, `${code}%`))
|
||||
}
|
||||
|
||||
const regKeyList = await query.orderBy(desc(regKey.regKeyId)).all();
|
||||
const roleList = await roleService.roleSelectUse(c);
|
||||
|
||||
const today = toUtc().tz('Asia/Shanghai').startOf('day')
|
||||
|
||||
regKeyList.forEach(regKeyRow => {
|
||||
|
||||
const index = roleList.findIndex(roleRow => roleRow.roleId === regKeyRow.roleId)
|
||||
regKeyRow.roleName = index > -1 ? roleList[index].name : ''
|
||||
|
||||
const expireTime = toUtc(regKeyRow.expireTime).tz('Asia/Shanghai').startOf('day');
|
||||
|
||||
if (expireTime.isBefore(today)) {
|
||||
regKeyRow.expireTime = null
|
||||
}
|
||||
})
|
||||
|
||||
return regKeyList;
|
||||
},
|
||||
|
||||
async reduceCount(c, code, count) {
|
||||
await orm(c).update(regKey).set({
|
||||
count: sql`${regKey.count}
|
||||
-
|
||||
${count}`
|
||||
}).where(eq(regKey.code, code)).run();
|
||||
},
|
||||
|
||||
async history(c, params) {
|
||||
const { regKeyId } = params;
|
||||
return userService.listByRegKeyId(c, regKeyId);
|
||||
}
|
||||
}
|
||||
|
||||
export default regKeyService;
|
||||
@@ -6,12 +6,15 @@ import rolePerm from '../entity/role-perm';
|
||||
import perm from '../entity/perm';
|
||||
import { permConst, roleConst } from '../const/entity-const';
|
||||
import userService from './user-service';
|
||||
import user from '../entity/user';
|
||||
import emailUtils from '../utils/email-utils';
|
||||
import verifyUtils from '../utils/verify-utils';
|
||||
|
||||
const roleService = {
|
||||
|
||||
async add(c, params, userId) {
|
||||
|
||||
let { name, permIds } = params;
|
||||
let { name, permIds, banEmail } = params;
|
||||
|
||||
if (!name) {
|
||||
throw new BizError('身份名不能为空');
|
||||
@@ -23,7 +26,15 @@ const roleService = {
|
||||
throw new BizError('身份名已存在');
|
||||
}
|
||||
|
||||
roleRow = await orm(c).insert(role).values({...params, userId}).returning().get();
|
||||
const notEmailIndex = banEmail.findIndex(item => !verifyUtils.isEmail(item))
|
||||
|
||||
if (notEmailIndex > -1) {
|
||||
throw new BizError('非法邮箱');
|
||||
}
|
||||
|
||||
banEmail = banEmail.join(',')
|
||||
|
||||
roleRow = await orm(c).insert(role).values({...params, banEmail, userId}).returning().get();
|
||||
|
||||
if (permIds.length === 0) {
|
||||
return;
|
||||
@@ -44,6 +55,7 @@ const roleService = {
|
||||
.where(eq(perm.type, permConst.type.BUTTON)).all();
|
||||
|
||||
roleList.forEach(role => {
|
||||
role.banEmail = role.banEmail.split(",").filter(item => item !== "")
|
||||
role.permIds = permList.filter(perm => perm.roleId === role.roleId).map(perm => perm.permId);
|
||||
});
|
||||
|
||||
@@ -52,7 +64,7 @@ const roleService = {
|
||||
|
||||
async setRole(c, params) {
|
||||
|
||||
let { name, permIds, roleId } = params;
|
||||
let { name, permIds, roleId, banEmail } = params;
|
||||
|
||||
if (!name) {
|
||||
throw new BizError('名字不能为空');
|
||||
@@ -60,7 +72,15 @@ const roleService = {
|
||||
|
||||
delete params.isDefault
|
||||
|
||||
await orm(c).update(role).set({...params}).where(eq(role.roleId, roleId)).run();
|
||||
const notEmailIndex = banEmail.findIndex(item => !verifyUtils.isEmail(item))
|
||||
|
||||
if (notEmailIndex > -1) {
|
||||
throw new BizError('非法邮箱');
|
||||
}
|
||||
|
||||
banEmail = banEmail.join(',')
|
||||
|
||||
await orm(c).update(role).set({...params, banEmail}).where(eq(role.roleId, roleId)).run();
|
||||
await orm(c).delete(rolePerm).where(eq(rolePerm.roleId, roleId)).run();
|
||||
|
||||
if (permIds.length > 0) {
|
||||
@@ -126,6 +146,10 @@ const roleService = {
|
||||
.leftJoin(rolePerm, eq(perm.permId, rolePerm.permId))
|
||||
.leftJoin(role, eq(role.roleId, rolePerm.roleId))
|
||||
.where(and(eq(perm.permKey, permKey), eq(role.sendType, sendType))).all();
|
||||
},
|
||||
|
||||
selectByUserId(c, userId) {
|
||||
return orm(c).select(role).from(user).leftJoin(role, eq(role.roleId, user.type)).where(eq(user.userId, userId)).get();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -49,16 +49,6 @@ const settingService = {
|
||||
await this.refresh(c);
|
||||
},
|
||||
|
||||
async isRegister(c) {
|
||||
const { register } = await this.query(c);
|
||||
return register === settingConst.register.OPEN;
|
||||
},
|
||||
|
||||
async isReceive(c) {
|
||||
const { receive } = await this.query(c);
|
||||
return receive === settingConst.receive.OPEN;
|
||||
},
|
||||
|
||||
async isAddEmail(c) {
|
||||
const { addEmail, manyEmail } = await this.query(c);
|
||||
return addEmail === settingConst.addEmail.OPEN && manyEmail === settingConst.manyEmail.OPEN;
|
||||
@@ -125,7 +115,8 @@ const settingService = {
|
||||
siteKey: settingRow.siteKey,
|
||||
background: settingRow.background,
|
||||
loginOpacity: settingRow.loginOpacity,
|
||||
domainList:settingRow.domainList
|
||||
domainList:settingRow.domainList,
|
||||
regKey: settingRow.regKey
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -382,6 +382,15 @@ const userService = {
|
||||
await accountService.restoreByUserId(c, userId);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
listByRegKeyId(c, regKeyId) {
|
||||
return orm(c)
|
||||
.select({email: user.email,createTime: user.createTime})
|
||||
.from(user)
|
||||
.where(eq(user.regKeyId, regKeyId))
|
||||
.orderBy(desc(user.userId))
|
||||
.all();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user