feat: add cleanup for unbound addresses feature (#739)

- Add unboundAddress cleanup type to backend cleanup logic
- Update CleanupSettings model with unbound address fields
- Add scheduled task for automatic unbound address cleanup
- Add UI controls in admin Maintenance panel for manual cleanup
- Add i18n support (en/zh) for unbound address cleanup labels
- Clean addresses not bound to any user created before n days

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Dream Hunter
2025-10-12 13:46:13 +08:00
committed by GitHub
parent a2f3634c7e
commit 5f752c94f9
4 changed files with 31 additions and 0 deletions

View File

@@ -17,6 +17,8 @@ const cleanupModel = ref({
cleanAddressDays: 30,
enableInactiveAddressAutoCleanup: false,
cleanInactiveAddressDays: 30,
enableUnboundAddressAutoCleanup: false,
cleanUnboundAddressDays: 30,
})
const { t } = useI18n({
@@ -28,6 +30,7 @@ const { t } = useI18n({
sendBoxLabel: "Cleanup the sendbox before n days",
addressCreateLabel: "Cleanup the address created before n days",
inactiveAddressLabel: "Cleanup the inactive address before n days",
unboundAddressLabel: "Cleanup the unbound address before n days",
cleanupNow: "Cleanup now",
autoCleanup: "Auto cleanup",
cleanupSuccess: "Cleanup success",
@@ -41,6 +44,7 @@ const { t } = useI18n({
sendBoxLabel: "清理 n 天前的发件箱",
addressCreateLabel: "清理 n 天前创建的地址",
inactiveAddressLabel: "清理 n 天前的未活跃地址",
unboundAddressLabel: "清理 n 天前的未绑定用户地址",
autoCleanup: "自动清理",
cleanupSuccess: "清理成功",
cleanupNow: "立即清理",
@@ -161,6 +165,18 @@ onMounted(async () => {
{{ t('cleanupNow') }}
</n-button>
</n-form-item-row>
<n-form-item-row :label="t('unboundAddressLabel')">
<n-checkbox v-model:checked="cleanupModel.enableUnboundAddressAutoCleanup">
{{ t('autoCleanup') }}
</n-checkbox>
<n-input-number v-model:value="cleanupModel.cleanUnboundAddressDays" :placeholder="t('tip')" />
<n-button @click="cleanup('unboundAddress', cleanupModel.cleanUnboundAddressDays)">
<template #icon>
<n-icon :component="CleaningServicesFilled" />
</template>
{{ t('cleanupNow') }}
</n-button>
</n-form-item-row>
</n-form>
</n-card>
</div>

View File

@@ -251,6 +251,12 @@ export const cleanup = async (
`created_at < datetime('now', '-${cleanDays} day')`
)
break;
case "unboundAddress":
await batchDeleteAddressWithData(
c,
`id NOT IN (SELECT address_id FROM users_address) AND created_at < datetime('now', '-${cleanDays} day')`
)
break;
case "mails":
await c.env.DB.prepare(`
DELETE FROM raw_mails WHERE created_at < datetime('now', '-${cleanDays} day')`

View File

@@ -46,6 +46,8 @@ export type CleanupSettings = {
cleanAddressDays: number;
enableInactiveAddressAutoCleanup: boolean | undefined;
cleanInactiveAddressDays: number;
enableUnboundAddressAutoCleanup: boolean | undefined;
cleanUnboundAddressDays: number;
}
export class GeoData {

View File

@@ -50,4 +50,11 @@ export async function scheduled(event: ScheduledEvent, env: Bindings, ctx: any)
autoCleanupSetting.cleanAddressDays
);
}
if (autoCleanupSetting.enableUnboundAddressAutoCleanup) {
await cleanup(
{ env: env, } as Context<HonoCustomType>,
"unboundAddress",
autoCleanupSetting.cleanUnboundAddressDays
);
}
}