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

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

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