-
{{ setting.siteKey || '空' }}
+
{{ setting.siteKey }}
@@ -236,7 +253,7 @@
Secret Key
- {{ setting.secretKey || '空' }}
+ {{ setting.secretKey }}
@@ -279,7 +296,7 @@
保存
-
+
@@ -299,7 +316,7 @@
保存
-
-
+
-
+
+
+
+
+
+
+
-
-
+
+
diff --git a/mail-worker/package-lock.json b/mail-worker/package-lock.json
index d07d62b..e6e8966 100644
--- a/mail-worker/package-lock.json
+++ b/mail-worker/package-lock.json
@@ -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",
diff --git a/mail-worker/src/api/r2-api.js b/mail-worker/src/api/r2-api.js
index 8ef7eea..7c33958 100644
--- a/mail-worker/src/api/r2-api.js
+++ b/mail-worker/src/api/r2-api.js
@@ -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);
diff --git a/mail-worker/src/api/reg-key-api.js b/mail-worker/src/api/reg-key-api.js
new file mode 100644
index 0000000..5dd9589
--- /dev/null
+++ b/mail-worker/src/api/reg-key-api.js
@@ -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));
+})
diff --git a/mail-worker/src/api/sys-email-api.js b/mail-worker/src/api/sys-email-api.js
index 61b3af2..8e91705 100644
--- a/mail-worker/src/api/sys-email-api.js
+++ b/mail-worker/src/api/sys-email-api.js
@@ -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));
})
diff --git a/mail-worker/src/const/entity-const.js b/mail-worker/src/const/entity-const.js
index d73ef2a..9d0be47 100644
--- a/mail-worker/src/const/entity-const.js
+++ b/mail-worker/src/const/entity-const.js
@@ -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,
diff --git a/mail-worker/src/email/email.js b/mail-worker/src/email/email.js
index a0655fa..ec878a7 100644
--- a/mail-worker/src/email/email.js
+++ b/mail-worker/src/email/email.js
@@ -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 = [];
diff --git a/mail-worker/src/entity/reg-key.js b/mail-worker/src/entity/reg-key.js
new file mode 100644
index 0000000..918cc6b
--- /dev/null
+++ b/mail-worker/src/entity/reg-key.js
@@ -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
diff --git a/mail-worker/src/entity/role.js b/mail-worker/src/entity/role.js
index 952242f..8dbf622 100644
--- a/mail-worker/src/entity/role.js
+++ b/mail-worker/src/entity/role.js
@@ -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(),
diff --git a/mail-worker/src/entity/setting.js b/mail-worker/src/entity/setting.js
index 5389ea1..fe23dd1 100644
--- a/mail-worker/src/entity/setting.js
+++ b/mail-worker/src/entity/setting.js
@@ -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(),
diff --git a/mail-worker/src/entity/user.js b/mail-worker/src/entity/user.js
index 208b4e9..0a8d101 100644
--- a/mail-worker/src/entity/user.js
+++ b/mail-worker/src/entity/user.js
@@ -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
diff --git a/mail-worker/src/hono/webs.js b/mail-worker/src/hono/webs.js
index 74ca656..1eae786 100644
--- a/mail-worker/src/hono/webs.js
+++ b/mail-worker/src/hono/webs.js
@@ -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;
diff --git a/mail-worker/src/init/init.js b/mail-worker/src/init/init.js
index 0114d59..0daaf1b 100644
--- a/mail-worker/src/init/init.js
+++ b/mail-worker/src/init/init.js
@@ -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();
},
diff --git a/mail-worker/src/security/security.js b/mail-worker/src/security/security.js
index 27de3a8..c5ee74b 100644
--- a/mail-worker/src/security/security.js
+++ b/mail-worker/src/security/security.js
@@ -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) => {
diff --git a/mail-worker/src/service/account-service.js b/mail-worker/src/service/account-service.js
index 8039162..ac63a92 100644
--- a/mail-worker/src/service/account-service.js
+++ b/mail-worker/src/service/account-service.js
@@ -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();
}
};
diff --git a/mail-worker/src/service/analysis-service.js b/mail-worker/src/service/analysis-service.js
index 11ff98d..66b0a7f 100644
--- a/mail-worker/src/service/analysis-service.js
+++ b/mail-worker/src/service/analysis-service.js
@@ -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();
diff --git a/mail-worker/src/service/email-service.js b/mail-worker/src/service/email-service.js
index 673e278..1bb8f3c 100644
--- a/mail-worker/src/service/email-service.js
+++ b/mail-worker/src/service/email-service.js
@@ -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);
diff --git a/mail-worker/src/service/login-service.js b/mail-worker/src/service/login-service.js
index 99de6fc..8aad731 100644
--- a/mail-worker/src/service/login-service.js
+++ b/mail-worker/src/service/login-service.js
@@ -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) {
diff --git a/mail-worker/src/service/r2-service.js b/mail-worker/src/service/r2-service.js
index 03a52c5..c527e6e 100644
--- a/mail-worker/src/service/r2-service.js
+++ b/mail-worker/src/service/r2-service.js
@@ -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, {
diff --git a/mail-worker/src/service/reg-key-service.js b/mail-worker/src/service/reg-key-service.js
new file mode 100644
index 0000000..84a1443
--- /dev/null
+++ b/mail-worker/src/service/reg-key-service.js
@@ -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;
diff --git a/mail-worker/src/service/role-service.js b/mail-worker/src/service/role-service.js
index 6d833cb..ba7bb6f 100644
--- a/mail-worker/src/service/role-service.js
+++ b/mail-worker/src/service/role-service.js
@@ -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();
}
};
diff --git a/mail-worker/src/service/setting-service.js b/mail-worker/src/service/setting-service.js
index 1be7f87..1f128f7 100644
--- a/mail-worker/src/service/setting-service.js
+++ b/mail-worker/src/service/setting-service.js
@@ -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
};
}
};
diff --git a/mail-worker/src/service/user-service.js b/mail-worker/src/service/user-service.js
index 20b345f..293c135 100644
--- a/mail-worker/src/service/user-service.js
+++ b/mail-worker/src/service/user-service.js
@@ -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();
}
};
diff --git a/mail-worker/src/utils/date-uitil.js b/mail-worker/src/utils/date-uitil.js
new file mode 100644
index 0000000..3a0ba45
--- /dev/null
+++ b/mail-worker/src/utils/date-uitil.js
@@ -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())
+}
diff --git a/mail-worker/wrangler.toml b/mail-worker/wrangler.toml
index 05b92b4..cb78b4c 100644
--- a/mail-worker/wrangler.toml
+++ b/mail-worker/wrangler.toml
@@ -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令牌的密钥,随便填一串字符串