Skip to content

Commit

Permalink
feat: init send mail (#113)
Browse files Browse the repository at this point in the history
* feat: init send mail

* feat: init send mail
  • Loading branch information
dreamhunter2333 authored Apr 12, 2024
1 parent ee2fdab commit b8f0fa4
Show file tree
Hide file tree
Showing 21 changed files with 1,143 additions and 296 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# CHANGE LOG

## 2024-04-12 v0.0.2

- support send email

DB changes:

- `db/2024-04-12-patch.sql`

## 2024-04-10 v0.0.1

Breaking changes:
Expand Down
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
- [Cloudflare Workers 后端](#cloudflare-workers-后端-1)
- [Cloudflare Email Routing](#cloudflare-email-routing)
- [Cloudflare Pages 前端](#cloudflare-pages-前端)
- [配置发送邮件](#配置发送邮件)
- [参考资料](#参考资料)


Expand All @@ -60,6 +61,7 @@
- [x] 增加自动回复功能
- [x] 增加查看附件功能
- [x] 使用 rust wasm 解析邮件
- [x] 支持发送邮件

---

Expand Down Expand Up @@ -201,6 +203,19 @@ pnpm run deploy

![pages](readme_assets/pages.png)

## 配置发送邮件

找到域名 `DNS` 记录的 `TXT``SPF` 记录, 增加 `include:relay.mailchannels.net`

```bash
v=spf1 include:_spf.mx.cloudflare.net include:relay.mailchannels.net ~all
```

新建 `_mailchannels` 记录, 类型为 `TXT`, 内容为 `v=mc1 cfid=你的worker域名`

- 此处 worker 域名为后端 api 的域名,比如我部署在 `https://temp-email-api.awsl.uk/`,则填写 `v=mc1 cfid=awsl.uk`
- 如果你的域名是 `https://temp-email-api.xxx.workers.dev`,则填写 `v=mc1 cfid=xxx.workers.dev`

## 参考资料

- https://developers.cloudflare.com/d1/
Expand Down
14 changes: 14 additions & 0 deletions README_EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ This is a temporary email service that uses Cloudflare Workers to create a tempo
- [x] Add auto reply feature
- [x] Add attachment viewing function
- [x] use rust wasm to parse email
- [x] support send email

![demo](readme_assets/demo.png)

Expand Down Expand Up @@ -92,3 +93,16 @@ pnpm run deploy
```

![pages](readme_assets/pages.png)

## Configure sending emails

Find the `SPF` record of `TXT` in the domain name `DNS` record, and add `include:relay.mailchannels.net`

```bash
v=spf1 include:_spf.mx.cloudflare.net include:relay.mailchannels.net ~all
```

Create a new `_mailchannels` record, the type is `TXT`, the content is `v=mc1 cfid=your worker domain name`

- The worker domain name here is the domain name of the back-end api. For example, if I deploy it at `https://temp-email-api.awsl.uk/`, fill in `v=mc1 cfid=awsl.uk`
- If your domain name is `https://temp-email-api.xxx.workers.dev`, fill in `v=mc1 cfid=xxx.workers.dev`
14 changes: 14 additions & 0 deletions db/2024-04-12-patch.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
CREATE TABLE IF NOT EXISTS address_sender (
id INTEGER PRIMARY KEY,
address TEXT UNIQUE,
balance INTEGER DEFAULT 0,
enabled INTEGER DEFAULT 1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE IF NOT EXISTS sendbox (
id INTEGER PRIMARY KEY,
address TEXT,
raw TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
27 changes: 27 additions & 0 deletions db/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ CREATE TABLE IF NOT EXISTS mails (
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX IF NOT EXISTS idx_mails_address ON mails(address);

CREATE TABLE IF NOT EXISTS raw_mails (
id INTEGER PRIMARY KEY,
message_id TEXT,
Expand All @@ -17,13 +19,17 @@ CREATE TABLE IF NOT EXISTS raw_mails (
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX IF NOT EXISTS idx_raw_mails_address ON raw_mails(address);

CREATE TABLE IF NOT EXISTS address (
id INTEGER PRIMARY KEY,
name TEXT UNIQUE,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX IF NOT EXISTS idx_address_name ON address(name);

CREATE TABLE IF NOT EXISTS auto_reply_mails (
id INTEGER PRIMARY KEY,
source_prefix TEXT,
Expand All @@ -35,6 +41,8 @@ CREATE TABLE IF NOT EXISTS auto_reply_mails (
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX IF NOT EXISTS idx_auto_reply_mails_address ON auto_reply_mails(address);

CREATE TABLE IF NOT EXISTS attachments (
id INTEGER PRIMARY KEY,
source TEXT,
Expand All @@ -43,3 +51,22 @@ CREATE TABLE IF NOT EXISTS attachments (
data TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE IF NOT EXISTS address_sender (
id INTEGER PRIMARY KEY,
address TEXT UNIQUE,
balance INTEGER DEFAULT 0,
enabled INTEGER DEFAULT 1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX IF NOT EXISTS idx_address_sender_address ON address_sender(address);

CREATE TABLE IF NOT EXISTS sendbox (
id INTEGER PRIMARY KEY,
address TEXT,
raw TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX IF NOT EXISTS idx_sendbox_address ON sendbox(address);
1 change: 1 addition & 0 deletions frontend/src/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ const getSettings = async () => {
address: res["address"],
auto_reply: res["auto_reply"],
has_v1_mails: res["has_v1_mails"],
send_balance: res["send_balance"],
};
} finally {
settings.value.fetched = true;
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const i18n = createI18n({
'en': {
messages: {}
},
'zhCN': {
'zh': {
messages: {}
}
})
Expand Down
10 changes: 10 additions & 0 deletions frontend/src/router/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { createRouter, createWebHistory } from 'vue-router'
import Index from '../views/Index.vue'
import Settings from '../views/Settings.vue'
import SendMail from '../views/send/SendMail.vue'
import Admin from '../views/Admin.vue'
import SendBox from '../views/send/SendBox.vue'

const router = createRouter({
history: createWebHistory(),
Expand All @@ -14,6 +16,14 @@ const router = createRouter({
path: '/settings',
component: Settings
},
{
path: '/send',
component: SendMail
},
{
path: '/sendbox',
component: SendBox
},
{
path: '/admin',
component: Admin
Expand Down
1 change: 1 addition & 0 deletions frontend/src/store/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const useGlobalState = createGlobalState(
const settings = ref({
fetched: false,
has_v1_mails: false,
send_balance: 0,
address: '',
auto_reply: {
subject: '',
Expand Down
8 changes: 7 additions & 1 deletion frontend/src/views/Admin.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import { User, UserCheck, MailBulk } from '@vicons/fa'
import { useGlobalState } from '../store'
import { api } from '../api'
import { processItem, getDownloadEmlUrl } from '../utils/email-parser'
import { processItem } from '../utils/email-parser'
import SenderAccess from './admin/SenderAccess.vue'
const { localeCache, adminAuth, showAdminAuth } = useGlobalState()
const router = useRouter()
Expand Down Expand Up @@ -49,6 +50,7 @@ const { t } = useI18n({
account: 'Account',
unknow: 'Unknow',
addressQueryTip: 'Leave blank to query all addresses',
senderAccess: 'Sender Access Control',
},
zh: {
title: '临时邮件 Admin',
Expand All @@ -72,6 +74,7 @@ const { t } = useI18n({
account: '账号',
unknow: '未知',
addressQueryTip: '留空查询所有地址',
senderAccess: '发件权限控制',
}
}
});
Expand Down Expand Up @@ -396,6 +399,9 @@ const fetchMailUnknowData = async () => {
</n-list-item>
</n-list>
</n-tab-pane>
<n-tab-pane name="senderAccess" :tab="t('senderAccess')">
<SenderAccess />
</n-tab-pane>
</n-tabs>
</div>
</template>
Expand Down
24 changes: 23 additions & 1 deletion frontend/src/views/Header.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ref, h, computed, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRoute, useRouter } from 'vue-router'
import { useIsMobile } from '../utils/composables'
import { DarkModeFilled, LightModeFilled, MenuFilled, AdminPanelSettingsFilled } from '@vicons/material'
import { DarkModeFilled, LightModeFilled, MenuFilled, AdminPanelSettingsFilled, SendFilled } from '@vicons/material'
import { GithubAlt, Language, User, Home, Copy } from '@vicons/fa'
import { useGlobalState } from '../store'
Expand Down Expand Up @@ -73,6 +73,8 @@ const { t } = useI18n({
home: 'Home',
menu: 'Menu',
user: 'User',
sendbox: 'Send Box',
sendMail: 'Send Mail',
pleaseGetNewEmail: 'Please login or click "Get New Email" button to get a new email address',
getNewEmail: 'Get New Email',
getNewEmailTip1: 'Please input the email you want to use. only allow ., a-z, A-Z and 0-9',
Expand Down Expand Up @@ -104,6 +106,8 @@ const { t } = useI18n({
home: '主页',
menu: '菜单',
user: '用户',
sendbox: '发件箱',
sendMail: '发送邮件',
pleaseGetNewEmail: '请"登录"或点击 "获取新邮箱" 按钮来获取一个新的邮箱地址',
getNewEmail: '获取新邮箱',
getNewEmailTip1: '请输入你想要使用的邮箱地址, 只允许 ., a-z, A-Z, 0-9',
Expand Down Expand Up @@ -176,6 +180,19 @@ const menuOptions = computed(() => [
show: showUserMenu.value,
key: "user",
children: [
{
label: () => h(
NButton,
{
tertiary: true,
ghost: true,
size: "small",
onClick: () => router.push('/sendbox')
},
{ default: () => t('sendbox') }
),
key: "sendbox"
},
{
label: () => h(
NButton,
Expand Down Expand Up @@ -358,6 +375,7 @@ const deleteAccount = async () => {
onMounted(async () => {
await api.getOpenSettings(message);
emailDomain.value = openSettings.value.domains ? openSettings.value.domains[0].value : "";
await api.getSettings();
});
</script>

Expand Down Expand Up @@ -386,6 +404,10 @@ onMounted(async () => {
<n-alert type="info" show-icon>
<span>
<b>{{ t('yourAddress') }} <b>{{ settings.address }}</b></b>
<n-button style="margin-left: 10px" @click="router.push('/send')" size="small" tertiary round
type="primary">
<n-icon :component="SendFilled" /> {{ t('sendMail') }}
</n-button>
<n-button style="margin-left: 10px" @click="copy" size="small" tertiary round type="primary">
<n-icon :component="Copy" /> {{ t('copy') }}
</n-button>
Expand Down
Loading

0 comments on commit b8f0fa4

Please sign in to comment.