新增公告弹窗和无人收件开关
This commit is contained in:
@@ -1,3 +1,3 @@
|
|||||||
NODE_ENV = 'remote'
|
NODE_ENV = 'remote'
|
||||||
VITE_APP_TITLE = '远程环境'
|
VITE_APP_TITLE = '远程环境'
|
||||||
VITE_BASE_URL = 'xxxxxx'
|
VITE_BASE_URL = 'https://mornglow.top/api'
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
<title></title>
|
<title></title>
|
||||||
<link rel="icon" href="./src/assets/favicon.svg" type="image/svg+xml">
|
<link rel="icon" href="./src/assets/favicon.svg" type="image/svg+xml">
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap" rel="stylesheet">
|
||||||
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
|
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
8
mail-vue/package-lock.json
generated
8
mail-vue/package-lock.json
generated
@@ -16,7 +16,7 @@
|
|||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"dexie": "^4.0.11",
|
"dexie": "^4.0.11",
|
||||||
"echarts": "^5.6.0",
|
"echarts": "^5.6.0",
|
||||||
"element-plus": "^2.9.5",
|
"element-plus": "^2.9.11",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
"pinia": "^3.0.2",
|
"pinia": "^3.0.2",
|
||||||
@@ -1916,9 +1916,9 @@
|
|||||||
"integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
|
"integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
|
||||||
},
|
},
|
||||||
"node_modules/element-plus": {
|
"node_modules/element-plus": {
|
||||||
"version": "2.9.7",
|
"version": "2.10.4",
|
||||||
"resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.9.7.tgz",
|
"resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.10.4.tgz",
|
||||||
"integrity": "sha512-6vjZh5SXBncLhUwJGTVKS5oDljfgGMh6J4zVTeAZK3YdMUN76FgpvHkwwFXocpJpMbii6rDYU3sgie64FyPerQ==",
|
"integrity": "sha512-UD4elWHrCnp1xlPhbXmVcaKFLCRaRAY6WWRwemGfGW3ceIjXm9fSYc9RNH3AiOEA6Ds1p9ZvhCs76CR9J8Vd+A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ctrl/tinycolor": "^3.4.1",
|
"@ctrl/tinycolor": "^3.4.1",
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"dexie": "^4.0.11",
|
"dexie": "^4.0.11",
|
||||||
"echarts": "^5.6.0",
|
"echarts": "^5.6.0",
|
||||||
"element-plus": "^2.9.5",
|
"element-plus": "^2.9.11",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
"pinia": "^3.0.2",
|
"pinia": "^3.0.2",
|
||||||
|
|||||||
@@ -5,9 +5,11 @@
|
|||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
|
import { watch } from "vue";
|
||||||
import {useSettingStore} from "@/store/setting.js";
|
import {useSettingStore} from "@/store/setting.js";
|
||||||
const settingStore = useSettingStore()
|
const settingStore = useSettingStore()
|
||||||
import zhCn from 'element-plus/es/locale/lang/zh-cn';;
|
import zhCn from 'element-plus/es/locale/lang/zh-cn';
|
||||||
const { locale } = useI18n()
|
const { locale } = useI18n()
|
||||||
locale.value = settingStore.lang
|
locale.value = settingStore.lang
|
||||||
|
watch(() => settingStore.lang, () => locale.value = settingStore.lang)
|
||||||
</script>
|
</script>
|
||||||
@@ -5,18 +5,18 @@ const en = {
|
|||||||
starred: 'Starred',
|
starred: 'Starred',
|
||||||
settings: 'Settings',
|
settings: 'Settings',
|
||||||
analytics: 'Analytics',
|
analytics: 'Analytics',
|
||||||
allUsers: 'All users',
|
allUsers: 'All Users',
|
||||||
allMail: 'All mail',
|
allMail: 'All Mail',
|
||||||
permissions: 'Role',
|
permissions: 'Role',
|
||||||
inviteCode: 'Invite code',
|
inviteCode: 'Invite Code',
|
||||||
SystemSettings: 'System settings',
|
SystemSettings: 'System Settings',
|
||||||
noMoreData: 'No more data',
|
noMoreData: 'No more data',
|
||||||
noMessagesFound: 'No messages found',
|
noMessagesFound: 'No messages found',
|
||||||
addAccount: 'Add account',
|
addAccount: 'Add Account',
|
||||||
emailAccount: 'Email',
|
emailAccount: 'Email',
|
||||||
deleteUser: 'Delete account',
|
deleteUser: 'Delete Account',
|
||||||
deleteUserBtn: 'Delete',
|
deleteUserBtn: 'Delete',
|
||||||
changePassword: 'Change password',
|
changePassword: 'Change Password',
|
||||||
newPassword: 'New password',
|
newPassword: 'New password',
|
||||||
confirmPassword: 'Confirm password',
|
confirmPassword: 'Confirm password',
|
||||||
add: 'Add',
|
add: 'Add',
|
||||||
@@ -29,22 +29,23 @@ const en = {
|
|||||||
changePwdBtn: 'Change',
|
changePwdBtn: 'Change',
|
||||||
username: 'Username',
|
username: 'Username',
|
||||||
password: 'Password',
|
password: 'Password',
|
||||||
delAccount: 'Delete account',
|
delAccount: 'Delete Account',
|
||||||
delAccountMsg: 'This will permanently delete your account and data. It cannot be reactivated.',
|
delAccountMsg: 'This will permanently delete your account and data. It cannot be reactivated.',
|
||||||
totalReceived: 'Total received',
|
totalReceived: 'Total Received',
|
||||||
totalSent: 'Total sent',
|
totalSent: 'Total Sent',
|
||||||
totalMailboxes: 'Total accounts',
|
totalMailboxes: 'Total Accounts',
|
||||||
totalUsers: 'Total users',
|
totalUsers: 'Total Users',
|
||||||
deleted: 'Deleted',
|
deleted: 'Deleted',
|
||||||
|
selectDeleted: 'Deleted',
|
||||||
active: 'Active',
|
active: 'Active',
|
||||||
emailSource: 'Email source',
|
emailSource: 'Email Source',
|
||||||
userGrowth: 'User growth',
|
userGrowth: 'User Growth',
|
||||||
emailGrowth: 'Email growth',
|
emailGrowth: 'Email Growth',
|
||||||
emailSent: 'Sent',
|
emailSent: 'Sent',
|
||||||
emailReceived: 'Received',
|
emailReceived: 'Received',
|
||||||
sentToday: 'Sent today',
|
sentToday: 'Sent Today',
|
||||||
total: 'Total',
|
total: 'Total',
|
||||||
growthTotalUsers: 'Total users',
|
growthTotalUsers: 'Total Users',
|
||||||
searchByEmail: 'Enter email to search',
|
searchByEmail: 'Enter email to search',
|
||||||
tabEmailAddress: 'Email',
|
tabEmailAddress: 'Email',
|
||||||
tabReceived: 'Received',
|
tabReceived: 'Received',
|
||||||
@@ -63,9 +64,9 @@ const en = {
|
|||||||
tabSetting: 'Settings',
|
tabSetting: 'Settings',
|
||||||
registrationIp: 'Registration IP',
|
registrationIp: 'Registration IP',
|
||||||
recentIP: 'Recent IP',
|
recentIP: 'Recent IP',
|
||||||
recentActivity: 'Recent activity',
|
recentActivity: 'Recent Activity',
|
||||||
loginDevice: 'Login device',
|
loginDevice: 'Login Device',
|
||||||
loginSystem: 'Login system',
|
loginSystem: 'Login System',
|
||||||
browserLogin: 'Browser Login',
|
browserLogin: 'Browser Login',
|
||||||
unauthorized: 'Unauthorized',
|
unauthorized: 'Unauthorized',
|
||||||
unlimited: 'Unlimited',
|
unlimited: 'Unlimited',
|
||||||
@@ -76,10 +77,10 @@ const en = {
|
|||||||
perm: 'Role',
|
perm: 'Role',
|
||||||
btnBan: 'Ban',
|
btnBan: 'Ban',
|
||||||
admin: 'Admin',
|
admin: 'Admin',
|
||||||
addUser: 'Add user',
|
addUser: 'Add User',
|
||||||
select: 'Select',
|
select: 'Select',
|
||||||
unknown: 'Unknown',
|
unknown: 'Unknown',
|
||||||
changePerm: 'Change role',
|
changePerm: 'Change Role',
|
||||||
from: 'From',
|
from: 'From',
|
||||||
subject: 'Subject',
|
subject: 'Subject',
|
||||||
sender: 'Sender',
|
sender: 'Sender',
|
||||||
@@ -91,22 +92,22 @@ const en = {
|
|||||||
order: 'Order',
|
order: 'Order',
|
||||||
default: 'Default',
|
default: 'Default',
|
||||||
description: 'Description',
|
description: 'Description',
|
||||||
removeBody: 'Remove body',
|
removeContent: 'Remove content',
|
||||||
removeAll: 'Remove all',
|
removeAll: 'Remove all',
|
||||||
expand: 'Expand',
|
expand: 'Expand',
|
||||||
collapse: 'Collapse',
|
collapse: 'Collapse',
|
||||||
daily: 'Daily',
|
daily: 'Daily',
|
||||||
searchRegKeyDesc: 'Enter invite code to search',
|
searchRegKeyDesc: 'Enter invite code to search',
|
||||||
remainingUses: 'Remaining uses',
|
remainingUses: 'Remaining Uses',
|
||||||
exhausted: 'Exhausted',
|
exhausted: 'Exhausted',
|
||||||
validUntil: 'Valid until',
|
validUntil: 'Valid Until',
|
||||||
expired: 'Expired',
|
expired: 'Expired',
|
||||||
copy: 'Copy',
|
copy: 'Copy',
|
||||||
history: 'History',
|
history: 'History',
|
||||||
addRegKey: 'Add invite code',
|
addRegKey: 'Add Invite Code',
|
||||||
regKey: 'Invite code',
|
regKey: 'Invite Code',
|
||||||
noCodeFound: 'No messages found',
|
noCodeFound: 'No messages found',
|
||||||
useHistory: 'Usage history',
|
useHistory: 'Usage History',
|
||||||
date: 'Date',
|
date: 'Date',
|
||||||
roleDesc: 'Role',
|
roleDesc: 'Role',
|
||||||
noSubject: 'No subject',
|
noSubject: 'No subject',
|
||||||
@@ -131,43 +132,43 @@ const en = {
|
|||||||
regSwitch: 'Sign up',
|
regSwitch: 'Sign up',
|
||||||
loginSwitch: 'Sign in',
|
loginSwitch: 'Sign in',
|
||||||
websiteSetting: 'Website',
|
websiteSetting: 'Website',
|
||||||
websiteReg: 'Sign up',
|
websiteReg: 'Sign Up',
|
||||||
multipleEmail: 'Multiple accounts',
|
multipleEmail: 'Multiple Accounts',
|
||||||
multipleEmailDesc: 'Enable this feature to allow users to add multiple accounts',
|
multipleEmailDesc: 'Enable this feature to allow users to add multiple accounts.',
|
||||||
physicallyWipeData: 'Physically wipe data',
|
physicallyWipeData: 'Physically Wipe Data',
|
||||||
physicallyWipeDataDesc: 'This action will permanently erase all deleted data',
|
physicallyWipeDataDesc: 'This action will permanently erase all deleted data.',
|
||||||
customization: 'Customization',
|
customization: 'Customization',
|
||||||
websiteTitle: 'Title',
|
websiteTitle: 'Title',
|
||||||
loginBoxOpacity: 'Login box opacity',
|
loginBoxOpacity: 'Login Box Opacity',
|
||||||
loginBackground: 'Background',
|
loginBackground: 'Background',
|
||||||
emailSetting: 'Email',
|
emailSetting: 'Email',
|
||||||
receiveEmails: 'Receive email',
|
receiveEmail: 'Receive Email',
|
||||||
autoRefresh: 'Auto refresh',
|
autoRefresh: 'Auto Refresh',
|
||||||
autoRefreshDesc: 'Automatically fetch the latest emails from the server',
|
autoRefreshDesc: 'Automatically fetch the latest emails from the server.',
|
||||||
sendEmail: 'Send email',
|
sendEmail: 'Send Email',
|
||||||
resendToken: 'Resend Token',
|
resendToken: 'Resend Token',
|
||||||
R2OS: 'R2 Object storage',
|
R2OS: 'R2 Object Storage',
|
||||||
osDomain: 'Domain',
|
osDomain: 'Domain',
|
||||||
emailPush: 'Email push',
|
emailPush: 'Email Push',
|
||||||
tgBot: 'Telegram bot',
|
tgBot: 'Telegram Bot',
|
||||||
disable: 'Disable',
|
disable: 'Disable',
|
||||||
disabled: 'Disabled',
|
disabled: 'Disabled',
|
||||||
otherEmail: 'Forwarding to external email',
|
otherEmail: 'Forwarding to External Email',
|
||||||
forwardingRules: 'Forwarding Rules',
|
forwardingRules: 'Forwarding Rules',
|
||||||
forwardAll: 'All',
|
forwardAll: 'All',
|
||||||
rules: 'Rules',
|
rules: 'Rules',
|
||||||
turnstileSetting: 'Turnstile',
|
turnstileSetting: 'Turnstile',
|
||||||
signUpVerification: 'Sign up verification',
|
signUpVerification: 'Sign Up Verification',
|
||||||
addEmailVerification: 'Add account verification',
|
addEmailVerification: 'Add Account Verification',
|
||||||
about: 'About',
|
about: 'About',
|
||||||
version: 'Version',
|
version: 'Version',
|
||||||
community: 'Community',
|
community: 'Community',
|
||||||
changeTitle: 'Change title',
|
changeTitle: 'Change Title',
|
||||||
addResendTokenDesc: 'Input to add; leave empty to delete.',
|
addResendTokenDesc: 'Input to add; leave empty to delete.',
|
||||||
addOsDomain: 'Add domain',
|
addOsDomain: 'Add Domain',
|
||||||
domainDesc: 'Domain',
|
domainDesc: 'Domain',
|
||||||
addTurnstileSecret: 'Add turnstile secret',
|
addTurnstileSecret: 'Add turnstile secret',
|
||||||
backgroundTitle: 'Change background',
|
backgroundTitle: 'Change Background',
|
||||||
tgBotDesc: 'Forward received emails to a Telegram bot',
|
tgBotDesc: 'Forward received emails to a Telegram bot',
|
||||||
tgBotToken: 'Bot token',
|
tgBotToken: 'Bot token',
|
||||||
toBotTokenDesc: 'Multiple user chat_ids, separated by commas',
|
toBotTokenDesc: 'Multiple user chat_ids, separated by commas',
|
||||||
@@ -175,11 +176,11 @@ const en = {
|
|||||||
otherEmailInputDesc: 'Separate multiple email addresses with commas.',
|
otherEmailInputDesc: 'Separate multiple email addresses with commas.',
|
||||||
forwardingRulesDesc: 'Rule-based forwarding only forwards emails received by the specified address.',
|
forwardingRulesDesc: 'Rule-based forwarding only forwards emails received by the specified address.',
|
||||||
ruleEmailsInputDesc: 'Separate multiple email addresses with commas.',
|
ruleEmailsInputDesc: 'Separate multiple email addresses with commas.',
|
||||||
resendTokenList: 'Token list',
|
resendTokenList: 'Token List',
|
||||||
domain: 'Domain',
|
domain: 'Domain',
|
||||||
optional: 'Optional',
|
optional: 'Optional',
|
||||||
subjectInputDesc: 'Please enter the email subject.',
|
subjectInputDesc: 'Please enter the email subject.',
|
||||||
changeUserName: 'Change username',
|
changeUserName: 'Change Username',
|
||||||
sendSeparately: 'Separately',
|
sendSeparately: 'Separately',
|
||||||
send: 'Send',
|
send: 'Send',
|
||||||
reply: 'Reply',
|
reply: 'Reply',
|
||||||
@@ -204,9 +205,9 @@ const en = {
|
|||||||
addSuccessMsg: 'Addition successful',
|
addSuccessMsg: 'Addition successful',
|
||||||
delConfirm: 'Confirm deleting {msg}?',
|
delConfirm: 'Confirm deleting {msg}?',
|
||||||
emptyRoleNameMsg: 'Role name cannot be empty',
|
emptyRoleNameMsg: 'Role name cannot be empty',
|
||||||
changSuccessMsg: 'Changes saved successfully',
|
saveSuccessMsg: 'Saved successfully',
|
||||||
changeRoleTitle: 'Change role',
|
changeRoleTitle: 'Change Role',
|
||||||
addRoleTitle: 'Add role',
|
addRoleTitle: 'Add Role',
|
||||||
emptyUserNameMsg: 'Name cannot be empty',
|
emptyUserNameMsg: 'Name cannot be empty',
|
||||||
delAccountConfirm: 'Confirm deleting current account and all associated data?',
|
delAccountConfirm: 'Confirm deleting current account and all associated data?',
|
||||||
clearAllDelConfirm: 'This action is irreversible. Enter <b style="font-weight: bold">DELETE</b> to proceed',
|
clearAllDelConfirm: 'This action is irreversible. Enter <b style="font-weight: bold">DELETE</b> to proceed',
|
||||||
@@ -216,7 +217,7 @@ const en = {
|
|||||||
delBackgroundConfirm: 'Confirm deleting this background?',
|
delBackgroundConfirm: 'Confirm deleting this background?',
|
||||||
enable: 'Enable',
|
enable: 'Enable',
|
||||||
enabled: 'Enabled',
|
enabled: 'Enabled',
|
||||||
reSendConfirm: 'Confirm reset of {msg} send total?',
|
reSendConfirm: 'Confirm reset of {msg} send count?',
|
||||||
reSuccessMsg: 'Reset successful',
|
reSuccessMsg: 'Reset successful',
|
||||||
restoreConfirm: 'Confirm restoring {msg}?',
|
restoreConfirm: 'Confirm restoring {msg}?',
|
||||||
normalRestore: 'Normal restore',
|
normalRestore: 'Normal restore',
|
||||||
@@ -234,7 +235,7 @@ const en = {
|
|||||||
sendFailMsg: 'Send failed',
|
sendFailMsg: 'Send failed',
|
||||||
saveDraftConfirm: 'Save draft?',
|
saveDraftConfirm: 'Save draft?',
|
||||||
delEmailsConfirm: 'Confirm batch delete these emails?',
|
delEmailsConfirm: 'Confirm batch delete these emails?',
|
||||||
sending: 'Sending Email...',
|
sending: 'Sending email...',
|
||||||
sendingErrorMsg: 'Sending in progress',
|
sendingErrorMsg: 'Sending in progress',
|
||||||
networkErrorMsg: 'Network error. Check your internet',
|
networkErrorMsg: 'Network error. Check your internet',
|
||||||
timeoutErrorMsg: 'Timeout. Try again later',
|
timeoutErrorMsg: 'Timeout. Try again later',
|
||||||
@@ -249,8 +250,8 @@ const en = {
|
|||||||
supportDesc: 'Buy me tea',
|
supportDesc: 'Buy me tea',
|
||||||
featDesc: 'Feature Description',
|
featDesc: 'Feature Description',
|
||||||
emailInterception: 'Email Interception',
|
emailInterception: 'Email Interception',
|
||||||
emailInterceptionDesc: '*Intercept emails by blocking entire domain using @example.com to prevent users from receiving emails from certain websites.',
|
emailInterceptionDesc: 'Enter a domain or email address to prevent users from receiving emails from certain websites.',
|
||||||
availableDomains: 'Available domains',
|
availableDomains: 'Available Domains',
|
||||||
availableDomainsDesc: 'Restrict users to email domains specified. Domains not on the approved list will be blocked from registration, adding email addresses, and sending/receiving emails. If left blank, all domains will be allowed by default.',
|
availableDomainsDesc: 'Restrict users to email domains specified. Domains not on the approved list will be blocked from registration, adding email addresses, and sending/receiving emails. If left blank, all domains will be allowed by default.',
|
||||||
backgroundUrlDesc: 'Image URL',
|
backgroundUrlDesc: 'Image URL',
|
||||||
localUpload: ' Local upload',
|
localUpload: ' Local upload',
|
||||||
@@ -260,6 +261,24 @@ const en = {
|
|||||||
rulesVerify: 'Rules',
|
rulesVerify: 'Rules',
|
||||||
rulesVerifyTitle: 'Trigger After {count} Daily Uses per IP',
|
rulesVerifyTitle: 'Trigger After {count} Daily Uses per IP',
|
||||||
botVerifyMsg: 'Please verify that you are human',
|
botVerifyMsg: 'Please verify that you are human',
|
||||||
|
noticeTitle: 'Notice',
|
||||||
|
noticePopup: 'Sign-in Popup',
|
||||||
|
icon: 'Icon',
|
||||||
|
position: 'Position',
|
||||||
|
offset: 'Offset',
|
||||||
|
duration: 'Duration',
|
||||||
|
topRight: 'Top Right',
|
||||||
|
topLeft: 'Top Left',
|
||||||
|
bottomRight: 'Bottom Right',
|
||||||
|
bottomLeft: 'Bottom Left',
|
||||||
|
width: 'Width',
|
||||||
|
titleDesc: 'Title',
|
||||||
|
noticeContentDesc: 'Notice content supports HTML',
|
||||||
|
verifyModuleFailed: 'Verification module failed to load. Please refresh the page',
|
||||||
|
popUp: 'Pop Up',
|
||||||
|
noRecipientTitle: 'No Recipient',
|
||||||
|
noRecipientDesc: 'Emails can be received even without a registered email address.',
|
||||||
|
preview: 'Preview'
|
||||||
}
|
}
|
||||||
|
|
||||||
export default en
|
export default en
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ const zh = {
|
|||||||
totalMailboxes: '邮箱数量',
|
totalMailboxes: '邮箱数量',
|
||||||
totalUsers: '用户数量',
|
totalUsers: '用户数量',
|
||||||
deleted: '删除',
|
deleted: '删除',
|
||||||
|
selectDeleted: '已删除',
|
||||||
active: '正常',
|
active: '正常',
|
||||||
emailSource: '邮件来源',
|
emailSource: '邮件来源',
|
||||||
userGrowth: '用户增长',
|
userGrowth: '用户增长',
|
||||||
@@ -91,7 +92,7 @@ const zh = {
|
|||||||
order: '排序',
|
order: '排序',
|
||||||
default: '默认',
|
default: '默认',
|
||||||
description: '描述',
|
description: '描述',
|
||||||
removeBody: '移除正文',
|
removeContent: '移除正文',
|
||||||
removeAll: '丢弃邮件',
|
removeAll: '丢弃邮件',
|
||||||
expand: '展开',
|
expand: '展开',
|
||||||
collapse: '收起',
|
collapse: '收起',
|
||||||
@@ -141,7 +142,7 @@ const zh = {
|
|||||||
loginBoxOpacity: '登录透明',
|
loginBoxOpacity: '登录透明',
|
||||||
loginBackground: '登录背景',
|
loginBackground: '登录背景',
|
||||||
emailSetting: '邮件设置',
|
emailSetting: '邮件设置',
|
||||||
receiveEmails: '邮件接收',
|
receiveEmail: '邮件接收',
|
||||||
autoRefresh: '自动刷新',
|
autoRefresh: '自动刷新',
|
||||||
autoRefreshDesc: '轮询请求服务器获取最新邮件',
|
autoRefreshDesc: '轮询请求服务器获取最新邮件',
|
||||||
sendEmail: '邮件发送',
|
sendEmail: '邮件发送',
|
||||||
@@ -164,7 +165,7 @@ const zh = {
|
|||||||
community: '交流',
|
community: '交流',
|
||||||
changeTitle: '修改标题',
|
changeTitle: '修改标题',
|
||||||
addResendTokenDesc: '输入内容添加,不填则删除',
|
addResendTokenDesc: '输入内容添加,不填则删除',
|
||||||
addOsDomain: '添加访问域名',
|
addOsDomain: '添加域名',
|
||||||
domainDesc: '域名',
|
domainDesc: '域名',
|
||||||
addTurnstileSecret: '添加 Turnstile 密钥',
|
addTurnstileSecret: '添加 Turnstile 密钥',
|
||||||
backgroundTitle: '设置背景',
|
backgroundTitle: '设置背景',
|
||||||
@@ -204,7 +205,7 @@ const zh = {
|
|||||||
addSuccessMsg: '添加成功',
|
addSuccessMsg: '添加成功',
|
||||||
delConfirm: '确认删除{msg}吗?',
|
delConfirm: '确认删除{msg}吗?',
|
||||||
emptyRoleNameMsg: '身份名不能为空',
|
emptyRoleNameMsg: '身份名不能为空',
|
||||||
changSuccessMsg: '修改成功',
|
saveSuccessMsg: '保存成功',
|
||||||
changeRoleTitle: '修改身份',
|
changeRoleTitle: '修改身份',
|
||||||
addRoleTitle: '添加身份',
|
addRoleTitle: '添加身份',
|
||||||
emptyUserNameMsg: '用户名不能为空',
|
emptyUserNameMsg: '用户名不能为空',
|
||||||
@@ -249,7 +250,7 @@ const zh = {
|
|||||||
supportDesc: '请我喝杯奶茶',
|
supportDesc: '请我喝杯奶茶',
|
||||||
featDesc: '功能说明',
|
featDesc: '功能说明',
|
||||||
emailInterception: '邮件拦截',
|
emailInterception: '邮件拦截',
|
||||||
emailInterceptionDesc: '拦截邮件, 要拦截整个域名输入 *@example.com, 可用于禁止用户接收某些网站的邮件',
|
emailInterceptionDesc: '输入邮箱或域名拦截邮件,可用于禁止用户接收某些网站的邮件',
|
||||||
availableDomains: '可用域名',
|
availableDomains: '可用域名',
|
||||||
availableDomainsDesc: '限制用户只能使用指定的域名邮箱,不在配置名单内的域名会被禁止使用注册添加邮箱,接收发送邮件等功能,留空默认允许可用所有域名',
|
availableDomainsDesc: '限制用户只能使用指定的域名邮箱,不在配置名单内的域名会被禁止使用注册添加邮箱,接收发送邮件等功能,留空默认允许可用所有域名',
|
||||||
backgroundUrlDesc: '在线图片链接',
|
backgroundUrlDesc: '在线图片链接',
|
||||||
@@ -260,5 +261,24 @@ const zh = {
|
|||||||
rulesVerify: '规则',
|
rulesVerify: '规则',
|
||||||
rulesVerifyTitle: 'IP 每天使用 {count} 次后触发',
|
rulesVerifyTitle: 'IP 每天使用 {count} 次后触发',
|
||||||
botVerifyMsg: '请完成人机验证',
|
botVerifyMsg: '请完成人机验证',
|
||||||
|
noticeTitle: '网站公告',
|
||||||
|
noticePopup: '登录弹窗',
|
||||||
|
icon: '图标',
|
||||||
|
position: '位置',
|
||||||
|
offset: '偏移距离',
|
||||||
|
duration: '显示时长',
|
||||||
|
topRight: '右上',
|
||||||
|
topLeft: '左上',
|
||||||
|
bottomRight: '右下',
|
||||||
|
bottomLeft: '左下',
|
||||||
|
width: '宽度',
|
||||||
|
titleDesc: '标题',
|
||||||
|
noticeContentDesc: '公告内容,支持HTML',
|
||||||
|
verifyModuleFailed: '人机验证模块加载失败,请刷新页面',
|
||||||
|
popUp: '弹出',
|
||||||
|
noRecipientTitle: '无人收件',
|
||||||
|
noRecipientDesc: '即使没有注册的邮箱也能收到邮件',
|
||||||
|
preview: '预览'
|
||||||
|
|
||||||
}
|
}
|
||||||
export default zh
|
export default zh
|
||||||
@@ -12,10 +12,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="opt">
|
<div class="opt">
|
||||||
<div class="send-email" @click.stop>
|
<div class="send-email" @click.stop>
|
||||||
<Icon icon="eva:email-fill" width="22" height="22" color="#fbbd08" />
|
<Icon icon="eva:email-fill" width="22" height="22" color="#fccb1a" />
|
||||||
</div>
|
</div>
|
||||||
<div class="settings" @click.stop>
|
<div class="settings" @click.stop>
|
||||||
<Icon icon="streamline-ultimate-color:copy-paste-1" width="19" height="19" @click.stop="copyAccount(item.email)"/>
|
<Icon icon="fluent-color:clipboard-24" width="22" height="22" @click.stop="copyAccount(item.email)"/>
|
||||||
<Icon icon="fluent:settings-24-filled" width="21" height="21" color="#909399" v-if="showNullSetting(item)" />
|
<Icon icon="fluent:settings-24-filled" width="21" height="21" color="#909399" v-if="showNullSetting(item)" />
|
||||||
<el-dropdown v-else>
|
<el-dropdown v-else>
|
||||||
<Icon icon="fluent:settings-24-filled" width="21" height="21" color="#909399" />
|
<Icon icon="fluent:settings-24-filled" width="21" height="21" color="#909399" />
|
||||||
@@ -217,7 +217,7 @@ function setName() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ElMessage({
|
ElMessage({
|
||||||
message: t('changSuccessMsg'),
|
message: t('saveSuccessMsg'),
|
||||||
type: "success",
|
type: "success",
|
||||||
plain: true
|
plain: true
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ const route = useRoute();
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
background: linear-gradient(135deg, #1890ff, #1c6dd0);
|
background: linear-gradient(135deg, #1890ff, #3a80dd);
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
:deep(.el-icon) {
|
:deep(.el-icon) {
|
||||||
|
|||||||
@@ -10,8 +10,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="toolbar">
|
<div class="toolbar">
|
||||||
<div class="email">
|
<el-dropdown>
|
||||||
<span>{{ userStore.user.email }}</span>
|
<div class="translate icon-item">
|
||||||
|
<Icon icon="carbon:ibm-watson-language-translator" />
|
||||||
|
</div>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item @click="changeLang('zh')">简体中文</el-dropdown-item>
|
||||||
|
<el-dropdown-item @click="changeLang('en')">English</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
<div class="notice icon-item" @click="openNotice">
|
||||||
|
<Icon icon="streamline-plump:announcement-megaphone" />
|
||||||
</div>
|
</div>
|
||||||
<el-dropdown :teleported="false" popper-class="detail-dropdown" >
|
<el-dropdown :teleported="false" popper-class="detail-dropdown" >
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
@@ -28,7 +39,7 @@
|
|||||||
<div class="user-name">
|
<div class="user-name">
|
||||||
{{userStore.user.name}}
|
{{userStore.user.name}}
|
||||||
</div>
|
</div>
|
||||||
<div class="detail-email">
|
<div class="detail-email" @click="copyEmail(userStore.user.email)">
|
||||||
{{ userStore.user.email }}
|
{{ userStore.user.email }}
|
||||||
</div>
|
</div>
|
||||||
<div class="detail-user-type">
|
<div class="detail-user-type">
|
||||||
@@ -136,6 +147,32 @@ const sendCount = computed(() => {
|
|||||||
return userStore.user.sendCount + '/' + userStore.user.role.sendCount
|
return userStore.user.sendCount + '/' + userStore.user.role.sendCount
|
||||||
})
|
})
|
||||||
|
|
||||||
|
async function copyEmail(email) {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(email);
|
||||||
|
ElMessage({
|
||||||
|
message: t('copySuccessMsg'),
|
||||||
|
type: 'success',
|
||||||
|
plain: true,
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`${t('copyFailMsg')}:`, err);
|
||||||
|
ElMessage({
|
||||||
|
message: t('copyFailMsg'),
|
||||||
|
type: 'error',
|
||||||
|
plain: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeLang(lang) {
|
||||||
|
settingStore.lang = lang
|
||||||
|
}
|
||||||
|
|
||||||
|
function openNotice() {
|
||||||
|
uiStore.showNotice()
|
||||||
|
}
|
||||||
|
|
||||||
function openSend() {
|
function openSend() {
|
||||||
uiStore.writerRef.open()
|
uiStore.writerRef.open()
|
||||||
}
|
}
|
||||||
@@ -224,6 +261,7 @@ function formatName(email) {
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #5c5958;
|
color: #5c5958;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.logout {
|
.logout {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
@@ -272,11 +310,11 @@ function formatName(email) {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
.writer {
|
.writer {
|
||||||
width: 36px;
|
width: 34px;
|
||||||
height: 36px;
|
height: 34px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
background: linear-gradient(135deg, #1890ff, #1c6dd0);
|
background: linear-gradient(135deg, #1890ff, #3a80dd);
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -298,38 +336,41 @@ function formatName(email) {
|
|||||||
|
|
||||||
|
|
||||||
.toolbar {
|
.toolbar {
|
||||||
display: grid;
|
display: flex;
|
||||||
grid-template-columns: 1fr auto auto;
|
justify-content: end;
|
||||||
margin-left: auto;
|
gap: 15px;
|
||||||
@media (max-width: 1024px) {
|
@media (max-width: 767px) {
|
||||||
grid-template-columns: 1fr auto;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
.full {
|
.icon-item {
|
||||||
|
align-self: center;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
border-radius: 4px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding-right: 20px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@media (max-width: 1024px) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.email {
|
|
||||||
align-self: center;
|
.icon-item:hover {
|
||||||
font-size: 14px;
|
background: #F0F2F5;
|
||||||
margin-right: 10px;
|
}
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
.notice {
|
||||||
text-overflow: ellipsis;
|
font-size: 22px;
|
||||||
font-weight: bold;
|
margin-right: 4px;
|
||||||
width: 100%;
|
}
|
||||||
|
|
||||||
|
.translate {
|
||||||
|
padding-top: 2px;
|
||||||
|
font-size: 21px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar {
|
.avatar {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin-left: 10px;
|
|
||||||
.avatar-text {
|
.avatar-text {
|
||||||
height: 30px;
|
height: 30px;
|
||||||
width: 30px;
|
width: 30px;
|
||||||
|
|||||||
@@ -13,23 +13,74 @@
|
|||||||
import account from '@/layout/account/index.vue'
|
import account from '@/layout/account/index.vue'
|
||||||
import {useUiStore} from "@/store/ui.js";
|
import {useUiStore} from "@/store/ui.js";
|
||||||
import {useSettingStore} from "@/store/setting.js";
|
import {useSettingStore} from "@/store/setting.js";
|
||||||
import {computed, onBeforeUnmount, onMounted} from "vue";
|
import {computed, onBeforeUnmount, onMounted, watch} from "vue";
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { hasPerm } from "@/perm/perm.js"
|
import { hasPerm } from "@/perm/perm.js"
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
openSend: Function
|
|
||||||
})
|
|
||||||
|
|
||||||
const settingStore = useSettingStore()
|
const settingStore = useSettingStore()
|
||||||
const uiStore = useUiStore();
|
const uiStore = useUiStore();
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
let innerWidth = window.innerWidth
|
let innerWidth = window.innerWidth
|
||||||
|
|
||||||
|
let elNotification = null
|
||||||
|
|
||||||
const accountShow = computed(() => {
|
const accountShow = computed(() => {
|
||||||
return uiStore.accountShow && settingStore.settings.manyEmail === 0
|
return uiStore.accountShow && settingStore.settings.manyEmail === 0
|
||||||
})
|
})
|
||||||
|
|
||||||
|
watch(() => uiStore.changeNotice, () => {
|
||||||
|
|
||||||
|
const settings = settingStore.settings
|
||||||
|
|
||||||
|
let data = {
|
||||||
|
notice: settings.notice,
|
||||||
|
noticeWidth: settings.noticeWidth,
|
||||||
|
noticeTitle: settings.noticeTitle,
|
||||||
|
noticeContent: settings.noticeContent,
|
||||||
|
noticeType: settings.noticeType,
|
||||||
|
noticeDuration: settings.noticeDuration,
|
||||||
|
noticePosition: settings.noticePosition,
|
||||||
|
noticeOffset: settings.noticeOffset
|
||||||
|
}
|
||||||
|
|
||||||
|
showNotice(data)
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(() => uiStore.changePreview, () => {
|
||||||
|
showNotice(uiStore.previewData)
|
||||||
|
})
|
||||||
|
|
||||||
|
function showNotice(data) {
|
||||||
|
|
||||||
|
if (data.notice === 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elNotification) {
|
||||||
|
elNotification.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
const style = document.createElement('style');
|
||||||
|
style.innerHTML = `
|
||||||
|
.custom-notice.el-notification {
|
||||||
|
--el-notification-width: min(${data.noticeWidth}px,calc(100% - 30px)) !important;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
document.head.appendChild(style);
|
||||||
|
|
||||||
|
elNotification = ElNotification({
|
||||||
|
title: data.noticeTitle,
|
||||||
|
message: `<div style="width: 100%;height: 100%;">${data.noticeContent}</div>`,
|
||||||
|
type: data.noticeType === 'none' ? '' : data.noticeType,
|
||||||
|
duration: data.noticeDuration,
|
||||||
|
position: data.noticePosition,
|
||||||
|
offset: data.noticeOffset,
|
||||||
|
dangerouslyUseHTMLString: true,
|
||||||
|
customClass: 'custom-notice'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
window.addEventListener('resize', handleResize)
|
window.addEventListener('resize', handleResize)
|
||||||
handleResize()
|
handleResize()
|
||||||
@@ -49,7 +100,6 @@ const handleResize = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
.block-show {
|
.block-show {
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ export const useUiStore = defineStore('ui', {
|
|||||||
asideShow: window.innerWidth > 1024,
|
asideShow: window.innerWidth > 1024,
|
||||||
accountShow: false,
|
accountShow: false,
|
||||||
backgroundLoading: true,
|
backgroundLoading: true,
|
||||||
|
changeNotice: 0,
|
||||||
writerRef: null,
|
writerRef: null,
|
||||||
|
changePreview: 0,
|
||||||
|
previewData: {},
|
||||||
key: 0,
|
key: 0,
|
||||||
asideCount: {
|
asideCount: {
|
||||||
email: 0,
|
email: 0,
|
||||||
@@ -13,6 +16,15 @@ export const useUiStore = defineStore('ui', {
|
|||||||
sysEmail: 0
|
sysEmail: 0
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
actions: {
|
||||||
|
showNotice() {
|
||||||
|
this.changeNotice ++
|
||||||
|
},
|
||||||
|
previewNotice(data) {
|
||||||
|
this.previewData = data
|
||||||
|
this.changePreview ++
|
||||||
|
}
|
||||||
|
},
|
||||||
persist: {
|
persist: {
|
||||||
pick: ['accountShow'],
|
pick: ['accountShow'],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ import 'dayjs/locale/zh-cn'
|
|||||||
import utc from 'dayjs/plugin/utc'
|
import utc from 'dayjs/plugin/utc'
|
||||||
import timezone from 'dayjs/plugin/timezone'
|
import timezone from 'dayjs/plugin/timezone'
|
||||||
import {useSettingStore} from "@/store/setting.js";
|
import {useSettingStore} from "@/store/setting.js";
|
||||||
const { lang } = useSettingStore();
|
const settingStore = useSettingStore();
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
dayjs.locale(lang === 'zh' ? 'zh-cn' : '')
|
dayjs.locale(settingStore.lang === 'zh' ? 'zh-cn' : '')
|
||||||
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
||||||
|
|
||||||
export function fromNow(date) {
|
export function fromNow(date) {
|
||||||
@@ -16,7 +16,7 @@ export function fromNow(date) {
|
|||||||
const diffMinutes = now.diff(d, 'minute');
|
const diffMinutes = now.diff(d, 'minute');
|
||||||
const diffHours = now.diff(d, 'hour');
|
const diffHours = now.diff(d, 'hour');
|
||||||
const isToday = now.isSame(d, 'day');
|
const isToday = now.isSame(d, 'day');
|
||||||
if (lang === 'zh') {
|
if (settingStore.lang === 'zh') {
|
||||||
|
|
||||||
if (isToday) {
|
if (isToday) {
|
||||||
if (diffSeconds < 60) return `几秒前`;
|
if (diffSeconds < 60) return `几秒前`;
|
||||||
@@ -63,7 +63,7 @@ export function formatDetailDate(time) {
|
|||||||
|
|
||||||
const isSameYear = now.year() === d.year();
|
const isSameYear = now.year() === d.year();
|
||||||
|
|
||||||
if (lang === 'zh') {
|
if (settingStore.lang === 'zh') {
|
||||||
return d.format('YYYY年M月D日 ddd AH:mm');
|
return d.format('YYYY年M月D日 ddd AH:mm');
|
||||||
} else {
|
} else {
|
||||||
return isSameYear
|
return isSameYear
|
||||||
|
|||||||
@@ -2,3 +2,7 @@ export function isEmail(email) {
|
|||||||
const reg = /^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~.-]+@([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/;
|
const reg = /^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~.-]+@([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/;
|
||||||
return reg.test(email);
|
return reg.test(email);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isDomain(str) {
|
||||||
|
return /^(?!:\/\/)([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/.test(str);
|
||||||
|
}
|
||||||
@@ -45,8 +45,8 @@
|
|||||||
<el-option key="1" :label="$t('all')" value="all"/>
|
<el-option key="1" :label="$t('all')" value="all"/>
|
||||||
<el-option key="3" :label="$t('received')" value="receive"/>
|
<el-option key="3" :label="$t('received')" value="receive"/>
|
||||||
<el-option key="2" :label="$t('sent')" value="send"/>
|
<el-option key="2" :label="$t('sent')" value="send"/>
|
||||||
<el-option key="4" :label="$t('deleted')" value="delete"/>
|
<el-option key="4" :label="$t('selectDeleted')" value="delete"/>
|
||||||
<el-option key="4" :label="$t('noRecipient')" value="noone"/>
|
<el-option key="4" :label="$t('noRecipientTitle')" value="noone"/>
|
||||||
</el-select>
|
</el-select>
|
||||||
<Icon class="icon" icon="iconoir:search" @click="search" width="20" height="20"/>
|
<Icon class="icon" icon="iconoir:search" @click="search" width="20" height="20"/>
|
||||||
<Icon class="icon" @click="changeTimeSort" icon="material-symbols-light:timer-arrow-down-outline"
|
<Icon class="icon" @click="changeTimeSort" icon="material-symbols-light:timer-arrow-down-outline"
|
||||||
|
|||||||
@@ -130,7 +130,6 @@ import {debounce} from "lodash-es";
|
|||||||
import loading from "@/components/loading/index.vue";
|
import loading from "@/components/loading/index.vue";
|
||||||
import {useRoute} from "vue-router";
|
import {useRoute} from "vue-router";
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import {toUtc, tzDayjs} from "@/utils/day.js";
|
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'analysis'
|
name: 'analysis'
|
||||||
@@ -693,7 +692,7 @@ function createSendGauge() {
|
|||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
.number-item {
|
.number-item {
|
||||||
background: #fff;
|
background: var(--el-bg-color);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
border: 1px solid var(--el-border-color);
|
border: 1px solid var(--el-border-color);
|
||||||
padding: 21px 20px;
|
padding: 21px 20px;
|
||||||
|
|||||||
@@ -81,7 +81,7 @@
|
|||||||
data-after-interactive-callback="loadAfter"
|
data-after-interactive-callback="loadAfter"
|
||||||
data-before-interactive-callback="loadBefore"
|
data-before-interactive-callback="loadBefore"
|
||||||
>
|
>
|
||||||
<span style="font-size: 12px;color: #F56C6C" v-if="botJsError">人机验证模块加载失败,请刷新浏览器</span>
|
<span style="font-size: 12px;color: #F56C6C" v-if="botJsError">{{$t('verifyModuleFailed')}}</span>
|
||||||
</div>
|
</div>
|
||||||
<el-button class="btn" type="primary" @click="submitRegister" :loading="registerLoading"
|
<el-button class="btn" type="primary" @click="submitRegister" :loading="registerLoading"
|
||||||
>{{$t('regBtn')}}
|
>{{$t('regBtn')}}
|
||||||
@@ -105,6 +105,7 @@ import {isEmail} from "@/utils/verify-utils.js";
|
|||||||
import {useSettingStore} from "@/store/setting.js";
|
import {useSettingStore} from "@/store/setting.js";
|
||||||
import {useAccountStore} from "@/store/account.js";
|
import {useAccountStore} from "@/store/account.js";
|
||||||
import {useUserStore} from "@/store/user.js";
|
import {useUserStore} from "@/store/user.js";
|
||||||
|
import {useUiStore} from "@/store/ui.js";
|
||||||
import {Icon} from "@iconify/vue";
|
import {Icon} from "@iconify/vue";
|
||||||
import {cvtR2Url} from "@/utils/convert.js";
|
import {cvtR2Url} from "@/utils/convert.js";
|
||||||
import {loginUserInfo} from "@/request/my.js";
|
import {loginUserInfo} from "@/request/my.js";
|
||||||
@@ -114,6 +115,7 @@ import {useI18n} from "vue-i18n";
|
|||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const accountStore = useAccountStore();
|
const accountStore = useAccountStore();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
const uiStore = useUiStore();
|
||||||
const settingStore = useSettingStore();
|
const settingStore = useSettingStore();
|
||||||
const loginLoading = ref(false)
|
const loginLoading = ref(false)
|
||||||
const show = ref('login')
|
const show = ref('login')
|
||||||
@@ -220,6 +222,7 @@ const submit = () => {
|
|||||||
router.addRoute('layout', routerData);
|
router.addRoute('layout', routerData);
|
||||||
});
|
});
|
||||||
await router.replace({name: 'layout'})
|
await router.replace({name: 'layout'})
|
||||||
|
uiStore.showNotice()
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
loginLoading.value = false
|
loginLoading.value = false
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -72,7 +72,7 @@
|
|||||||
<el-input-tag class="dialog-input-tag" tag-type="warning" :class="form.banEmail.length === 0 ? 'dialog-input' : '' " v-model="form.banEmail" @add-tag="banEmailAddTag" type="text" :placeholder="$t('emailInterception')" autocomplete="off" />
|
<el-input-tag class="dialog-input-tag" tag-type="warning" :class="form.banEmail.length === 0 ? 'dialog-input' : '' " v-model="form.banEmail" @add-tag="banEmailAddTag" type="text" :placeholder="$t('emailInterception')" autocomplete="off" />
|
||||||
<el-radio-group class="dialog-radio" v-model="form.banEmailType" v-if="form.banEmail.length > 0">
|
<el-radio-group class="dialog-radio" v-model="form.banEmailType" v-if="form.banEmail.length > 0">
|
||||||
<el-radio :label="$t('removeAll')" :value="0" />
|
<el-radio :label="$t('removeAll')" :value="0" />
|
||||||
<el-radio :label="$t('removeBody')" :value="1" />
|
<el-radio :label="$t('removeContent')" :value="1" />
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
<el-select
|
<el-select
|
||||||
class="dialog-input"
|
class="dialog-input"
|
||||||
@@ -146,7 +146,7 @@ import loading from '@/components/loading/index.vue';
|
|||||||
import {useRoleStore} from "@/store/role.js";
|
import {useRoleStore} from "@/store/role.js";
|
||||||
import {useUserStore} from "@/store/user.js";
|
import {useUserStore} from "@/store/user.js";
|
||||||
import {useSettingStore} from "@/store/setting.js";
|
import {useSettingStore} from "@/store/setting.js";
|
||||||
import {isEmail} from "@/utils/verify-utils.js";
|
import {isEmail, isDomain} from "@/utils/verify-utils.js";
|
||||||
import {useI18n} from "vue-i18n";
|
import {useI18n} from "vue-i18n";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
@@ -198,7 +198,10 @@ rolePermTree().then(tree => {
|
|||||||
treeList.push(...tree)
|
treeList.push(...tree)
|
||||||
})
|
})
|
||||||
|
|
||||||
domainOptions = domainList.map(domain => ({label: domain,value: domain}))
|
domainOptions = domainList.map(domain => {
|
||||||
|
const cleanDomain = domain.replace(/^@/, '');
|
||||||
|
return { label: cleanDomain, value: cleanDomain };
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
function availDomainChange() {
|
function availDomainChange() {
|
||||||
@@ -218,7 +221,7 @@ function banEmailAddTag(val) {
|
|||||||
form.banEmail.splice(form.banEmail.length - 1, 1)
|
form.banEmail.splice(form.banEmail.length - 1, 1)
|
||||||
|
|
||||||
emails.forEach(email => {
|
emails.forEach(email => {
|
||||||
if (isEmail(email) && !form.banEmail.includes(email)) {
|
if ((isEmail(email) || isDomain(email)) && !form.banEmail.includes(email)) {
|
||||||
form.banEmail.push(email)
|
form.banEmail.push(email)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -236,7 +239,7 @@ function roleFormClick() {
|
|||||||
function setDef(role) {
|
function setDef(role) {
|
||||||
roleSetDef(role.roleId).then(() => {
|
roleSetDef(role.roleId).then(() => {
|
||||||
ElMessage({
|
ElMessage({
|
||||||
message: t('changSuccessMsg'),
|
message: t('saveSuccessMsg'),
|
||||||
type: "success",
|
type: "success",
|
||||||
plain: true
|
plain: true
|
||||||
})
|
})
|
||||||
@@ -297,7 +300,7 @@ function setRole() {
|
|||||||
permLoading.value = true
|
permLoading.value = true
|
||||||
roleSet(params).then(() => {
|
roleSet(params).then(() => {
|
||||||
ElMessage({
|
ElMessage({
|
||||||
message: t('changSuccessMsg'),
|
message: t('saveSuccessMsg'),
|
||||||
type: "success",
|
type: "success",
|
||||||
plain: true
|
plain: true
|
||||||
})
|
})
|
||||||
@@ -340,6 +343,7 @@ function openRoleSet(role) {
|
|||||||
form.sendCount = role.sendCount
|
form.sendCount = role.sendCount
|
||||||
form.accountCount = role.accountCount
|
form.accountCount = role.accountCount
|
||||||
form.banEmail = role.banEmail
|
form.banEmail = role.banEmail
|
||||||
|
form.banEmailType = role.banEmailType
|
||||||
form.availDomain = role.availDomain
|
form.availDomain = role.availDomain
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
tree.value.setCheckedKeys(role.permIds)
|
tree.value.setCheckedKeys(role.permIds)
|
||||||
|
|||||||
@@ -30,21 +30,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="container lang">
|
|
||||||
<div class="title">{{$t('language')}}</div>
|
|
||||||
<div>
|
|
||||||
<el-select v-model="lang" placeholder="Select" style="width: 100px">
|
|
||||||
<el-option
|
|
||||||
key="zh"
|
|
||||||
label="简体中文"
|
|
||||||
value="zh"/>
|
|
||||||
<el-option
|
|
||||||
key="en"
|
|
||||||
label="English"
|
|
||||||
value="en"/>
|
|
||||||
</el-select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="del-email" v-perm="'my:delete'">
|
<div class="del-email" v-perm="'my:delete'">
|
||||||
<div class="title">{{$t('deleteUser')}}</div>
|
<div class="title">{{$t('deleteUser')}}</div>
|
||||||
<div style="color: #585d69;">
|
<div style="color: #585d69;">
|
||||||
@@ -64,33 +49,25 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import {reactive, ref, defineOptions, watch} from 'vue'
|
import {reactive, ref, defineOptions} from 'vue'
|
||||||
import {resetPassword, userDelete} from "@/request/my.js";
|
import {resetPassword, userDelete} from "@/request/my.js";
|
||||||
import {useUserStore} from "@/store/user.js";
|
import {useUserStore} from "@/store/user.js";
|
||||||
import router from "@/router/index.js";
|
import router from "@/router/index.js";
|
||||||
import { storeToRefs } from 'pinia'
|
|
||||||
import {accountSetName} from "@/request/account.js";
|
import {accountSetName} from "@/request/account.js";
|
||||||
import {useAccountStore} from "@/store/account.js";
|
import {useAccountStore} from "@/store/account.js";
|
||||||
import {useI18n} from "vue-i18n";
|
import {useI18n} from "vue-i18n";
|
||||||
import {useSettingStore} from "@/store/setting.js";
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const settingStore = useSettingStore()
|
|
||||||
const accountStore = useAccountStore()
|
const accountStore = useAccountStore()
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const setPwdLoading = ref(false)
|
const setPwdLoading = ref(false)
|
||||||
const setNameShow = ref(false)
|
const setNameShow = ref(false)
|
||||||
const accountName = ref(null)
|
const accountName = ref(null)
|
||||||
const { lang } = storeToRefs(settingStore)
|
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'setting'
|
name: 'setting'
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(() => lang.value,() => {
|
|
||||||
window.location.reload()
|
|
||||||
})
|
|
||||||
|
|
||||||
function showSetName() {
|
function showSetName() {
|
||||||
accountName.value = userStore.user.name
|
accountName.value = userStore.user.name
|
||||||
setNameShow.value = true
|
setNameShow.value = true
|
||||||
@@ -118,7 +95,7 @@ function setName() {
|
|||||||
|
|
||||||
accountSetName(userStore.user.accountId,name).then(() => {
|
accountSetName(userStore.user.accountId,name).then(() => {
|
||||||
ElMessage({
|
ElMessage({
|
||||||
message: t('changSuccessMsg'),
|
message: t('saveSuccessMsg'),
|
||||||
type: 'success',
|
type: 'success',
|
||||||
plain: true,
|
plain: true,
|
||||||
})
|
})
|
||||||
@@ -187,7 +164,7 @@ function submitPwd() {
|
|||||||
setPwdLoading.value = true
|
setPwdLoading.value = true
|
||||||
resetPassword(form.password).then(() => {
|
resetPassword(form.password).then(() => {
|
||||||
ElMessage({
|
ElMessage({
|
||||||
message: t('changSuccessMsg'),
|
message: t('saveSuccessMsg'),
|
||||||
type: 'success',
|
type: 'success',
|
||||||
plain: true,
|
plain: true,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -124,7 +124,7 @@
|
|||||||
<div class="card-title">{{$t('emailSetting')}}</div>
|
<div class="card-title">{{$t('emailSetting')}}</div>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<div class="setting-item">
|
<div class="setting-item">
|
||||||
<div><span>{{$t('receiveEmails')}}</span></div>
|
<div><span>{{$t('receiveEmail')}}</span></div>
|
||||||
<div>
|
<div>
|
||||||
<el-switch @change="change" :before-change="beforeChange" :active-value="0" :inactive-value="1"
|
<el-switch @change="change" :before-change="beforeChange" :active-value="0" :inactive-value="1"
|
||||||
v-model="setting.receive"/>
|
v-model="setting.receive"/>
|
||||||
@@ -160,6 +160,18 @@
|
|||||||
v-model="setting.send"/>
|
v-model="setting.send"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="setting-item">
|
||||||
|
<div>
|
||||||
|
<span>{{$t('noRecipientTitle')}}</span>
|
||||||
|
<el-tooltip effect="dark" :content="$t('noRecipientDesc')">
|
||||||
|
<Icon class="warning" icon="fe:warning" width="18" height="18"/>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<el-switch @change="change" :before-change="beforeChange" :active-value="0" :inactive-value="1"
|
||||||
|
v-model="setting.noRecipient"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="setting-item">
|
<div class="setting-item">
|
||||||
<div><span>{{$t('resendToken')}}</span></div>
|
<div><span>{{$t('resendToken')}}</span></div>
|
||||||
<div>
|
<div>
|
||||||
@@ -286,12 +298,35 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-card">
|
||||||
|
<div class="card-title">{{$t('noticeTitle')}}</div>
|
||||||
|
<div class="card-content">
|
||||||
|
<div class="setting-item">
|
||||||
|
<div><span>{{$t('noticePopup')}}</span></div>
|
||||||
|
<div class="forward">
|
||||||
|
<span>{{ setting.notice === 0 ? $t('enabled') : $t('disabled') }}</span>
|
||||||
|
<el-button class="opt-button" size="small" type="primary" @click="openNoticePopupSetting">
|
||||||
|
<Icon icon="fluent:settings-48-regular" width="18" height="18"/>
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="setting-item">
|
||||||
|
<div><span>{{$t('popUp')}}</span></div>
|
||||||
|
<div class="forward">
|
||||||
|
<el-button class="opt-button" size="small" type="primary" @click="openNoticePopup">
|
||||||
|
<Icon icon="mynaui:click-solid" width="18" height="18"/>
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="settings-card about">
|
<div class="settings-card about">
|
||||||
<div class="card-title">{{$t('about')}}</div>
|
<div class="card-title">{{$t('about')}}</div>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<div class="concerning-item">
|
<div class="concerning-item">
|
||||||
<span>{{$t('version')}} :</span>
|
<span>{{$t('version')}} :</span>
|
||||||
<span>v1.5.0</span>
|
<span>v1.6.0</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="concerning-item">
|
<div class="concerning-item">
|
||||||
<span>{{$t('community')}} : </span>
|
<span>{{$t('community')}} : </span>
|
||||||
@@ -323,7 +358,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Dialogs remain the same -->
|
<!-- Dialogs remain the same -->
|
||||||
<el-dialog v-model="editTitleShow" :title="$t('changeTitle')" width="340" >
|
<el-dialog v-model="editTitleShow" :title="$t('changeTitle')" width="340" @closed="editTitle = setting.title">
|
||||||
<form>
|
<form>
|
||||||
<el-input type="text" :placeholder="$t('websiteTitle')" v-model="editTitle"/>
|
<el-input type="text" :placeholder="$t('websiteTitle')" v-model="editTitle"/>
|
||||||
<el-button type="primary" :loading="settingLoading" @click="saveTitle">{{$t('save')}}</el-button>
|
<el-button type="primary" :loading="settingLoading" @click="saveTitle">{{$t('save')}}</el-button>
|
||||||
@@ -474,19 +509,90 @@
|
|||||||
<el-table-column :width="tokenColumnWidth" property="value" label="Token" fixed="right" :show-overflow-tooltip="true" />
|
<el-table-column :width="tokenColumnWidth" property="value" label="Token" fixed="right" :show-overflow-tooltip="true" />
|
||||||
</el-table>
|
</el-table>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
<el-dialog v-model="showRegVerifyCount" :title="$t('rulesVerifyTitle',{count: regVerifyCount})" @closed="regVerifyCount = setting.regVerifyCount" >
|
<el-dialog v-model="regVerifyCountShow" :title="$t('rulesVerifyTitle',{count: regVerifyCount})" @closed="regVerifyCount = setting.regVerifyCount" >
|
||||||
<form>
|
<form>
|
||||||
<el-input-number type="text" v-model="regVerifyCount" :min="1" >
|
<el-input-number type="text" v-model="regVerifyCount" :min="1" >
|
||||||
</el-input-number>
|
</el-input-number>
|
||||||
<el-button type="primary" :loading="settingLoading" @click="saveRegVerifyCount">{{$t('save')}}</el-button>
|
<el-button type="primary" :loading="settingLoading" @click="saveRegVerifyCount">{{$t('save')}}</el-button>
|
||||||
</form>
|
</form>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
<el-dialog v-model="showAddVerifyCount" :title="$t('rulesVerifyTitle',{count: addVerifyCount})" @closed="addVerifyCount = setting.addVerifyCount">
|
<el-dialog v-model="addVerifyCountShow" :title="$t('rulesVerifyTitle',{count: addVerifyCount})" @closed="addVerifyCount = setting.addVerifyCount">
|
||||||
<form>
|
<form>
|
||||||
<el-input-number type="text" v-model="addVerifyCount" :min="1"/>
|
<el-input-number type="text" v-model="addVerifyCount" :min="1"/>
|
||||||
<el-button type="primary" :loading="settingLoading" @click="saveAddVerifyCount">{{$t('save')}}</el-button>
|
<el-button type="primary" :loading="settingLoading" @click="saveAddVerifyCount">{{$t('save')}}</el-button>
|
||||||
</form>
|
</form>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
<el-dialog top="5vh" v-model="noticePopupShow" :title="$t('noticePopup')" class="notice-popup" @closed="resetNoticeForm">
|
||||||
|
<form>
|
||||||
|
<el-input v-model="noticeForm.noticeTitle" :placeholder="t('titleDesc')" />
|
||||||
|
<div class="notice-line-item" >
|
||||||
|
<el-select v-model="noticeForm.noticeType" >
|
||||||
|
<template #prefix>
|
||||||
|
<span style="margin-right: 10px">{{$t('icon')}}</span>
|
||||||
|
</template>
|
||||||
|
<el-option key="none" label="None" value="none" />
|
||||||
|
<el-option key="primary" label="Primary" value="primary" />
|
||||||
|
<el-option key="success" label="Success" value="success" />
|
||||||
|
<el-option key="warning" label="Warning" value="warning" />
|
||||||
|
<el-option key="info" label="Info" value="info" />
|
||||||
|
</el-select>
|
||||||
|
<el-select v-model="noticeForm.noticePosition" >
|
||||||
|
<template #prefix>
|
||||||
|
<span style="margin-right: 10px">{{$t('position')}}</span>
|
||||||
|
</template>
|
||||||
|
<el-option key="top-left" :label="t('topLeft')" value="top-left" />
|
||||||
|
<el-option key="top-right" :label="t('topRight')" value="top-right" />
|
||||||
|
<el-option key="bottom-left" :label="t('bottomLeft')" value="bottom-left" />
|
||||||
|
<el-option key="bottom-right" :label="t('bottomRight')" value="bottom-right" />
|
||||||
|
</el-select>
|
||||||
|
<el-input-number v-model="noticeForm.noticeWidth" >
|
||||||
|
<template #prefix >
|
||||||
|
{{$t('width')}}
|
||||||
|
</template>
|
||||||
|
<template #suffix >
|
||||||
|
px
|
||||||
|
</template>
|
||||||
|
</el-input-number>
|
||||||
|
<el-input-number v-model="noticeForm.noticeOffset" >
|
||||||
|
<template #prefix >
|
||||||
|
{{$t('offset')}}
|
||||||
|
</template>
|
||||||
|
<template #suffix >
|
||||||
|
px
|
||||||
|
</template>
|
||||||
|
</el-input-number>
|
||||||
|
<el-input-number v-model="noticeForm.noticeDuration" >
|
||||||
|
<template #prefix >
|
||||||
|
{{$t('duration')}}
|
||||||
|
</template>
|
||||||
|
<template #suffix >
|
||||||
|
ms
|
||||||
|
</template>
|
||||||
|
</el-input-number>
|
||||||
|
</div>
|
||||||
|
<div class="notice-popup-item">
|
||||||
|
<el-input
|
||||||
|
v-model="noticeForm.noticeContent"
|
||||||
|
:autosize="{ minRows: 15, maxRows: 25 }"
|
||||||
|
type="textarea"
|
||||||
|
:placeholder="t('noticeContentDesc')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-switch v-model="noticeForm.notice" :active-value="0" :inactive-value="1" :active-text="$t('enable')" :inactive-text="$t('disable')" />
|
||||||
|
<div>
|
||||||
|
<el-button @click="previewNoticePopup">
|
||||||
|
{{$t('preview')}}
|
||||||
|
</el-button>
|
||||||
|
<el-button :loading="settingLoading" type="primary" @click="saveNoticePopup">
|
||||||
|
{{$t('save')}}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -495,6 +601,7 @@
|
|||||||
import {computed, defineOptions, reactive, ref} from "vue";
|
import {computed, defineOptions, reactive, ref} from "vue";
|
||||||
import {physicsDeleteAll, setBackground, settingQuery, settingSet} from "@/request/setting.js";
|
import {physicsDeleteAll, setBackground, settingQuery, settingSet} from "@/request/setting.js";
|
||||||
import {useSettingStore} from "@/store/setting.js";
|
import {useSettingStore} from "@/store/setting.js";
|
||||||
|
import {useUiStore} from "@/store/ui.js";
|
||||||
import {useUserStore} from "@/store/user.js";
|
import {useUserStore} from "@/store/user.js";
|
||||||
import {useAccountStore} from "@/store/account.js";
|
import {useAccountStore} from "@/store/account.js";
|
||||||
import {Icon} from "@iconify/vue";
|
import {Icon} from "@iconify/vue";
|
||||||
@@ -522,10 +629,12 @@ const resendTokenFormShow = ref(false)
|
|||||||
const r2DomainShow = ref(false)
|
const r2DomainShow = ref(false)
|
||||||
const turnstileShow = ref(false)
|
const turnstileShow = ref(false)
|
||||||
const tgSettingShow = ref(false)
|
const tgSettingShow = ref(false)
|
||||||
|
const noticePopupShow = ref(false)
|
||||||
const thirdEmailShow = ref(false)
|
const thirdEmailShow = ref(false)
|
||||||
const forwardRulesShow = ref(false)
|
const forwardRulesShow = ref(false)
|
||||||
const showResendList = ref(false)
|
const showResendList = ref(false)
|
||||||
const settingStore = useSettingStore();
|
const settingStore = useSettingStore();
|
||||||
|
const uiStore = useUiStore();
|
||||||
const {settings: setting} = storeToRefs(settingStore);
|
const {settings: setting} = storeToRefs(settingStore);
|
||||||
const editTitle = ref('')
|
const editTitle = ref('')
|
||||||
const settingLoading = ref(false)
|
const settingLoading = ref(false)
|
||||||
@@ -537,8 +646,8 @@ const showSetBackground = ref(false)
|
|||||||
let regVerifyCount = ref(1)
|
let regVerifyCount = ref(1)
|
||||||
let addVerifyCount = ref(1)
|
let addVerifyCount = ref(1)
|
||||||
let backup = '{}'
|
let backup = '{}'
|
||||||
const showAddVerifyCount = ref(false)
|
const addVerifyCountShow = ref(false)
|
||||||
const showRegVerifyCount = ref(false)
|
const regVerifyCountShow = ref(false)
|
||||||
const resendTokenForm = reactive({
|
const resendTokenForm = reactive({
|
||||||
domain: '',
|
domain: '',
|
||||||
token: '',
|
token: '',
|
||||||
@@ -548,13 +657,24 @@ const turnstileForm = reactive({
|
|||||||
secretKey: ''
|
secretKey: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
const regKeyOptions = [
|
const noticeForm = reactive({
|
||||||
|
noticeTitle: '',
|
||||||
|
noticeContent: '',
|
||||||
|
noticeType: '',
|
||||||
|
noticeDuration: '',
|
||||||
|
noticePosition: '',
|
||||||
|
noticeOffset: 0,
|
||||||
|
notice: 0,
|
||||||
|
noticeWidth: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
const regKeyOptions = computed(() => [
|
||||||
{label: t('enable'), value: 0},
|
{label: t('enable'), value: 0},
|
||||||
{label: t('disable'), value: 1},
|
{label: t('disable'), value: 1},
|
||||||
{label: t('optional'), value: 2},
|
{label: t('optional'), value: 2},
|
||||||
]
|
])
|
||||||
|
|
||||||
const options = [
|
const options = computed(() => [
|
||||||
{label: t('disable'), value: 0},
|
{label: t('disable'), value: 0},
|
||||||
{label: '3s', value: 3},
|
{label: '3s', value: 3},
|
||||||
{label: '5s', value: 5},
|
{label: '5s', value: 5},
|
||||||
@@ -562,7 +682,7 @@ const options = [
|
|||||||
{label: '10s', value: 10},
|
{label: '10s', value: 10},
|
||||||
{label: '15s', value: 15},
|
{label: '15s', value: 15},
|
||||||
{label: '20s', value: 20}
|
{label: '20s', value: 20}
|
||||||
]
|
])
|
||||||
|
|
||||||
const tgChatId = ref([])
|
const tgChatId = ref([])
|
||||||
const tgBotStatus = ref(0)
|
const tgBotStatus = ref(0)
|
||||||
@@ -574,27 +694,44 @@ const tokenColumnWidth = ref(0)
|
|||||||
const ruleType = ref(0)
|
const ruleType = ref(0)
|
||||||
const ruleEmail = ref([])
|
const ruleEmail = ref([])
|
||||||
|
|
||||||
|
getSettings()
|
||||||
|
|
||||||
settingQuery().then(settingData => {
|
function getSettings() {
|
||||||
setting.value = settingData
|
settingQuery().then(settingData => {
|
||||||
resendTokenForm.domain = setting.value.domainList[0]
|
setting.value = settingData
|
||||||
loginOpacity.value = setting.value.loginOpacity
|
settingStore.domainList = settingData.domainList;
|
||||||
firstLoading.value = false
|
resendTokenForm.domain = setting.value.domainList[0]
|
||||||
backgroundUrl.value = setting.value.background?.startsWith('http') ? setting.value.background : ''
|
loginOpacity.value = setting.value.loginOpacity
|
||||||
editTitle.value = setting.value.title
|
firstLoading.value = false
|
||||||
r2DomainInput.value = setting.value.r2Domain
|
backgroundUrl.value = setting.value.background?.startsWith('http') ? setting.value.background : ''
|
||||||
addVerifyCount.value = setting.value.addVerifyCount
|
editTitle.value = setting.value.title
|
||||||
regVerifyCount.value = setting.value.regVerifyCount
|
r2DomainInput.value = setting.value.r2Domain
|
||||||
})
|
addVerifyCount.value = setting.value.addVerifyCount
|
||||||
|
regVerifyCount.value = setting.value.regVerifyCount
|
||||||
|
noticeForm.notice = setting.value.notice
|
||||||
|
noticeForm.noticeContent = setting.value.noticeContent
|
||||||
|
noticeForm.noticeDuration = setting.value.noticeDuration
|
||||||
|
noticeForm.noticeTitle = setting.value.noticeTitle
|
||||||
|
noticeForm.noticePosition = setting.value.noticePosition
|
||||||
|
noticeForm.noticeType = setting.value.noticeType
|
||||||
|
noticeForm.noticeOffset = setting.value.noticeOffset
|
||||||
|
noticeForm.noticeWidth = setting.value.noticeWidth
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function openNoticePopup() {
|
||||||
|
uiStore.showNotice()
|
||||||
|
}
|
||||||
|
|
||||||
function openAddVerifyCount() {
|
function openAddVerifyCount() {
|
||||||
if (settingLoading.value) return
|
if (settingLoading.value) return
|
||||||
showAddVerifyCount.value = true
|
addVerifyCountShow.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
function openRegVerifyCount() {
|
function openRegVerifyCount() {
|
||||||
if (settingLoading.value) return
|
if (settingLoading.value) return
|
||||||
showRegVerifyCount.value = true
|
regVerifyCountShow.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
const resendList = computed(() => {
|
const resendList = computed(() => {
|
||||||
@@ -659,10 +796,36 @@ function openTgSetting() {
|
|||||||
tgSettingShow.value = true
|
tgSettingShow.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openNoticePopupSetting() {
|
||||||
|
noticePopupShow.value = true
|
||||||
|
}
|
||||||
|
|
||||||
function openResendList() {
|
function openResendList() {
|
||||||
showResendList.value = true
|
showResendList.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resetNoticeForm() {
|
||||||
|
noticeForm.notice = setting.value.notice
|
||||||
|
noticeForm.noticeContent = setting.value.noticeContent
|
||||||
|
noticeForm.noticeDuration = setting.value.noticeDuration
|
||||||
|
noticeForm.noticeTitle = setting.value.noticeTitle
|
||||||
|
noticeForm.noticePosition = setting.value.noticePosition
|
||||||
|
noticeForm.noticeType = setting.value.noticeType
|
||||||
|
noticeForm.noticeOffset = setting.value.noticeOffset
|
||||||
|
noticeForm.noticeWidth = setting.value.noticeWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveNoticePopup() {
|
||||||
|
noticeForm.noticeOffset = noticeForm.noticeOffset || 0
|
||||||
|
noticeForm.noticeWidth = noticeForm.noticeWidth || 0
|
||||||
|
noticeForm.noticeDuration = noticeForm.noticeDuration || 0
|
||||||
|
editSetting({...noticeForm})
|
||||||
|
}
|
||||||
|
|
||||||
|
function previewNoticePopup() {
|
||||||
|
uiStore.previewNotice({...noticeForm})
|
||||||
|
}
|
||||||
|
|
||||||
function openThirdEmailSetting() {
|
function openThirdEmailSetting() {
|
||||||
forwardEmail.value = []
|
forwardEmail.value = []
|
||||||
forwardStatus.value = setting.value.forwardStatus
|
forwardStatus.value = setting.value.forwardStatus
|
||||||
@@ -825,7 +988,7 @@ async function saveBackground() {
|
|||||||
setting.value.background = key
|
setting.value.background = key
|
||||||
showSetBackground.value = false
|
showSetBackground.value = false
|
||||||
ElMessage({
|
ElMessage({
|
||||||
message: t('changSuccessMsg'),
|
message: t('saveSuccessMsg'),
|
||||||
type: "success",
|
type: "success",
|
||||||
plain: true
|
plain: true
|
||||||
})
|
})
|
||||||
@@ -855,7 +1018,7 @@ function openCut() {
|
|||||||
|
|
||||||
function saveR2domain() {
|
function saveR2domain() {
|
||||||
const settingForm = {r2Domain: r2DomainInput.value}
|
const settingForm = {r2Domain: r2DomainInput.value}
|
||||||
editSetting(settingForm, true)
|
editSetting(settingForm)
|
||||||
}
|
}
|
||||||
|
|
||||||
function openResendForm() {
|
function openResendForm() {
|
||||||
@@ -897,13 +1060,6 @@ function change(e) {
|
|||||||
editSetting(settingForm, false)
|
editSetting(settingForm, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
function refresh() {
|
|
||||||
settingQuery().then(setting => {
|
|
||||||
settingStore.settings = setting;
|
|
||||||
settingStore.domainList = setting.domainList;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveTitle() {
|
function saveTitle() {
|
||||||
editSetting({title: editTitle.value})
|
editSetting({title: editTitle.value})
|
||||||
}
|
}
|
||||||
@@ -922,7 +1078,7 @@ function editSetting(settingForm, refreshStatus = true) {
|
|||||||
settingSet(settingForm).then(() => {
|
settingSet(settingForm).then(() => {
|
||||||
settingLoading.value = false
|
settingLoading.value = false
|
||||||
ElMessage({
|
ElMessage({
|
||||||
message: t('changSuccessMsg'),
|
message: t('saveSuccessMsg'),
|
||||||
type: "success",
|
type: "success",
|
||||||
plain: true
|
plain: true
|
||||||
})
|
})
|
||||||
@@ -930,7 +1086,7 @@ function editSetting(settingForm, refreshStatus = true) {
|
|||||||
accountStore.currentAccountId = userStore.user.accountId;
|
accountStore.currentAccountId = userStore.user.accountId;
|
||||||
}
|
}
|
||||||
if (refreshStatus) {
|
if (refreshStatus) {
|
||||||
refresh()
|
getSettings()
|
||||||
}
|
}
|
||||||
editTitleShow.value = false
|
editTitleShow.value = false
|
||||||
r2DomainShow.value = false
|
r2DomainShow.value = false
|
||||||
@@ -939,8 +1095,9 @@ function editSetting(settingForm, refreshStatus = true) {
|
|||||||
tgSettingShow.value = false
|
tgSettingShow.value = false
|
||||||
thirdEmailShow.value = false
|
thirdEmailShow.value = false
|
||||||
forwardRulesShow.value = false
|
forwardRulesShow.value = false
|
||||||
showAddVerifyCount.value = false
|
addVerifyCountShow.value = false
|
||||||
showRegVerifyCount.value = false
|
regVerifyCountShow.value = false
|
||||||
|
noticePopupShow.value = false
|
||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
loginOpacity.value = setting.value.loginOpacity
|
loginOpacity.value = setting.value.loginOpacity
|
||||||
setting.value = {...setting.value, ...JSON.parse(backup)}
|
setting.value = {...setting.value, ...JSON.parse(backup)}
|
||||||
@@ -1045,7 +1202,7 @@ function editSetting(settingForm, refreshStatus = true) {
|
|||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: auto 1fr;
|
grid-template-columns: auto 1fr;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
font-weight: bold;
|
font-weight: normal;
|
||||||
> div:first-child {
|
> div:first-child {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -1069,12 +1226,14 @@ function editSetting(settingForm, refreshStatus = true) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.warning {
|
.warning {
|
||||||
margin-left: 5px;
|
margin-left: 4px;
|
||||||
color: gray;
|
color: gray;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cropper {
|
.cropper {
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #D4D7DE;
|
||||||
height: 397px;
|
height: 397px;
|
||||||
width: 705px;
|
width: 705px;
|
||||||
@media (max-width: 767px) {
|
@media (max-width: 767px) {
|
||||||
@@ -1088,6 +1247,26 @@ function editSetting(settingForm, refreshStatus = true) {
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.notice-popup-item {
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice-line-item {
|
||||||
|
margin-top: 15px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr 1fr;
|
||||||
|
gap: 15px;
|
||||||
|
> * {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
@media (max-width: 840px) {
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
}
|
||||||
|
@media (max-width: 580px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.background-url {
|
.background-url {
|
||||||
width: min(calc(100vw - 70px), 500px);
|
width: min(calc(100vw - 70px), 500px);
|
||||||
}
|
}
|
||||||
@@ -1112,6 +1291,16 @@ function editSetting(settingForm, refreshStatus = true) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:deep(.notice-popup.el-dialog) {
|
||||||
|
min-height: 300px;
|
||||||
|
width: 820px !important;
|
||||||
|
@media (max-width: 860px) {
|
||||||
|
width: calc(100% - 40px) !important;
|
||||||
|
margin-right: 20px !important;
|
||||||
|
margin-left: 20px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
:deep(.resend-table .el-dialog__header) {
|
:deep(.resend-table .el-dialog__header) {
|
||||||
padding-bottom: 5px;
|
padding-bottom: 5px;
|
||||||
}
|
}
|
||||||
@@ -1257,7 +1446,7 @@ function editSetting(settingForm, refreshStatus = true) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
> span:first-child {
|
> span:first-child {
|
||||||
font-weight: bold;
|
font-weight: normal;
|
||||||
padding-right: 20px;
|
padding-right: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -502,6 +502,7 @@ function submit() {
|
|||||||
function formatSendType(user) {
|
function formatSendType(user) {
|
||||||
if (user.sendAction.sendType === 'day') return t('daily')
|
if (user.sendAction.sendType === 'day') return t('daily')
|
||||||
if (user.sendAction.sendType === 'count') return t('total')
|
if (user.sendAction.sendType === 'count') return t('total')
|
||||||
|
if (user.sendAction.sendType === 'ban') return t('sendBanned')
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatSendCount(user) {
|
function formatSendCount(user) {
|
||||||
@@ -617,7 +618,7 @@ function httpSetStatus(user) {
|
|||||||
userSetStatus({status: status, userId: user.userId}).then(() => {
|
userSetStatus({status: status, userId: user.userId}).then(() => {
|
||||||
user.status = status
|
user.status = status
|
||||||
ElMessage({
|
ElMessage({
|
||||||
message: t('changSuccessMsg'),
|
message: t('saveSuccessMsg'),
|
||||||
type: "success",
|
type: "success",
|
||||||
plain: true
|
plain: true
|
||||||
})
|
})
|
||||||
@@ -630,7 +631,7 @@ function setType() {
|
|||||||
chooseUser.type = userForm.type
|
chooseUser.type = userForm.type
|
||||||
setTypeShow.value = false
|
setTypeShow.value = false
|
||||||
ElMessage({
|
ElMessage({
|
||||||
message: t('changSuccessMsg'),
|
message: t('saveSuccessMsg'),
|
||||||
type: "success",
|
type: "success",
|
||||||
plain: true
|
plain: true
|
||||||
})
|
})
|
||||||
@@ -675,7 +676,7 @@ function updatePwd() {
|
|||||||
userSetPwd({password: userForm.password, userId: userForm.userId}).then(() => {
|
userSetPwd({password: userForm.password, userId: userForm.userId}).then(() => {
|
||||||
setPwdShow.value = false
|
setPwdShow.value = false
|
||||||
ElMessage({
|
ElMessage({
|
||||||
message: t('changSuccessMsg'),
|
message: t('saveSuccessMsg'),
|
||||||
type: "success",
|
type: "success",
|
||||||
plain: true
|
plain: true
|
||||||
})
|
})
|
||||||
|
|||||||
1
mail-worker/dist/assets/index-BEDjT-8v.css
vendored
1
mail-worker/dist/assets/index-BEDjT-8v.css
vendored
File diff suppressed because one or more lines are too long
1
mail-worker/dist/assets/index-BP2DuLPL.css
vendored
Normal file
1
mail-worker/dist/assets/index-BP2DuLPL.css
vendored
Normal file
File diff suppressed because one or more lines are too long
193
mail-worker/dist/assets/index-BwB6muO3.js
vendored
193
mail-worker/dist/assets/index-BwB6muO3.js
vendored
File diff suppressed because one or more lines are too long
197
mail-worker/dist/assets/index-DQO7jFFS.js
vendored
Normal file
197
mail-worker/dist/assets/index-DQO7jFFS.js
vendored
Normal file
File diff suppressed because one or more lines are too long
5
mail-worker/dist/index.html
vendored
5
mail-worker/dist/index.html
vendored
@@ -5,9 +5,10 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
<title></title>
|
<title></title>
|
||||||
<link rel="icon" href="/assets/favicon-C5dAZutX.svg" type="image/svg+xml">
|
<link rel="icon" href="/assets/favicon-C5dAZutX.svg" type="image/svg+xml">
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap" rel="stylesheet">
|
||||||
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
|
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
|
||||||
<script type="module" crossorigin src="/assets/index-BwB6muO3.js"></script>
|
<script type="module" crossorigin src="/assets/index-DQO7jFFS.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-BEDjT-8v.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-BP2DuLPL.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="loading-first">
|
<div id="loading-first">
|
||||||
|
|||||||
@@ -103,6 +103,10 @@ export const settingConst = {
|
|||||||
ruleType: {
|
ruleType: {
|
||||||
ALL: 0,
|
ALL: 0,
|
||||||
RULE: 1
|
RULE: 1
|
||||||
|
},
|
||||||
|
noRecipient: {
|
||||||
|
OPEN: 0,
|
||||||
|
CLOSE: 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import dayjs from 'dayjs';
|
|||||||
import utc from 'dayjs/plugin/utc';
|
import utc from 'dayjs/plugin/utc';
|
||||||
import timezone from 'dayjs/plugin/timezone';
|
import timezone from 'dayjs/plugin/timezone';
|
||||||
import roleService from '../service/role-service';
|
import roleService from '../service/role-service';
|
||||||
|
import verifyUtils from '../utils/verify-utils';
|
||||||
|
|
||||||
dayjs.extend(utc);
|
dayjs.extend(utc);
|
||||||
dayjs.extend(timezone);
|
dayjs.extend(timezone);
|
||||||
@@ -28,7 +29,8 @@ export async function email(message, env, ctx) {
|
|||||||
forwardEmail,
|
forwardEmail,
|
||||||
ruleEmail,
|
ruleEmail,
|
||||||
ruleType,
|
ruleType,
|
||||||
r2Domain
|
r2Domain,
|
||||||
|
noRecipient
|
||||||
} = await settingService.query({ env });
|
} = await settingService.query({ env });
|
||||||
|
|
||||||
if (receive === settingConst.receive.CLOSE) {
|
if (receive === settingConst.receive.CLOSE) {
|
||||||
@@ -47,7 +49,11 @@ export async function email(message, env, ctx) {
|
|||||||
|
|
||||||
const email = await PostalMime.parse(content);
|
const email = await PostalMime.parse(content);
|
||||||
|
|
||||||
const account = await accountService.selectByEmailIncludeDelNoCase({ env: env }, message.to);
|
const account = await accountService.selectByEmailIncludeDel({ env: env }, message.to);
|
||||||
|
|
||||||
|
if (!account && noRecipient === settingConst.noRecipient.CLOSE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (account && account.email !== env.admin) {
|
if (account && account.email !== env.admin) {
|
||||||
|
|
||||||
@@ -61,9 +67,9 @@ export async function email(message, env, ctx) {
|
|||||||
|
|
||||||
for (const item of banEmail) {
|
for (const item of banEmail) {
|
||||||
|
|
||||||
if (item.startsWith('*@')) {
|
if (verifyUtils.isDomain(item)) {
|
||||||
|
|
||||||
const banDomain = emailUtils.getDomain(item.toLowerCase());
|
const banDomain = item.toLowerCase();
|
||||||
const receiveDomain = emailUtils.getDomain(email.from.address.toLowerCase());
|
const receiveDomain = emailUtils.getDomain(email.from.address.toLowerCase());
|
||||||
|
|
||||||
if (banDomain === receiveDomain) {
|
if (banDomain === receiveDomain) {
|
||||||
|
|||||||
@@ -25,5 +25,14 @@ export const setting = sqliteTable('setting', {
|
|||||||
ruleType: integer('rule_type').default(0).notNull(),
|
ruleType: integer('rule_type').default(0).notNull(),
|
||||||
loginOpacity: integer('login_opacity').default(0.88),
|
loginOpacity: integer('login_opacity').default(0.88),
|
||||||
resendTokens: text('resend_tokens').default("{}").notNull(),
|
resendTokens: text('resend_tokens').default("{}").notNull(),
|
||||||
|
noticeTitle: text('notice_title').default('').notNull(),
|
||||||
|
noticeContent: text('notice_content').default('').notNull(),
|
||||||
|
noticeType: text('notice_type').default('').notNull(),
|
||||||
|
noticeDuration: integer('notice_duration').default(0).notNull(),
|
||||||
|
noticePosition: text('notice_position').default('').notNull(),
|
||||||
|
noticeOffset: integer('notice_offset').default(0).notNull(),
|
||||||
|
noticeWidth: integer('notice_width').default(400).notNull(),
|
||||||
|
notice: integer('notice').default(0).notNull(),
|
||||||
|
noRecipient: integer('no_recipient').default(1).notNull()
|
||||||
});
|
});
|
||||||
export default setting
|
export default setting
|
||||||
|
|||||||
@@ -61,38 +61,39 @@ const en = {
|
|||||||
JWTMismatch: 'JWT secret mismatch',
|
JWTMismatch: 'JWT secret mismatch',
|
||||||
perms: {
|
perms: {
|
||||||
"邮件": "Email",
|
"邮件": "Email",
|
||||||
"邮件发送": "Send email",
|
"邮件发送": "Send Email",
|
||||||
"邮件删除": "Delete email",
|
"邮件删除": "Delete Email",
|
||||||
"邮箱侧栏": "Account",
|
"邮箱侧栏": "Account",
|
||||||
"邮箱查看": "View account",
|
"邮箱查看": "View Account",
|
||||||
"邮箱添加": "Add account",
|
"邮箱添加": "Add Account",
|
||||||
"邮箱删除": "Delete account",
|
"邮箱删除": "Delete Account",
|
||||||
"个人设置": "Settings",
|
"个人设置": "Settings",
|
||||||
"用户注销": "Delete user",
|
"用户注销": "Delete User",
|
||||||
"分析页": "Analytics",
|
"分析页": "Analytics",
|
||||||
"数据查看": "View data",
|
"数据查看": "View Data",
|
||||||
"用户信息": "All users",
|
"用户信息": "All Users",
|
||||||
"用户查看": "View user",
|
"用户查看": "View User",
|
||||||
"用户添加": "Add user",
|
"用户添加": "Add User",
|
||||||
"密码修改": "Change password",
|
"密码修改": "Change Password",
|
||||||
"状态修改": "Change status",
|
"状态修改": "Change Status",
|
||||||
"权限修改": "Change role",
|
"权限修改": "Change Role",
|
||||||
"用户删除": "Delete user",
|
"用户删除": "Delete User",
|
||||||
"邮件列表": "All mail",
|
"邮件列表": "All Mail",
|
||||||
"邮件查看": "View email",
|
"邮件查看": "View Email",
|
||||||
"权限控制": "Role",
|
"权限控制": "Role",
|
||||||
"身份查看": "View role",
|
"身份添加": "Add Role",
|
||||||
"身份修改": "Change role",
|
"身份查看": "View Role",
|
||||||
"身份删除": "Delete role",
|
"身份修改": "Change Role",
|
||||||
"注册密钥": "Invite code",
|
"身份删除": "Delete Role",
|
||||||
"密钥查看": "View code",
|
"注册密钥": "Invite Code",
|
||||||
"密钥添加": "Add code",
|
"密钥查看": "View Code",
|
||||||
"密钥删除": "Delete code",
|
"密钥添加": "Add Code",
|
||||||
"系统设置": "System settings",
|
"密钥删除": "Delete Code",
|
||||||
"设置查看": "View settings",
|
"系统设置": "System Settings",
|
||||||
"设置修改": "Change settings",
|
"设置查看": "View Settings",
|
||||||
"物理清空": "Physical purge",
|
"设置修改": "Change Settings",
|
||||||
"发件重置": "Reset send count"
|
"物理清空": "Physical Purge",
|
||||||
|
"发件重置": "Reset Send Count"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ const zh = {
|
|||||||
"邮件查看": "邮件查看",
|
"邮件查看": "邮件查看",
|
||||||
"权限控制": "权限控制",
|
"权限控制": "权限控制",
|
||||||
"身份查看": "身份查看",
|
"身份查看": "身份查看",
|
||||||
|
"身份添加": "身份添加",
|
||||||
"身份修改": "身份修改",
|
"身份修改": "身份修改",
|
||||||
"身份删除": "身份删除",
|
"身份删除": "身份删除",
|
||||||
"注册密钥": "注册密钥",
|
"注册密钥": "注册密钥",
|
||||||
|
|||||||
@@ -2,13 +2,14 @@ import settingService from '../service/setting-service';
|
|||||||
import emailUtils from '../utils/email-utils';
|
import emailUtils from '../utils/email-utils';
|
||||||
import {emailConst} from "../const/entity-const";
|
import {emailConst} from "../const/entity-const";
|
||||||
import { t } from '../i18n/i18n'
|
import { t } from '../i18n/i18n'
|
||||||
|
|
||||||
const init = {
|
const init = {
|
||||||
async init(c) {
|
async init(c) {
|
||||||
|
|
||||||
const secret = c.req.param('secret');
|
const secret = c.req.param('secret');
|
||||||
|
|
||||||
if (secret !== c.env.jwt_secret) {
|
if (secret !== c.env.jwt_secret) {
|
||||||
return c.text('jwt_secret 不匹配');
|
return c.text(t('JWTMismatch'));
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.intDB(c);
|
await this.intDB(c);
|
||||||
@@ -25,6 +26,19 @@ const init = {
|
|||||||
|
|
||||||
async v1_6DB(c) {
|
async v1_6DB(c) {
|
||||||
|
|
||||||
|
const noticeContent = '<div style="color: teal;margin-bottom: 5px;">欢迎使用 Cloud Mail 🎉 </div >\n' +
|
||||||
|
'本项目仅供学习交流,禁止用于违法业务\n' +
|
||||||
|
'<br>\n' +
|
||||||
|
'请遵守当地法规,作者不承担任何法律责任\n' +
|
||||||
|
'<div style="display: flex;gap: 18px;margin-top: 10px;">\n' +
|
||||||
|
'<a href="https://github.com/eoao/cloud-mail" target="_blank" >\n' +
|
||||||
|
'<img src="https://api.iconify.design/codicon:github-inverted.svg" alt="GitHub" width="25" height="25" />\n' +
|
||||||
|
'</a>\n' +
|
||||||
|
'<a href="https://t.me/cloud_mail_tg" target="_blank" >\n' +
|
||||||
|
'<img src="https://api.iconify.design/logos:telegram.svg" alt="GitHub" width="25" height="25" />\n' +
|
||||||
|
'</a>\n' +
|
||||||
|
'</div>\n'
|
||||||
|
|
||||||
const ADD_COLUMN_SQL_LIST = [
|
const ADD_COLUMN_SQL_LIST = [
|
||||||
`ALTER TABLE setting ADD COLUMN reg_verify_count INTEGER NOT NULL DEFAULT 1;`,
|
`ALTER TABLE setting ADD COLUMN reg_verify_count INTEGER NOT NULL DEFAULT 1;`,
|
||||||
`ALTER TABLE setting ADD COLUMN add_verify_count INTEGER NOT NULL DEFAULT 1;`,
|
`ALTER TABLE setting ADD COLUMN add_verify_count INTEGER NOT NULL DEFAULT 1;`,
|
||||||
@@ -34,16 +48,42 @@ const init = {
|
|||||||
count INTEGER NOT NULL DEFAULT 1,
|
count INTEGER NOT NULL DEFAULT 1,
|
||||||
type INTEGER NOT NULL DEFAULT 0,
|
type INTEGER NOT NULL DEFAULT 0,
|
||||||
update_time DATETIME DEFAULT CURRENT_TIMESTAMP
|
update_time DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
)`
|
)`,
|
||||||
|
`ALTER TABLE setting ADD COLUMN notice_title TEXT NOT NULL DEFAULT '公告';`,
|
||||||
|
`ALTER TABLE setting ADD COLUMN notice_content TEXT NOT NULL DEFAULT '';`,
|
||||||
|
`ALTER TABLE setting ADD COLUMN notice_type TEXT NOT NULL DEFAULT 'none';`,
|
||||||
|
`ALTER TABLE setting ADD COLUMN notice_duration INTEGER NOT NULL DEFAULT 0;`,
|
||||||
|
`ALTER TABLE setting ADD COLUMN notice_offset INTEGER NOT NULL DEFAULT 0;`,
|
||||||
|
`ALTER TABLE setting ADD COLUMN notice_position TEXT NOT NULL DEFAULT 'top-right';`,
|
||||||
|
`ALTER TABLE setting ADD COLUMN notice_width INTEGER NOT NULL DEFAULT 340;`,
|
||||||
|
`ALTER TABLE setting ADD COLUMN notice INTEGER NOT NULL DEFAULT 0;`,
|
||||||
|
`ALTER TABLE setting ADD COLUMN no_recipient INTEGER NOT NULL DEFAULT 1;`,
|
||||||
|
`UPDATE role SET avail_domain = '';`,
|
||||||
|
`UPDATE role SET ban_email = '';`,
|
||||||
|
`CREATE INDEX IF NOT EXISTS idx_email_user_id_account_id ON email(user_id, account_id);`
|
||||||
];
|
];
|
||||||
|
|
||||||
for (let sql of ADD_COLUMN_SQL_LIST) {
|
const promises = ADD_COLUMN_SQL_LIST.map(async (sql) => {
|
||||||
try {
|
try {
|
||||||
await c.env.db.prepare(sql).run();
|
await c.env.db.prepare(sql).run();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(`跳过字段添加,原因:${e.message}`);
|
console.warn(`通过字段,原因:${e.message}`);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all(promises);
|
||||||
|
await c.env.db.prepare(`UPDATE setting SET notice_content = ? WHERE notice_content = '';`).bind(noticeContent).run();
|
||||||
|
try {
|
||||||
|
await c.env.db.batch([
|
||||||
|
c.env.db.prepare(`DROP INDEX IF EXISTS idx_account_email`),
|
||||||
|
c.env.db.prepare(`DROP INDEX IF EXISTS idx_user_email`),
|
||||||
|
c.env.db.prepare(`CREATE UNIQUE INDEX IF NOT EXISTS idx_account_email_nocase ON account (email COLLATE NOCASE)`),
|
||||||
|
c.env.db.prepare(`CREATE UNIQUE INDEX IF NOT EXISTS idx_user_email_nocase ON user (email COLLATE NOCASE)`)
|
||||||
|
]);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
async v1_5DB(c) {
|
async v1_5DB(c) {
|
||||||
@@ -97,13 +137,15 @@ const init = {
|
|||||||
`ALTER TABLE user ADD COLUMN reg_key_id 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) {
|
const promises = ADD_COLUMN_SQL_LIST.map(async (sql) => {
|
||||||
try {
|
try {
|
||||||
await c.env.db.prepare(sql).run();
|
await c.env.db.prepare(sql).run();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(`跳过字段添加,原因:${e.message}`);
|
console.warn(`跳过字段添加,原因:${e.message}`);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
|
await Promise.all(promises);
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -123,13 +165,15 @@ const init = {
|
|||||||
`ALTER TABLE setting ADD COLUMN rule_type INTEGER NOT NULL DEFAULT 0;`
|
`ALTER TABLE setting ADD COLUMN rule_type INTEGER NOT NULL DEFAULT 0;`
|
||||||
];
|
];
|
||||||
|
|
||||||
for (let sql of ADD_COLUMN_SQL_LIST) {
|
const promises = ADD_COLUMN_SQL_LIST.map(async (sql) => {
|
||||||
try {
|
try {
|
||||||
await c.env.db.prepare(sql).run();
|
await c.env.db.prepare(sql).run();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(`跳过字段添加,原因:${e.message}`);
|
console.warn(`跳过字段添加,原因:${e.message}`);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
|
await Promise.all(promises);
|
||||||
|
|
||||||
const nameColumn = await c.env.db.prepare(`SELECT * FROM pragma_table_info('email') WHERE name = 'to_email' limit 1`).first();
|
const nameColumn = await c.env.db.prepare(`SELECT * FROM pragma_table_info('email') WHERE name = 'to_email' limit 1`).first();
|
||||||
|
|
||||||
@@ -158,13 +202,15 @@ const init = {
|
|||||||
`ALTER TABLE email ADD COLUMN relation TEXT NOT NULL DEFAULT '';`
|
`ALTER TABLE email ADD COLUMN relation TEXT NOT NULL DEFAULT '';`
|
||||||
];
|
];
|
||||||
|
|
||||||
for (let sql of ADD_COLUMN_SQL_LIST) {
|
const promises = ADD_COLUMN_SQL_LIST.map(async (sql) => {
|
||||||
try {
|
try {
|
||||||
await c.env.db.prepare(sql).run();
|
await c.env.db.prepare(sql).run();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(`跳过字段添加,原因:${e.message}`);
|
console.warn(`跳过字段添加,原因:${e.message}`);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
|
await Promise.all(promises);
|
||||||
|
|
||||||
await this.receiveEmailToRecipient(c);
|
await this.receiveEmailToRecipient(c);
|
||||||
await this.initAccountName(c);
|
await this.initAccountName(c);
|
||||||
@@ -178,13 +224,6 @@ const init = {
|
|||||||
console.warn(`跳过数据,原因:${e.message}`);
|
console.warn(`跳过数据,原因:${e.message}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
await c.env.db.prepare(`CREATE UNIQUE INDEX IF NOT EXISTS idx_account_email ON account (email)`).run();
|
|
||||||
await c.env.db.prepare(`CREATE UNIQUE INDEX IF NOT EXISTS idx_user_email ON user (email)`).run();
|
|
||||||
} catch (e) {
|
|
||||||
console.warn(`跳过添加唯一邮箱索引,原因:${e.message}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
async v1_1DB(c) {
|
async v1_1DB(c) {
|
||||||
@@ -215,13 +254,15 @@ const init = {
|
|||||||
`ALTER TABLE attachments ADD COLUMN type INTEGER NOT NULL DEFAULT 0;`
|
`ALTER TABLE attachments ADD COLUMN type INTEGER NOT NULL DEFAULT 0;`
|
||||||
];
|
];
|
||||||
|
|
||||||
for (let sql of ADD_COLUMN_SQL_LIST) {
|
const promises = ADD_COLUMN_SQL_LIST.map(async (sql) => {
|
||||||
try {
|
try {
|
||||||
await c.env.db.prepare(sql).run();
|
await c.env.db.prepare(sql).run();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(`跳过字段添加,原因:${e.message}`);
|
console.warn(`跳过字段添加,原因:${e.message}`);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
|
await Promise.all(promises);
|
||||||
|
|
||||||
// 创建 perm 表并初始化
|
// 创建 perm 表并初始化
|
||||||
await c.env.db.prepare(`
|
await c.env.db.prepare(`
|
||||||
@@ -298,7 +339,7 @@ const init = {
|
|||||||
INSERT INTO role (
|
INSERT INTO role (
|
||||||
role_id, name, key, create_time, sort, description, user_id, is_default, send_count, send_type, account_count
|
role_id, name, key, create_time, sort, description, user_id, is_default, send_count, send_type, account_count
|
||||||
) VALUES (
|
) VALUES (
|
||||||
1, '普通用户', NULL, '0000-00-00 00:00:00', 0, '只有普通使用权限', 0, 1, NULL, 'count', 10
|
1, '普通用户', NULL, '0000-00-00 00:00:00', 0, '只有普通使用权限', 0, 1, NULL, 'ban', 10
|
||||||
)
|
)
|
||||||
`).run();
|
`).run();
|
||||||
}
|
}
|
||||||
@@ -323,7 +364,8 @@ const init = {
|
|||||||
(104, 1, 24),
|
(104, 1, 24),
|
||||||
(105, 1, 4),
|
(105, 1, 4),
|
||||||
(106, 1, 5),
|
(106, 1, 5),
|
||||||
(107, 1, 1)
|
(107, 1, 1),
|
||||||
|
(108, 1, 3)
|
||||||
`).run();
|
`).run();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -468,5 +510,4 @@ const init = {
|
|||||||
await c.env.db.batch(queryList);
|
await c.env.db.batch(queryList);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default init;
|
export default init;
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ const accountService = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let accountRow = await this.selectByEmailIncludeDelNoCase(c, email);
|
let accountRow = await this.selectByEmailIncludeDel(c, email);
|
||||||
|
|
||||||
if (accountRow && accountRow.isDel === isDel.DELETE) {
|
if (accountRow && accountRow.isDel === isDel.DELETE) {
|
||||||
throw new BizError(t('isDelAccount'));
|
throw new BizError(t('isDelAccount'));
|
||||||
@@ -92,23 +92,8 @@ const accountService = {
|
|||||||
return accountRow;
|
return accountRow;
|
||||||
},
|
},
|
||||||
|
|
||||||
selectByEmailIncludeDelNoCase(c, email) {
|
|
||||||
return orm(c)
|
|
||||||
.select()
|
|
||||||
.from(account)
|
|
||||||
.where(sql`${account.email} COLLATE NOCASE = ${email}`)
|
|
||||||
.get();
|
|
||||||
},
|
|
||||||
selectByEmailIncludeDel(c, email) {
|
selectByEmailIncludeDel(c, email) {
|
||||||
return orm(c).select().from(account).where(eq(account.email, email)).get();
|
return orm(c).select().from(account).where(sql`${account.email} COLLATE NOCASE = ${email}`).get();
|
||||||
},
|
|
||||||
|
|
||||||
selectByEmail(c, email) {
|
|
||||||
return orm(c).select().from(account).where(
|
|
||||||
and(
|
|
||||||
eq(account.email, email),
|
|
||||||
eq(account.isDel, isDel.NORMAL)))
|
|
||||||
.get();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
list(c, params, userId) {
|
list(c, params, userId) {
|
||||||
|
|||||||
@@ -58,9 +58,9 @@ const emailService = {
|
|||||||
)
|
)
|
||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
timeSort ? gt(email.emailId, emailId) : lt(email.emailId, emailId),
|
|
||||||
eq(email.accountId, accountId),
|
|
||||||
eq(email.userId, userId),
|
eq(email.userId, userId),
|
||||||
|
eq(email.accountId, accountId),
|
||||||
|
timeSort ? gt(email.emailId, emailId) : lt(email.emailId, emailId),
|
||||||
eq(email.type, type),
|
eq(email.type, type),
|
||||||
eq(email.isDel, isDel.NORMAL)
|
eq(email.isDel, isDel.NORMAL)
|
||||||
)
|
)
|
||||||
@@ -280,7 +280,7 @@ const emailService = {
|
|||||||
|
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
throw new BizError(error.error);
|
throw new BizError(error.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
html = this.imgReplace(html, null, r2Domain);
|
html = this.imgReplace(html, null, r2Domain);
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ const loginService = {
|
|||||||
regKeyId = result?.regKeyId
|
regKeyId = result?.regKeyId
|
||||||
}
|
}
|
||||||
|
|
||||||
const accountRow = await accountService.selectByEmailIncludeDelNoCase(c, email);
|
const accountRow = await accountService.selectByEmailIncludeDel(c, email);
|
||||||
|
|
||||||
if (accountRow && accountRow.isDel === isDel.DELETE) {
|
if (accountRow && accountRow.isDel === isDel.DELETE) {
|
||||||
throw new BizError(t('isDelUser'));
|
throw new BizError(t('isDelUser'));
|
||||||
|
|||||||
@@ -23,11 +23,7 @@ const roleService = {
|
|||||||
|
|
||||||
let roleRow = await orm(c).select().from(role).where(eq(role.name, name)).get();
|
let roleRow = await orm(c).select().from(role).where(eq(role.name, name)).get();
|
||||||
|
|
||||||
if (roleRow) {
|
const notEmailIndex = banEmail.findIndex(item => (!verifyUtils.isEmail(item) && !verifyUtils.isDomain(item)))
|
||||||
throw new BizError(t('roleNameExist'));
|
|
||||||
}
|
|
||||||
|
|
||||||
const notEmailIndex = banEmail.findIndex(item => !verifyUtils.isEmail(item))
|
|
||||||
|
|
||||||
if (notEmailIndex > -1) {
|
if (notEmailIndex > -1) {
|
||||||
throw new BizError(t('notEmail'));
|
throw new BizError(t('notEmail'));
|
||||||
@@ -76,7 +72,7 @@ const roleService = {
|
|||||||
|
|
||||||
delete params.isDefault
|
delete params.isDefault
|
||||||
|
|
||||||
const notEmailIndex = banEmail.findIndex(item => !verifyUtils.isEmail(item))
|
const notEmailIndex = banEmail.findIndex(item => (!verifyUtils.isEmail(item) && !verifyUtils.isDomain(item)))
|
||||||
|
|
||||||
if (notEmailIndex > -1) {
|
if (notEmailIndex > -1) {
|
||||||
throw new BizError(t('notEmail'));
|
throw new BizError(t('notEmail'));
|
||||||
@@ -168,7 +164,7 @@ const roleService = {
|
|||||||
|
|
||||||
const availIndex = availDomain.findIndex(item => {
|
const availIndex = availDomain.findIndex(item => {
|
||||||
const domain = emailUtils.getDomain(email.toLowerCase());
|
const domain = emailUtils.getDomain(email.toLowerCase());
|
||||||
const availDomainItem = emailUtils.getDomain(item.toLowerCase());
|
const availDomainItem = item.toLowerCase();
|
||||||
console.log(domain,availDomainItem)
|
console.log(domain,availDomainItem)
|
||||||
return domain === availDomainItem
|
return domain === availDomainItem
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -139,7 +139,15 @@ const settingService = {
|
|||||||
domainList:settingRow.domainList,
|
domainList:settingRow.domainList,
|
||||||
regKey: settingRow.regKey,
|
regKey: settingRow.regKey,
|
||||||
regVerifyOpen: settingRow.regVerifyOpen,
|
regVerifyOpen: settingRow.regVerifyOpen,
|
||||||
addVerifyOpen: settingRow.addVerifyOpen
|
addVerifyOpen: settingRow.addVerifyOpen,
|
||||||
|
noticeTitle: settingRow.noticeTitle,
|
||||||
|
noticeContent: settingRow.noticeContent,
|
||||||
|
noticeType: settingRow.noticeType,
|
||||||
|
noticeDuration: settingRow.noticeDuration,
|
||||||
|
noticePosition: settingRow.noticePosition,
|
||||||
|
noticeWidth: settingRow.noticeWidth,
|
||||||
|
noticeOffset: settingRow.noticeOffset,
|
||||||
|
notice: settingRow.notice,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ const userService = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
selectByEmailIncludeDel(c, email) {
|
selectByEmailIncludeDel(c, email) {
|
||||||
return orm(c).select().from(user).where(eq(user.email, email)).get();
|
return orm(c).select().from(user).where(sql`${user.email} COLLATE NOCASE = ${email}`).get();
|
||||||
},
|
},
|
||||||
|
|
||||||
selectById(c, userId) {
|
selectById(c, userId) {
|
||||||
@@ -355,6 +355,8 @@ const userService = {
|
|||||||
|
|
||||||
const userId = await userService.insert(c, { email, password: hash, salt, type });
|
const userId = await userService.insert(c, { email, password: hash, salt, type });
|
||||||
|
|
||||||
|
await userService.updateUserInfo(c, userId, true);
|
||||||
|
|
||||||
await accountService.insert(c, { userId: userId, email, type, name: emailUtils.getName(email) });
|
await accountService.insert(c, { userId: userId, email, type, name: emailUtils.getName(email) });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
const verifyUtils = {
|
const verifyUtils = {
|
||||||
isEmail(str) {
|
isEmail(str) {
|
||||||
return /^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~.-]+@([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/.test(str);
|
return /^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~.-]+@([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/.test(str);
|
||||||
|
},
|
||||||
|
isDomain(str) {
|
||||||
|
return /^(?!:\/\/)([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/.test(str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
name = "cloud-mail"
|
name = "cloud-mail"
|
||||||
main = "src/index.js"
|
main = "src/index.js"
|
||||||
compatibility_date = "2025-04-09"
|
compatibility_date = "2025-07-29"
|
||||||
keep_vars = true
|
keep_vars = true
|
||||||
|
|
||||||
[observability]
|
[observability]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
name = "cloud-mail-dev"
|
name = "cloud-mail-dev"
|
||||||
main = "src/index.js"
|
main = "src/index.js"
|
||||||
compatibility_date = "2025-04-09"
|
compatibility_date = "2025-07-29"
|
||||||
keep_vars = true
|
keep_vars = true
|
||||||
|
|
||||||
[observability]
|
[observability]
|
||||||
@@ -32,7 +32,7 @@ run_worker_first = true
|
|||||||
crons = ["0 16 * * *"]
|
crons = ["0 16 * * *"]
|
||||||
|
|
||||||
[vars]
|
[vars]
|
||||||
orm_log = false
|
orm_log = true
|
||||||
domain = ["example.com", "example2.com", "example3.com", "example4.com"]
|
domain = ["example.com", "example2.com", "example3.com", "example4.com"]
|
||||||
admin = "admin@example.com"
|
admin = "admin@example.com"
|
||||||
jwt_secret = "b7f29a1d-18e2-4d3b-941f-f6b2c97c02fd"
|
jwt_secret = "b7f29a1d-18e2-4d3b-941f-f6b2c97c02fd"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
name = "cloud-mail-test"
|
name = "cloud-mail-test"
|
||||||
main = "src/index.js"
|
main = "src/index.js"
|
||||||
compatibility_date = "2025-04-09"
|
compatibility_date = "2025-07-29"
|
||||||
keep_vars = true
|
keep_vars = true
|
||||||
|
|
||||||
[observability]
|
[observability]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
name = "cloud-mail"
|
name = "cloud-mail"
|
||||||
main = "src/index.js"
|
main = "src/index.js"
|
||||||
compatibility_date = "2025-04-09"
|
compatibility_date = "2025-07-29"
|
||||||
keep_vars = true
|
keep_vars = true
|
||||||
|
|
||||||
[observability]
|
[observability]
|
||||||
|
|||||||
Reference in New Issue
Block a user