很久以前就想搭建一个专属于自己的邮箱服务。曾经尝试使用了宝塔面板,但是由于服务器硬盘容量 系统版本 费用的问题也就暂时搁置了。今天无意中发现了这一款免费搭建自己的邮箱服务的项目,那个劲头立马就窜起来。
项目地址:https://github.com/eoao/cloud-mail
图文搭建教程: https://doc.skymail.ink/guide/via-ui
本项目使用Cloudflare worker部署,Resend推送邮件,无需服务器费用,就可以实现类似各大邮箱平台,如QQ邮箱,谷歌邮箱等自己的邮箱服务
前提是需要有个域名,并且dns设置为cloudflare的dns
好了,废话不多说了。开始了
一. 登录到cloudflare官网控制台,https://dash.cloudflare.com/
二. 点击左侧栏的 Workers 和 Pages,点击右上角的创建应用程序,点击导入存储库,导入该fork后的github项目。别跟我说不懂,哈哈
三. 点击高级设置-根目录-路径,修改路径为 /mail-worker,再点击创建和部署,等待完成
四. 构建和部署完成后。进入该项目,点击设置,点击域和路由,点击添加,选择自定义域,之后选择你之前绑定到cloudflare的域名
说一下,这里可以是子域名。比如你转发到cloudflare的域名是123456.com 那么你可以添加子域名mail.123456.com,这个稍后我会再讲到
五. 点击变量和机密,添加三个变量名
1 2 3 4 5 6 7 domain ["mail.123456.com"] admin 管理员的邮箱,如admin@mail.123456.com jwt_secret 123456
注意:domain是json类型的,其它两个都是文本。管理员账号之后需要你初始化数据后,在注册页面重新注册,否则是登录不了的
六. 创建kv数据库,点击控制面板左侧菜单栏,点击存储和数据库,创建kv数据库,命名空间名称随意,比如mail
七. 创建d1数据库,数据库名字随意,比如db。位置选择北美洲西部,不知道为什么,其它位置好像无法识别
八. 返回到worker项目,点击绑定-添加绑定-d1数据库-变量名称db-选择第7步创建的d1数据库,比如db
九. 点击绑定-添加绑定-kv命名空间-变量名称kv-选择第6步创建的kv数据库,比如mail
十. 浏览器输入https://你的自定义域名/api/init/你绑定的的jwt_secret 会自动初始化数据库(如果之前部署过只会更新不会覆盖原有数据)
十一. 浏览器输入你绑定的自定义域名网站,成功后,点击创建账号,使用admin账号注册,密码记住了。之后登录就直接有管理员的权限了
十二. 启用邮件接收,设置完成后才能接收邮件。回到cloudflare控制台,点击菜单账户主页,再点你的域名-点击电子邮件-开始使用-跳过入门指南-启用电子邮件路由-路由规则-状态改为活动-点击编辑-操作发送到worker,目标mail-点击保存
十三. 点击设置-点击添加子域(这里就是我之前第四步讲到的添加子域)-修改需要的子域,比如mail.123456.com
十四. 测试时候接受到邮件即可
十五. 设置附件收发,设置完这一步后才能接收附件和发送附件。由于这个可能会出现付费操作,所以我就不多说了。上面一般都够用了
十六. 如果看不懂,或者需要添加附件和人机验证功能,TG和其他邮箱转发,建议看看原文教程
结束语录:
总的来说,这个还是相当实用的,能在如今这年头搭建一个属于自己的邮箱系统,真的有一种“科技掌握在自己手里”的成就感。更关键的是——全程免费、无需服务器、可完全自定义域名,这一点简直太香了。
折腾的过程虽然略显繁琐,但当你看到那个熟悉的“收件箱”页面出现在自己域名下的那一刻,一切都值了。无论是作为个人项目、团队邮箱,还是单纯想体验自建服务的乐趣,这个项目都值得一试。
下载地址:https://wwwm.lanzn.com/b00hr8p0fe
密码:ezi2
补充:
后续发现在ios14系统以下,均无法正常加载主页面,一直转圈圈。安卓和pc端均正常访问。特提供解决方案
1.要完全兼容iOS14以下系统,需使用 @vitejs/plugin-legacy插件,并加 regenerator-runtime/runtime
2.在main.js文件中,把顶层 await init() 改为 async bootstrap 包裹,兼容所有浏览器
3.build.target 不要高于 es2017,否则 legacy 插件可能无法生成完整 ES5 代码
4.需要修改的文件有package.json vite.config.js main.js
5.数据对应不一致,需要删除\mail-vue里面的lock文件,然后重新生成
以2.2版本为例,具体步骤如下:
a.修改vite.config.js文件,增加vitejs/plugin-legacy插件
vite.config.js修改的完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 import {defineConfig, loadEnv} from 'vite' import vue from '@vitejs/plugin-vue' import legacy from '@vitejs/plugin-legacy' import path from 'path' import AutoImport from 'unplugin-auto-import/vite' import Components from 'unplugin-vue-components/vite' import {ElementPlusResolver} from 'unplugin-vue-components/resolvers' import {VitePWA} from 'vite-plugin-pwa'; export default defineConfig(({mode}) => { const env = loadEnv(mode, process.cwd(), 'VITE') return { server: { host: true, port: 3001, hmr: true, }, base: env.VITE_STATIC_URL || '/', plugins: [vue(), VitePWA({ injectRegister: 'script-defer', manifest: { name: env.VITE_PWA_NAME, short_name: env.VITE_PWA_NAME, background_color: '#FFFFFF', theme_color: '#FFFFFF', icons: [ { src: 'mail-pwa.png', sizes: '192x192', type: 'image/png', } ], }, workbox: { disableDevLogs: true, globPatterns: [], runtimeCaching: [], navigateFallback: null, cleanupOutdatedCaches: true, } }), legacy({ targets: ['defaults', 'not IE 11', 'iOS >= 10'], additionalLegacyPolyfills: ['regenerator-runtime/runtime'], modernPolyfills: true }), AutoImport({ resolvers: [ElementPlusResolver()], }), Components({ resolvers: [ElementPlusResolver()], }) ], resolve: { alias: { '@': path.resolve(__dirname, 'src') } }, build: { target: 'es2017', outDir: env.VITE_OUT_DIR || 'dist', emptyOutDir: true, assetsInclude: ['**/*.json'] } } })
b. 在main.js文件,把顶层 await init() 改为 async bootstrap 包裹,兼容所有浏览器:
main.js完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import 'regenerator-runtime/runtime' import { createApp } from 'vue' import App from './App.vue' import router from './router' import './style.css' import { init } from '@/init/init.js' import { createPinia } from 'pinia' import piniaPersistedState from 'pinia-plugin-persistedstate' import perm from '@/perm/perm.js' import i18n from '@/i18n/index.js' const pinia = createPinia().use(piniaPersistedState) const app = createApp(App).use(pinia) async function bootstrap() { await init() app.use(router).use(i18n).directive('perm', perm) app.mount('#app') } bootstrap()
c.修改package.json,vitejs/plugin-vue的版本号,使其和vite版本号对应
完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 { "name": "mail-vue", "private": true, "version": "0.0.0", "type": "module", "scripts": { "dev": "vite --mode dev", "remote": "vite --mode remote", "build": "vite build --mode release", "eo": "vite build --mode eo", "preview": "vite preview" }, "dependencies": { "@iconify/vue": "^4.3.0", "@vueuse/core": "^12.0.0", "axios": "^1.7.8", "compressorjs": "^1.2.1", "date-time-format-timezone": "^1.0.22", "dayjs": "^1.11.13", "dexie": "^4.0.11", "echarts": "^5.6.0", "element-plus": "^2.9.11", "lodash-es": "^4.17.21", "nprogress": "^0.2.0", "path": "^0.12.7", "pinia": "^3.0.2", "pinia-plugin-persistedstate": "^4.2.0", "regenerator-runtime": "^0.14.1", "vue": "^3.5.13", "vue-i18n": "^11.1.10", "vue-router": "^4.5.0" }, "devDependencies": { "@vitejs/plugin-legacy": "^7.2.1", "@vitejs/plugin-vue": "^6.0.1", "less": "^4.2.2", "sass": "^1.82.0", "terser": "^5.39.0", "unplugin-auto-import": "^19.3.0", "unplugin-vue-components": "^28.7.0", "vite": "^7.1.5", "vite-plugin-pwa": "^1.0.3" } }
d.定位到mail-vue目录下安装插件。若数据对应不一致,需要删除mail-vue目录里面的两个lock文件,然后重新生成
e.依次按照下面代码输入
1 2 3 4 5 6 7 pnpm install vite@7.1.5 --save-dev npm install vite@7.1.5 --save-dev npm install regenerator-runtime --save npm install -D @vitejs/plugin-vue@6.0.1 npm install -D @vitejs/plugin-legacy@7.2.1 --legacy-peer-deps
f.对项目进行重新构建
g.最后推送到仓库,在cloudflare重新部署就行了