From a66c9cfa3190aee3442541d8a5f81e7f61cece89 Mon Sep 17 00:00:00 2001 From: eoao Date: Tue, 15 Jul 2025 20:09:56 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=B3=A8=E5=86=8C=E7=A0=81?= =?UTF-8?q?=E3=80=81=E8=8D=89=E7=A8=BF=E7=AE=B1=E3=80=81=E6=9D=83=E9=99=90?= =?UTF-8?q?=E6=8B=A6=E6=88=AA=E9=82=AE=E4=BB=B6=E3=80=81=E5=8F=91=E4=BB=B6?= =?UTF-8?q?=E8=8F=9C=E5=8D=95=E9=9A=90=E8=97=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mail-vue/package-lock.json | 14 +- mail-vue/package.json | 1 + mail-vue/src/axios/index.js | 4 +- .../src/components/email-scroll/index.vue | 63 ++- mail-vue/src/components/tiny-editor/index.vue | 11 +- mail-vue/src/db/db.js | 25 + mail-vue/src/layout/account/index.vue | 29 +- mail-vue/src/layout/aside/index.vue | 14 +- mail-vue/src/layout/header/index.vue | 19 +- mail-vue/src/layout/main/index.vue | 2 +- mail-vue/src/layout/write/index.vue | 117 +++- mail-vue/src/request/reg-key.js | 21 + mail-vue/src/request/sys-email.js | 6 +- mail-vue/src/router/index.js | 10 + mail-vue/src/store/draft.js | 8 + mail-vue/src/utils/day.js | 4 + mail-vue/src/utils/perm.js | 10 + mail-vue/src/utils/text.js | 9 + mail-vue/src/views/analysis/index.vue | 4 +- mail-vue/src/views/draft/index.vue | 104 ++++ mail-vue/src/views/login/index.vue | 30 +- mail-vue/src/views/reg-key/index.vue | 528 ++++++++++++++++++ mail-vue/src/views/role/index.vue | 65 ++- mail-vue/src/views/sys-email/index.vue | 47 +- mail-vue/src/views/sys-setting/index.vue | 156 ++++-- mail-vue/src/views/user/index.vue | 14 + mail-worker/dist/assets/index-B33pG0J-.js | 181 ++++++ mail-worker/dist/assets/index-BXq2c1cR.js | 179 ------ mail-worker/dist/assets/index-DAVUbrwg.css | 1 - mail-worker/dist/assets/index-GJlLLp1y.css | 1 + mail-worker/dist/index.html | 4 +- mail-worker/package-lock.json | 4 +- mail-worker/src/api/r2-api.js | 1 - mail-worker/src/api/reg-key-api.js | 29 + mail-worker/src/api/sys-email-api.js | 4 +- mail-worker/src/const/entity-const.js | 9 + mail-worker/src/email/email.js | 56 +- mail-worker/src/entity/reg-key.js | 12 + mail-worker/src/entity/role.js | 2 + mail-worker/src/entity/setting.js | 1 + mail-worker/src/entity/user.js | 1 + mail-worker/src/hono/webs.js | 1 + mail-worker/src/init/init.js | 52 ++ mail-worker/src/security/security.js | 20 +- mail-worker/src/service/account-service.js | 5 +- mail-worker/src/service/analysis-service.js | 7 +- mail-worker/src/service/email-service.js | 24 +- mail-worker/src/service/login-service.js | 99 +++- mail-worker/src/service/r2-service.js | 3 - mail-worker/src/service/reg-key-service.js | 100 ++++ mail-worker/src/service/role-service.js | 32 +- mail-worker/src/service/setting-service.js | 13 +- mail-worker/src/service/user-service.js | 9 + mail-worker/src/utils/date-uitil.js | 13 + mail-worker/wrangler.toml | 2 +- 55 files changed, 1819 insertions(+), 361 deletions(-) create mode 100644 mail-vue/src/db/db.js create mode 100644 mail-vue/src/request/reg-key.js create mode 100644 mail-vue/src/store/draft.js create mode 100644 mail-vue/src/utils/text.js create mode 100644 mail-vue/src/views/draft/index.vue create mode 100644 mail-vue/src/views/reg-key/index.vue create mode 100644 mail-worker/dist/assets/index-B33pG0J-.js delete mode 100644 mail-worker/dist/assets/index-BXq2c1cR.js delete mode 100644 mail-worker/dist/assets/index-DAVUbrwg.css create mode 100644 mail-worker/dist/assets/index-GJlLLp1y.css create mode 100644 mail-worker/src/api/reg-key-api.js create mode 100644 mail-worker/src/entity/reg-key.js create mode 100644 mail-worker/src/service/reg-key-service.js create mode 100644 mail-worker/src/utils/date-uitil.js diff --git a/mail-vue/package-lock.json b/mail-vue/package-lock.json index d4f6703..b2e79a3 100644 --- a/mail-vue/package-lock.json +++ b/mail-vue/package-lock.json @@ -14,13 +14,13 @@ "compressorjs": "^1.2.1", "date-time-format-timezone": "^1.0.22", "dayjs": "^1.11.13", + "dexie": "^4.0.11", "echarts": "^5.6.0", "element-plus": "^2.9.5", "lodash-es": "^4.17.21", "path": "^0.12.7", "pinia": "^3.0.2", "pinia-plugin-persistedstate": "^4.2.0", - "postal-mime": "^2.4.3", "screenfull": "^6.0.2", "vue": "^3.5.13", "vue-cropper": "^1.1.4", @@ -1826,6 +1826,12 @@ "node": ">=0.10" } }, + "node_modules/dexie": { + "version": "4.0.11", + "resolved": "https://registry.npmmirror.com/dexie/-/dexie-4.0.11.tgz", + "integrity": "sha512-SOKO002EqlvBYYKQSew3iymBoN2EQ4BDw/3yprjh7kAfFzjBYkaMNa/pZvcA7HSWlcKSQb9XhPe3wKyQ0x4A8A==", + "license": "Apache-2.0" + }, "node_modules/dotenv": { "version": "16.5.0", "resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-16.5.0.tgz", @@ -2954,12 +2960,6 @@ "pathe": "^2.0.3" } }, - "node_modules/postal-mime": { - "version": "2.4.3", - "resolved": "https://registry.npmmirror.com/postal-mime/-/postal-mime-2.4.3.tgz", - "integrity": "sha512-KT8P+/We7LH48EhjmLRLEiQx+HI6zZ5TTeuzhuZkMbcJnp9mdmxm/FgiRIzmhZm8mxmk8mtisIAelq1wNwumxg==", - "license": "MIT-0" - }, "node_modules/postcss": { "version": "8.5.3", "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.3.tgz", diff --git a/mail-vue/package.json b/mail-vue/package.json index 7a33420..8499d3b 100644 --- a/mail-vue/package.json +++ b/mail-vue/package.json @@ -16,6 +16,7 @@ "compressorjs": "^1.2.1", "date-time-format-timezone": "^1.0.22", "dayjs": "^1.11.13", + "dexie": "^4.0.11", "echarts": "^5.6.0", "element-plus": "^2.9.5", "lodash-es": "^4.17.21", diff --git a/mail-vue/src/axios/index.js b/mail-vue/src/axios/index.js index 1831dc5..ece12b7 100644 --- a/mail-vue/src/axios/index.js +++ b/mail-vue/src/axios/index.js @@ -51,7 +51,9 @@ http.interceptors.response.use((res) => { }) reject(data) } - resolve(data.data) + setTimeout(() => { + resolve(data.data) + },1000) }) }, (error) => { diff --git a/mail-vue/src/components/email-scroll/index.vue b/mail-vue/src/components/email-scroll/index.vue index 6f5aede..5500186 100644 --- a/mail-vue/src/components/email-scroll/index.vue +++ b/mail-vue/src/components/email-scroll/index.vue @@ -2,10 +2,10 @@
@@ -33,7 +33,7 @@ :data-checked="item.checked" @click="jumpDetails(item)" > - +
@@ -102,7 +102,9 @@
- {{ item.name }} + + {{ item.name }} + @@ -111,7 +113,11 @@
- 已删除 + 已删除
@@ -201,11 +207,15 @@ const props = defineProps({ allowStar: { type: Boolean, default: true + }, + type: { + type: String, + default: '' } }) -const emit = defineEmits(['jump', 'refresh-before']) +const emit = defineEmits(['jump', 'refresh-before', 'delete-draft']) const settingStore = useSettingStore() const uiStore = useUiStore(); @@ -355,13 +365,19 @@ function changeAccountShow() { uiStore.accountShow = !uiStore.accountShow; } - const handleDelete = () => { ElMessageBox.confirm('确认批量删除这些邮件吗?', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { + + if (props.type === 'draft') { + const draftIds = getSelectedDraftsIds(); + emit('delete-draft', draftIds); + return; + } + const emailIds = getSelectedMailsIds(); props.emailDelete(emailIds).then(() => { ElMessage({ @@ -395,7 +411,6 @@ function addItem(email) { return } - if (props.timeSort) { if (noLoading.value) { emailList.push(email) @@ -430,6 +445,9 @@ function getSelectedMailsIds() { return emailList.filter(item => item.checked).map(item => item.emailId); } +function getSelectedDraftsIds() { + return emailList.filter(item => item.checked).map(item => item.draftId); +} function updateCheckStatus() { const checkedCount = emailList.filter(item => item.checked).length; @@ -543,6 +561,7 @@ function loadData() { justify-content: center; align-items: center; height: 100%; + width: 100%; } .noLoading { @@ -625,13 +644,26 @@ function loadData() { } } - .el-checkbox { + .checkbox { display: flex; padding-left: 15px; padding-right: 20px; justify-content: center; } + .sys-email-checkbox { + display: flex; + padding-left: 15px; + padding-right: 20px; + justify-content: center; + @media (min-width: 1200px) { + justify-content: start; + height: 100%; + align-self: start; + padding-top: 3px; + } + } + .title-column { @media (max-width: 1200px) { grid-template-columns: 1fr !important; @@ -642,7 +674,7 @@ function loadData() { .title { flex: 1; display: grid; - grid-template-columns: 220px 1fr; + grid-template-columns: 240px 1fr; @media (max-width: 1199px) { padding-right: 15px; } @@ -702,6 +734,9 @@ function loadData() { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; + @media (min-width: 1200px) { + padding-left: 5px; + } } .email-content { @@ -788,7 +823,7 @@ function loadData() { display: flex; flex-wrap: wrap; align-items: center; - column-gap: 15px; + column-gap: 18px; row-gap: 8px; padding-left: 2px; } diff --git a/mail-vue/src/components/tiny-editor/index.vue b/mail-vue/src/components/tiny-editor/index.vue index f580289..c3146c7 100644 --- a/mail-vue/src/components/tiny-editor/index.vue +++ b/mail-vue/src/components/tiny-editor/index.vue @@ -6,12 +6,13 @@ diff --git a/mail-vue/src/request/reg-key.js b/mail-vue/src/request/reg-key.js new file mode 100644 index 0000000..c9332b4 --- /dev/null +++ b/mail-vue/src/request/reg-key.js @@ -0,0 +1,21 @@ +import http from '@/axios/index.js'; + +export function regKeyList(params) { + return http.get('/regKey/list', {params:{...params}}) +} + +export function regKeyAdd(form) { + return http.post('/regKey/add',form) +} + +export function regKeyDelete(regKeyIds) { + return http.delete('/regKey/delete?regKeyIds='+ regKeyIds) +} + +export function regKeyClearNotUse() { + return http.delete('/regKey/clearNotUse') +} + +export function regKeyHistory(regKeyId) { + return http.get('/regKey/history', {params:{regKeyId}}) +} diff --git a/mail-vue/src/request/sys-email.js b/mail-vue/src/request/sys-email.js index 07c1a5f..43fdebf 100644 --- a/mail-vue/src/request/sys-email.js +++ b/mail-vue/src/request/sys-email.js @@ -1,9 +1,9 @@ import http from '@/axios/index.js'; -export function sysEmailAll(params) { - return http.get('/sys-email/list', {params: {...params}}) +export function sysEmailList(params) { + return http.get('/sysEmail/list', {params: {...params}}) } export function sysEmailDelete(emailIds) { - return http.delete('/sys-email/delete?emailIds=' + emailIds) + return http.delete('/sysEmail/delete?emailIds=' + emailIds) } diff --git a/mail-vue/src/router/index.js b/mail-vue/src/router/index.js index d644629..75c91c2 100644 --- a/mail-vue/src/router/index.js +++ b/mail-vue/src/router/index.js @@ -28,6 +28,16 @@ const routes = [ menu: true } }, + { + path: '/draft', + name: 'draft', + component: () => import('@/views/draft/index.vue'), + meta: { + title: '草稿箱', + name: 'draft', + menu: true + } + }, { path: '/content', name: 'content', diff --git a/mail-vue/src/store/draft.js b/mail-vue/src/store/draft.js new file mode 100644 index 0000000..93bb469 --- /dev/null +++ b/mail-vue/src/store/draft.js @@ -0,0 +1,8 @@ +import { defineStore } from 'pinia' + +export const userDraftStore = defineStore('draft', { + state: () => ({ + refreshList: 0, + setDraft: {}, + }) +}) \ No newline at end of file diff --git a/mail-vue/src/utils/day.js b/mail-vue/src/utils/day.js index d322d44..aef5fa1 100644 --- a/mail-vue/src/utils/day.js +++ b/mail-vue/src/utils/day.js @@ -40,4 +40,8 @@ export function formatDetailDate(time) { export function tzDayjs(time) { return dayjs.utc(time).tz('Asia/Shanghai') +} + +export function toUtc() { + return dayjs().utc() } \ No newline at end of file diff --git a/mail-vue/src/utils/perm.js b/mail-vue/src/utils/perm.js index 78fb1c4..39db6c4 100644 --- a/mail-vue/src/utils/perm.js +++ b/mail-vue/src/utils/perm.js @@ -47,6 +47,16 @@ const routers = { menu: true } }, + 'reg-key:query': { + path: '/sys/reg-key', + name: 'reg-key', + component: () => import('@/views/reg-key/index.vue'), + meta: { + title: '注册密钥', + name: 'reg-key', + menu: true + } + }, 'sys-email:query': { path: '/sys/email', name: 'sys-email', diff --git a/mail-vue/src/utils/text.js b/mail-vue/src/utils/text.js new file mode 100644 index 0000000..df7a74d --- /dev/null +++ b/mail-vue/src/utils/text.js @@ -0,0 +1,9 @@ +export function getTextWidth(text, font = '14px sans-serif') { + // 强制设置 Canvas 分辨率 + const canvas = document.createElement('canvas'); + canvas.width = 2000; // 足够大的画布 + canvas.style.width = '1000px'; // 避免 CSS 缩放影响 + const ctx = canvas.getContext('2d'); + ctx.font = font; + return ctx.measureText(text).width; +} \ No newline at end of file diff --git a/mail-vue/src/views/analysis/index.vue b/mail-vue/src/views/analysis/index.vue index 032ae64..91a8428 100644 --- a/mail-vue/src/views/analysis/index.vue +++ b/mail-vue/src/views/analysis/index.vue @@ -680,7 +680,7 @@ function createSendGauge() { display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; gap: 20px; - @media (max-width: 1024px) { + @media (max-width: 1199px) { grid-template-columns: 1fr 1fr; gap: 15px; } @@ -691,7 +691,7 @@ function createSendGauge() { background: #fff; border-radius: 8px; border: 1px solid var(--el-border-color); - padding: 25px 20px; + padding: 21px 20px; .top { display: grid; justify-content: space-between; diff --git a/mail-vue/src/views/draft/index.vue b/mail-vue/src/views/draft/index.vue new file mode 100644 index 0000000..955b14b --- /dev/null +++ b/mail-vue/src/views/draft/index.vue @@ -0,0 +1,104 @@ + + + + \ No newline at end of file diff --git a/mail-vue/src/views/login/index.vue b/mail-vue/src/views/login/index.vue index bdbfa4f..b494dea 100644 --- a/mail-vue/src/views/login/index.vue +++ b/mail-vue/src/views/login/index.vue @@ -69,6 +69,8 @@ + +
{ + + const form = { + email: registerForm.email + suffix.value, + password: registerForm.password, + token: verifyToken, + code: registerForm.code + } + + register(form).then(() => { show.value = 'login' registerForm.email = '' registerForm.password = '' registerForm.confirmPassword = '' + registerForm.code = '' registerLoading.value = false turnstileId = null verifyToken = '' diff --git a/mail-vue/src/views/reg-key/index.vue b/mail-vue/src/views/reg-key/index.vue new file mode 100644 index 0000000..b184211 --- /dev/null +++ b/mail-vue/src/views/reg-key/index.vue @@ -0,0 +1,528 @@ + + + + + \ No newline at end of file diff --git a/mail-vue/src/views/role/index.vue b/mail-vue/src/views/role/index.vue index 0c7ce34..73b607d 100644 --- a/mail-vue/src/views/role/index.vue +++ b/mail-vue/src/views/role/index.vue @@ -47,10 +47,15 @@
- +
+ + + + +
@@ -74,15 +79,18 @@
{{node.label}} - + + + + - +
@@ -102,6 +110,7 @@ import {roleAdd, roleDelete, rolePermTree, roleRoleList, roleSet, roleSetDef} fr import loading from '@/components/loading/index.vue'; import {useRoleStore} from "@/store/role.js"; import {useUserStore} from "@/store/user.js"; +import {isEmail} from "@/utils/verify-utils.js"; defineOptions({ name: 'role' @@ -128,9 +137,11 @@ const dialogType = reactive({ const form = reactive({ name: null, description: null, + banEmail: [], + banEmailType: 0, sendType: 'count', - sendCount: '', - accountCount: '', + sendCount: 0, + accountCount: 0, sort: 0, isDefault: 0, }) @@ -145,6 +156,21 @@ rolePermTree().then(tree => { treeList.push(...tree) }) +function banEmailAddTag(val) { + const emails = Array.from(new Set( + val.split(/[,,]/).map(item => item.trim()).filter(item => item) + )); + + form.banEmail.splice(form.banEmail.length - 1, 1) + + emails.forEach(email => { + if (isEmail(email) && !form.banEmail.includes(email)) { + form.banEmail.push(email) + } + }) +} + + function roleFormClick() { if (dialogType.type === 'add') { addRole() @@ -240,8 +266,10 @@ function resetForm() { form.description = null form.sort = 0 form.sendType = 'count' - form.sendCount = '' - form.accountCount = '' + form.sendCount = 0 + form.accountCount = 0 + form.banEmail = [] + form.banEmailType = 0 tree.value.setCheckedKeys([]) } @@ -256,6 +284,7 @@ function openRoleSet(role) { form.sendType = role.sendType form.sendCount = role.sendCount form.accountCount = role.accountCount + form.banEmail = role.banEmail nextTick(() => { tree.value.setCheckedKeys(role.permIds) }) @@ -346,7 +375,7 @@ window.onresize = () => { padding: 9px 15px; display: flex; align-items: center; - gap: 20px; + gap: 18px; box-shadow: inset 0 -1px 0 0 rgba(100, 121, 143, 0.12); font-size: 18px; .search { @@ -362,6 +391,14 @@ window.onresize = () => { } } +.warning { + position: relative; + left: 5px; + top: 5px; + color: gray; + cursor: pointer; +} + :deep(.description) { white-space: nowrap; overflow: hidden; @@ -397,6 +434,12 @@ window.onresize = () => { .dialog-input { margin-bottom: 15px !important; } + .dialog-radio { + margin-top: 5px; + margin-bottom: 5px; + } + .dialog-input-tag { + } } .perm-expand { @@ -406,11 +449,11 @@ window.onresize = () => { bottom: 5px; } + :deep(.el-dialog) { - margin-top: 15vh !important; margin-bottom: 20px !important; - width: 400px !important; - @media (max-width: 440px) { + width: 460px !important; + @media (max-width: 500px) { width: calc(100% - 40px) !important; margin-right: 20px !important; margin-left: 20px !important; diff --git a/mail-vue/src/views/sys-email/index.vue b/mail-vue/src/views/sys-email/index.vue index 6f8fff1..a6d7373 100644 --- a/mail-vue/src/views/sys-email/index.vue +++ b/mail-vue/src/views/sys-email/index.vue @@ -8,9 +8,11 @@ :show-star="false" show-user-info show-status + actionLeft="4px" :show-account-icon="false" @jump="jumpContent" @refresh-before="refreshBefore" + :type="'sys-email'" >
- +
- +
+ + + + + +
- - + +
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令牌的密钥,随便填一串字符串