Hi my new friend!

基于 CloudBase 实现静态博客评论 (一)

  • Home
  • cloudbase_comment
Scroll down
距离上次更新已经 1782 天了, 文章内容可能已经过时。

使用 CloudBase 给静态博客添加评论功能。

申请 CloudBase

新建CloudBase环境

选中包年包月,选择基础版1 (或者选择按量计费,开启免费额度)

在安全配置->Web安全域名 将自己得域名添加进去

评论大致流程

数据结构

json
{
    "id":"唯一ID",
    "articleID":"文章ID",
    "nick":"昵称",
    "email":"邮箱",
    "url":"链接",
    "date":"时间",
    "content":"评论内容",
    "top":"置顶",
    "childer":[
        {
            "id":"唯一ID",
            "nick":"昵称",
            "email":"邮箱",
            "url":"链接",
            "date":"时间",
            "content":"评论内容",
            "at":{ //回复
                "nick":"昵称",
                "email":"邮箱",
            }
        },
        {
            "id":"唯一ID",
            "nick":"昵称",
            "email":"邮箱",
            "url":"链接",
            "date":"时间",
            "content":"评论内容",
            "at":{
                "nick":"昵称",
                "email":"邮箱",
            }
        }
    ]
}

CloudBase 开发

在 VS Code 安装 tencentcloud.cloudbase-toolkit 插件,直接在 VS Code 上去创建、编写、部署。参考地址 常用操作。

创建集合

在数据库新增两个集合 article,comments 。

云函数

目前云函数支持 Node,PHP,Python 三种,本文使用 Node 为云函数环境

获取文章ID

新建一个云函数 GetArticleID,代码如下

js
'use strict';
const tcb = require('tcb-admin-node');
const app = tcb.init({ env: '环境ID' });
const db = app.database();
const articleDb = db.collection('article');

exports.main = async (event) => {
    try {
        let { hash, url } = event;
        //根据 hash 获取 article id
        let { data } = await articleDb.field({ _id: 1 }).where({ hash }).get();
        let articleID = '';
        if (data.length === 0) {
            //不存在  新增一条 article 数据
            let { id } = await articleDb.add({ date: new Date(), url, hash, })
            articleID = id;
        } else {
            let { _id } = data[0]
            articleID = _id;
        }

        return {
            success: true,
            data: articleID
        }
    } catch (error) {
        return {
            success: false,
            data: error
        }
    }
}

获取评论列表

新建一个云函数 GetComments,代码如下

js
'use strict';
const tcb = require('tcb-admin-node');
const app = tcb.init({ env:"环境ID" });
const db = app.database();
const _ = db.command;
const $ = _.aggregate;
const commentsDB = db.collection('comments');

exports.main = async (event, context) => {
    let { pagesize, articleID = '' } = event;
    if (isNaN(parseInt(pagesize)) || pagesize < 1) {
        return []
    }

    let { data: list } = await commentsDB
        .aggregate()
        //查询条件
        .match({ articleID })
        //排序
        .sort({
            top: -1,
            date: -1
        })
        //分页
        .skip((pagesize - 1) * 10)
        .limit(10)
        //指定需要查询字段
        .project({
            _id: 0,
            id: "$_id",
            articleID: 1,
            nick: 1,
            link: 1,
            date: 1,
            content: 1,
            top: 1,
            childer: {
                id: 1,
                nick: 1,
                link: 1,
                date: 1,
                content: 1,
                at: {
                    nick: 1,
                    link: 1,
                }
            }
        })
        .end()

    return list
};

新增、回复

新建一个云函数 AddComment,代码如下 使用 nodemailer 来发送邮件通知,nodemailer 参考文档

js
'use strict';
const tcb = require('tcb-admin-node');
const nodemailer = require('nodemailer');
const uuid = require('node-uuid');
const app = tcb.init({ env:"环境ID" });
const db = app.database();
const _ = db.command;
const $ = _.aggregate
const comments = db.collection('comments');

//邮件配置
const config = {
    host: 'smtp.qq.com',
    port: 465,
    secure: true,
    auth: {
        user: '邮箱@xx.com',
        pass: '密码或者授权码' //推荐使用授权码
    }
};

let transporter = nodemailer.createTransport(config);


exports.main = async (event, context) => {
    let { articleID = '', url = '',  nick = 'Anonymous', email = '', link = '', content = '', at = false, istop = true, topID = '', userID = '' } = event;

    if (content == '' || articleID == '' || (!istop && topID == '')) {
        return { success: false, data: '数据格式有误' };
    }

    let date = new Date();
    let par = { userID, articleID, content, url, nick, email, link, at, istop, date }

    if (at) {
        par.id= uuid.v1().replace(/\-/g,'');
        let res = await comments.where({ _id: topID }).update({ childer: _.push([par]) })

        let _atemail = await getEmailByID(at.id);
        if (!!_atemail && _atemail != email) {
            sendemail(_atemail, nick, content)
        }

        return { success: true, data: { date } }
    } else {
        let { code = false, message, id } = await comments.add(par)
        return { success: !code, data: !code ? { id, date } : message }
    }
};

/*
*根据回复id获取 对应人 邮箱
*/
async function getEmailByID(id) {
    let { data } = await comments.where({ _id: id }).get()
    if (data.length === 0) {
        let { data } = await comments
            .aggregate()
            .match({ 'childer.id': id })
            .project({
                _id: 0,
                item: $.filter({
                    input: '$childer',
                    as: 'item',
                    cond: $.eq(['$$item.id', id])
                })
            })
            .end();
        console.log(JSON.stringify(data))
        if (data.length > 0) return data[0].item[0].email
        return ''
    } else {
        return data[0].email;
    }
}

/**
 * 
 * email 收件人邮箱
 * nick 评论人昵称
 * content 评论类容
*/
async function sendemail(email, nick, content) {
    let date = new Date();
    let str = `${date.getFullYear()}${date.getMonth() + 1}${date.getDate()}${date.getHours()}${date.getMinutes()}${date.getSeconds()}秒`;
    let mail = {
        from: '白云苍狗 <xxxxx@qq.com>',
        subject: '[白云苍狗][博客] 收到新的回复',
        to: email,
        html: `
        <h2>${nick} ${str} 回复了您<h2>
        ${content}
        `
    }
    return transporter.sendMail(mail);
};

web 端

对 tcb 做一个简单封装

js
export default class tcbComment {

    constructor(env, hash) {
        if (!env) {
            console.error("未设置CloudBase环境id:env");
            return;
        }
        if (!hash) {
            console.error("未设置当前Comment的hash值");
            return;
        }
        this.env = env;
        this.skip = 1
        this.hash = hash;
    }

    /**
     * 初始化
     */
    async _init() {
        let tcb = await TcbLoader()
        this.app = tcb.init({ env: this.env });
        let auth = this.app.auth({
            persistence: "local"
        });
        if (!auth.hasLoginState()) {
            await auth.anonymousAuthProvider().signIn()
        }

        await this.getArticleID(this.hash)
    }

    //获取
    async getArticleID(hash) {
        let res = await this.app.callFunction({
            name: 'getArticleID',
            data: {
                hash,
                url: location.pathname,
            }
        })
        let { result: { success, data } } = res;
        if (success) {
            this.articleID = data;
        }
    }

    //获取评论列表
    async getComment() {
        let res = await this.app.callFunction({
            name: 'getComments',
            data: {
                pagesize: this.skip++,
                articleID: this.articleID
            }
        })
        return res.result;
    }

    //新增
    async addComment(parm) {
        let res = await this.app.callFunction({
            name: 'addComment',
            data: {
                articleID: this.articleID,
                ...parm
            }
        })
        return res;
    }
}

/**
* 加载tcb-js-jdk
*/
const TcbLoader = function (v = '1.6.0') {
    return new Promise((resolve, reject) => {
        if (window.tcb) {
            resolve(window.tcb)
        } else {
            var script = document.createElement('script')
            script.type = 'text/javascript'
            script.async = true
            script.src = `//imgcache.qq.com/qcloud/tcbjs/${v}/tcb.js`
            script.onerror = reject
            script.onload = () => resolve(window.tcb)
            document.head.appendChild(script)
        }
    })
}

小结

弄完cloudbase 云函数,和对 web 端简单使用封装,剩下得就是页面上弄个评论列表组件和评论组件了

  • 本文作者:白云苍狗
  • 本文链接:
  • 版权声明:本博客所有文章除特别声明外,均默认采用 CC BY-NC-SA 4.0 许可协议。
其他文章
cover
node 实现简单 http 转发
  • 20-05-12
  • 09:50
  • 记录类
本站已加入BLOGS·CN