新增中英文切换

This commit is contained in:
eoao
2025-07-22 22:02:23 +08:00
parent 249edb9e19
commit bbcff4908d
64 changed files with 1951 additions and 1298 deletions

View File

@@ -10,37 +10,39 @@ import { isDel } from '../const/entity-const';
import settingService from './setting-service';
import turnstileService from './turnstile-service';
import roleService from './role-service';
import { t } from '../i18n/i18n';
const accountService = {
async add(c, params, userId) {
if (!await settingService.isAddEmail(c)) {
throw new BizError('添加邮箱功能已关闭');
throw new BizError(t('addAccountDisabled'));
}
let { email, token } = params;
if (!email) {
throw new BizError('邮箱不能为空');
throw new BizError(t('emptyEmail'));
}
if (!verifyUtils.isEmail(email)) {
throw new BizError('非法邮箱');
throw new BizError(t('notEmail'));
}
if (!c.env.domain.includes(emailUtils.getDomain(email))) {
throw new BizError('不存在的邮箱域名');
throw new BizError(t('notExistDomain'));
}
const accountRow = await this.selectByEmailIncludeDelNoCase(c, email);
if (accountRow && accountRow.isDel === isDel.DELETE) {
throw new BizError('该邮箱已被注销');
throw new BizError(t('isDelAccount'));
}
if (accountRow) {
throw new BizError('该邮箱已被注册');
throw new BizError(t('isRegAccount'));
}
const userRow = await userService.selectById(c, userId);
@@ -48,7 +50,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(`添加邮箱数量到达限制`, 403);
if(userAccountCount >= roleRow.accountCount) throw new BizError(t('accountLimit'), 403);
}
if (await settingService.isAddEmailVerify(c)) {
@@ -109,11 +111,11 @@ const accountService = {
const accountRow = await this.selectById(c, accountId);
if (accountRow.email === user.email) {
throw new BizError('不可以删除自己的邮箱');
throw new BizError(t('delMyAccount'));
}
if (accountRow.userId !== user.userId) {
throw new BizError('该邮箱不属于当前用户');
throw new BizError(t('noUserAccount'));
}
await orm(c).update(account).set({ isDel: isDel.DELETE }).where(
@@ -182,7 +184,7 @@ const accountService = {
async setName(c, params, userId) {
const { name, accountId } = params
if (name.length > 30) {
throw new BizError('用户名长度超出限制');
throw new BizError(t('usernameLengthLimit'));
}
await orm(c).update(account).set({name}).where(and(eq(account.userId, userId),eq(account.accountId, accountId))).run();
}

View File

@@ -8,7 +8,21 @@ import dayjs from 'dayjs';
import { toUtc } from '../utils/date-uitil';
const analysisService = {
async echarts(c) {
async echarts(c, params) {
const { timeZone } = params;
let utcDate = toUtc().startOf('day');
let localDate = utcDate.tz(timeZone);
utcDate = dayjs(utcDate.format('YYYY-MM-DD HH:mm:ss'))
localDate = dayjs(localDate.format('YYYY-MM-DD HH:mm:ss'))
//获取时差
const diffHours = localDate.diff(utcDate, 'hour',true);
const [
@@ -30,17 +44,17 @@ const analysisService = {
.limit(6),
analysisDao.userDayCount(c),
analysisDao.receiveDayCount(c),
analysisDao.sendDayCount(c),
analysisDao.userDayCount(c, diffHours),
analysisDao.receiveDayCount(c, diffHours),
analysisDao.sendDayCount(c, diffHours),
c.env.kv.get(kvConst.SEND_DAY_COUNT + dayjs().format('YYYY-MM-DD')),
]);
const userDayCount = this.filterEmptyDay(userDayCountRaw);
const receiveDayCount = this.filterEmptyDay(receiveDayCountRaw);
const sendDayCount = this.filterEmptyDay(sendDayCountRaw);
const userDayCount = this.filterEmptyDay(userDayCountRaw, timeZone);
const receiveDayCount = this.filterEmptyDay(receiveDayCountRaw, timeZone);
const sendDayCount = this.filterEmptyDay(sendDayCountRaw, timeZone);
const daySendTotal = daySendTotalRaw || 0;
@@ -58,8 +72,8 @@ const analysisService = {
};
},
filterEmptyDay(data) {
const today = toUtc().tz('Asia/Shanghai').subtract(1, 'day');
filterEmptyDay(data, timeZone) {
const today = toUtc().tz(timeZone).subtract(1, 'day');
const previousDays = Array.from({ length: 15 }, (_, i) => {
return today.subtract(i, 'day').format('YYYY-MM-DD');
}).reverse();

View File

@@ -16,6 +16,7 @@ import user from '../entity/user';
import starService from './star-service';
import dayjs from 'dayjs';
import kvConst from '../const/kv-const';
import { t } from '../i18n/i18n'
const emailService = {
@@ -134,27 +135,27 @@ const emailService = {
let { attDataList, html } = await attService.toImageUrlHtml(c, content, r2Domain);
if (attDataList.length > 0 && !r2Domain) {
throw new BizError('r2域名未配置不能发送正文图片');
throw new BizError(t('noOsDomainSendPic'));
}
if (attDataList.length > 0 && !c.env.r2) {
throw new BizError('r2对象存储未配置不能发送正文图片');
throw new BizError(t('noOsSendPic'));
}
if (attachments.length > 0 && !r2Domain) {
throw new BizError('r2域名未配置不能发送附件');
throw new BizError(t('noOsDomainSendAtt'));
}
if (attachments.length > 0 && !c.env.r2) {
throw new BizError('r2对象存储未配置不能发送附件');
throw new BizError(t('noOsSendAtt'));
}
if (send === settingConst.send.CLOSE) {
throw new BizError('邮件发送功能已停用', 403);
throw new BizError(t('disabledSend'), 403);
}
if (attachments.length > 0 && manyType === 'divide') {
throw new BizError('分别发送暂时不支持附件');
throw new BizError(t('noSeparateSend'));
}
@@ -164,17 +165,17 @@ const emailService = {
if (c.env.admin !== userRow.email && roleRow.sendCount) {
if (roleRow.sendCount < 0) {
throw new BizError('用户无发送次数', 403);
throw new BizError(t('userNoSendTotal'), 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(t('daySendLimit'), 403);
if (roleRow.sendType === 'count') throw new BizError(t('totalSendLimit'), 403);
}
if (userRow.sendCount + receiveEmail.length > 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(t('daySendLack'), 403);
if (roleRow.sendType === 'count') throw new BizError(t('totalSendLack'), 403);
}
}
@@ -183,19 +184,19 @@ const emailService = {
const accountRow = await accountService.selectById(c, accountId);
if (!accountRow) {
throw new BizError('发件人邮箱不存在');
throw new BizError(t('senderAccountNotExist'));
}
const domain = emailUtils.getDomain(accountRow.email);
const resendToken = resendTokens[domain];
if (!resendToken) {
throw new BizError('resend密钥未配置');
throw new BizError(t('noResendToken'));
}
if (accountRow.userId !== userId) {
throw new BizError('发件人邮箱非当前用户所有');
throw new BizError(t('sendEmailNotCurUser'));
}
if (!name) {
@@ -211,7 +212,7 @@ const emailService = {
emailRow = await this.selectById(c, emailId);
if (!emailRow) {
throw new BizError('邮件不存在无法回复');
throw new BizError(t('notExistEmailReply'));
}
}

View File

@@ -16,7 +16,8 @@ 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';
import { toUtc } from '../utils/date-uitil';
import { t } from '../i18n/i18n.js';
const loginService = {
@@ -27,27 +28,27 @@ const loginService = {
const {regKey, register} = await settingService.query(c)
if (register === settingConst.register.CLOSE) {
throw new BizError('注册功能已关闭');
throw new BizError(t('regDisabled'));
}
if (!verifyUtils.isEmail(email)) {
throw new BizError('非法邮箱');
throw new BizError(t('notEmail'));
}
if (password.length > 30) {
throw new BizError('密码长度超出限制');
throw new BizError(t('pwdLengthLimit'));
}
if (emailUtils.getName(email).length > 30) {
throw new BizError('邮箱长度超出限制');
throw new BizError(t('emailLengthLimit'));
}
if (password.length < 6) {
throw new BizError('密码至少6位');
throw new BizError(t('pwdMinLengthLimit'));
}
if (!c.env.domain.includes(emailUtils.getDomain(email))) {
throw new BizError('非法邮箱域名');
throw new BizError(t('notEmailDomain'));
}
let type = null;
@@ -68,11 +69,11 @@ const loginService = {
const accountRow = await accountService.selectByEmailIncludeDelNoCase(c, email);
if (accountRow && accountRow.isDel === isDel.DELETE) {
throw new BizError('该邮箱已被注销');
throw new BizError(t('isDelUser'));
}
if (accountRow) {
throw new BizError('该邮箱已被注册');
throw new BizError(t('isRegAccount'));
}
if (await settingService.isRegisterVerify(c)) {
@@ -103,24 +104,24 @@ const loginService = {
async handleOpenRegKey(c, regKey, code) {
if (!code) {
throw new BizError('注册码不能为空');
throw new BizError(t('emptyRegKey'));
}
const regKeyRow = await regKeyService.selectByCode(c, code);
if (!regKeyRow) {
throw new BizError('注册码不存在');
throw new BizError(t('notExistRegKey'));
}
if (regKeyRow.count <= 0) {
throw new BizError('注册码使用次数已耗尽');
throw new BizError(t('noRegKeyCount'));
}
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('注册码已过期');
throw new BizError(t('regKeyExpire'));
}
return { type: regKeyRow.roleId, regKeyId: regKeyRow.regKeyId };
@@ -153,25 +154,25 @@ const loginService = {
const { email, password } = params;
if (!email || !password) {
throw new BizError('邮箱和密码不能为空');
throw new BizError(t('emailAndPwdEmpty'));
}
const userRow = await userService.selectByEmailIncludeDel(c, email);
if (!userRow) {
throw new BizError('该用户不存在');
throw new BizError(t('notExistUser'));
}
if(userRow.isDel === isDel.DELETE) {
throw new BizError('该用户已被注销');
throw new BizError(t('isDelUser'));
}
if(userRow.status === userConst.status.BAN) {
throw new BizError('该用户已被禁用');
throw new BizError(t('isBanUser'));
}
if (!await cryptoUtils.verifyPassword(password, userRow.salt, userRow.password)) {
throw new BizError('密码输入错误');
throw new BizError(t('IncorrectPwd'));
}
const uuid = uuidv4();

View File

@@ -5,13 +5,19 @@ import rolePerm from '../entity/role-perm';
import user from '../entity/user';
import role from '../entity/role';
import { permConst } from '../const/entity-const';
import { t } from '../i18n/i18n'
const permService = {
async tree(c) {
const pList = await orm(c).select().from(perm).where(eq(perm.pid, 0)).orderBy(asc(perm.sort)).all();
const cList = await orm(c).select().from(perm).where(ne(perm.pid, 0)).orderBy(asc(perm.sort)).all();
cList.forEach(cItem => {
cItem.name = t('perms.' + cItem.name)
})
pList.forEach(pItem => {
pItem.name = t('perms.' + pItem.name)
pItem.children = cList.filter(cItem => cItem.pid === pItem.permId)
})
return pList;

View File

@@ -5,6 +5,8 @@ import roleService from './role-service';
import BizError from '../error/biz-error';
import { formatDetailDate, toUtc } from '../utils/date-uitil';
import userService from './user-service';
import { t } from '../i18n/i18n.js';
const regKeyService = {
async add(c, params, userId) {
@@ -12,26 +14,26 @@ const regKeyService = {
let {code,roleId,count,expireTime} = params;
if (!code) {
throw new BizError('注册码不能为空');
throw new BizError(t('emptyRegKey'));
}
if (!count) {
throw new BizError('使用次数不能为空');
throw new BizError(t('emptyRegKey'));
}
if (!expireTime) {
throw new BizError('有效时间不能为空');
throw new BizError(t('emptyRegKeyExpire'));
}
const regKeyRow = await orm(c).select().from(regKey).where(eq(regKey.code, code)).get();
if (regKeyRow) {
throw new BizError('注册码已存在');
throw new BizError(t('isExistRegKye'));
}
const roleRow = roleService.selectById(c, roleId);
if (!roleRow) {
throw new BizError('权限身份不存在');
throw new BizError(t('roleNotExist'));
}
expireTime = formatDetailDate(expireTime)

View File

@@ -7,8 +7,8 @@ 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';
import { t } from '../i18n/i18n.js';
const roleService = {
@@ -17,19 +17,19 @@ const roleService = {
let { name, permIds, banEmail } = params;
if (!name) {
throw new BizError('身份名不能为空');
throw new BizError(t('emptyRoleName'));
}
let roleRow = await orm(c).select().from(role).where(eq(role.name, name)).get();
if (roleRow) {
throw new BizError('身份名已存在');
throw new BizError(t('roleNameExist'));
}
const notEmailIndex = banEmail.findIndex(item => !verifyUtils.isEmail(item))
if (notEmailIndex > -1) {
throw new BizError('非法邮箱');
throw new BizError(t('notEmail'));
}
banEmail = banEmail.join(',')
@@ -67,7 +67,7 @@ const roleService = {
let { name, permIds, roleId, banEmail } = params;
if (!name) {
throw new BizError('名字不能为空');
throw new BizError(t('emptyRoleName'));
}
delete params.isDefault
@@ -75,7 +75,7 @@ const roleService = {
const notEmailIndex = banEmail.findIndex(item => !verifyUtils.isEmail(item))
if (notEmailIndex > -1) {
throw new BizError('非法邮箱');
throw new BizError(t('notEmail'));
}
banEmail = banEmail.join(',')
@@ -97,11 +97,11 @@ const roleService = {
const roleRow = await orm(c).select().from(role).where(eq(role.roleId, roleId)).get();
if (!roleRow) {
throw new BizError('身份不存在');
throw new BizError(t('notExist'));
}
if (roleRow.isDefault) {
throw new BizError('默认身份不能删除');
throw new BizError(t('delDefRole'));
}
const defRoleRow = await orm(c).select().from(role).where(eq(role.isDefault, roleConst.isDefault.OPEN)).get();
@@ -124,7 +124,7 @@ const roleService = {
async setDefault(c, params) {
const roleRow = await orm(c).select().from(role).where(eq(role.roleId, params.roleId)).get();
if (!roleRow) {
throw new BizError('身份不存在');
throw new BizError(t('roleNotExist'));
}
await orm(c).update(role).set({ isDefault: 0 }).run();
await orm(c).update(role).set({ isDefault: 1 }).where(eq(role.roleId, params.roleId)).run();

View File

@@ -9,6 +9,7 @@ import accountService from './account-service';
import userService from './user-service';
import constant from '../const/constant';
import BizError from '../error/biz-error';
import { t } from '../i18n/i18n'
const settingService = {
@@ -22,7 +23,7 @@ const settingService = {
const setting = await c.env.kv.get(KvConst.SETTING, { type: 'json' });
let domainList = c.env.domain;
if (typeof domainList === 'string') {
throw new BizError('环境变量domain必须是JSON类型');
throw new BizError(t('notJsonDomain'));
}
domainList = domainList.map(item => '@' + item);
setting.domainList = domainList;
@@ -70,11 +71,11 @@ const settingService = {
if (!c.env.r2) {
throw new BizError('r2对象存储未配置不能上传背景');
throw new BizError(t('noOsUpBack'));
}
if (!settingRow.r2Domain) {
throw new BizError('r2域名未配置不上传背景');
throw new BizError(t('noOsDomainUpBack'));
}
const { background } = params;

View File

@@ -6,17 +6,17 @@ import { and, desc, eq, lt, sql, inArray } from 'drizzle-orm';
import email from '../entity/email';
import { isDel } from '../const/entity-const';
import attService from "./att-service";
import { t } from '../i18n/i18n'
const starService = {
async add(c, params, userId) {
const { emailId } = params;
const email = await emailService.selectById(c, emailId);
if (!email) {
throw new BizError('星标的邮件不存在');
throw new BizError(t('starNotExistEmail'));
}
if (!email.userId === userId) {
throw new BizError('星标的邮件非当前用户所有');
throw new BizError(t('starNotExistEmail'));
}
const exist = await orm(c).select().from(star).where(
and(

View File

@@ -1,12 +1,13 @@
import BizError from '../error/biz-error';
import settingService from './setting-service';
import { t } from '../i18n/i18n'
const turnstileService = {
async verify(c, token) {
if (!token) {
throw new BizError('验证token不能为空');
throw new BizError(t('emptyBotToken'));
}
const settingRow = await settingService.query(c)
@@ -26,7 +27,7 @@ const turnstileService = {
const result = await res.json();
if (!result.success) {
throw new BizError('人机验证失败,请重试',400)
throw new BizError(t('botVerifyFail'),400)
}
}
};

View File

@@ -2,7 +2,7 @@ import BizError from '../error/biz-error';
import accountService from './account-service';
import orm from '../entity/orm';
import user from '../entity/user';
import { and, asc, count, desc, eq, inArray, like, sql } from 'drizzle-orm';
import { and, asc, count, desc, eq, inArray, sql } from 'drizzle-orm';
import { emailConst, isDel, roleConst, userConst } from '../const/entity-const';
import kvConst from '../const/kv-const';
import KvConst from '../const/kv-const';
@@ -15,6 +15,7 @@ import roleService from './role-service';
import emailUtils from '../utils/email-utils';
import saltHashUtils from '../utils/crypto-utils';
import constant from '../const/constant';
import { t } from '../i18n/i18n'
const userService = {
@@ -50,7 +51,7 @@ const userService = {
const { password } = params;
if (password < 6) {
throw new BizError('密码不能小于6位');
throw new BizError(t('pwdMinLengthLimit'));
}
const { salt, hash } = await cryptoUtils.hashPassword(password);
await orm(c).update(user).set({ password: hash, salt: salt }).where(eq(user.userId, userId)).run();
@@ -295,7 +296,7 @@ const userService = {
const roleRow = await roleService.selectById(c, type);
if (!roleRow) {
throw new BizError('身份不存在');
throw new BizError(t('roleNotExist'));
}
await orm(c)
@@ -327,27 +328,27 @@ const userService = {
const { email, type, password } = params;
if (!c.env.domain.includes(emailUtils.getDomain(email))) {
throw new BizError('非法邮箱域名');
throw new BizError(t('notEmailDomain'));
}
if (password.length < 6) {
throw new BizError('密码必须大于6位');
throw new BizError(t('pwdMinLengthLimit'));
}
const accountRow = await accountService.selectByEmailIncludeDel(c, email);
if (accountRow && accountRow.isDel === isDel.DELETE) {
throw new BizError('该邮箱已被注销');
throw new BizError(t('isDelUser'));
}
if (accountRow) {
throw new BizError('该邮箱已被注册');
throw new BizError(t('isRegAccount'));
}
const role = roleService.selectById(c, type);
if (!role) {
throw new BizError('权限身份不存在');
throw new BizError(t('roleNotExist'));
}
const { salt, hash } = await saltHashUtils.hashPassword(password);