优化多个地方

This commit is contained in:
eoao
2025-09-03 08:31:44 +08:00
parent 97a7950808
commit b4954e948b
30 changed files with 156 additions and 89 deletions

View File

@@ -1,3 +1,4 @@
NODE_ENV = 'dev' NODE_ENV = 'dev'
VITE_APP_TITLE = '开发环境' VITE_APP_TITLE = '开发环境'
VITE_BASE_URL = 'http://127.0.0.1:8787/api' VITE_BASE_URL = 'http://127.0.0.1:8787/api'
VITE_PWA_NAME = 'Cloud Mail'

View File

@@ -1,3 +1,4 @@
NODE_ENV = 'eo' NODE_ENV = 'eo'
VITE_APP_TITLE = 'eo环境' VITE_APP_TITLE = 'eo环境'
VITE_BASE_URL = '' VITE_BASE_URL = ''
VITE_PWA_NAME = 'Cloud Mail'

View File

@@ -1,4 +1,5 @@
NODE_ENV = 'release' NODE_ENV = 'release'
VITE_APP_TITLE = '发布环境' VITE_APP_TITLE = '发布环境'
VITE_BASE_URL = '/api' VITE_BASE_URL = '/api'
VITE_PWA_NAME = 'Cloud Mail'
VITE_OUT_DIR = ../mail-worker/dist VITE_OUT_DIR = ../mail-worker/dist

View File

@@ -1,3 +1,4 @@
NODE_ENV = 'remote' NODE_ENV = 'remote'
VITE_APP_TITLE = '远程环境' VITE_APP_TITLE = '远程环境'
VITE_BASE_URL = '' VITE_BASE_URL = ''
VITE_PWA_NAME = 'Cloud Mail'

View File

@@ -238,7 +238,7 @@ let scrollTop = 0
const latestEmail = ref(null) const latestEmail = ref(null)
const scrollbarRef = ref(null) const scrollbarRef = ref(null)
let reqLock = false let reqLock = false
let isMobile = innerWidth < 1025 let isMobile = innerWidth < 1367
let skeletonRows = 0 let skeletonRows = 0
const queryParam = reactive({ const queryParam = reactive({
emailId: 0, emailId: 0,
@@ -632,7 +632,7 @@ function loadData() {
margin-top: 5px; margin-top: 5px;
margin-bottom: 2px; margin-bottom: 2px;
color: var(--email-scroll-content-color); color: var(--email-scroll-content-color);
@media (max-width: 1199px) { @media (max-width: 1366px) {
flex-direction: column; flex-direction: column;
} }
@@ -673,7 +673,7 @@ function loadData() {
padding-left: 15px; padding-left: 15px;
padding-right: 20px; padding-right: 20px;
justify-content: center; justify-content: center;
@media (min-width: 1200px) { @media (min-width: 1367px) {
justify-content: start; justify-content: start;
height: 100%; height: 100%;
align-self: start; align-self: start;
@@ -682,7 +682,7 @@ function loadData() {
} }
.title-column { .title-column {
@media (max-width: 1199px) { @media (max-width: 1366px) {
grid-template-columns: 1fr !important; grid-template-columns: 1fr !important;
gap: 4px !important; gap: 4px !important;
} }
@@ -692,10 +692,10 @@ function loadData() {
flex: 1; flex: 1;
display: grid; display: grid;
grid-template-columns: 240px 1fr; grid-template-columns: 240px 1fr;
@media (max-width: 1199px) { @media (max-width: 1366px) {
padding-right: 15px; padding-right: 15px;
} }
@media (max-width: 1024px) { @media (max-width: 1366px) {
grid-template-columns: 1fr; grid-template-columns: 1fr;
gap: 4px; gap: 4px;
} }
@@ -710,7 +710,7 @@ function loadData() {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-content: center; align-content: center;
@media (max-width: 1199px) { @media (max-width: 1366px) {
flex-direction: row; flex-direction: row;
gap: 5px; gap: 5px;
} }
@@ -720,7 +720,7 @@ function loadData() {
display: grid; display: grid;
gap: 5px; gap: 5px;
grid-template-columns: auto 1fr; grid-template-columns: auto 1fr;
@media (min-width: 1024px) { @media (min-width: 1366px) {
grid-template-columns: 1fr; grid-template-columns: 1fr;
> span:last-child { > span:last-child {
display: none; display: none;
@@ -745,7 +745,7 @@ function loadData() {
.phone-time { .phone-time {
font-weight: normal; font-weight: normal;
font-size: 12px; font-size: 12px;
@media (min-width: 1200px) { @media (min-width: 1367px) {
display: none; display: none;
} }
} }
@@ -755,7 +755,7 @@ function loadData() {
.text-skeleton-one { .text-skeleton-one {
width: 80%; width: 80%;
height: 16px; height: 16px;
@media (max-width: 1199px) { @media (max-width: 1366px) {
width: 40%; width: 40%;
} }
@media (max-width: 767px) { @media (max-width: 767px) {
@@ -766,10 +766,10 @@ function loadData() {
.text-skeleton-two { .text-skeleton-two {
width: min(300px, 100%); width: min(300px, 100%);
height: 16px; height: 16px;
@media (min-width: 1200px) { @media (min-width: 1367px) {
display: none; display: none;
} }
@media (max-width: 1199px) { @media (max-width: 1366px) {
width: 100%; width: 100%;
} }
} }
@@ -778,7 +778,7 @@ function loadData() {
.email-text { .email-text {
display: grid; display: grid;
grid-template-columns: auto 1fr; grid-template-columns: auto 1fr;
@media (max-width: 1199px) { @media (max-width: 1366px) {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
@@ -786,7 +786,7 @@ function loadData() {
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
@media (min-width: 1200px) { @media (min-width: 1367px) {
padding-left: 5px; padding-left: 5px;
} }
} }
@@ -797,7 +797,7 @@ function loadData() {
text-overflow: ellipsis; text-overflow: ellipsis;
padding-left: 10px; padding-left: 10px;
color: var(--email-scroll-content-color); color: var(--email-scroll-content-color);
@media (max-width: 1199px) { @media (max-width: 1366px) {
padding-left: 0; padding-left: 0;
margin-top: 0; margin-top: 0;
} }
@@ -813,13 +813,13 @@ function loadData() {
display: flex; display: flex;
padding-left: 15px; padding-left: 15px;
align-items: center; align-items: center;
@media (max-width: 1199px) { @media (max-width: 1366px) {
display: none; display: none;
} }
} }
.email-right-skeleton { .email-right-skeleton {
@media (max-width: 1199px) { @media (max-width: 1366px) {
display: none; display: none;
} }
} }
@@ -844,7 +844,7 @@ function loadData() {
width: 40px; width: 40px;
} }
@media (max-width: 1024px) { @media (max-width: 1366px) {
.pc-star { .pc-star {
display: none; display: none;
} }

View File

@@ -115,7 +115,7 @@ import {Icon} from "@iconify/vue";
width: 40px; width: 40px;
} }
@media (max-width: 1024px) { @media (max-width: 1366px) {
.pc-star { .pc-star {
display: none; display: none;
} }

View File

@@ -156,10 +156,7 @@ const route = useRoute();
.el-menu { .el-menu {
border-right: 0; border-right: 0;
width: 250px; width: 260px;
@media (max-width: 1199px) {
width: 250px;
}
} }
:deep(.el-divider__text) { :deep(.el-divider__text) {

View File

@@ -30,8 +30,8 @@
<div class="notice icon-item" @click="openNotice"> <div class="notice icon-item" @click="openNotice">
<Icon icon="streamline-plump:announcement-megaphone"/> <Icon icon="streamline-plump:announcement-megaphone"/>
</div> </div>
<el-dropdown :teleported="false" popper-class="detail-dropdown"> <el-dropdown ref="userinfoRef" @visible-change="e => userInfoShow = e" :teleported="false" popper-class="detail-dropdown">
<div class="avatar"> <div class="avatar" @click="userInfoHide" >
<div class="avatar-text"> <div class="avatar-text">
<div>{{ formatName(userStore.user.email) }}</div> <div>{{ formatName(userStore.user.email) }}</div>
</div> </div>
@@ -103,6 +103,8 @@ const settingStore = useSettingStore();
const userStore = useUserStore(); const userStore = useUserStore();
const uiStore = useUiStore(); const uiStore = useUiStore();
const logoutLoading = ref(false) const logoutLoading = ref(false)
const userInfoShow = ref(false)
const userinfoRef = ref({})
const accountCount = computed(() => { const accountCount = computed(() => {
return userStore.user.role.accountCount return userStore.user.role.accountCount
@@ -157,6 +159,14 @@ const sendCount = computed(() => {
return userStore.user.sendCount + '/' + userStore.user.role.sendCount return userStore.user.sendCount + '/' + userStore.user.role.sendCount
}) })
function userInfoHide(e) {
if (userInfoShow.value) {
userinfoRef.value.handleClose()
} else {
userinfoRef.value.handleOpen()
}
}
async function copyEmail(email) { async function copyEmail(email) {
try { try {
await navigator.clipboard.writeText(email); await navigator.clipboard.writeText(email);

View File

@@ -128,7 +128,7 @@ const handleResize = () => {
@media (max-width: 767px) { @media (max-width: 767px) {
position: fixed; position: fixed;
z-index: 100; z-index: 100;
width: 250px; width: 260px;
} }
} }
@@ -138,7 +138,7 @@ const handleResize = () => {
transform: translateX(-100%); transform: translateX(-100%);
opacity: 0; opacity: 0;
@media (max-width: 1024px) { @media (max-width: 1024px) {
width: 250px; width: 260px;
z-index: 100; z-index: 100;
} }
} }
@@ -148,9 +148,6 @@ const handleResize = () => {
display: grid; display: grid;
grid-template-columns: 260px 1fr; grid-template-columns: 260px 1fr;
height: calc(100% - 60px); height: calc(100% - 60px);
@media (max-width: 1200px) {
grid-template-columns: 250px 1fr;
}
@media (max-width: 767px) { @media (max-width: 767px) {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }

View File

@@ -440,7 +440,7 @@ function close() {
.write-box { .write-box {
background: var(--el-bg-color); background: var(--el-bg-color);
width: min(1200px, calc(100% - 80px)); width: min(1367px, calc(100% - 80px));
box-shadow: var(--el-box-shadow-light); box-shadow: var(--el-box-shadow-light);
border: 1px solid var(--el-border-color-light); border: 1px solid var(--el-border-color-light);
transition: var(--el-transition-duration); transition: var(--el-transition-duration);
@@ -453,6 +453,7 @@ function close() {
width: 100%; width: 100%;
height: 100%; height: 100%;
border-radius: 0; border-radius: 0;
border: 0;
padding-top: 10px; padding-top: 10px;
} }

View File

@@ -94,7 +94,7 @@ router.beforeEach((to, from, next) => {
// 延迟 50ms 才启动进度条 // 延迟 50ms 才启动进度条
timer = setTimeout(() => { timer = setTimeout(() => {
NProgress.start() NProgress.start()
}, 50) }, 100)
const token = localStorage.getItem('token') const token = localStorage.getItem('token')
@@ -116,7 +116,7 @@ router.beforeEach((to, from, next) => {
}) })
function loadBackground(next) { function loadBackground(next) {
console.log(131231)
const settingStore = useSettingStore(); const settingStore = useSettingStore();
const src = cvtR2Url(settingStore.settings.background); const src = cvtR2Url(settingStore.settings.background);
@@ -150,6 +150,7 @@ router.afterEach((to) => {
if (window.innerWidth < 1025) { if (window.innerWidth < 1025) {
uiStore.asideShow = false uiStore.asideShow = false
} }
}) })
export default router export default router

View File

@@ -13,7 +13,6 @@ export const useSettingStore = defineStore('setting', {
}, },
persist: { persist: {
storage: sessionStorage,
pick: ['lang'], pick: ['lang'],
}, },
}) })

View File

@@ -21,4 +21,21 @@ export function cvtR2Url(key) {
domain = domain.slice(0, -1); domain = domain.slice(0, -1);
} }
return domain + '/' + key return domain + '/' + key
} }
export function toOssDomain(domain) {
if (!domain) {
return null
}
if (!domain.startsWith('http')) {
return 'https://' + domain
}
if (domain.endsWith("/")) {
domain = domain.slice(0, -1);
}
return domain
}

View File

@@ -768,7 +768,7 @@ function createSendGauge() {
display: grid; display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr; grid-template-columns: 1fr 1fr 1fr 1fr;
gap: 20px; gap: 20px;
@media (max-width: 1199px) { @media (max-width: 1366px) {
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;
gap: 15px; gap: 15px;
} }

View File

@@ -84,7 +84,7 @@ import {useAccountStore} from "@/store/account.js";
import {formatDetailDate} from "@/utils/day.js"; import {formatDetailDate} from "@/utils/day.js";
import {starAdd, starCancel} from "@/request/star.js"; import {starAdd, starCancel} from "@/request/star.js";
import {getExtName, formatBytes} from "@/utils/file-utils.js"; import {getExtName, formatBytes} from "@/utils/file-utils.js";
import {cvtR2Url} from "@/utils/convert.js"; import {cvtR2Url,toOssDomain} from "@/utils/convert.js";
import {getIconByName} from "@/utils/icon-utils.js"; import {getIconByName} from "@/utils/icon-utils.js";
import {useSettingStore} from "@/store/setting.js"; import {useSettingStore} from "@/store/setting.js";
import {allEmailDelete} from "@/request/all-email.js"; import {allEmailDelete} from "@/request/all-email.js";
@@ -116,7 +116,8 @@ function toMessage(message) {
function formatImage(content) { function formatImage(content) {
content = content || ''; content = content || '';
const domain = settingStore.settings.r2Domain; const domain = settingStore.settings.r2Domain;
return content.replace(/{{domain}}/g, domain + '/'); console.log(domain)
return content.replace(/{{domain}}/g, toOssDomain(domain) + '/');
} }
function showImage(key) { function showImage(key) {
@@ -258,7 +259,7 @@ const handleDelete = () => {
border-radius: 6px; border-radius: 6px;
width: fit-content; width: fit-content;
.att-box { .att-box {
min-width: min(410px,calc(100vw - 53px)); min-width: min(410px,calc(100vw - 60px));
max-width: 600px; max-width: 600px;
display: grid; display: grid;
gap: 12px; gap: 12px;

View File

@@ -775,7 +775,7 @@ adjustWidth()
function adjustWidth() { function adjustWidth() {
const width = window.innerWidth const width = window.innerWidth
statusShow.value = width > 1090 statusShow.value = width > 1090
createTimeShow.value = width > 1200 createTimeShow.value = width > 1367
accountNumShow.value = width > 650 accountNumShow.value = width > 650
sendNumShow.value = width > 685 sendNumShow.value = width > 685
typeShow.value = width > 767 typeShow.value = width > 767

View File

@@ -20,8 +20,8 @@ export default defineConfig(({mode}) => {
registerType: 'autoUpdate', // 配置 service worker 的注册方式 registerType: 'autoUpdate', // 配置 service worker 的注册方式
includeAssets: ['favicon.svg', 'robots.txt'], // 指定需要包含的静态资源 includeAssets: ['favicon.svg', 'robots.txt'], // 指定需要包含的静态资源
manifest: { manifest: {
name: 'Cloud Mail', name: env.VITE_PWA_NAME,
short_name: 'Cloud Mail', short_name: env.VITE_PWA_NAME,
background_color: '#FFFFFF', background_color: '#FFFFFF',
theme_color: '#FFFFFF', theme_color: '#FFFFFF',
icons: [ icons: [
@@ -32,6 +32,9 @@ export default defineConfig(({mode}) => {
} }
], ],
}, },
workbox: {
navigateFallbackDenylist: [/^\/api\/init/]
}
}), }),
AutoImport({ AutoImport({
resolvers: [ElementPlusResolver()], resolvers: [ElementPlusResolver()],

View File

@@ -12,6 +12,7 @@ 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'; import verifyUtils from '../utils/verify-utils';
import r2Service from '../service/r2-service';
dayjs.extend(utc); dayjs.extend(utc);
dayjs.extend(timezone); dayjs.extend(timezone);
@@ -152,7 +153,7 @@ export async function email(message, env, ctx) {
attachment.accountId = emailRow.accountId; attachment.accountId = emailRow.accountId;
}); });
if (attachments.length > 0 && env.r2) { if (attachments.length > 0 && await r2Service.hasOSS({env})) {
await attService.addAtt({ env }, attachments); await attService.addAtt({ env }, attachments);
} }

View File

@@ -14,15 +14,15 @@ app.onError((err, c) => {
} }
if (err.message === `Cannot read properties of undefined (reading 'get')`) { if (err.message === `Cannot read properties of undefined (reading 'get')`) {
return c.text('初始化失败:KV数据库未绑定或变量名错误 Initialization failed: KV database not bound or invalid variable name'); return c.json(result.fail('初始化失败:KV数据库未绑定或变量名错误'));
} }
if (err.message === `Cannot read properties of undefined (reading 'put')`) { if (err.message === `Cannot read properties of undefined (reading 'put')`) {
return c.text('初始化失败:KV数据库未绑定或变量名错误 Initialization failed: KV database not bound or invalid variable name'); return c.json(result.fail('初始化失败:KV数据库未绑定或变量名错误'));
} }
if (err.message === `Cannot read properties of undefined (reading 'prepare')`) { if (err.message === `Cannot read properties of undefined (reading 'prepare')`) {
return c.text('初始化失败:D1数据库未绑定或变量名错误 Initialization failed: D1 database not bound or invalid variable name'); return c.json(result.fail('初始化失败:D1数据库未绑定或变量名错误'));
} }
return c.json(result.fail(err.message, err.code)); return c.json(result.fail(err.message, err.code));

View File

@@ -96,7 +96,6 @@ const en = {
"系统设置": "System Settings", "系统设置": "System Settings",
"设置查看": "View Settings", "设置查看": "View Settings",
"设置修改": "Change Settings", "设置修改": "Change Settings",
"物理清空": "Physical Purge",
"发件重置": "Reset Send Count" "发件重置": "Reset Send Count"
} }
}; };

View File

@@ -96,7 +96,6 @@ const zh = {
"系统设置": "系统设置", "系统设置": "系统设置",
"设置查看": "设置查看", "设置查看": "设置查看",
"设置修改": "设置修改", "设置修改": "设置修改",
"物理清空": "物理清空",
'发件重置': '发件重置' '发件重置': '发件重置'
} }
} }

View File

@@ -21,7 +21,6 @@ const init = {
await this.v1_5DB(c); await this.v1_5DB(c);
await this.v1_6DB(c); await this.v1_6DB(c);
await this.v1_7DB(c); await this.v1_7DB(c);
await this.v1_7DB(c);
await this.v2DB(c); await this.v2DB(c);
await settingService.refresh(c); await settingService.refresh(c);
return c.text(t('initSuccess')); return c.text(t('initSuccess'));
@@ -34,7 +33,8 @@ const init = {
c.env.db.prepare(`ALTER TABLE setting ADD COLUMN region TEXT NOT NULL DEFAULT '';`), c.env.db.prepare(`ALTER TABLE setting ADD COLUMN region TEXT NOT NULL DEFAULT '';`),
c.env.db.prepare(`ALTER TABLE setting ADD COLUMN endpoint TEXT NOT NULL DEFAULT '';`), c.env.db.prepare(`ALTER TABLE setting ADD COLUMN endpoint TEXT NOT NULL DEFAULT '';`),
c.env.db.prepare(`ALTER TABLE setting ADD COLUMN s3_access_key TEXT NOT NULL DEFAULT '';`), c.env.db.prepare(`ALTER TABLE setting ADD COLUMN s3_access_key TEXT NOT NULL DEFAULT '';`),
c.env.db.prepare(`ALTER TABLE setting ADD COLUMN s3_secret_key TEXT NOT NULL DEFAULT '';`) c.env.db.prepare(`ALTER TABLE setting ADD COLUMN s3_secret_key TEXT NOT NULL DEFAULT '';`),
c.env.db.prepare(`DELETE FROM perm WHERE perm_key = 'setting:clean'`)
]); ]);
} catch (e) { } catch (e) {
console.error(e.message) console.error(e.message)

View File

@@ -1,21 +1,31 @@
import orm from '../entity/orm'; import orm from '../entity/orm';
import { att } from '../entity/att'; import { att } from '../entity/att';
import { and, eq, isNull, inArray, notInArray } from 'drizzle-orm'; import { and, eq, isNull, inArray } from 'drizzle-orm';
import r2Service from './r2-service'; import r2Service from './r2-service';
import constant from '../const/constant'; import constant from '../const/constant';
import fileUtils from '../utils/file-utils'; import fileUtils from '../utils/file-utils';
import { attConst } from '../const/entity-const'; import { attConst } from '../const/entity-const';
import { parseHTML } from 'linkedom'; import { parseHTML } from 'linkedom';
import domainUtils from '../utils/domain-uitls';
const attService = { const attService = {
async addAtt(c, attachments) { async addAtt(c, attachments) {
for (let attachment of attachments) { for (let attachment of attachments) {
await r2Service.putObj(c, attachment.key, attachment.content, {
let metadate = {
contentType: attachment.mimeType, contentType: attachment.mimeType,
contentDisposition: `attachment; filename="${attachment.filename}"` }
});
if (!attachment.contentId) {
metadate.contentDisposition = `attachment; filename="${attachment.filename}"`
} else {
metadate.contentDisposition = `inline; filename="${attachment.filename}"`
metadate.cacheControl = `max-age=604800`
}
await r2Service.putObj(c, attachment.key, attachment.content, metadate);
} }
await orm(c).insert(att).values(attachments).run(); await orm(c).insert(att).values(attachments).run();
@@ -49,7 +59,7 @@ const attService = {
const file = fileUtils.base64ToFile(src); const file = fileUtils.base64ToFile(src);
const buff = await file.arrayBuffer(); const buff = await file.arrayBuffer();
const key = constant.ATTACHMENT_PREFIX + await fileUtils.getBuffHash(buff) + fileUtils.getExtFileName(file.name); const key = constant.ATTACHMENT_PREFIX + await fileUtils.getBuffHash(buff) + fileUtils.getExtFileName(file.name);
img.setAttribute('src', r2Domain + '/' + key); img.setAttribute('src', domainUtils.toOssDomain(r2Domain) + '/' + key);
const attData = {}; const attData = {};
attData.key = key; attData.key = key;
@@ -108,7 +118,9 @@ const attService = {
attData.accountId = accountId; attData.accountId = accountId;
attData.type = attConst.type.EMBED; attData.type = attConst.type.EMBED;
await r2Service.putObj(c, attData.key, attData.buff, { await r2Service.putObj(c, attData.key, attData.buff, {
contentType: attData.mimeType contentType: attData.mimeType,
cacheControl: `max-age=604800`,
contentDisposition: `inline; filename="${attData.filename}"`
}); });
} }
@@ -153,9 +165,7 @@ const attService = {
await this.batchDelete(c, delKeyList); await this.batchDelete(c, delKeyList);
} }
const delAttSql = fieldValues.map(value => c.env.db.prepare(`DELETE const delAttSql = fieldValues.map(value => c.env.db.prepare(`DELETE FROM attachments WHERE ${fieldName} = ?`).bind(value));
FROM attachments
WHERE ${fieldName} = ?`).bind(value));
await c.env.db.batch(delAttSql); await c.env.db.batch(delAttSql);
}, },

View File

@@ -18,6 +18,7 @@ import dayjs from 'dayjs';
import kvConst from '../const/kv-const'; import kvConst from '../const/kv-const';
import { t } from '../i18n/i18n' import { t } from '../i18n/i18n'
import r2Service from './r2-service'; import r2Service from './r2-service';
import domainUtils from '../utils/domain-uitls';
const emailService = { const emailService = {
@@ -407,6 +408,8 @@ const emailService = {
} }
r2domain = domainUtils.toOssDomain(r2domain)
if (src && src.startsWith(r2domain + '/')) { if (src && src.startsWith(r2domain + '/')) {
img.setAttribute('src', src.replace(r2domain + '/', '{{domain}}')); img.setAttribute('src', src.replace(r2domain + '/', '{{domain}}'));
} }

View File

@@ -9,9 +9,23 @@ const s3Service = {
const { bucket } = await settingService.query(c); const { bucket } = await settingService.query(c);
await client.send( let obj = { Bucket: bucket, Key: key, Body: content,
new PutObjectCommand({ Bucket: bucket, Key: key, Body: content, ...metadata }) CacheControl: metadata.cacheControl
) }
if (metadata.cacheControl) {
obj.CacheControl = metadata.cacheControl
}
if (metadata.contentDisposition) {
obj.ContentDisposition = metadata.contentDisposition
}
if (metadata.contentType) {
obj.ContentType = metadata.contentType
}
await client.send(new PutObjectCommand(obj))
}, },
async deleteObj(c,keys) { async deleteObj(c,keys) {

View File

@@ -119,7 +119,9 @@ const settingService = {
await r2Service.putObj(c, background, arrayBuffer, { await r2Service.putObj(c, background, arrayBuffer, {
contentType: file.type contentType: file.type,
cacheControl: `public, max-age=31536000, immutable`,
contentDisposition: `inline; filename="${file.name}"`
}); });
} }

View File

@@ -34,3 +34,6 @@ crons = ["0 16 * * *"] #定时任务每天晚上12点执行
#domain = [] #邮件域名可可配置多个 示例: ["example1.com","example2.com"] #domain = [] #邮件域名可可配置多个 示例: ["example1.com","example2.com"]
#admin = "" #管理员的邮箱 示例: admin@example.com #admin = "" #管理员的邮箱 示例: admin@example.com
#jwt_secret = "" #jwt令牌的密钥,随便填一串字符串 #jwt_secret = "" #jwt令牌的密钥,随便填一串字符串
[build]
command = "pnpm --prefix ../mail-vue install && pnpm --prefix ../mail-vue run build"

View File

@@ -18,9 +18,10 @@ database_id = "a4c1a63a-6ef5-4e6d-8e8c-b6d9e8feb810"
binding = "kv" binding = "kv"
id = "2io01d4b299e481b9de060ece9e7785c" id = "2io01d4b299e481b9de060ece9e7785c"
[[r2_buckets]] #[[r2_buckets]]
binding = "r2" #binding = "r2"
bucket_name = "email" #bucket_name = "email"
[vars] [vars]
orm_log = false orm_log = false

View File

@@ -1,36 +1,40 @@
name = "cloud-mail-test" name = "test-mail"
main = "src/index.js" main = "src/index.js"
compatibility_date = "2025-06-04" compatibility_date = "2025-06-04"
keep_vars = true keep_vars = true
[observability] [observability]
enabled = true enabled = true
head_sampling_rate = 1
[[d1_databases]] [[d1_databases]]
binding = "db" binding = "db" #d1数据库绑定名默认不可修改
database_name = "email-test" database_name = "test-email" #d1数据库名字
database_id = "" database_id = "e65f099d-796d-4eaa-8dff-529b368b20db" #d1数据库id
[[kv_namespaces]] [[kv_namespaces]]
binding = "kv" binding = "kv" #kv绑定名默认不可修改
id = "" id = "9be10cd058e04c55b526f8c0c116f50c" #kv数据库id
[[r2_buckets]] #(可选)
binding = "r2" #[[r2_buckets]]
bucket_name = "email-test" #binding = "r2" #r2对象存储绑定名默认不可修改
#bucket_name = "test-email" #r2对象存储桶的名字
[assets] [assets]
binding = "assets" binding = "assets" #静态资源绑定名默认不可修改
directory = "./dist" directory = "./dist" #前端vue项目打包的静态资源存放位置,默认dist
not_found_handling = "single-page-application" not_found_handling = "single-page-application"
run_worker_first = true run_worker_first = true
[triggers] [triggers]
crons = ["0 16 * * *"] crons = ["0 16 * * *"] #定时任务每天晚上12点执行
[vars] [vars]
orm_log = true orm_log = false
domain = ["", ""] domain = ["example.com"] #邮件域名可可配置多个 示例: ["example1.com","example2.com"]
admin = "" admin = "admin@example.com" #管理员的邮箱 示例: admin@example.com
jwt_secret = "" jwt_secret = "b7f29a1d-18e2-4d3b-941f-f6b2c97c02fd" #jwt令牌的密钥,随便填一串字符串
[build]
command = "pnpm --prefix ../mail-vue install && pnpm --prefix ../mail-vue run build"

View File

@@ -8,16 +8,17 @@ enabled = true
#[[d1_databases]] #[[d1_databases]]
#binding = "db" #d1数据库绑定名默认不可修改 #binding = "db" #d1数据库绑定名默认不可修改
#database_name = "" #d1数据库名字 #database_name = "email" #d1数据库名字
#database_id = "" #d1数据库id #database_id = "b1b1a63a-6ef5-4e6d-8e8c-b6d9e8feb810" #d1数据库id
#
#[[kv_namespaces]] #[[kv_namespaces]]
#binding = "kv" #kv绑定名默认不可修改 #binding = "kv" #kv绑定名默认不可修改
#id = "" #kv数据库id #id = "0fa01d4b299e481b9de060ece9e7785c" #kv数据库id
##(可选)
#[[r2_buckets]] #[[r2_buckets]]
#binding = "r2" #r2对象存储绑定名默认不可修改 #binding = "r2" #r2对象存储绑定名默认不可修改
#bucket_name = "" #r2对象存储桶的名字 #bucket_name = "email" #r2对象存储桶的名字
[assets] [assets]
binding = "assets" #静态资源绑定名默认不可修改 binding = "assets" #静态资源绑定名默认不可修改