feat: cleanup support address and inactive address (#671)

This commit is contained in:
Dream Hunter
2025-06-18 17:31:15 +08:00
committed by GitHub
parent c694b07380
commit 9ac9cd46b0
6 changed files with 115 additions and 29 deletions
+1
View File
@@ -5,6 +5,7 @@
- fix: |UI| 修复 User 查看收件箱,不选择地址时,关键词查询不生效 - fix: |UI| 修复 User 查看收件箱,不选择地址时,关键词查询不生效
- fix: 修复自动清理任务,时间为 0 时不生效的问题 - fix: 修复自动清理任务,时间为 0 时不生效的问题
- feat: 清理功能增加 创建 n 天前地址清理,n 天前未活跃地址清理
## v0.10.0 ## v0.10.0
+38 -6
View File
@@ -11,10 +11,12 @@ const cleanupModel = ref({
cleanMailsDays: 30, cleanMailsDays: 30,
enableUnknowMailsAutoCleanup: false, enableUnknowMailsAutoCleanup: false,
cleanUnknowMailsDays: 30, cleanUnknowMailsDays: 30,
enableAddressAutoCleanup: false,
cleanAddressDays: 30,
enableSendBoxAutoCleanup: false, enableSendBoxAutoCleanup: false,
cleanSendBoxDays: 30, cleanSendBoxDays: 30,
enableAddressAutoCleanup: false,
cleanAddressDays: 30,
enableInactiveAddressAutoCleanup: false,
cleanInactiveAddressDays: 30,
}) })
const { t } = useI18n({ const { t } = useI18n({
@@ -24,6 +26,8 @@ const { t } = useI18n({
mailBoxLabel: 'Cleanup the inbox before n days', mailBoxLabel: 'Cleanup the inbox before n days',
mailUnknowLabel: "Cleanup the unknow mail before n days", mailUnknowLabel: "Cleanup the unknow mail before n days",
sendBoxLabel: "Cleanup the sendbox before n days", sendBoxLabel: "Cleanup the sendbox before n days",
addressCreateLabel: "Cleanup the address created before n days",
inactiveAddressLabel: "Cleanup the inactive address before n days",
cleanupNow: "Cleanup now", cleanupNow: "Cleanup now",
autoCleanup: "Auto cleanup", autoCleanup: "Auto cleanup",
cleanupSuccess: "Cleanup success", cleanupSuccess: "Cleanup success",
@@ -35,6 +39,8 @@ const { t } = useI18n({
mailBoxLabel: '清理 n 天前的收件箱', mailBoxLabel: '清理 n 天前的收件箱',
mailUnknowLabel: "清理 n 天前的无收件人邮件", mailUnknowLabel: "清理 n 天前的无收件人邮件",
sendBoxLabel: "清理 n 天前的发件箱", sendBoxLabel: "清理 n 天前的发件箱",
addressCreateLabel: "清理 n 天前创建的地址",
inactiveAddressLabel: "清理 n 天前的未活跃地址",
autoCleanup: "自动清理", autoCleanup: "自动清理",
cleanupSuccess: "清理成功", cleanupSuccess: "清理成功",
cleanupNow: "立即清理", cleanupNow: "立即清理",
@@ -86,9 +92,14 @@ onMounted(async () => {
<template> <template>
<div class="center"> <div class="center">
<n-card :bordered="false" embedded> <n-card :bordered="false" embedded>
<n-alert :show-icon="false" :bordered="false"> <n-alert :show-icon="false" :bordered="false" type="warning">
<span>{{ t('cronTip') }}</span> <span>{{ t('cronTip') }}</span>
</n-alert> </n-alert>
<n-flex justify="end">
<n-button @click="save" type="primary" :loading="loading">
{{ t('save') }}
</n-button>
</n-flex>
<n-form :model="cleanupModel"> <n-form :model="cleanupModel">
<n-form-item-row :label="t('mailBoxLabel')"> <n-form-item-row :label="t('mailBoxLabel')">
<n-checkbox v-model:checked="cleanupModel.enableMailsAutoCleanup"> <n-checkbox v-model:checked="cleanupModel.enableMailsAutoCleanup">
@@ -126,9 +137,30 @@ onMounted(async () => {
{{ t('cleanupNow') }} {{ t('cleanupNow') }}
</n-button> </n-button>
</n-form-item-row> </n-form-item-row>
<n-button @click="save" type="primary" block :loading="loading"> <n-form-item-row :label="t('addressCreateLabel')">
{{ t('save') }} <n-checkbox v-model:checked="cleanupModel.enableAddressAutoCleanup">
</n-button> {{ t('autoCleanup') }}
</n-checkbox>
<n-input-number v-model:value="cleanupModel.cleanAddressDays" :placeholder="t('tip')" />
<n-button @click="cleanup('addressCreated', cleanupModel.cleanAddressDays)">
<template #icon>
<n-icon :component="CleaningServicesFilled" />
</template>
{{ t('cleanupNow') }}
</n-button>
</n-form-item-row>
<n-form-item-row :label="t('inactiveAddressLabel')">
<n-checkbox v-model:checked="cleanupModel.enableInactiveAddressAutoCleanup">
{{ t('autoCleanup') }}
</n-checkbox>
<n-input-number v-model:value="cleanupModel.cleanInactiveAddressDays" :placeholder="t('tip')" />
<n-button @click="cleanup('inactiveAddress', cleanupModel.cleanInactiveAddressDays)">
<template #icon>
<n-icon :component="CleaningServicesFilled" />
</template>
{{ t('cleanupNow') }}
</n-button>
</n-form-item-row>
</n-form> </n-form>
</n-card> </n-card>
</div> </div>
+2 -4
View File
@@ -17,13 +17,11 @@ export default {
return c.json({ success: true }) return c.json({ success: true })
}, },
getCleanup: async (c: Context<HonoCustomType>) => { getCleanup: async (c: Context<HonoCustomType>) => {
const value = await getJsonSetting(c, CONSTANTS.AUTO_CLEANUP_KEY); const cleanupSetting = await getJsonSetting<CleanupSettings>(c, CONSTANTS.AUTO_CLEANUP_KEY);
const cleanupSetting = new CleanupSettings(value);
return c.json(cleanupSetting) return c.json(cleanupSetting)
}, },
saveCleanup: async (c: Context<HonoCustomType>) => { saveCleanup: async (c: Context<HonoCustomType>) => {
const value = await c.req.json(); const cleanupSetting = await c.req.json<CleanupSettings>();
const cleanupSetting = new CleanupSettings(value);
await saveSetting(c, CONSTANTS.AUTO_CLEANUP_KEY, JSON.stringify(cleanupSetting)); await saveSetting(c, CONSTANTS.AUTO_CLEANUP_KEY, JSON.stringify(cleanupSetting));
return c.json({ success: true }) return c.json({ success: true })
} }
+50 -1
View File
@@ -155,6 +155,18 @@ export const cleanup = async (
} }
console.log(`Cleanup ${cleanType} before ${cleanDays} days`); console.log(`Cleanup ${cleanType} before ${cleanDays} days`);
switch (cleanType) { switch (cleanType) {
case "inactiveAddress":
await batchDeleteAddressWithData(
c,
`updated_at < datetime('now', '-${cleanDays} day')`
)
break;
case "addressCreated":
await batchDeleteAddressWithData(
c,
`created_at < datetime('now', '-${cleanDays} day')`
)
break;
case "mails": case "mails":
await c.env.DB.prepare(` await c.env.DB.prepare(`
DELETE FROM raw_mails WHERE created_at < datetime('now', '-${cleanDays} day')` DELETE FROM raw_mails WHERE created_at < datetime('now', '-${cleanDays} day')`
@@ -177,6 +189,37 @@ export const cleanup = async (
return true; return true;
} }
const batchDeleteAddressWithData = async (
c: Context<HonoCustomType>,
addressQueryCondition: string,
): Promise<boolean> => {
await c.env.DB.prepare(
`DELETE FROM raw_mails WHERE address IN ( ` +
`SELECT name FROM address WHERE ${addressQueryCondition})`
).run();
await c.env.DB.prepare(
`DELETE FROM sendbox WHERE address IN ( ` +
`SELECT name FROM address WHERE ${addressQueryCondition})`
).run();
await c.env.DB.prepare(
`DELETE FROM auto_reply_mails WHERE address IN ( ` +
`SELECT name FROM address WHERE ${addressQueryCondition})`
).run();
await c.env.DB.prepare(
`DELETE FROM address_sender WHERE address IN ( ` +
`SELECT name FROM address WHERE ${addressQueryCondition})`
).run();
await c.env.DB.prepare(
`DELETE FROM users_address WHERE address_id IN ( ` +
`SELECT id FROM address WHERE ${addressQueryCondition})`
).run();
// delete address
await c.env.DB.prepare(`
DELETE FROM address WHERE ${addressQueryCondition}`
).run();
return true;
}
/** /**
* TODO: need senbox delete? * TODO: need senbox delete?
*/ */
@@ -214,13 +257,19 @@ export const deleteAddressWithData = async (
const { success: sendAccess } = await c.env.DB.prepare( const { success: sendAccess } = await c.env.DB.prepare(
`DELETE FROM address_sender WHERE address = ? ` `DELETE FROM address_sender WHERE address = ? `
).bind(address).run(); ).bind(address).run();
const { success: sendboxSuccess } = await c.env.DB.prepare(
`DELETE FROM sendbox WHERE address = ? `
).bind(address).run();
const { success: addressSuccess } = await c.env.DB.prepare( const { success: addressSuccess } = await c.env.DB.prepare(
`DELETE FROM users_address WHERE address_id = ? ` `DELETE FROM users_address WHERE address_id = ? `
).bind(address_id).run(); ).bind(address_id).run();
const { success: autoReplySuccess } = await c.env.DB.prepare(
`DELETE FROM auto_reply_mails WHERE address = ? `
).bind(address).run();
const { success } = await c.env.DB.prepare( const { success } = await c.env.DB.prepare(
`DELETE FROM address WHERE name = ? ` `DELETE FROM address WHERE name = ? `
).bind(address).run(); ).bind(address).run();
if (!success || !mailSuccess || !addressSuccess || !sendAccess) { if (!success || !mailSuccess || !sendboxSuccess || !addressSuccess || !sendAccess || !autoReplySuccess) {
throw new Error("Failed to delete address") throw new Error("Failed to delete address")
} }
return true; return true;
+5 -16
View File
@@ -32,7 +32,7 @@ export type WebhookMail = {
parsedHtml: string; parsedHtml: string;
} }
export class CleanupSettings { export type CleanupSettings = {
enableMailsAutoCleanup: boolean | undefined; enableMailsAutoCleanup: boolean | undefined;
cleanMailsDays: number; cleanMailsDays: number;
@@ -40,23 +40,12 @@ export class CleanupSettings {
cleanUnknowMailsDays: number; cleanUnknowMailsDays: number;
enableSendBoxAutoCleanup: boolean | undefined; enableSendBoxAutoCleanup: boolean | undefined;
cleanSendBoxDays: number; cleanSendBoxDays: number;
enableAddressAutoCleanup: boolean | undefined;
constructor(data: CleanupSettings | undefined | null) { cleanAddressDays: number;
const { enableInactiveAddressAutoCleanup: boolean | undefined;
enableMailsAutoCleanup, cleanMailsDays, cleanInactiveAddressDays: number;
enableUnknowMailsAutoCleanup, cleanUnknowMailsDays,
enableSendBoxAutoCleanup, cleanSendBoxDays
} = data || {};
this.enableMailsAutoCleanup = enableMailsAutoCleanup;
this.cleanMailsDays = cleanMailsDays || 0;
this.enableUnknowMailsAutoCleanup = enableUnknowMailsAutoCleanup;
this.cleanUnknowMailsDays = cleanUnknowMailsDays || 0;
this.enableSendBoxAutoCleanup = enableSendBoxAutoCleanup;
this.cleanSendBoxDays = cleanSendBoxDays || 0;
}
} }
export class GeoData { export class GeoData {
ip: string; ip: string;
+19 -2
View File
@@ -6,11 +6,14 @@ import { CleanupSettings } from './models';
export async function scheduled(event: ScheduledEvent, env: Bindings, ctx: any) { export async function scheduled(event: ScheduledEvent, env: Bindings, ctx: any) {
console.log("Scheduled event: ", event); console.log("Scheduled event: ", event);
const value = await getJsonSetting( const autoCleanupSetting = await getJsonSetting<CleanupSettings>(
{ env: env, } as Context<HonoCustomType>, { env: env, } as Context<HonoCustomType>,
CONSTANTS.AUTO_CLEANUP_KEY CONSTANTS.AUTO_CLEANUP_KEY
); );
const autoCleanupSetting = new CleanupSettings(value); if (!autoCleanupSetting) {
console.log("No auto cleanup settings found, skipping cleanup.");
return;
}
console.log("autoCleanupSetting:", JSON.stringify(autoCleanupSetting)); console.log("autoCleanupSetting:", JSON.stringify(autoCleanupSetting));
if (autoCleanupSetting.enableMailsAutoCleanup) { if (autoCleanupSetting.enableMailsAutoCleanup) {
await cleanup( await cleanup(
@@ -33,4 +36,18 @@ export async function scheduled(event: ScheduledEvent, env: Bindings, ctx: any)
autoCleanupSetting.cleanSendBoxDays autoCleanupSetting.cleanSendBoxDays
); );
} }
if (autoCleanupSetting.enableInactiveAddressAutoCleanup) {
await cleanup(
{ env: env, } as Context<HonoCustomType>,
"inactiveAddress",
autoCleanupSetting.cleanInactiveAddressDays
);
}
if (autoCleanupSetting.enableAddressAutoCleanup) {
await cleanup(
{ env: env, } as Context<HonoCustomType>,
"addressCreated",
autoCleanupSetting.cleanAddressDays
);
}
} }