新增注册码、草稿箱、权限拦截邮件、发件菜单隐藏

This commit is contained in:
eoao
2025-07-15 20:09:56 +08:00
parent 7e5a383bc6
commit 254325431b
55 changed files with 1819 additions and 361 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -6,8 +6,8 @@
<title></title>
<link rel="icon" href="/assets/favicon-C5dAZutX.svg" type="image/svg+xml">
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
<script type="module" crossorigin src="/assets/index-BXq2c1cR.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DAVUbrwg.css">
<script type="module" crossorigin src="/assets/index-B33pG0J-.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-GJlLLp1y.css">
</head>
<body>
<div id="loading-first">

View File

@@ -1,11 +1,11 @@
{
"name": "mail-cf",
"name": "mail-worker",
"version": "0.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "mail-cf",
"name": "mail-worker",
"version": "0.0.0",
"dependencies": {
"@cloudflare/vite-plugin": "^1.0.5",

View File

@@ -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);

View 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));
})

View File

@@ -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));
})

View File

@@ -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,

View File

@@ -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 = [];

View 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

View File

@@ -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(),

View File

@@ -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(),

View File

@@ -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

View File

@@ -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;

View File

@@ -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();
},

View File

@@ -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) => {

View File

@@ -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();
}
};

View File

@@ -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();

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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, {

View 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;

View File

@@ -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();
}
};

View File

@@ -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
};
}
};

View File

@@ -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();
}
};

View 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())
}

View File

@@ -31,6 +31,6 @@ crons = ["0 16 * * *"] #定时任务每天晚上12点执行
#[vars]
#orm_log = false
#domain = [] #邮件域名可配置多个 示例: ["example1.com","example2.com"]
#domain = [] #邮件域名可配置多个 示例: ["example1.com","example2.com"]
#admin = "" #管理员的邮箱 示例: admin@example.com
#jwt_secret = "" #jwt令牌的密钥,随便填一串字符串