# Markdown-Blog **Repository Path**: TwilightLemon/Markdown-Blog ## Basic Information - **Project Name**: Markdown-Blog - **Description**: No description available - **Primary Language**: JavaScript - **License**: GPL-3.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-03-03 - **Last Updated**: 2024-04-29 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## Markdown-Blog # 新生项目课程:云环境下基于Markdown的博客系统 ## Runtime ~~这是一个使用远古技术栈的项目~~ Server: Node.js Express MongoDB Frontend: jQuery Bootstrap ## 主要功能 - [x] 账号管理:注册、登录、重置、修改信息 - [x] 文章管理:发布、修改、删除、查看 - [x] 互动功能:评论、回复、文章点赞 ## 开发文档: ### 数据库结构 使用了MongoDB 数据库模型位于models文件夹下 ```mermaid graph LR blog[blog]-->ar[articles]-..->id ar-..->tittle ar-..->date ar-..->markdown ar-..->authorId blog-->user[users]-..->email user-..->saltedPsw user-..->name user-..->auth blog-->comm[comments]-..->likes comm-..->comment ``` ### apis 一些简陋的apis: #### commentMng 评论管理 comment与article数据库独立,避免干扰,二者通过articleId关联 comments和likes是两个独立的集合,likes用于存储点赞的用户id - getArticleComments:获取评论 ```js async function getArticleComments(articleId) { let comments = await commentDB.findOne({articleId: articleId}); let commentsList = []; if (comments) { for (const comment of comments.comments) { commentsList.push({ // id: comment._id,... }); } } return {comments: commentsList, likeCount: comments ? comments.likes.length : 0}; } ``` - addComment:添加评论 - deleteComment:删除评论 - clearCommentsForArticle:清空文章所有评论 - setLikesForArticle:文章点赞 评论回复功能做的也比较简陋 只是在回复后插入新的评论而已.. ```js if (replyTo != null) { //查找commendId为replyTo的评论 let replyToIndex = 0; for (var i = 0; i < comments.comments.length; i++) { if (comments.comments[i]._id.toString() === replyTo) { replyToIndex = i; break; } } comments.comments.splice(i + 1, 0, item); item = comments.comments[i + 1]; } else { comments.comments.push(item); item = comments.comments[comments.comments.length - 1]; } await comments.save(); return item; ``` #### articleMng 文章管理 在文章的增删查改的基础上新增加载md、评论、点赞、作者信息 - new - get - edit - delete #### loginAuth 登录验证 前端做了Js混淆和密码的AES加密,后端做了密码的SHA256加盐加密 验证通过的token通过cookie储存.. 这是一个简单暴力的方式,可能安全性并不高 - signPsw 在数据库中存储的是加盐后的密码 ```js function signPsw(psw) { return crypto.createHash('md5').update(salt + psw).digest('hex'); } ``` - createLoginToken ```js function createLoginToken(res, email, saltedPsw) { let token = crypto.createHash('sha256').update(salt + email + salt + saltedPsw).digest('hex'); let conf = {httpOnly: true, secure: true, maxAge: 1000 * 60 * 60 * 24 * 7}; res.cookie('loginToken', token, conf); res.cookie('email', email, conf); } ``` - checkLoginToken ```js async function checkLoginToken(cookies) { let result = { login: false, user: null }; if (cookies.loginToken && cookies.email) { const User = require('../models/user'); let user = await User.findOne({email: cookies.email}); if (user) { let token = crypto.createHash('sha256').update(salt + cookies.email + salt + user.saltedPsw).digest('hex'); if (token === cookies.loginToken) { result.user = user; result.login = true; } } } return result; } ``` - packConfWith - reset router收到请求后会先通过cookie验证token,通过验证的token会再通过 packConfWith将登录信息附带参数传递给render并在前端做出相应的显示 #### userProfile 用户信息 - sendVerificationCode 发送验证码(用于注册和修改密码 似乎不是很好分类) - get/setName 修改昵称 ## 功能实现 #### Where app starts ```mermaid graph LR app[server.js]-->router app-->timer-->UpdateGlobalService router-->articles router-->comments router-->login router-->profile ``` #### 用户管理 注册、登录、修改信息: ```mermaid graph TD login(Login/Register)-->en(Enter Email) en-->check{already exist?}--No-->send(发送验证码,并将验证码加密成token存入cookie) send-->验证成功-->设置密码和昵称-->loginin(createLoginToken) check--Yes-->psw(请求输入密码)-->验证通过 -->loginin ``` 验证登录信息: api经过封装,只需要 ```js const auth=require('../apis/loginAuth'); let authData=await auth.checkLoginToken(req.cookies); if(authData.login){ //do something //authData.user.email \ name ... } ``` 或者在router中: ```js res.render('xxxxxurlxxxxx', await auth.packConfWith(req.cookies, {YOURDATA})); ``` 修改名称: ```js $.ajax({ url:'/profile/', method:'POST', data:{ method:"setName", newName:newName }, success:(res)=> { if (res.success) { alert("succeeded to change your name to " + newName); $('#titleH1').html("Hello, " + newName); $('#loginUser').html(`welcome back, ` + newName.toString() + `! `); }else{ alert("failed to change you name"); } } }); ``` 重置密码: ```js const resetHandler=async(req,res)=> { let loginData = await auth.checkLoginToken(req.cookies); let email = undefined; if (req.body.email) email = req.body.email; if (email) { const up = require('../apis/userProfile'); let code = await up.sendVerificationCode(email); res.cookie('VerificationCode', auth.signPsw(code), {maxAge: 1000 * 60 * 5}); res.render('account/login', { title:"Reset", psw: "", name: loginData.login ? loginData.user.name : "", login: false, exist: false, confirmed: true, email: email, msg: "" }); } else { setGlobalEncoder(); res.render('account/login', {title:"Reset",exist: false, confirmed: false, login: false, msg: "Verify your email:"}); } }; router.get('/reset',resetHandler); router.post('/reset',resetHandler); ``` #### 文章管理 新增、修改文章: ```mermaid graph TD new --new Article-->new.ejs-->saver edit--article.findOne-->edit.ejs-->saver saver[saveArticleAndRedirect] saver-->article.save&redirect ``` 查看文章;包括加载md、评论、点赞、作者信息 ```js let commentsData = await commentMng.getArticleComments(article._id); let likeCount = commentsData.likeCount; const {marked} = require('marked'); const createDomPurify = require('dompurify'); const {JSDOM} = require('jsdom'); const dompurify = createDomPurify(new JSDOM().window); const usProf = require('../apis/userProfile'); article.sanitizedHtml = dompurify.sanitize(marked(article.markdown)); article.authorName = await usProf.getName(article.author); res.render('articles/show', await auth.packConfWith(req.cookies, { article: article, comments: commentsData.comments, likes: likeCount })); ``` #### 文章互动 评论、回复、点赞 前端:用ajax异步提交评论,成功后在dom中插入新的评论 ```js if(url.toString().includes('type=reply')) { var commentId = form.find('input[name="commentId"]').val(); console.log(commentId); comList.find('.card-body').each(function () { if ($(this).find('p#commentItemId').text() === commentId) { $(this).parent().after(item); $("#myModal").modal('hide'); } }); }else{ comList.append(item); } ``` 点个赞 ```js function like(articleId){ $.ajax({ url:'/comments/like', type:'POST', data:{articleId:articleId}, success:function(data){ $('#likeBtn').html(data.likes+' Likes'); } }); } ``` ## 工作量统计 | 工作量统计表 | 基础功能 | 新增功能1 | 新增功能2 | 新增功能3 | 新增功能4 | 新增功能5 | 新增功能6 | 新增功能7 | | ----------- | ----------- | ----------- | ----------- | ----------- | ----------- |----------- |----------- |----------- | 描述 | 文章数据增删查改(已重构) | 多账号管理(注册、发邮、登录、重置、退出)|登录Token分发验证等加密措施| 文章互动功能(文章点赞、评论、回复评论) | 修改部分功能为异步请求 | 同步bing每日一图为背景 | 评论支持md、\块支持语法高亮 | CSS美化 | 学时 | 3 | 4|2 | 5 | 3 | 1 | 1 | 3 ## 注释: mongoDB linker: ``` mongodb://my-mongo/TwlmBlog ``` available ports: ``` 12301~12349 ```