新增注册码、草稿箱、权限拦截邮件、发件菜单隐藏
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
import r2Service from '../service/r2-service';
|
||||
import app from '../hono/hono';
|
||||
|
||||
|
||||
app.get('/file/*', async (c) => {
|
||||
const key = c.req.path.split('/file/')[1];
|
||||
const obj = await r2Service.getObj(c, key);
|
||||
|
||||
29
mail-worker/src/api/reg-key-api.js
Normal file
29
mail-worker/src/api/reg-key-api.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import app from '../hono/hono';
|
||||
import result from '../model/result';
|
||||
import regKeyService from '../service/reg-key-service';
|
||||
import userContext from '../security/user-context';
|
||||
|
||||
app.post('/regKey/add', async (c) => {
|
||||
await regKeyService.add(c, await c.req.json(), await userContext.getUserId(c));
|
||||
return c.json(result.ok());
|
||||
})
|
||||
|
||||
app.get('/regKey/list', async (c) => {
|
||||
const list = await regKeyService.list(c, c.req.query());
|
||||
return c.json(result.ok(list));
|
||||
})
|
||||
|
||||
app.delete('/regKey/delete', async (c) => {
|
||||
await regKeyService.delete(c, c.req.query());
|
||||
return c.json(result.ok());
|
||||
})
|
||||
|
||||
app.delete('/regKey/clearNotUse', async (c) => {
|
||||
await regKeyService.clearNotUse(c);
|
||||
return c.json(result.ok());
|
||||
})
|
||||
|
||||
app.get('/regKey/history', async (c) => {
|
||||
const list = await regKeyService.history(c, c.req.query());
|
||||
return c.json(result.ok(list));
|
||||
})
|
||||
@@ -2,12 +2,12 @@ import app from '../hono/hono';
|
||||
import emailService from '../service/email-service';
|
||||
import result from '../model/result';
|
||||
|
||||
app.get('/sys-email/list',async (c) => {
|
||||
app.get('/sysEmail/list',async (c) => {
|
||||
const data = await emailService.allList(c, c.req.query());
|
||||
return c.json(result.ok(data));
|
||||
})
|
||||
|
||||
app.delete('/sys-email/delete',async (c) => {
|
||||
app.delete('/sysEmail/delete',async (c) => {
|
||||
const list = await emailService.physicsDelete(c, c.req.query());
|
||||
return c.json(result.ok(list));
|
||||
})
|
||||
|
||||
@@ -10,6 +10,10 @@ export const roleConst = {
|
||||
CLOSE: 0,
|
||||
OPEN: 1
|
||||
},
|
||||
banEmailType: {
|
||||
ALL: 0,
|
||||
CONTENT: 1
|
||||
},
|
||||
sendType: {
|
||||
COUNT: 'count',
|
||||
DAY: 'day'
|
||||
@@ -55,6 +59,11 @@ export const settingConst = {
|
||||
OPEN: 0,
|
||||
CLOSE: 1,
|
||||
},
|
||||
regKey: {
|
||||
OPEN: 0,
|
||||
CLOSE: 1,
|
||||
OPTIONAL: 2,
|
||||
},
|
||||
receive: {
|
||||
OPEN: 0,
|
||||
CLOSE: 1,
|
||||
|
||||
@@ -5,11 +5,12 @@ import settingService from '../service/setting-service';
|
||||
import attService from '../service/att-service';
|
||||
import constant from '../const/constant';
|
||||
import fileUtils from '../utils/file-utils';
|
||||
import { attConst, emailConst, isDel, settingConst } from '../const/entity-const';
|
||||
import { emailConst, isDel, roleConst, settingConst } from '../const/entity-const';
|
||||
import emailUtils from '../utils/email-utils';
|
||||
import dayjs from 'dayjs';
|
||||
import utc from 'dayjs/plugin/utc'
|
||||
import timezone from 'dayjs/plugin/timezone'
|
||||
import roleService from '../service/role-service';
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(timezone)
|
||||
|
||||
@@ -33,7 +34,6 @@ export async function email(message, env, ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
const account = await accountService.selectByEmailIncludeDelNoCase({ env: env }, message.to);
|
||||
|
||||
const reader = message.raw.getReader();
|
||||
let content = '';
|
||||
@@ -46,6 +46,53 @@ export async function email(message, env, ctx) {
|
||||
|
||||
const email = await PostalMime.parse(content);
|
||||
|
||||
const account = await accountService.selectByEmailIncludeDelNoCase({ env: env }, message.to);
|
||||
|
||||
if (account && account.email !== env.admin) {
|
||||
|
||||
let { banEmail, banEmailType } = await roleService.selectByUserId({ env: env}, account.userId);
|
||||
|
||||
banEmail = banEmail.split(",").filter(item => item !== "")
|
||||
|
||||
for (const item of banEmail) {
|
||||
|
||||
if (item.startsWith('*@')) {
|
||||
|
||||
const banDomain = emailUtils.getDomain(item.toLowerCase())
|
||||
const receiveDomain = emailUtils.getDomain(email.from.address.toLowerCase())
|
||||
|
||||
if (banDomain === receiveDomain) {
|
||||
|
||||
if (banEmailType === roleConst.banEmailType.ALL) return
|
||||
|
||||
if (banEmailType === roleConst.banEmailType.CONTENT) {
|
||||
email.html = '邮件内容已被移除'
|
||||
email.text = '邮件内容已被移除'
|
||||
email.attachments = []
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (item.toLowerCase() === email.from.address.toLowerCase()) {
|
||||
|
||||
if (banEmailType === roleConst.banEmailType.ALL) return
|
||||
|
||||
if (banEmailType === roleConst.banEmailType.CONTENT) {
|
||||
email.html = '邮件内容已被移除'
|
||||
email.text = '邮件内容已被移除'
|
||||
email.attachments = []
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const toName = email.to.find(item => item.address === message.to)?.name || '';
|
||||
|
||||
const params = {
|
||||
@@ -68,6 +115,11 @@ export async function email(message, env, ctx) {
|
||||
status: emailConst.status.SAVING
|
||||
};
|
||||
|
||||
let headers = message.headers
|
||||
|
||||
console.log(headers.get('X-Cf-Spamh-Score'))
|
||||
console.log(email)
|
||||
|
||||
const attachments = [];
|
||||
const cidAttachments = [];
|
||||
|
||||
|
||||
12
mail-worker/src/entity/reg-key.js
Normal file
12
mail-worker/src/entity/reg-key.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';
|
||||
import { sql } from 'drizzle-orm';
|
||||
export const regKey = sqliteTable('reg_key', {
|
||||
regKeyId: integer('rege_key_id').primaryKey({ autoIncrement: true }),
|
||||
code: text('code').notNull().default(''),
|
||||
count: integer('count').notNull().default(0),
|
||||
roleId: integer('role_id').notNull().default(0),
|
||||
userId: integer('user_id').notNull().default(0),
|
||||
expireTime: text('expire_time'),
|
||||
createTime: text('create_time').notNull().default(sql`CURRENT_TIMESTAMP`)
|
||||
});
|
||||
export default regKey
|
||||
@@ -5,6 +5,8 @@ export const role = sqliteTable('role', {
|
||||
name: text('name').notNull(),
|
||||
key: text('key').notNull(),
|
||||
description: text('description'),
|
||||
banEmail: text('ban_email').notNull().default(''),
|
||||
banEmailType: integer('ban_email_type').notNull().default(0),
|
||||
sort: integer('sort'),
|
||||
isDefault: integer('is_default').default(0),
|
||||
createTime: text('create_time').default(sql`CURRENT_TIMESTAMP`).notNull(),
|
||||
|
||||
@@ -12,6 +12,7 @@ export const setting = sqliteTable('setting', {
|
||||
r2Domain: text('r2_domain'),
|
||||
secretKey: text('secret_key'),
|
||||
siteKey: text('site_key'),
|
||||
regKey: integer('reg_key').default(1).notNull(),
|
||||
background: text('background'),
|
||||
tgBotToken: text('tg_bot_token').default('').notNull(),
|
||||
tgChatId: text('tg_chat_id').default('').notNull(),
|
||||
|
||||
@@ -16,6 +16,7 @@ const user = sqliteTable('user', {
|
||||
device: text('device'),
|
||||
sort: text('sort').default(0),
|
||||
sendCount: text('send_count').default(0),
|
||||
regKeyId: integer('reg_key_id').default(0).notNull(),
|
||||
isDel: integer('is_del').default(0).notNull()
|
||||
});
|
||||
export default user
|
||||
|
||||
@@ -16,4 +16,5 @@ import '../api/role-api'
|
||||
import '../api/sys-email-api'
|
||||
import '../api/init-api'
|
||||
import '../api/analysis-api'
|
||||
import '../api/reg-key-api'
|
||||
export default app;
|
||||
|
||||
@@ -15,10 +15,62 @@ const init = {
|
||||
await this.v1_2DB(c);
|
||||
await this.v1_3DB(c);
|
||||
await this.v1_3_1DB(c);
|
||||
await this.v1_4DB(c);
|
||||
await settingService.refresh(c);
|
||||
return c.text('初始化成功');
|
||||
},
|
||||
|
||||
async v1_4DB(c) {
|
||||
await c.env.db.prepare(`
|
||||
CREATE TABLE IF NOT EXISTS reg_key (
|
||||
rege_key_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
code TEXT NOT NULL COLLATE NOCASE DEFAULT '',
|
||||
count INTEGER NOT NULL DEFAULT 0,
|
||||
role_id INTEGER NOT NULL DEFAULT 0,
|
||||
user_id INTEGER NOT NULL DEFAULT 0,
|
||||
expire_time DATETIME,
|
||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
`).run();
|
||||
|
||||
// 添加不区分大小写的唯一索引
|
||||
try {
|
||||
await c.env.db.prepare(`
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_setting_code ON reg_key(code COLLATE NOCASE)
|
||||
`).run();
|
||||
} catch (e) {
|
||||
console.warn(`跳过创建索引,原因:${e.message}`);
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
await c.env.db.prepare(`
|
||||
INSERT INTO perm (perm_id, name, perm_key, pid, type, sort) VALUES
|
||||
(33,'注册密钥', NULL, 0, 1, 5.1),
|
||||
(34,'密钥查看', 'reg-key:query', 33, 2, 0),
|
||||
(35,'密钥添加', 'reg-key:add', 33, 2, 1),
|
||||
(36,'密钥删除', 'reg-key:delete', 33, 2, 2)`).run();
|
||||
} catch (e) {
|
||||
console.warn(`跳过数据,原因:${e.message}`);
|
||||
}
|
||||
|
||||
const ADD_COLUMN_SQL_LIST = [
|
||||
`ALTER TABLE setting ADD COLUMN reg_key INTEGER NOT NULL DEFAULT 1;`,
|
||||
`ALTER TABLE role ADD COLUMN ban_email TEXT NOT NULL DEFAULT '';`,
|
||||
`ALTER TABLE role ADD COLUMN ban_email_type INTEGER NOT NULL DEFAULT 0;`,
|
||||
`ALTER TABLE user ADD COLUMN reg_key_id INTEGER NOT NULL DEFAULT 0;`
|
||||
];
|
||||
|
||||
for (let sql of ADD_COLUMN_SQL_LIST) {
|
||||
try {
|
||||
await c.env.db.prepare(sql).run();
|
||||
} catch (e) {
|
||||
console.warn(`跳过字段添加,原因:${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
async v1_3_1DB(c) {
|
||||
await c.env.db.prepare(`UPDATE email SET name = SUBSTR(send_email, 1, INSTR(send_email, '@') - 1) WHERE (name IS NULL OR name = '') AND type = ${emailConst.type.RECEIVE}`).run();
|
||||
},
|
||||
|
||||
@@ -29,8 +29,8 @@ const requirePerms = [
|
||||
'/role/tree',
|
||||
'/role/set',
|
||||
'/role/setDefault',
|
||||
'/sys-email/list',
|
||||
'/sys-email/delete',
|
||||
'/sysEmail/list',
|
||||
'/sysEmail/delete',
|
||||
'/setting/physicsDeleteAll',
|
||||
'/setting/setBackground',
|
||||
'/setting/set',
|
||||
@@ -41,7 +41,12 @@ const requirePerms = [
|
||||
'/user/setType',
|
||||
'/user/list',
|
||||
'/user/resetSendCount',
|
||||
'/user/add'
|
||||
'/user/add',
|
||||
'/regKey/add',
|
||||
'/regKey/list',
|
||||
'/regKey/delete',
|
||||
'/regKey/clearNotUse',
|
||||
'/regKey/history'
|
||||
];
|
||||
|
||||
const premKey = {
|
||||
@@ -62,12 +67,15 @@ const premKey = {
|
||||
'user:set-status': ['/user/setStatus'],
|
||||
'user:set-type': ['/user/setType'],
|
||||
'user:delete': ['/user/delete'],
|
||||
'sys-email:query': ['/sys-email/list'],
|
||||
'sys-email:delete': ['/sys-email/delete'],
|
||||
'sys-email:query': ['/sysEmail/list'],
|
||||
'sys-email:delete': ['/sysEmail/delete'],
|
||||
'setting:query': ['/setting/query'],
|
||||
'setting:set': ['/setting/set', '/setting/setBackground'],
|
||||
'setting:clean': ['/setting/physicsDeleteAll'],
|
||||
'analysis:query': ['/analysis/echarts']
|
||||
'analysis:query': ['/analysis/echarts'],
|
||||
'role-key:add': ['/regKey/add'],
|
||||
'role-key:query': ['/regKey/list','/regKey/history'],
|
||||
'role-key:delete': ['/regKey/delete','/regKey/clearNotUse'],
|
||||
};
|
||||
|
||||
app.use('*', async (c, next) => {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
13
mail-worker/src/utils/date-uitil.js
Normal file
13
mail-worker/src/utils/date-uitil.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import dayjs from 'dayjs'
|
||||
import utc from 'dayjs/plugin/utc'
|
||||
import timezone from 'dayjs/plugin/timezone'
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(timezone)
|
||||
|
||||
export function formatDetailDate(time) {
|
||||
return dayjs(time).format('YYYY-MM-DD HH:mm:ss')
|
||||
}
|
||||
|
||||
export function toUtc(time) {
|
||||
return dayjs.utc(time || dayjs())
|
||||
}
|
||||
Reference in New Issue
Block a user