新增KV数据库存储附件
This commit is contained in:
@@ -494,8 +494,8 @@ function getEmailList(refresh = false) {
|
|||||||
props.getEmailList(queryParam.emailId, queryParam.size).then(async data => {
|
props.getEmailList(queryParam.emailId, queryParam.size).then(async data => {
|
||||||
let end = Date.now();
|
let end = Date.now();
|
||||||
let duration = end - start;
|
let duration = end - start;
|
||||||
if (duration < 500 && !queryParam.emailId) {
|
if (duration < 300 && !queryParam.emailId) {
|
||||||
await sleep(500 - duration)
|
await sleep(300 - duration)
|
||||||
}
|
}
|
||||||
firstLoad.value = false
|
firstLoad.value = false
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ const en = {
|
|||||||
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',
|
||||||
@@ -137,7 +137,7 @@ const en = {
|
|||||||
websiteReg: 'Sign Up',
|
websiteReg: 'Sign Up',
|
||||||
loginDomain: 'Sign-In Box Domain',
|
loginDomain: 'Sign-In Box Domain',
|
||||||
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',
|
||||||
customization: 'Customization',
|
customization: 'Customization',
|
||||||
websiteTitle: 'Title',
|
websiteTitle: 'Title',
|
||||||
loginBoxOpacity: 'Login Box Opacity',
|
loginBoxOpacity: 'Login Box Opacity',
|
||||||
@@ -145,7 +145,7 @@ const en = {
|
|||||||
emailSetting: 'Email',
|
emailSetting: 'Email',
|
||||||
receiveEmail: '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',
|
||||||
oss: 'Object Storage',
|
oss: 'Object Storage',
|
||||||
@@ -165,7 +165,7 @@ const en = {
|
|||||||
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',
|
||||||
@@ -173,14 +173,14 @@ const en = {
|
|||||||
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',
|
||||||
otherEmailDesc: 'emails can be forwarded to external email, but must be verified via cloudflare.',
|
otherEmailDesc: 'emails can be forwarded to external email, but must be verified via cloudflare',
|
||||||
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',
|
||||||
@@ -294,7 +294,10 @@ const en = {
|
|||||||
s3Configuration: 'S3 Configuration',
|
s3Configuration: 'S3 Configuration',
|
||||||
confirmDeletionOfContacts: 'Confirm clearing contacts?',
|
confirmDeletionOfContacts: 'Confirm clearing contacts?',
|
||||||
recentContacts: 'Recent contacts',
|
recentContacts: 'Recent contacts',
|
||||||
selectContacts: 'Select'
|
selectContacts: 'Select',
|
||||||
|
forcePathStyleDesc: 'Some self-hosted object storages require path-style access to be enabled',
|
||||||
|
kvStorageDesc: 'Replace object storage with KV, and update the access domain to a Worker custom domain',
|
||||||
|
kvStorage: 'KV Storage'
|
||||||
}
|
}
|
||||||
|
|
||||||
export default en
|
export default en
|
||||||
|
|||||||
@@ -292,8 +292,12 @@ const zh = {
|
|||||||
include: '包含',
|
include: '包含',
|
||||||
delAllEmailConfirm: '确定要删除吗?',
|
delAllEmailConfirm: '确定要删除吗?',
|
||||||
s3Configuration: 'S3 配置',
|
s3Configuration: 'S3 配置',
|
||||||
|
s3Desc: '设置其他S3协议存储,如果绑定了R2会优先用R2',
|
||||||
confirmDeletionOfContacts: '确认清除这些联系人吗?',
|
confirmDeletionOfContacts: '确认清除这些联系人吗?',
|
||||||
recentContacts: '最近联系人',
|
recentContacts: '最近联系人',
|
||||||
selectContacts: '选中'
|
selectContacts: '选中',
|
||||||
|
forcePathStyleDesc: '路径样式访问,一些自建的对象存储需要打开',
|
||||||
|
kvStorageDesc: '使用KV替代对象存储,访问域名改成worker自定义域',
|
||||||
|
kvStorage: 'KV存储'
|
||||||
}
|
}
|
||||||
export default zh
|
export default zh
|
||||||
|
|||||||
@@ -343,9 +343,8 @@ function getAccountList() {
|
|||||||
|
|
||||||
let end = Date.now();
|
let end = Date.now();
|
||||||
let duration = end - start;
|
let duration = end - start;
|
||||||
if (duration < 500) {
|
if (duration < 300) {
|
||||||
await sleep(500 - duration)
|
await sleep(300 - duration)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (list.length < queryParams.size) {
|
if (list.length < queryParams.size) {
|
||||||
|
|||||||
@@ -16,6 +16,6 @@ export function setBackground(background) {
|
|||||||
return http.put('/setting/setBackground',{background})
|
return http.put('/setting/setBackground',{background})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function physicsDeleteAll() {
|
export function deleteBackground() {
|
||||||
return http.delete('/setting/physicsDeleteAll')
|
return http.delete('/setting/deleteBackground')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -197,13 +197,30 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="setting-item">
|
<div class="setting-item">
|
||||||
<div><span>{{ $t('s3Configuration') }}</span></div>
|
<div>
|
||||||
|
<span>{{ $t('s3Configuration') }}</span>
|
||||||
|
<el-tooltip effect="dark" :content="$t('s3Desc')">
|
||||||
|
<Icon class="warning" icon="fe:warning" width="18" height="18"/>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
<div class="r2domain">
|
<div class="r2domain">
|
||||||
<el-button class="opt-button" size="small" type="primary" @click="addS3Show = true">
|
<el-button class="opt-button" size="small" type="primary" @click="addS3Show = true">
|
||||||
<Icon icon="fluent:settings-48-regular" width="16" height="16"/>
|
<Icon icon="fluent:settings-48-regular" width="16" height="16"/>
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="setting-item">
|
||||||
|
<div>
|
||||||
|
<span>{{ $t('kvStorage') }}</span>
|
||||||
|
<el-tooltip effect="dark" :content="$t('kvStorageDesc')">
|
||||||
|
<Icon class="warning" icon="fe:warning" width="18" height="18"/>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
<div class="r2domain">
|
||||||
|
<el-switch @change="change" :before-change="beforeChange" :active-value="0" :inactive-value="1"
|
||||||
|
v-model="setting.kvStorage"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -636,7 +653,17 @@
|
|||||||
<el-input class="dialog-input" type="text" placeholder="Region" v-model="s3.region"/>
|
<el-input class="dialog-input" type="text" placeholder="Region" v-model="s3.region"/>
|
||||||
<el-input class="dialog-input" type="text" :placeholder="setting.s3AccessKey || 'Access Key'"
|
<el-input class="dialog-input" type="text" :placeholder="setting.s3AccessKey || 'Access Key'"
|
||||||
v-model="s3.s3AccessKey"/>
|
v-model="s3.s3AccessKey"/>
|
||||||
<el-input type="text" :placeholder="setting.s3SecretKey || 'Secret Key'" v-model="s3.s3SecretKey"/>
|
<el-input style="margin-bottom: 10px" type="text" :placeholder="setting.s3SecretKey || 'Secret Key'" v-model="s3.s3SecretKey"/>
|
||||||
|
<div class="force-path-style">
|
||||||
|
<div class="force-path-style-left">
|
||||||
|
<span>ForcePathStyle</span>
|
||||||
|
<el-tooltip effect="dark" :content="$t('forcePathStyleDesc')">
|
||||||
|
<Icon class="warning" icon="fe:warning" width="18" height="18"/>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
<el-switch :before-change="beforeChange" :active-value="0" :inactive-value="1"
|
||||||
|
v-model="s3.forcePathStyle"/>
|
||||||
|
</div>
|
||||||
<div class="s3-button">
|
<div class="s3-button">
|
||||||
<el-button :loading="clearS3Loading" @click="clearS3">{{ t('clear') }}</el-button>
|
<el-button :loading="clearS3Loading" @click="clearS3">{{ t('clear') }}</el-button>
|
||||||
<el-button type="primary" :loading="settingLoading && !clearS3Loading" @click="saveS3">{{ t('save') }}</el-button>
|
<el-button type="primary" :loading="settingLoading && !clearS3Loading" @click="saveS3">{{ t('save') }}</el-button>
|
||||||
@@ -649,7 +676,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {computed, defineOptions, reactive, ref} from "vue";
|
import {computed, defineOptions, reactive, ref} from "vue";
|
||||||
import {physicsDeleteAll, setBackground, settingQuery, settingSet} from "@/request/setting.js";
|
import {deleteBackground, 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 {useUiStore} from "@/store/ui.js";
|
||||||
import {useUserStore} from "@/store/user.js";
|
import {useUserStore} from "@/store/user.js";
|
||||||
@@ -718,7 +745,8 @@ const s3 = reactive({
|
|||||||
endpoint: '',
|
endpoint: '',
|
||||||
region: '',
|
region: '',
|
||||||
s3AccessKey: '',
|
s3AccessKey: '',
|
||||||
s3SecretKey: ''
|
s3SecretKey: '',
|
||||||
|
forcePathStyle: 1
|
||||||
})
|
})
|
||||||
|
|
||||||
const noticeForm = reactive({
|
const noticeForm = reactive({
|
||||||
@@ -799,6 +827,7 @@ function resetAddS3Form() {
|
|||||||
s3.region = setting.value.region
|
s3.region = setting.value.region
|
||||||
s3.s3AccessKey = ''
|
s3.s3AccessKey = ''
|
||||||
s3.s3SecretKey = ''
|
s3.s3SecretKey = ''
|
||||||
|
s3.forcePathStyle = setting.value.forcePathStyle
|
||||||
}
|
}
|
||||||
|
|
||||||
const resendList = computed(() => {
|
const resendList = computed(() => {
|
||||||
@@ -977,7 +1006,8 @@ function clearS3() {
|
|||||||
endpoint: '',
|
endpoint: '',
|
||||||
region: '',
|
region: '',
|
||||||
s3AccessKey: '',
|
s3AccessKey: '',
|
||||||
s3SecretKey: ''
|
s3SecretKey: '',
|
||||||
|
forcePathStyle: 1
|
||||||
}
|
}
|
||||||
clearS3Loading.value = true
|
clearS3Loading.value = true
|
||||||
editSetting(form)
|
editSetting(form)
|
||||||
@@ -988,7 +1018,8 @@ function saveS3() {
|
|||||||
const form = {
|
const form = {
|
||||||
bucket: s3.bucket,
|
bucket: s3.bucket,
|
||||||
endpoint: s3.endpoint,
|
endpoint: s3.endpoint,
|
||||||
region: s3.region
|
region: s3.region,
|
||||||
|
forcePathStyle: s3.forcePathStyle
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s3.s3AccessKey) form.s3AccessKey = s3.s3AccessKey
|
if (s3.s3AccessKey) form.s3AccessKey = s3.s3AccessKey
|
||||||
@@ -1034,35 +1065,21 @@ const opacityChange = debounce(doOpacityChange, 1000, {
|
|||||||
trailing: true
|
trailing: true
|
||||||
})
|
})
|
||||||
|
|
||||||
function physicsDeleteAllData() {
|
|
||||||
ElMessageBox.prompt(t('clearAllDelConfirm'), {
|
|
||||||
confirmButtonText: t('confirm'),
|
|
||||||
cancelButtonText: t('cancel'),
|
|
||||||
dangerouslyUseHTMLString: true,
|
|
||||||
title: t('warning'),
|
|
||||||
type: 'warning',
|
|
||||||
inputPattern: new RegExp(`^${t('delInputPattern')}$`),
|
|
||||||
inputErrorMessage: t('inputErrorMessage'),
|
|
||||||
}).then(() => {
|
|
||||||
physicsDeleteAll().then(() => {
|
|
||||||
ElMessage({
|
|
||||||
message: t('delSuccessMsg'),
|
|
||||||
type: "success",
|
|
||||||
plain: true
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function delBackground() {
|
function delBackground() {
|
||||||
ElMessageBox.confirm(t('delBackgroundConfirm'), {
|
ElMessageBox.confirm(t('delBackgroundConfirm'), {
|
||||||
confirmButtonText: t('confirm'),
|
confirmButtonText: t('confirm'),
|
||||||
cancelButtonText: t('cancel'),
|
cancelButtonText: t('cancel'),
|
||||||
type: 'warning'
|
type: 'warning'
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
backgroundUrl.value = ''
|
deleteBackground().then(() => {
|
||||||
setting.value.background = null
|
backgroundUrl.value = ''
|
||||||
editSetting({background: null})
|
setting.value.background = null
|
||||||
|
ElMessage({
|
||||||
|
message: t('delSuccessMsg'),
|
||||||
|
type: "success",
|
||||||
|
plain: true
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1368,7 +1385,7 @@ function editSetting(settingForm, refreshStatus = true) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.warning {
|
.warning {
|
||||||
margin-left: 4px;
|
margin-left: 2px;
|
||||||
color: grey;
|
color: grey;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
@@ -1595,6 +1612,19 @@ function editSetting(settingForm, refreshStatus = true) {
|
|||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.force-path-style {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 10px;
|
||||||
|
.force-path-style-left {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.concerning-item {
|
.concerning-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -1648,7 +1678,7 @@ function editSetting(settingForm, refreshStatus = true) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
form .el-button {
|
form .el-button {
|
||||||
margin-top: 15px;
|
margin-top: 10px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import r2Service from '../service/r2-service';
|
import r2Service from '../service/r2-service';
|
||||||
import app from '../hono/hono';
|
import app from '../hono/hono';
|
||||||
|
|
||||||
app.get('/file/*', async (c) => {
|
app.get('/oss/*', async (c) => {
|
||||||
const key = c.req.path.split('/file/')[1];
|
const key = c.req.path.split('/oss/')[1];
|
||||||
const obj = await r2Service.getObj(c, key);
|
const obj = await r2Service.getObj(c, key);
|
||||||
return new Response(obj.body, {
|
return new Response(obj.body, {
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -21,3 +21,9 @@ app.put('/setting/setBackground', async (c) => {
|
|||||||
const key = await settingService.setBackground(c, await c.req.json());
|
const key = await settingService.setBackground(c, await c.req.json());
|
||||||
return c.json(result.ok(key));
|
return c.json(result.ok(key));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.delete('/setting/deleteBackground', async (c) => {
|
||||||
|
await settingService.deleteBackground(c);
|
||||||
|
return c.json(result.ok());
|
||||||
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -107,6 +107,14 @@ export const settingConst = {
|
|||||||
noRecipient: {
|
noRecipient: {
|
||||||
OPEN: 0,
|
OPEN: 0,
|
||||||
CLOSE: 1,
|
CLOSE: 1,
|
||||||
|
},
|
||||||
|
kvStorage: {
|
||||||
|
OPEN: 0,
|
||||||
|
CLOSE: 1
|
||||||
|
},
|
||||||
|
forcePathStyle: {
|
||||||
|
OPEN: 0,
|
||||||
|
CLOSE: 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ export const setting = sqliteTable('setting', {
|
|||||||
region: text('region').default('').notNull(),
|
region: text('region').default('').notNull(),
|
||||||
endpoint: text('endpoint').default('').notNull(),
|
endpoint: text('endpoint').default('').notNull(),
|
||||||
s3AccessKey: text('s3_access_key').default('').notNull(),
|
s3AccessKey: text('s3_access_key').default('').notNull(),
|
||||||
s3SecretKey: text('s3_secret_key').default('').notNull()
|
s3SecretKey: text('s3_secret_key').default('').notNull(),
|
||||||
|
kvStorage: integer('kv_storage').default(1).notNull(),
|
||||||
|
forcePathStyle: integer('force_path_style').default(1).notNull()
|
||||||
});
|
});
|
||||||
export default setting
|
export default setting
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ const en = {
|
|||||||
publicTokenFail: 'Token validation failed',
|
publicTokenFail: 'Token validation failed',
|
||||||
notAdmin: 'The entered email is not an administrator email',
|
notAdmin: 'The entered email is not an administrator email',
|
||||||
emailExistDatabase: 'Email already exists in the database',
|
emailExistDatabase: 'Email already exists in the database',
|
||||||
|
notConfigOss: 'Object storage not configured',
|
||||||
perms: {
|
perms: {
|
||||||
"邮件": "Email",
|
"邮件": "Email",
|
||||||
"邮件发送": "Send Email",
|
"邮件发送": "Send Email",
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ const zh = {
|
|||||||
publicTokenFail: 'token验证失败',
|
publicTokenFail: 'token验证失败',
|
||||||
notAdmin: '输入的邮箱不是管理员邮箱',
|
notAdmin: '输入的邮箱不是管理员邮箱',
|
||||||
emailExistDatabase: '有邮箱已存在数据库中',
|
emailExistDatabase: '有邮箱已存在数据库中',
|
||||||
|
notConfigOss: '对象存储未配置',
|
||||||
perms: {
|
perms: {
|
||||||
"邮件": "邮件",
|
"邮件": "邮件",
|
||||||
"邮件发送": "邮件发送",
|
"邮件发送": "邮件发送",
|
||||||
|
|||||||
@@ -3,10 +3,11 @@ import { email } from './email/email';
|
|||||||
import userService from './service/user-service';
|
import userService from './service/user-service';
|
||||||
import verifyRecordService from './service/verify-record-service';
|
import verifyRecordService from './service/verify-record-service';
|
||||||
import emailService from './service/email-service';
|
import emailService from './service/email-service';
|
||||||
|
import kvObjService from './service/kv-obj-service';
|
||||||
export default {
|
export default {
|
||||||
async fetch(req, env, ctx) {
|
async fetch(req, env, ctx) {
|
||||||
const url = new URL(req.url)
|
|
||||||
|
|
||||||
|
const url = new URL(req.url)
|
||||||
|
|
||||||
if (url.pathname.startsWith('/api/')) {
|
if (url.pathname.startsWith('/api/')) {
|
||||||
url.pathname = url.pathname.replace('/api', '')
|
url.pathname = url.pathname.replace('/api', '')
|
||||||
@@ -14,12 +15,15 @@ export default {
|
|||||||
return app.fetch(req, env, ctx);
|
return app.fetch(req, env, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (['/static/','/attachments/'].some(p => url.pathname.startsWith(p))) {
|
||||||
|
return await kvObjService.toObjResp( { env }, url.pathname.substring(1));
|
||||||
|
}
|
||||||
|
|
||||||
return env.assets.fetch(req);
|
return env.assets.fetch(req);
|
||||||
},
|
},
|
||||||
email: email,
|
email: email,
|
||||||
async scheduled(c, env, ctx) {
|
async scheduled(c, env, ctx) {
|
||||||
await verifyRecordService.clearRecord({env})
|
await verifyRecordService.clearRecord({ env })
|
||||||
await userService.resetDaySendCount({ env })
|
await userService.resetDaySendCount({ env })
|
||||||
await emailService.completeReceiveAll({ env })
|
await emailService.completeReceiveAll({ env })
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -22,10 +22,22 @@ const init = {
|
|||||||
await this.v1_6DB(c);
|
await this.v1_6DB(c);
|
||||||
await this.v1_7DB(c);
|
await this.v1_7DB(c);
|
||||||
await this.v2DB(c);
|
await this.v2DB(c);
|
||||||
|
await this.v2_3DB(c);
|
||||||
await settingService.refresh(c);
|
await settingService.refresh(c);
|
||||||
return c.text(t('initSuccess'));
|
return c.text(t('initSuccess'));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async v2_3DB(c) {
|
||||||
|
try {
|
||||||
|
await c.env.db.batch([
|
||||||
|
c.env.db.prepare(`ALTER TABLE setting ADD COLUMN force_path_style INTEGER NOT NULL DEFAULT 1;`),
|
||||||
|
c.env.db.prepare(`ALTER TABLE setting ADD COLUMN kv_storage INTEGER NOT NULL DEFAULT 1;`)
|
||||||
|
]);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e.message)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
async v2DB(c) {
|
async v2DB(c) {
|
||||||
try {
|
try {
|
||||||
await c.env.db.batch([
|
await c.env.db.batch([
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import app from '../hono/hono';
|
|||||||
const exclude = [
|
const exclude = [
|
||||||
'/login',
|
'/login',
|
||||||
'/register',
|
'/register',
|
||||||
'/file',
|
'/oss',
|
||||||
'/setting/websiteConfig',
|
'/setting/websiteConfig',
|
||||||
'/webhooks',
|
'/webhooks',
|
||||||
'/init',
|
'/init',
|
||||||
@@ -34,6 +34,7 @@ const requirePerms = [
|
|||||||
'/allEmail/list',
|
'/allEmail/list',
|
||||||
'/allEmail/delete',
|
'/allEmail/delete',
|
||||||
'/setting/setBackground',
|
'/setting/setBackground',
|
||||||
|
'/setting/deleteBackground',
|
||||||
'/setting/set',
|
'/setting/set',
|
||||||
'/setting/query',
|
'/setting/query',
|
||||||
'/user/delete',
|
'/user/delete',
|
||||||
@@ -73,7 +74,7 @@ const premKey = {
|
|||||||
'all-email:query': ['/allEmail/list'],
|
'all-email:query': ['/allEmail/list'],
|
||||||
'all-email:delete': ['/allEmail/delete','/allEmail/batchDelete'],
|
'all-email:delete': ['/allEmail/delete','/allEmail/batchDelete'],
|
||||||
'setting:query': ['/setting/query'],
|
'setting:query': ['/setting/query'],
|
||||||
'setting:set': ['/setting/set', '/setting/setBackground'],
|
'setting:set': ['/setting/set', '/setting/setBackground','/setting/deleteBackground'],
|
||||||
'analysis:query': ['/analysis/echarts'],
|
'analysis:query': ['/analysis/echarts'],
|
||||||
'reg-key:add': ['/regKey/add'],
|
'reg-key:add': ['/regKey/add'],
|
||||||
'reg-key:query': ['/regKey/list','/regKey/history'],
|
'reg-key:query': ['/regKey/list','/regKey/history'],
|
||||||
|
|||||||
36
mail-worker/src/service/kv-obj-service.js
Normal file
36
mail-worker/src/service/kv-obj-service.js
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
const kvObjService = {
|
||||||
|
|
||||||
|
async putObj(c, key, content, metadata) {
|
||||||
|
await c.env.kv.put(key, content, { metadata: metadata });
|
||||||
|
},
|
||||||
|
|
||||||
|
async deleteObj(c, keys) {
|
||||||
|
|
||||||
|
if (typeof keys === 'string') {
|
||||||
|
keys = [keys];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keys.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(keys.map( key => c.env.kv.delete(key)));
|
||||||
|
},
|
||||||
|
|
||||||
|
async toObjResp(c, key) {
|
||||||
|
|
||||||
|
const obj = await c.env.kv.getWithMetadata(key, { type: "arrayBuffer"});
|
||||||
|
|
||||||
|
return new Response(obj.value, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': obj.metadata?.contentType || 'application/octet-stream',
|
||||||
|
'Content-Disposition': obj.metadata?.contentDisposition || null,
|
||||||
|
'Cache-Control': obj.metadata?.cacheControl || null
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
export default kvObjService;
|
||||||
@@ -1,23 +1,35 @@
|
|||||||
import s3Service from './s3-service';
|
import s3Service from './s3-service';
|
||||||
import settingService from './setting-service';
|
import settingService from './setting-service';
|
||||||
|
import kvObjService from './kv-obj-service';
|
||||||
|
import { settingConst } from '../const/entity-const';
|
||||||
|
|
||||||
const r2Service = {
|
const r2Service = {
|
||||||
|
|
||||||
async hasOSS(c) {
|
async hasOSS(c) {
|
||||||
|
|
||||||
if (c.env.r2) {
|
const setting = await settingService.query(c);
|
||||||
|
const { kvStorage, bucket, endpoint, s3AccessKey, s3SecretKey } = setting;
|
||||||
|
|
||||||
|
if (kvStorage === settingConst.kvStorage.OPEN) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const setting = await settingService.query(c);
|
if (c.env.r2) {
|
||||||
const { bucket, endpoint, s3AccessKey, s3SecretKey } = setting;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return !!(bucket && endpoint && s3AccessKey && s3SecretKey);
|
return !!(bucket && endpoint && s3AccessKey && s3SecretKey);
|
||||||
},
|
},
|
||||||
|
|
||||||
async putObj(c, key, content, metadata) {
|
async putObj(c, key, content, metadata) {
|
||||||
|
|
||||||
if (c.env.r2) {
|
const { kvStorage } = await settingService.query(c);
|
||||||
|
|
||||||
|
if (kvStorage === settingConst.kvStorage.OPEN) {
|
||||||
|
|
||||||
|
await kvObjService.putObj(c, key, content, metadata);
|
||||||
|
|
||||||
|
} else if (c.env.r2) {
|
||||||
|
|
||||||
await c.env.r2.put(key, content, {
|
await c.env.r2.put(key, content, {
|
||||||
httpMetadata: { ...metadata }
|
httpMetadata: { ...metadata }
|
||||||
@@ -37,7 +49,13 @@ const r2Service = {
|
|||||||
|
|
||||||
async delete(c, key) {
|
async delete(c, key) {
|
||||||
|
|
||||||
if (c.env.r2) {
|
const { kvStorage } = await settingService.query(c);
|
||||||
|
|
||||||
|
if (kvStorage === settingConst.kvStorage.OPEN) {
|
||||||
|
|
||||||
|
await kvObjService.deleteObj(c, key);
|
||||||
|
|
||||||
|
} else if (c.env.r2) {
|
||||||
|
|
||||||
await c.env.r2.delete(key);
|
await c.env.r2.delete(key);
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
import KvConst from '../const/kv-const';
|
import KvConst from '../const/kv-const';
|
||||||
import setting from '../entity/setting';
|
import setting from '../entity/setting';
|
||||||
import orm from '../entity/orm';
|
import orm from '../entity/orm';
|
||||||
import { settingConst, verifyRecordType } from '../const/entity-const';
|
import { verifyRecordType } from '../const/entity-const';
|
||||||
import fileUtils from '../utils/file-utils';
|
import fileUtils from '../utils/file-utils';
|
||||||
import r2Service from './r2-service';
|
import r2Service from './r2-service';
|
||||||
import emailService from './email-service';
|
|
||||||
import accountService from './account-service';
|
|
||||||
import userService from './user-service';
|
|
||||||
import constant from '../const/constant';
|
import constant from '../const/constant';
|
||||||
import BizError from '../error/biz-error';
|
import BizError from '../error/biz-error';
|
||||||
import { t } from '../i18n/i18n'
|
import { t } from '../i18n/i18n'
|
||||||
@@ -100,12 +97,38 @@ const settingService = {
|
|||||||
await this.refresh(c);
|
await this.refresh(c);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async deleteBackground(c) {
|
||||||
|
|
||||||
|
const { background } = await this.query(c);
|
||||||
|
if (!background) return
|
||||||
|
|
||||||
|
if (background.startsWith('http')) {
|
||||||
|
await orm(c).update(setting).set({ background: '' }).run();
|
||||||
|
await this.refresh(c)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasOss = await r2Service.hasOSS(c);
|
||||||
|
|
||||||
|
if (hasOss) {
|
||||||
|
|
||||||
|
if (background) {
|
||||||
|
await r2Service.delete(c,background)
|
||||||
|
await orm(c).update(setting).set({ background: '' }).run();
|
||||||
|
await this.refresh(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
async setBackground(c, params) {
|
async setBackground(c, params) {
|
||||||
|
|
||||||
const settingRow = await this.query(c);
|
const settingRow = await this.query(c);
|
||||||
|
|
||||||
let { background } = params
|
let { background } = params
|
||||||
|
|
||||||
|
await this.deleteBackground(c);
|
||||||
|
|
||||||
if (background && !background.startsWith('http')) {
|
if (background && !background.startsWith('http')) {
|
||||||
|
|
||||||
if (!await r2Service.hasOSS(c)) {
|
if (!await r2Service.hasOSS(c)) {
|
||||||
@@ -130,14 +153,6 @@ const settingService = {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (settingRow.background) {
|
|
||||||
try {
|
|
||||||
await r2Service.delete(c, settingRow.background);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await orm(c).update(setting).set({ background }).run();
|
await orm(c).update(setting).set({ background }).run();
|
||||||
await this.refresh(c);
|
await this.refresh(c);
|
||||||
return background;
|
return background;
|
||||||
|
|||||||
Reference in New Issue
Block a user