mirror of
https://github.com/dreamhunter2333/cloudflare_temp_email.git
synced 2026-06-01 13:39:59 +08:00
feat: add cron auto clean up (#189)
This commit is contained in:
@@ -5,9 +5,7 @@ import { useI18n } from 'vue-i18n'
|
||||
import { useGlobalState } from '../../store'
|
||||
import { api } from '../../api'
|
||||
|
||||
const {
|
||||
localeCache, loading, openSettings,
|
||||
} = useGlobalState()
|
||||
const { localeCache, loading } = useGlobalState()
|
||||
const message = useMessage()
|
||||
|
||||
const { t } = useI18n({
|
||||
|
||||
@@ -8,29 +8,41 @@ import { api } from '../../api'
|
||||
|
||||
const { localeCache, adminAuth, showAdminAuth } = useGlobalState()
|
||||
const message = useMessage()
|
||||
const cleanMailsDays = ref(30)
|
||||
const cleanUnknowMailsDays = ref(30)
|
||||
const cleanAddressDays = ref(30)
|
||||
const cleanSendBoxDays = ref(30)
|
||||
const cleanupModel = ref({
|
||||
enableMailsAutoCleanup: false,
|
||||
cleanMailsDays: 30,
|
||||
enableUnknowMailsAutoCleanup: false,
|
||||
cleanUnknowMailsDays: 30,
|
||||
enableAddressAutoCleanup: false,
|
||||
cleanAddressDays: 30,
|
||||
enableSendBoxAutoCleanup: false,
|
||||
cleanSendBoxDays: 30,
|
||||
})
|
||||
|
||||
const { t } = useI18n({
|
||||
locale: localeCache.value || 'zh',
|
||||
messages: {
|
||||
en: {
|
||||
tip: 'Please input the cleanup days',
|
||||
mailBoxTip: "Clean up {day} days ago mailbox",
|
||||
mailUnknowTip: "Clean up {day} days ago mails with unknow receiver",
|
||||
addressUnActiveTip: "Clean up {day} days ago unactive address",
|
||||
sendBoxTip: "Clean up {day} days ago sendbox",
|
||||
mailBoxLabel: 'Clean up days for mailbox',
|
||||
mailUnknowLabel: "Clean up days for unknow receiver",
|
||||
addressUnActiveLabel: "Clean up days for unactive address",
|
||||
sendBoxLabel: "Clean up days for sendbox",
|
||||
cleanupNow: "Cleanup now",
|
||||
autoCleanup: "Auto cleanup",
|
||||
cleanupSuccess: "Cleanup success",
|
||||
save: "Save",
|
||||
},
|
||||
zh: {
|
||||
tip: '请输入清理天数',
|
||||
mailBoxTip: "清理{day}天前的收件箱",
|
||||
mailUnknowTip: "清理{day}天前的无收件人邮件",
|
||||
addressUnActiveTip: "清理{day}天前的未活动地址",
|
||||
sendBoxTip: "清理{day}天前的发件箱",
|
||||
mailBoxLabel: '收件箱清理天数',
|
||||
mailUnknowLabel: "无收件人邮件清理天数",
|
||||
addressUnActiveLabel: "未激活地址清理天数",
|
||||
sendBoxLabel: "发件箱清理天数",
|
||||
autoCleanup: "自动清理",
|
||||
cleanupSuccess: "清理成功",
|
||||
cleanupNow: "立即清理",
|
||||
save: "保存",
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -47,11 +59,33 @@ const cleanup = async (cleanType, cleanDays) => {
|
||||
}
|
||||
}
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const res = await api.fetch('/admin/auto_cleanup');
|
||||
if (res) Object.assign(cleanupModel.value, res);
|
||||
} catch (error) {
|
||||
message.error(error.message || "error");
|
||||
}
|
||||
}
|
||||
|
||||
const save = async () => {
|
||||
try {
|
||||
await api.fetch('/admin/auto_cleanup', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(cleanupModel.value)
|
||||
});
|
||||
message.success(t('cleanupSuccess'));
|
||||
} catch (error) {
|
||||
message.error(error.message || "error");
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
if (!adminAuth.value) {
|
||||
showAdminAuth.value = true;
|
||||
return;
|
||||
}
|
||||
await fetchData();
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -59,42 +93,59 @@ onMounted(async () => {
|
||||
<template>
|
||||
<div class="center">
|
||||
<n-card>
|
||||
<div class="item">
|
||||
<n-input-number v-model:value="cleanMailsDays" :placeholder="t('tip')" />
|
||||
<n-button @click="cleanup('mails', cleanMailsDays)">
|
||||
<template #icon>
|
||||
<n-icon :component="CleaningServicesFilled" />
|
||||
</template>
|
||||
{{ t('mailBoxTip', { day: cleanMailsDays }) }}
|
||||
<n-form :model="cleanupModel">
|
||||
<n-form-item-row :label="t('mailBoxLabel')">
|
||||
<n-checkbox v-model:checked="cleanupModel.enableMailsAutoCleanup">
|
||||
{{ t('autoCleanup') }}
|
||||
</n-checkbox>
|
||||
<n-input-number v-model:value="cleanupModel.cleanMailsDays" :placeholder="t('tip')" />
|
||||
<n-button @click="cleanup('mails', cleanupModel.cleanMailsDays)">
|
||||
<template #icon>
|
||||
<n-icon :component="CleaningServicesFilled" />
|
||||
</template>
|
||||
{{ t('cleanupNow') }}
|
||||
</n-button>
|
||||
</n-form-item-row>
|
||||
<n-form-item-row :label="t('mailUnknowLabel')">
|
||||
<n-checkbox v-model:checked="cleanupModel.enableUnknowMailsAutoCleanup">
|
||||
{{ t('autoCleanup') }}
|
||||
</n-checkbox>
|
||||
<n-input-number v-model:value="cleanupModel.cleanUnknowMailsDays" :placeholder="t('tip')" />
|
||||
<n-button @click="cleanup('mails_unknow', cleanupModel.cleanUnknowMailsDays)">
|
||||
<template #icon>
|
||||
<n-icon :component="CleaningServicesFilled" />
|
||||
</template>
|
||||
{{ t('cleanupNow') }}
|
||||
</n-button>
|
||||
</n-form-item-row>
|
||||
<n-form-item-row :label="t('addressUnActiveLabel')">
|
||||
<n-checkbox v-model:checked="cleanupModel.enableAddressAutoCleanup">
|
||||
{{ t('autoCleanup') }}
|
||||
</n-checkbox>
|
||||
<n-input-number v-model:value="cleanupModel.cleanAddressDays" :placeholder="t('tip')" />
|
||||
<n-button @click="cleanup('address', cleanupModel.cleanAddressDays)">
|
||||
<template #icon>
|
||||
<n-icon :component="CleaningServicesFilled" />
|
||||
</template>
|
||||
{{ t('cleanupNow') }}
|
||||
</n-button>
|
||||
</n-form-item-row>
|
||||
<n-form-item-row :label="t('mailBoxLabel')">
|
||||
<n-checkbox v-model:checked="cleanupModel.enableSendBoxAutoCleanup">
|
||||
{{ t('autoCleanup') }}
|
||||
</n-checkbox>
|
||||
<n-input-number v-model:value="cleanupModel.cleanSendBoxDays" :placeholder="t('tip')" />
|
||||
<n-button @click="cleanup('sendbox', cleanupModel.cleanSendBoxDays)">
|
||||
<template #icon>
|
||||
<n-icon :component="CleaningServicesFilled" />
|
||||
</template>
|
||||
{{ t('cleanupNow') }}
|
||||
</n-button>
|
||||
</n-form-item-row>
|
||||
<n-button @click="save" type="primary" block :loading="loading">
|
||||
{{ t('save') }}
|
||||
</n-button>
|
||||
</div>
|
||||
<div class="item">
|
||||
<n-input-number v-model:value="cleanUnknowMailsDays" :placeholder="t('tip')" />
|
||||
<n-button @click="cleanup('mails_unknow', cleanUnknowMailsDays)">
|
||||
<template #icon>
|
||||
<n-icon :component="CleaningServicesFilled" />
|
||||
</template>
|
||||
{{ t('mailUnknowTip', { day: cleanUnknowMailsDays }) }}
|
||||
</n-button>
|
||||
</div>
|
||||
<div class="item">
|
||||
<n-input-number v-model:value="cleanAddressDays" :placeholder="t('tip')" />
|
||||
<n-button @click="cleanup('address', cleanAddressDays)">
|
||||
<template #icon>
|
||||
<n-icon :component="CleaningServicesFilled" />
|
||||
</template>
|
||||
{{ t('addressUnActiveTip', { day: cleanAddressDays }) }}
|
||||
</n-button>
|
||||
</div>
|
||||
<div class="item">
|
||||
<n-input-number v-model:value="cleanSendBoxDays" :placeholder="t('tip')" />
|
||||
<n-button @click="cleanup('sendbox', cleanSendBoxDays)">
|
||||
<template #icon>
|
||||
<n-icon :component="CleaningServicesFilled" />
|
||||
</template>
|
||||
{{ t('sendBoxTip', { day: cleanSendBoxDays }) }}
|
||||
</n-button>
|
||||
</div>
|
||||
</n-form>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -41,17 +41,22 @@ const { t } = useI18n({
|
||||
}
|
||||
});
|
||||
|
||||
const getSettings = async () => {
|
||||
sourcePrefix.value = settings.value.auto_reply.source_prefix || ""
|
||||
enableAutoReply.value = settings.value.auto_reply.enabled || false
|
||||
name.value = settings.value.auto_reply.name || ""
|
||||
autoReplyMessage.value = settings.value.auto_reply.message || ""
|
||||
subject.value = settings.value.auto_reply.subject || ""
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const res = await api.fetch("/api/auto_reply")
|
||||
sourcePrefix.value = res.source_prefix || ""
|
||||
enableAutoReply.value = res.enabled || false
|
||||
name.value = res.name || ""
|
||||
autoReplyMessage.value = res.message || ""
|
||||
subject.value = res.subject || ""
|
||||
} catch (error) {
|
||||
message.error(error.message || "error");
|
||||
}
|
||||
}
|
||||
|
||||
const saveSettings = async () => {
|
||||
const saveData = async () => {
|
||||
try {
|
||||
await api.fetch("/api/settings", {
|
||||
await api.fetch("/api/auto_reply", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
auto_reply: {
|
||||
@@ -70,7 +75,7 @@ const saveSettings = async () => {
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await getSettings()
|
||||
await fetchData()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -78,7 +83,7 @@ onMounted(async () => {
|
||||
<div class="center">
|
||||
<n-card v-if="settings.address" :title='t("settings")'>
|
||||
<div class="right">
|
||||
<n-button type="primary" @click="saveSettings">{{ t('save') }}</n-button>
|
||||
<n-button type="primary" @click="saveData">{{ t('save') }}</n-button>
|
||||
</div>
|
||||
<div class="left">
|
||||
<n-form-item :label="t('enableAutoReply')" label-placement="left">
|
||||
|
||||
@@ -56,12 +56,16 @@ pnpm run deploy
|
||||
|
||||
`wrangler.toml`
|
||||
|
||||
```bash
|
||||
```toml
|
||||
name = "cloudflare_temp_email"
|
||||
main = "src/worker.js"
|
||||
compatibility_date = "2023-08-14"
|
||||
node_compat = true
|
||||
|
||||
# enable cron if you want set auto clean up
|
||||
# [triggers]
|
||||
# crons = [ "0 0 * * *" ]
|
||||
|
||||
[vars]
|
||||
PREFIX = "tmp" # The mailbox name prefix to be processed
|
||||
# If you want your site to be private, uncomment below and change your password
|
||||
|
||||
@@ -20,6 +20,10 @@ compatibility_date = "2023-12-01"
|
||||
# ]
|
||||
node_compat = true
|
||||
|
||||
# 如果你想要使用定时任务清理邮件,取消下面的注释,并修改 cron 表达式
|
||||
# [triggers]
|
||||
# crons = [ "0 0 * * *" ]
|
||||
|
||||
[vars]
|
||||
PREFIX = "tmp" # 要处理的邮箱名称前缀,不需要后缀可配置为空字符串
|
||||
# 如果你想要你的网站私有,取消下面的注释,并修改密码
|
||||
|
||||
25
worker/src/admin/cleanup_api.js
Normal file
25
worker/src/admin/cleanup_api.js
Normal file
@@ -0,0 +1,25 @@
|
||||
import { cleanup } from '../common';
|
||||
import { CONSTANTS } from '../constants';
|
||||
import { getJsonSetting, saveSetting } from '../utils';
|
||||
|
||||
export default {
|
||||
cleanup: async (c) => {
|
||||
const { cleanType, cleanDays } = await c.req.json();
|
||||
try {
|
||||
await cleanup(c, cleanType, cleanDays);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return c.text(`Failed to cleanup ${error.message}`, 500)
|
||||
}
|
||||
return c.json({ success: true })
|
||||
},
|
||||
getCleanup: async (c) => {
|
||||
const value = await getJsonSetting(c, CONSTANTS.AUTO_CLEANUP_KEY);
|
||||
return c.json(value || {})
|
||||
},
|
||||
saveCleanup: async (c) => {
|
||||
const value = await c.req.json();
|
||||
await saveSetting(c, CONSTANTS.AUTO_CLEANUP_KEY, JSON.stringify(value));
|
||||
return c.json({ success: true })
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
import { Hono } from 'hono'
|
||||
import { Jwt } from 'hono/utils/jwt'
|
||||
import { sendAdminInternalMail } from './utils'
|
||||
import { sendAdminInternalMail, getJsonSetting, saveSetting } from './utils'
|
||||
import { newAddress } from './common'
|
||||
import { CONSTANTS } from './constants'
|
||||
import cleanup_api from './admin/cleanup_api'
|
||||
|
||||
const api = new Hono()
|
||||
|
||||
@@ -294,49 +295,16 @@ api.get('/admin/statistics', async (c) => {
|
||||
})
|
||||
});
|
||||
|
||||
api.post('/admin/cleanup', async (c) => {
|
||||
const { cleanType, cleanDays } = await c.req.json();
|
||||
if (!cleanType || !cleanDays || cleanDays < 0 || cleanDays > 30) {
|
||||
return c.text("Invalid cleanType or cleanDays", 400)
|
||||
}
|
||||
console.log(`Cleanup ${cleanType} before ${cleanDays} days`);
|
||||
switch (cleanType) {
|
||||
case "mails":
|
||||
await c.env.DB.prepare(`
|
||||
DELETE FROM raw_mails WHERE created_at < datetime('now', '-${cleanDays} day')`
|
||||
).run();
|
||||
break;
|
||||
case "mails_unknow":
|
||||
await c.env.DB.prepare(`
|
||||
DELETE FROM raw_mails WHERE address NOT IN
|
||||
(select name from address) AND created_at < datetime('now', '-${cleanDays} day')`
|
||||
).run();
|
||||
break;
|
||||
case "address":
|
||||
await c.env.DB.prepare(`
|
||||
DELETE FROM address WHERE updated_at < datetime('now', '-${cleanDays} day')`
|
||||
).run();
|
||||
break;
|
||||
case "sendbox":
|
||||
await c.env.DB.prepare(`
|
||||
DELETE FROM sendbox WHERE created_at < datetime('now', '-${cleanDays} day')`
|
||||
).run();
|
||||
break;
|
||||
default:
|
||||
return c.text("Invalid cleanType", 400)
|
||||
}
|
||||
return c.json({
|
||||
success: true
|
||||
})
|
||||
})
|
||||
api.post('/admin/cleanup', cleanup_api.cleanup)
|
||||
api.get('/admin/auto_cleanup', cleanup_api.getCleanup)
|
||||
api.post('/admin/auto_cleanup', cleanup_api.saveCleanup)
|
||||
|
||||
|
||||
api.get('/admin/account_settings', async (c) => {
|
||||
try {
|
||||
const value = await c.env.DB.prepare(
|
||||
`SELECT value FROM settings where key = ?`
|
||||
).bind(CONSTANTS.ADDRESS_BLOCK_LIST_KEY).first("value");
|
||||
const value = await getJsonSetting(c, CONSTANTS.ADDRESS_BLOCK_LIST_KEY);
|
||||
return c.json({
|
||||
blockList: value ? JSON.parse(value) : []
|
||||
blockList: value || []
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
@@ -349,14 +317,10 @@ api.post('/admin/account_settings', async (c) => {
|
||||
if (!blockList) {
|
||||
return c.text("Invalid blockList", 400)
|
||||
}
|
||||
await c.env.DB.prepare(
|
||||
`INSERT or REPLACE INTO settings (key, value) VALUES (?, ?)`
|
||||
+ ` ON CONFLICT(key) DO UPDATE SET value = ?, updated_at = datetime('now')`
|
||||
).bind(
|
||||
CONSTANTS.ADDRESS_BLOCK_LIST_KEY,
|
||||
JSON.stringify(blockList),
|
||||
await saveSetting(
|
||||
c, CONSTANTS.ADDRESS_BLOCK_LIST_KEY,
|
||||
JSON.stringify(blockList)
|
||||
).run();
|
||||
);
|
||||
return c.json({
|
||||
success: true
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Jwt } from 'hono/utils/jwt'
|
||||
|
||||
import { getDomains } from './utils';
|
||||
import { getDomains, getStringValue } from './utils';
|
||||
|
||||
export const newAddress = async (c, name, domain, enablePrefix) => {
|
||||
// remove special characters
|
||||
@@ -19,7 +19,7 @@ export const newAddress = async (c, name, domain, enablePrefix) => {
|
||||
}
|
||||
// create address
|
||||
if (enablePrefix) {
|
||||
name = c.env.PREFIX + name + "@" + domain;
|
||||
name = getStringValue(c.env.PREFIX) + name + "@" + domain;
|
||||
} else {
|
||||
name = name + "@" + domain;
|
||||
}
|
||||
@@ -53,3 +53,36 @@ export const newAddress = async (c, name, domain, enablePrefix) => {
|
||||
jwt: jwt
|
||||
})
|
||||
}
|
||||
|
||||
export const cleanup = async (c, cleanType, cleanDays) => {
|
||||
if (!cleanType || !cleanDays || cleanDays < 0 || cleanDays > 30) {
|
||||
throw new Error("Invalid cleanType or cleanDays")
|
||||
}
|
||||
console.log(`Cleanup ${cleanType} before ${cleanDays} days`);
|
||||
switch (cleanType) {
|
||||
case "mails":
|
||||
await c.env.DB.prepare(`
|
||||
DELETE FROM raw_mails WHERE created_at < datetime('now', '-${cleanDays} day')`
|
||||
).run();
|
||||
break;
|
||||
case "mails_unknow":
|
||||
await c.env.DB.prepare(`
|
||||
DELETE FROM raw_mails WHERE address NOT IN
|
||||
(select name from address) AND created_at < datetime('now', '-${cleanDays} day')`
|
||||
).run();
|
||||
break;
|
||||
case "address":
|
||||
await c.env.DB.prepare(`
|
||||
DELETE FROM address WHERE updated_at < datetime('now', '-${cleanDays} day')`
|
||||
).run();
|
||||
break;
|
||||
case "sendbox":
|
||||
await c.env.DB.prepare(`
|
||||
DELETE FROM sendbox WHERE created_at < datetime('now', '-${cleanDays} day')`
|
||||
).run();
|
||||
break;
|
||||
default:
|
||||
throw new Error("Invalid cleanType")
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export const CONSTANTS = {
|
||||
ADDRESS_BLOCK_LIST_KEY: 'address_block_list',
|
||||
AUTO_CLEANUP_KEY: 'auto_cleanup',
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { Hono } from 'hono'
|
||||
|
||||
import { getDomains, getPasswords, getBooleanValue } from './utils';
|
||||
import {
|
||||
getDomains, getPasswords, getBooleanValue, getJsonSetting
|
||||
} from './utils';
|
||||
import { newAddress } from './common'
|
||||
import { CONSTANTS } from './constants'
|
||||
|
||||
@@ -83,21 +85,6 @@ api.get('/api/settings', async (c) => {
|
||||
} catch (e) {
|
||||
console.warn("Failed to update address")
|
||||
}
|
||||
let auto_reply = {};
|
||||
if (getBooleanValue(c.env.ENABLE_AUTO_REPLY)) {
|
||||
const results = await c.env.DB.prepare(
|
||||
`SELECT * FROM auto_reply_mails where address = ? `
|
||||
).bind(address).first();
|
||||
if (results) {
|
||||
auto_reply = {
|
||||
subject: results.subject,
|
||||
message: results.message,
|
||||
enabled: results.enabled == 1,
|
||||
source_prefix: results.source_prefix,
|
||||
name: results.name,
|
||||
}
|
||||
}
|
||||
}
|
||||
const { count: mailCountV1 } = await c.env.DB.prepare(
|
||||
`SELECT count(*) as count FROM mails where address = ?`
|
||||
).bind(address).first();
|
||||
@@ -106,41 +93,12 @@ api.get('/api/settings', async (c) => {
|
||||
where address = ? and enabled = 1`
|
||||
).bind(address).first("balance");
|
||||
return c.json({
|
||||
auto_reply: auto_reply,
|
||||
address: address,
|
||||
has_v1_mails: mailCountV1 && mailCountV1 > 0,
|
||||
send_balance: balance || 0,
|
||||
});
|
||||
})
|
||||
|
||||
api.post('/api/settings', async (c) => {
|
||||
const { address } = c.get("jwtPayload")
|
||||
if (!getBooleanValue(c.env.ENABLE_AUTO_REPLY)) {
|
||||
return c.text("Auto reply is disabled", 403)
|
||||
}
|
||||
const { auto_reply } = await c.req.json();
|
||||
const { name, subject, source_prefix, message, enabled } = auto_reply;
|
||||
if ((!subject || !message) && enabled) {
|
||||
return c.text("Invalid subject or message", 400)
|
||||
}
|
||||
else if (subject.length > 255 || message.length > 255) {
|
||||
return c.text("Subject or message too long", 400)
|
||||
}
|
||||
const { success } = await c.env.DB.prepare(
|
||||
`INSERT OR REPLACE INTO
|
||||
auto_reply_mails
|
||||
(name, address, source_prefix, subject, message, enabled)
|
||||
VALUES
|
||||
(?, ?, ?, ?, ?, ?)`
|
||||
).bind(name || '', address, source_prefix || '', subject || '', message || '', enabled ? 1 : 0).run();
|
||||
if (!success) {
|
||||
return c.text("Failed to save settings", 500)
|
||||
}
|
||||
return c.json({
|
||||
success: success
|
||||
})
|
||||
})
|
||||
|
||||
api.get('/open_api/settings', async (c) => {
|
||||
// check header x-custom-auth
|
||||
let needAuth = false;
|
||||
@@ -172,10 +130,8 @@ api.get('/api/new_address', async (c) => {
|
||||
}
|
||||
// check name block list
|
||||
try {
|
||||
const value = await c.env.DB.prepare(
|
||||
`SELECT value FROM settings where key = ?`
|
||||
).bind(CONSTANTS.ADDRESS_BLOCK_LIST_KEY).first("value");
|
||||
const blockList = value ? JSON.parse(value) : [];
|
||||
const value = await getJsonSetting(c, CONSTANTS.ADDRESS_BLOCK_LIST_KEY);
|
||||
const blockList = value || [];
|
||||
if (blockList.some((item) => name.includes(item))) {
|
||||
return c.text(`Name [${name}] is blocked`, 400)
|
||||
}
|
||||
|
||||
41
worker/src/scheduled.js
Normal file
41
worker/src/scheduled.js
Normal file
@@ -0,0 +1,41 @@
|
||||
import { cleanup } from './common'
|
||||
import { CONSTANTS } from './constants'
|
||||
import { getJsonSetting } from './utils';
|
||||
|
||||
export async function scheduled(event, env, ctx) {
|
||||
console.log("Scheduled event: ", event);
|
||||
let autoCleanupSetting = await getJsonSetting(
|
||||
{ env: env, },
|
||||
CONSTANTS.AUTO_CLEANUP_KEY
|
||||
);
|
||||
console.log("autoCleanupSetting:", JSON.stringify(autoCleanupSetting));
|
||||
autoCleanupSetting = autoCleanupSetting || {};
|
||||
if (autoCleanupSetting.enableMailsAutoCleanup && autoCleanupSetting.cleanMailsDays > 0) {
|
||||
await cleanup(
|
||||
{ env: env, },
|
||||
"mails",
|
||||
autoCleanupSetting.cleanMailsDays
|
||||
);
|
||||
}
|
||||
if (autoCleanupSetting.enableUnknowMailsAutoCleanup && autoCleanupSetting.cleanUnknowMailsDays > 0) {
|
||||
await cleanup(
|
||||
{ env: env, },
|
||||
"mails_unknow",
|
||||
autoCleanupSetting.cleanUnknowMailsDays
|
||||
);
|
||||
}
|
||||
if (autoCleanupSetting.enableAddressAutoCleanup && autoCleanupSetting.cleanAddressDays > 0) {
|
||||
await cleanup(
|
||||
{ env: env, },
|
||||
"address",
|
||||
autoCleanupSetting.cleanAddressDays
|
||||
);
|
||||
}
|
||||
if (autoCleanupSetting.enableSendBoxAutoCleanup && autoCleanupSetting.cleanSendBoxDays > 0) {
|
||||
await cleanup(
|
||||
{ env: env, },
|
||||
"sendbox",
|
||||
autoCleanupSetting.cleanSendBoxDays
|
||||
);
|
||||
}
|
||||
}
|
||||
52
worker/src/user/auto_reply.js
Normal file
52
worker/src/user/auto_reply.js
Normal file
@@ -0,0 +1,52 @@
|
||||
import { getBooleanValue } from "../utils";
|
||||
|
||||
|
||||
export default {
|
||||
getAutoReply: async (c) => {
|
||||
if (!getBooleanValue(c.env.ENABLE_AUTO_REPLY)) {
|
||||
return c.text("Auto reply is disabled", 403)
|
||||
}
|
||||
const { address } = c.get("jwtPayload")
|
||||
const results = await c.env.DB.prepare(
|
||||
`SELECT * FROM auto_reply_mails where address = ? `
|
||||
).bind(address).first();
|
||||
if (!results) {
|
||||
return c.json({});
|
||||
}
|
||||
return c.json({
|
||||
subject: results.subject,
|
||||
message: results.message,
|
||||
enabled: results.enabled == 1,
|
||||
source_prefix: results.source_prefix,
|
||||
name: results.name,
|
||||
})
|
||||
},
|
||||
saveAutoReply: async (c) => {
|
||||
if (!getBooleanValue(c.env.ENABLE_AUTO_REPLY)) {
|
||||
return c.text("Auto reply is disabled", 403)
|
||||
}
|
||||
const { address } = c.get("jwtPayload")
|
||||
const { auto_reply } = await c.req.json();
|
||||
const { name, subject, source_prefix, message, enabled } = auto_reply;
|
||||
if ((!subject || !message) && enabled) {
|
||||
return c.text("Invalid subject or message", 400)
|
||||
}
|
||||
else if (subject.length > 255 || message.length > 255) {
|
||||
return c.text("Subject or message too long", 400)
|
||||
}
|
||||
const { success } = await c.env.DB.prepare(
|
||||
`INSERT OR REPLACE INTO auto_reply_mails`
|
||||
+ ` (name, address, source_prefix, subject, message, enabled)`
|
||||
+ ` VALUES (?, ?, ?, ?, ?, ?)`
|
||||
).bind(
|
||||
name || '', address, source_prefix || '',
|
||||
subject || '', message || '', enabled ? 1 : 0
|
||||
).run();
|
||||
if (!success) {
|
||||
return c.text("Failed to auto_reply settings", 500)
|
||||
}
|
||||
return c.json({
|
||||
success: success
|
||||
})
|
||||
}
|
||||
}
|
||||
10
worker/src/user_api.js
Normal file
10
worker/src/user_api.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Hono } from 'hono'
|
||||
|
||||
import auto_reply from './user/auto_reply'
|
||||
|
||||
const api = new Hono()
|
||||
|
||||
api.get('/api/auto_reply', auto_reply.getAutoReply)
|
||||
api.post('/api/auto_reply', auto_reply.saveAutoReply)
|
||||
|
||||
export { api }
|
||||
@@ -1,5 +1,45 @@
|
||||
import { createMimeMessage } from "mimetext";
|
||||
|
||||
export const getJsonSetting = async (c, key) => {
|
||||
const value = await getSetting(c, key);
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return JSON.parse(value);
|
||||
} catch (e) {
|
||||
console.error(`GetJsonSetting: Failed to parse ${key}`, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export const getSetting = async (c, key) => {
|
||||
try {
|
||||
const value = await c.env.DB.prepare(
|
||||
`SELECT value FROM settings where key = ?`
|
||||
).bind(key).first("value");
|
||||
return value;
|
||||
} catch (error) {
|
||||
console.error(`GetSetting: Failed to get ${key}`, error);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export const saveSetting = async (c, key, value) => {
|
||||
await c.env.DB.prepare(
|
||||
`INSERT or REPLACE INTO settings (key, value) VALUES (?, ?)`
|
||||
+ ` ON CONFLICT(key) DO UPDATE SET value = ?, updated_at = datetime('now')`
|
||||
).bind(key, value, value).run();
|
||||
return true;
|
||||
}
|
||||
|
||||
export const getStringValue = (value) => {
|
||||
if (typeof value === "string") {
|
||||
return value;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
export const getBooleanValue = (value) => {
|
||||
if (typeof value === "boolean") {
|
||||
return value;
|
||||
|
||||
@@ -3,10 +3,12 @@ import { cors } from 'hono/cors';
|
||||
import { jwt } from 'hono/jwt'
|
||||
|
||||
import { api } from './router';
|
||||
import { api as userApi } from './user_api';
|
||||
import { api as adminApi } from './admin_api';
|
||||
import { api as apiV1 } from './api_v1';
|
||||
import { api as apiSendMail } from './send_mail_api'
|
||||
import { email } from './email';
|
||||
import { scheduled } from './scheduled';
|
||||
import { getAdminPasswords, getPasswords } from './utils';
|
||||
|
||||
const app = new Hono()
|
||||
@@ -53,6 +55,7 @@ app.use('/admin/*', async (c, next) => {
|
||||
|
||||
|
||||
app.route('/', api)
|
||||
app.route('/', userApi)
|
||||
app.route('/', adminApi)
|
||||
app.route('/', apiV1)
|
||||
app.route('/', apiSendMail)
|
||||
@@ -65,4 +68,5 @@ app.all('/*', async c => c.text("Not Found", 404))
|
||||
export default {
|
||||
fetch: app.fetch,
|
||||
email: email,
|
||||
scheduled: scheduled,
|
||||
}
|
||||
|
||||
@@ -7,6 +7,10 @@ node_compat = true
|
||||
# { pattern = "temp-email-api.xxxxx.xyz", custom_domain = true },
|
||||
# ]
|
||||
|
||||
# enable cron if you want set auto clean up
|
||||
# [triggers]
|
||||
# crons = [ "0 0 * * *" ]
|
||||
|
||||
[vars]
|
||||
PREFIX = "tmp"
|
||||
# IF YOU WANT TO MAKE YOUR SITE PRIVATE, UNCOMMENT THE FOLLOWING LINES
|
||||
|
||||
Reference in New Issue
Block a user