通过一个小小的需求来玩玩腾讯云的云开发
我有一个同事。
他叫小草。
是个开发,男的。
小草呢,最近在搞一个开源小项目。
这两天找到我,”小丁啊,给我加个小功能。简单!“
需求:页面上加几个输入框,一个输入框填一个邮箱地址,用户提交后,给这个邮箱发个邮件。
”小草啊,这个需求简单,给我一个接口,我一个切图仔唰唰唰切一个页面,完了调它一个接口,齐活。“
小草的这个项目啊,是一个nuxt的项目,UI框架用的ant-design。nuxt嘛,大家知道,跟vue差不多的写法,这简单啊,有手就行。开搞开搞。
首先,写个界面出来。
<a-form-model
:labelCol="{ span: 2, offset: 0 }"
:wrapperCol="{ span: 16, offset: 0 }"
ref="refForm"
:model="emailModel"
:rules="rules"
v-if="showForm"
>
<a-form-model-item ref="name" label="您的姓名:" prop="name">
<a-input size="large" v-model="emailModel.name" />
</a-form-model-item>
<a-form-model-item ref="email" label="您的邮箱:" name="email" prop="email">
<a-input size="large" v-model="emailModel.email" />
</a-form-model-item>
<a-form-model-item ref="content" label="您的内容:" prop="content">
<a-textarea v-model="emailModel.content" placeholder="邮件内容啊" :rows="4" />
</a-form-model-item>
<a-form-model-item :wrapper-col="{ span: 2, offset: 2 }">
<a-button size="large" type="primary" @click="onSubmit" :loading="loading">发送</a-button>
</a-form-model-item>
</a-form-model>
然后,写点 JS 吧。
/***********data里的数据啊***************/
data() {
return {
loading: false,
showForm: true,
rules: {
content: [{ required: true, message: '随便填点什么吧', trigger: 'blur' }],
email: [
{ required: true, message: '邮箱还是要必填的', trigger: 'blur' },
{ type: 'email', message: '邮箱格式注意一下', trigger: 'blur' },
],
},
emailModel: {
name: '',
email: '',
content: '',
},
};
},
/***********写个函数啊***************/
methods: {
onSubmit() {
this.$refs.refForm.validate((valid) => {
if (valid) {
this.loading = true;
// 开始调接口拉
} else {
console.log('error submit!!');
return false;
}
});
},
}
搞定,去找小草要接口去。
”小草,接口给我,联调了!“
”接口?什么接口!“
”这点小功能还需要专门出接口?你是不是不行?”
“!!!!!@@##1****”
开搞开搞
先整个 cloudbase 环境
这里因为就简简单单写个函数,创建空模板就好啦。
因为小草的项目是没有登录态的啦。这里就允许匿名访问吧。
我们再加白几个域名吧。
开始创建一个云函数吧。
创建好了后,就可以开始在线上写代码了。
但是,作为一个很厉害的程序员,当然要在本地写代码啦。
继续配置
安装腾讯云开发cli工具
sudo npm install -g @cloudbase/cli
完了后,找个空文件夹。
tcb -h
呃,列出命令看看。
登录 -> 同步云函数列表 -> 同步云函数内容啥的。自己看文档吧
开始写云函数啦,写个发邮件的服务。
怎么写?
当然是 要先 搜它一搜啊 !
从广大的互联网搬砖工那里学到了,用node写邮件服务,当然是用库啊!
这里用的库名叫 nodemailer
按照示例咱们小心翼翼来一点一点的copy。
npm install nodemailer
const nodemailer = require('nodemailer');
const transporter = nodemailer.createTransport({
service: 'qq',
auth: {
user: '你的邮箱地址',//发送者邮箱
pass: '授权码' //授权码,在准备工作中开启服务时候的授权码
}
});
const mailOptions = {
from: 'xxxxxx@qq.com', // 发送者
to: 'xxxxxx@qq.com', // 接受者,可以同时发送多个,以逗号隔开
cc: ',xxxxx@qq.com',//抄送
subject: '发送邮件测试', // 标题
text: 'Hello world', // 文本
html: `<h2>NodeJS发送邮件测试</h2>`
};
transporter.sendMail(mailOptions, function (err, info) {
if (err) {
console.log(err);
return;
}
console.log(`发送成功:${info.accepted}`);
});
抄完了,我摸了下日渐稀疏的头发。陷入了沉思!
这里还需要搞个发件人邮箱认证呀! 那得去弄下。那就再来一番操作。
先拿我的qq邮箱做个试验吧,进去后,拿到授权码。步骤如下。
再一看,还是不对劲呀!这里 发件人 是不是得需要配置呀。不能写死吧!别人要用怎么办。那得写个配置表呀。
那!
解决方案:开个云数据库,选用特定的配置单。
1、创建一个集合
2、写一条配置数据先
接下来,干点正事,写代码。
const nodemailer = require("nodemailer");
const cloudbase = require("@cloudbase/node-sdk");
const cloudApp = cloudbase.init({
region: "ap-guangzhou",
// 环境可以不写,默认当前环境
});
const db = cloudApp.database();
function getEmailInstance(options) {
const transport = {
host: options.host,
secureConnection: true, // 使用SSL方式(安全方式,防止被窃取信息)
auth: {
user: options.email,
pass: options.pass,
},
};
return nodemailer.createTransport(transport);
}
function sendEmail(opt, sendData) {
return new Promise((resolve, reject) => {
const mailTransport = getEmailInstance(opt);
const options = {
from: `"${opt.name}" <${opt.email}>`,
// to: sendData.to,
// // cc : '' //抄送
// // bcc : '' //密送
// subject: "一封来自Node Mailer的邮件",
// text: "一封来自Node Mailer的邮件",
// html: '<p>html标签文本</p>',
...sendData,
};
mailTransport.sendMail(options, function (err, msg) {
if (err) {
reject(err);
} else {
resolve();
}
});
});
}
const email = {
send: async (data, context) => {
// 查库,获取options
try {
const dbRes = await db.collection("email-user").doc(data.appid).get();
const row = dbRes.data[0];
await sendEmail(row, data);
return { code: 0, msg: "发送成功" };
} catch (e) {
console.error(e);
return { code: 500, msg: "邮件发送失败", error: e };
}
},
};
好,邮件服务的api主体基本上是搞完了,但是,我发现了一个有趣的地方。
这个nodemailer ,它是可以发html作为邮件文本的呀。邮件参数可参考 nodemailer参数配置
那前端不得搞个富文本编辑器呀,不然,都没意思!
那得搞啊!!!
怎么搞呢?先问问?
钓友们给我推荐了两个,看了看,选 wangeditor 吧,毕竟文档是中文的。
写代码写代码~~~
因为是nuxt项目中引入外部库,所以先得写个插件来引入
nuxt.config.js
plugins: [
{ src: '~/plugins/wangEditor', ssr: false },
],
plugins/wangEditor.js
import Wangeditor from 'wangeditor';
import Vue from 'vue';
Vue.prototype.$wangeditor = (content) => new Wangeditor(content);
WangEditor.vue 组件
<template>
<div :id="id"></div>
</template>
<script>
export default {
name: 'WangEditor',
data() {
return {
id: 'e',
editor: null,
};
},
model: {
prop: 'val',
event: 'change',
},
props: {
val: {
type: String,
defalut() {
return '';
},
},
},
watch: {},
mounted() {
this.id = `e${new Date().getTime()}`;
this.$nextTick(this.initEditor);
},
methods: {
initEditor() {
const editor = this.$wangeditor(`#${this.id}`);
this.editor = editor;
// 配置 onchange 回调函数
editor.config.onchange = (newHtml) => {
this.$emit('change', newHtml);
};
// 配置触发 onchange 的时间频率,默认为 200ms
editor.config.onchangeTimeout = 500; // 修改为 500ms
editor.config.customUploadImg = this.uploadEditorFile;
editor.config.customUploadVideo = this.uploadEditorFile;
editor.config.menus = [
'head',
'bold',
'fontSize',
'fontName',
'italic',
'underline',
'strikeThrough',
'indent',
'lineHeight',
'foreColor',
'backColor',
'link',
'justify',
'quote',
'image',
'table',
'code',
'splitLine',
'undo',
'redo',
];
editor.create();
editor.txt.html(this.val);
},
},
};
</script>
效果:
然后,似乎,又不对劲啊!!!
富文本图片编辑,那不得搞一个图片对象存储的能力呀。
???
先搞下配置。
因为小草这个项目是不需要登录的,所以这里暂时先搞成公共读公共写。
这样搞不安全,请不要效仿
然后写两个前端方法,来做文件上传。
function uploadFile(file) {
const cloudPath = `${new Date().toLocaleDateString().replace(/\//gi, '-')}/${new Date().getTime()}.${file.name.split('.').reverse()[0]}`;
return cloudApp.uploadFile({
cloudPath,
filePath: file,
});
}
function uploadFiles(arr) {
return Promise.all(arr.map((v) => uploadFile(v)));
}
然后,就用 wangeditor 绑定自定义上传文件的方式进行绑定就成了。参考文档
然后,我又发现,咱们现在在前端,已经有两个需要调用 cloudebase 的功能了。那不得?
抽出来!!!抽出来。在nuxt中的话,就搞成一个 插件 吧。
import cloudbase from '@cloudbase/js-sdk';
import Vue from 'vue';
const FUNCTION_NAME = 'tools';
const APPID = '<在云数据库中生成的那个配置单的id>';
const cloudApp = cloudbase.init({
env: '<环境>',
region: '<地域>',
});
function sendEmail(data) {
return cloudApp.callFunction({
name: FUNCTION_NAME,
data: {
action: 'email.send',
data: {
appid: APPID,
...data,
},
},
});
}
function uploadFile(file) {
return cloudApp.uploadFile({
cloudPath: `${new Date().toLocaleDateString().replace(/\\//gi, '-')}/${new Date().getTime()}.${
file.name.split('.').reverse()[0]
}`,
filePath: file,
});
}
function uploadFiles(arr) {
return Promise.all(arr.map((v) => uploadFile(v)));
}
Vue.prototype.$cloudtool = {
uploadFiles,
uploadFile,
sendEmail,
};
这个时候,功能都实现得差不多了。
我又摸了摸的稀疏的头发,既然前端都以抽成了一个独立的插件,我服务端废了那么大的劲就只实现了一个功能,难道就没法扩展吗?
基本思路就是,调用云函数的时候,其中一个路由参数代表要访问的功能,然后在云函数入口根据不同的路由做分发。
云函数入口 index.js
'use strict';
const actions = require("./actions/index.js");
exports.main = async (event, context, callback) => {
try{
return actions(event, context)
} catch (e) {
return {
code: 401,
msg: '参数错误',
e
}
}
};
主路由 /actions/index.js
const email = require("./email");
module.exports = async function(event, context) {
// 总的路由拦截
try {
const {action,data} = event
const farr = action.split('.')
return email({
action: farr[1],
data,
}, context)
} catch(e) {
throw new Error()
}
}
子路由 /actions/email.js
const email = {
send: async (data, context) => {
try {
const dbRes = await db.collection("email-user").doc(data.appid).get();
const row = dbRes.data[0];
await sendEmail(row, data);
return { code: 0, msg: "发送成功" };
} catch (e) {
console.error(e);
return { code: 500, msg: "邮件发送失败", error: e };
}
},
};
module.exports = async function (event, context) {
const { action, data } = event;
return email[action](data, event);
};
前端调用的话,传参格式如下:
return cloudApp.callFunction({
name: 'tools',
data: {
action: 'email.send',
data: {
appid: APPID,
...
},
},
});
那么,从现在开始,我的云函数,就可以扩展出很多路由啦。完美!
我找到了小草,很自信的给他演示了一遍效果。
小草也摸了摸他稀疏的头发,思考了一下。
“很棒,但是,暴露在外的邮箱发送功能,得有安全问题吧?是不是得想个办法处理一下?”
那么,我怀着沉重的心情,在腾讯云搜了搜。。。
未完待续.................................................
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。