preface

After learning KOA2 and Express and writing some demo, I plan to write a project to practice my hands. As I am a student in school, I have no good project to do. That is to develop a front-end forum as the goal, and refer to some communities for functional requirements.

  • Log in to register
  • Personal information maintenance, profile picture and other basic information
  • Publish articles, rich text editor wangEditor plug-in, edit, delete articles, articles classification and so on
  • Article comments, articles collection, likes, etc
  • Support article pagination, comment page loading
  • Follow the user
  • Resource (file) upload share, download, view
  • Recommended learning resources:…..
  • Author’s personal diary
  • But… Due to various reasons, only part of the function has been implemented, and resource sharing has not been written

Project operation effect:







http://120.77.211.212/home


Github: github.com/Jay214/mybl… If you think it will help you or you can still watch it, welcome to star~~ encourage me to encourage this front-end slag.

The development environment

Node: v8.3.0

Koa: ^ against 2.4.1

Mysql: 5.7.1

NPM: 5.3.0 or above

How to run the project

  • Clone the project locallygit clone [email protected]:Jay214/myblog-koa2.git
  • Installing modular middlewarenpm install
  • Mysql version 5.7 is a bug, and navicat for mysql is recommended as a graphical interface
  • Install supervisor (NPM install Supervisor project running tool, which is in listening mode after being opened. Save the file after modifying it, and no need to start the project again)Node Index or NPM Supervisor Index
  • Localhost :8080/home The port number can be changed by yourself

If you find that there are any bugs or good suggestions, welcome many suggestions, QQ :2752402930.

The preparatory work

Koa2 is based on the promise and await/async syntax of ES6 and ES7, so if you don’t understand es6/ ES7, please go through the documentation first. It is the key to build the database in the background, so please install mysql first. Mysql is recommended to install 5.7 or later, because 5.7.0 has a bug. You will need to change the configuration file, which you will know when you install it.

To install the node environment, use node -v to check the node version. Node needs a newer version that supports BOTH ES6 Promise and ES7 await/async syntax. NPM is now shipped with node versions, so you don’t need to install NPM.

The project structure

                      

  • 1. Config stores the default file (database connection configuration)
  • 2. Lib Stores database files
  • 3. Middlewares store middleware to determine whether the login is registered
  • 4. Public stores static files, js, and reference files such as bootstrap framework
  • 5. Routers store routing files
  • Views Save the template file
  • 7. Index is the program main file, definition interface, database interface, reference module, etc
  • 8. Package. json project configuration file, including project name, author, dependencies, modules, etc

To initialize the project: CD myblog1 -> NPM init

Since KOA2 is a lightweight framework, small and robust, we need to install some koA2 module middleware in order to promote our development efficiency and convenience:

npm install i koa koa-bodyparser koa-mysql-session koa-router koa-session-minimal koa-static koa-views md5 moment mysql ejs koa-static-cache --save-devCopy the code

Usage of each module

  1. koa nodeThe framework
  2. koa-bodyparserForm parsing middleware
  3. koa-mysql-session,koa-session-minimalMiddleware that handles databases
  4. koa-routerRouting middleware
  5. koa-staticStatic resources load the middleware
  6. ejsA template engine
  7. md5Password encryption
  8. momentTemporal middleware
  9. mysqlThe database
  10. koa-viewsTemplate rendering middleware
  11. koa-static-cacheThe file cache

Set up the basic framework of the project

Configuring a Database connection

Create default.js in the config folder:

Const config = {// start port: 8080, // configure database: {database:'nodesql',        
  USERNAME: 'root',       
 PASSWORD: '123456',      
  PORT: '3306',        
HOST: 'localhost'  
  }
}
module.exports = config;  Copy the code

Then create mysql.js in the lib folder:

var mysql = require('mysql');
var config = require('.. /config/default.js')/var/establish a database connection pool pool = mysql. CreatePool ({host: config. Database. The host, user: config. Database. The USERNAME and password: config.database.PASSWORD, database: config.database.DATABASE });let query = function(sql, values) {    
return new Promise((resolve, reject)=>{
        pool.getConnection(function (err,connection) {
            if(err){      reject(err);
                }else{                
connection.query(sql,values,(err,rows)=>{ 
                   if(err){ 
                       reject(err);
                    }else{ resolve(rows); } connection.release(); // Set up a connection for each request and call connection.release() when finished; Directly release resources. })}})})})}Copy the code

Here to establish a database connection pool and encapsulate a database table operation function, if the database connection to have do not understand, please baidu.

Create entry file

Create index.js in your home directory, the project entry file:

const koa = require("koa"); // nodeframe const path = require("path");  
const bodyParser = require("koa-bodyparser"); // form parsing middleware const ejs = require("ejs"); // Template engine const session = require("koa-session-minimal"); Const MysqlStore = require() const MysqlStore = require()"koa-mysql-session"); // const router = require("koa-router"); // routing middleware const config = require('./config/default.js'); // import the default file const views = require("koa-views"); // Template rendering middleware const koaStatic = require("koa-static"); // static resource loading middleware const staticCache = require('koa-static-cache') const app = new koa(); / / the session storage configuration, to store the session to the database const sessionMysqlConfig = {user: config. Database. The USERNAME and password: config.database.PASSWORD, database: config.database.DATABASE, host: Config.database. HOST,} // Configure session middleware app.use(session({key:'USER_SID', store: new MysqlStore(sessionMysqlConfig)})) // Configure static resources to load middleware app.use(koaStatic(path.join(__dirname,'./public'))) // Configure server-side template rendering engine middleware app.use(views(path.join(__dirname,'./views'),{
    extension: 'ejs'})) app.use(bodyParser({"formLimit":"5mb"."jsonLimit":"5mb"."textLimit":"5mb"})); // Log in to app.use(require('./routers/signin.js').routes()) // Register app.use(require()'./routers/signup.js').routes()) // Log out of app.use(require()'./routers/signout.js').routes()) // home app.use(require()'./routers/home.js').routes()) // personal homepage app.use(require()'./routers/personal').routes()) // post page app.use(require()'./routers/articles').routes()) // resource sharing app.use(require()'./routers/share').routes()) // personal diary app.use(require()'./routers/selfNote').routes()) app.listen(8080) console.log(' listening on port${config.port}`)

Copy the code

The above code has comments, I will not explain it, because the resource sharing and personal diary has not been written, so temporarily unified share… Alternative.

Mysql > create table, delete table, update table, update table, update table…

var users = `create table if not exists users(
    id INT(200) NOT NULL AUTO_INCREMENT,
    name VARCHAR(100) NOT NULL,
    pass VARCHAR(40) NOT NULL,
    avator VARCHAR(100) DEFAULT 'default.jpg', job VARCHAR(40), company VARCHAR(40), introdu VARCHAR(255), userhome VARCHAR(100), github VARCHAR(100), PRIMARY KEY (id) ); ` var posts = `create tableif not exists posts(
    id INT(200) NOT NULL AUTO_INCREMENT,
        name VARCHAR(100) NOT NULL,
        title VARCHAR(100) NOT NULL,
        content TEXT NOT NULL,
        uid INT(200) NOT NULL,
        moment VARCHAR(40) NOT NULL,
        comments VARCHAR(255) NOT NULL DEFAULT '0',
        pv VARCHAR(40) NOT NULL DEFAULT '0',
        likes INT(200) NOT NULL DEFAULT '0'.type VARCHAR(20) NOT NULL,
        avator VARCHAR(100),
        collection INT(200) NOT NULL DEFAULT '0', PRIMARY KEY (id) , FOREIGN KEY (uid) REFERENCES users(id) ON DELETE CASCADE ); ` var comment= `create tableifnot exists comment( id INT(200) NOT NULL AUTO_INCREMENT, name VARCHAR(100) NOT NULL, content TEXT NOT NULL, moment VARCHAR(40) NOT NULL, postid INT(200) NOT NULL, avator VARCHAR(100), PRIMARY KEY ( id ), FOREIGN KEY (postid) REFERENCES posts(id) ON DELETE CASCADE ); ` var likes = `create tableifnot exists likes( id INT(200) NOT NULL AUTO_INCREMENT, name VARCHAR(100) NOT NULL, postid INT(200) NOT NULL, PRIMARY KEY (id), FOREIGN KEY (postid) REFERENCES posts(id) ON DELETE CASCADE ); ` var collection = `create tableifnot exists collection( id INT(200) NOT NULL AUTO_INCREMENT, uid VARCHAR(100) NOT NULL, postid INT(200) NOT NULL, PRIMARY KEY (id), FOREIGN KEY (postid) REFERENCES posts(id) ON DELETE CASCADE ); ` var follow = `create tableif not exists follow(
         id INT(200) NOT NULL AUTO_INCREMENT,
         uid INT(200) NOT NULL,  
         fwid INT(200) NOT NULL DEFAULT '0',
         PRIMARY KEY (id),
         FOREIGN KEY (uid) REFERENCES users(id)
         ON DELETE CASCADE
     )
     `

let createTable = function(sql){
    returnquery(sql, []); } // createTable(users); createTable(posts); createTable(comment); createTable(likes); createTable(collection); createTable(follow); //createTable(follower); // Register userlet insertData = function(value){
    let _sql = "insert into users(name,pass) values(? ,?) ;"
    returnquery(_sql,value); } // Update the avatarlet updateUserImg = function(value){
    let _sql = "update users set avator=? where id=?"
    returnquery(_sql,value); } // Update user informationlet updateUser = function(value){
    let _sql = "update users set name=? ,job=? ,company=? ,introdu=? ,userhome=? ,github=? where id=?"
    returnquery(_sql,value); } // Publish an articlelet insertPost = function(value){
    let _sql = "insert into posts(name,title,content,uid,moment,type,avator) values(? ,? ,? ,? ,? ,? ,?) ;"
    returnquery(_sql,value); } // Update the number of commentslet updatePostComment = function(value){
    let _sql = "update posts set comments=? where id=?"
    returnquery(_sql,value); }...Copy the code

There are six tables in total: user table, article table, article comment table, article favorites table, article likes table, and user attention table.

Here refers to the foreign keys, but the development is not recommended use foreign keys, so you can modify by oneself, here the first time you start the project database creation failure occurs (due to a foreign key), as long as to restart with respect to ok, if the mysql also don’t know, here attached you a portal: introduction to mysql video tutorial password: C2q7.

Front-end page development

After the basic structure of the project is set up, the front-end page can be written. When using Node to develop web, we usually cooperate with template engine. In this project, I used EJS. Besides EJS, jade is more commonly used, but the code structure of Jade is not clear compared with EJS. Here is a brief introduction to ejS syntax:



Code reuse

Front-end page development should also have a clear structure. In order to realize code reuse, here are four public files: Header, footer, nav, login:

  • header.ejs

<! DOCTYPE html> <html lang="en">
    <head>
        <meta charset="utf-8">
        <title>Myblog</title>
        <link rel="stylesheet" href="/css/bootstrap.min.css">
        <link rel="stylesheet" href="/css/index.css">
        <script src="/ js/jquery - 3.2.1. Min. Js." " type="text/javascript"></script>
        <script src="/js/bootstrap.min.js" type="text/javascript"></script>
      Copy the code

  • nav.ejs

</head>
<body>
    <header class="nav-head">
        <div class="nav container">
            <ul>
                <li><a href="/home">< span style = "box-sizing: border-box; color: RGB (74, 74, 74); line-height: 22px; white-space: inherit! Important;"/share">< span style = "box-sizing: border-box; color: RGB (74, 74, 74); line-height: 22px; white-space: inherit! Important;"/share">< span style = "box-sizing: border-box; color: RGB (74, 74, 74); line-height: 22px; white-space: inherit! Important;"/share">< span style = "box-sizing: border-box; color: RGB (74, 74, 74); line-height: 22px; white-space: inherit! Important;"/about"</a></li> <li><inputtype="text" placeholder="Search" class="input-sm search"></li>
              
                <% if(session.user){ %>
                    <li>
                        <img src="/images/<%= session.avator %>" alt="" class="img-circle img-title">
                       
                        <ul class="menu">
                            <li class="personal menulist"><a href="/personal/<%= session.user %>">主页</a></li>
                         <!--   <li class="collection menulist"><a href="#"></ a></li> --> <li class="menulist"><a href="/articles"Word-wrap: break-word! Important; ">< span style =" box-sizing: border-box; color: RGB (74, 74, 74)"out"><a href="/signout"> logout < / a > < / li > < / ul > < / li > < script > var imgTitle = document. The getElementsByClassName ('img-title')[0],
           
                          menu = document.getElementsByClassName('menu') [0]; imgTitle.onclick =function(event) { showTap(); event.stopPropagation(); / / stop event bubbling} document. Body. AddEventListener ('click'.function (event) { 
                                menu.style.display = 'none';
                               // event.stopPropagation();
                             },true)
      
                              function showTap() {if(menu.style.display == 'block'){
                                     menu.style.display = 'none';
                                 }else {
                                     menu.style.display = 'block'; Var signOut =}} / / logged on the document. The getElementsByClassName ('out') [0]; /* signOut.onclick =function(){
                                ajax('get'.'/signout',null);
                                xhr.onreadystatechange = function () {
                              if(xhr.readyState==4&&xhr.status>=200&&xhr.status<300){
                                 lettext = xhr.responseText; // The object returned by the serverif(text){
                                window.location.reload = 'localhost:8080/home';
                                 }
                                 
                             }
                         }

                            }*/
                    </script>
                    <% }else{ %>
                        <li class="login">
                                <a class="loginup" href="javascript:;"><span class="glyphicon glyphicon-user"Login registered | > < / span > < / a > < / li > < %} % > < / ul > < / div > < header > < script > var searchInput = document. The getElementsByClassName ('search') [0]; searchInput.onfocus =function () {
            this.style.width = "300px";
        }
        searchInput.onblur = function () {
            this.style.width = "180px";
        }
        
    </script>

Copy the code

  • login.ejs

<div class="sign">
    <a href="javascript:;" title="Closed" class="login-close close"> x < / a > < div class ="sign-title"> < span style = "box-sizing: border-box! Important; word-wrap: break-word! Important;" </h3> </div> <form class="form signup" role="form">

            <div class="form-group">
                <input type="text" name="username" placeholder="Account no less than two characters" class="form-control">
            </div>
        <div class="form-group">
            <input type="password" name="pass" class="pass form-control" placeholder="Password">
        </div>
        <div class="form-group">
            <input type="password" name="repeatpass" id="repeat" placeholder="Repeat password" class="form-control">
        </div>
            <div class="form-group">
            <input type="button" value="Registered" class="btn btn-primary login-up">
            </div>

    </form>
    <form class="form signin" role="form">
       <div class="form-group">
           <input type="text" name="username" placeholder="Please enter your user name." class="form-control">
       </div>
       <div class="form-group">
           <input type="password" name="pass" class="pass form-control" placeholder="Please enter your password">
       </div>
      <div class="form-group">
          <input type="button" value="Login" class="btn btn-primary login-in">
      </div>
    </form>
    <div class="form-tips"Word-wrap: break-word! Important; "> <span> </span> <a href="javascript:;" class="register"> </a> </div> <div class="login-form-mask"></div>
<script>
  //  $(document).ready(function () {
        var $close = $('.login-close');
        var $sign = $('.sign');
        $close.click(function () {
            $sign.css("display"."none");
        })

        var $register = $('.register'), / / login/loginup switch$span = $('.form-tips span'),
            $signup = $('.signup'),
            $signTitle = $('.sign-title h1'),
            $signin = $('.signin');

        $register.click(function () {
            if($span.html() == "Already have an account?") {$signin.css('display'.'block');
                $signup.css('display'.'none');
                $(this).html('registered');
                $span.html("No account?");
                $signTitle.html("Welcome to login");

            }else{

                $signin.css('display'.'none');
                $signup.css('display'.'block');
                $(this).html('login');
                $span.html("Already have an account?");
                $signTitle.html("Welcome to register");
            }
        })

        var $loginup = $('.loginup'); // Click Login/Register to stop the event bubble$loginup.click(function () {
            $mask.fadeIn(100);
            $sign.slideDown(200);
            return false;
        })

        var $close = $('.login-close'),
            $mask = $('.login-form-mask'),
            $sign = $('.sign');

        $sign.click(function () {
            return false;
        })

        $close.click(function (e) {
           // e.stopPropagation();
            fadeOut();

        })

        $(document).click(function(e) {// Click anywhere to cancel the login box // e.topPropagation (); fadeOut(); })function fadeOut() {$mask.fadeOut(100);
            $sign.slideUp(200);
        }
    

    var loginUp = document.getElementsByClassName('login-up')[0],
        loginIn = document.getElementsByClassName('login-in')[0],
        signUp = document.getElementsByClassName('signup')[0],
        signIn = document.getElementsByClassName('signin') [0]; loginUp.onclick =function() {var data1 ='username=' + signUp["username"].value + '&' + 'pass='+ signUp["pass"].value + '&' + 'repeatpass=' + signUp["repeatpass"].value; Var/reg = ^ [\ u4E00 - \ u9FA5] {2, 5} $/; / *if(! reg.test(signUp["username"].value)){
            signUp["username"].classList.add("tips");
            signUp['username'].value()
        } */
        ajax('post'.'/signup',data1,"application/x-www-form-urlencoded");
        xhr.onreadystatechange = function () {
            if(xhr.readyState==4&&xhr.status>=200&&xhr.status<300){
             lettext = JSON.parse(xhr.responseText).code; Console.log (text) // The object returned by the serverif(text == 3){
                   fadeOut();
                   alert("Registration successful")
                   setTimeout(()=>{
                    window.location.reload();
                },1000)
               //    document.getElementsByClassName('login')[0].outerHTML = "<li class='users'><a href='/'>"+signUp["username"].value+ "(= ^ ^ =)" +"</a></li>"
               }else{
                fadeOut();
                alert("User already exists")
               }
               
            }
        }

    }

    loginIn.onclick = function() {var data2 ='username=' + signIn["username"].value + '&' + 'pass=' + signIn["pass"].value;
        ajax('post'.'/signin',data2,"application/x-www-form-urlencoded");
        xhr.onreadystatechange = function () {
            if(xhr.readyState==4&&xhr.status>=200&&xhr.status<300){
             lettext = JSON.parse(xhr.responseText).code; // The object returned by the server console.log(text); // document.getElementsByClassName('login')[0].outerHTML = "<li class='users'><a href='/'>"+signUp["username"].value+ "(= ^ ^ =)" +"</a></li>"
               if(text===1){
                fadeOut();
                // let imgTitle = document.getElementsByClassName('img-title') [0]; // imgTitle.setAttribute('src'.'/images/' + JSON.parse(xhr.responseText).avator)
                setTimeout(()=>{ window.location.reload(); }}, 1000)else if(text === 2){
                   alert('Password error')}else{
                   alert('Account does not exist')
               }
               
            }
        }
      
    }
</script>

Copy the code

  • footer.ejs

</body>
</html>Copy the code

Header is the page header structure, nav is the page navigation bar, login is the login, the registration content, and footer is the page top structure. And you can see that I have a lot of if else judgments in the EJS file, which is to determine whether the user is logged in to render different things based on the session. Now we need our page writing styles: home.css and index.css, respectively

To improve my understanding of native JS, I’m using a lot of native Ajax in my project (obviously jquery wrapped Ajax is better haha), so I’ll wrap a native Ajax request here:

  • ajax.js

    var xhr = null;
    function// var text; // var text; // var text; // var text;if(window.XMLHttpRequest){
            xhr = new XMLHttpRequest();
        }else if(window.ActiveXObject){
            xhr = new ActiveXObject("Microsoft.XMLHTTP");
        }else {
            alert('Your browser does not support Ajax');
            return false;
        }
      
        xhr.onerror = function (err) {
            alert("some err have hapened:",err);
        }
        xhr.open(method,url,true);
        if(method=="post"){
            xhr.setRequestHeader("Content-type",types);
         // xhr.setRequestHeader("Conent-Type".'application/json'"application/x-www-form-urlencoded")
        }
        try{
            setTimeout(()=>{ xhr.send(data); }, 0); }catch(err) { alert("some error have hapened in font:",err);
        }
        return xhr;
    }
Copy the code

Implementation of login registration

With the front-end basic page developed, we can write the backend login interface:

  • Registration: signup. Js

var router = require('koa-router') (); var userModel = require('.. /lib/mysql.js');
var md5 = require('md5') // Register page // post register router. Post ('/signup', async(ctx, next) => {
    console.log(ctx.request.body)
    var user = {
        name: ctx.request.body.username,
        pass: ctx.request.body.pass,
        repeatpass: ctx.request.body.repeatpass
    }
    let flag = 0;
    await userModel.findDataByName(user.name)
        .then(result => {
            console.log(result)
            if(result.length) {// process err console.log('User already exists')
                    ctx.body = {
                        code: 1
                    };               
                
            } else if(user.pass ! == user.repeatpass || user.pass ==' ') {cctx.body = {// should put this logic in the front end code: 2}; }else{ flag = 1; }})if(flag==1){
        let res = await  userModel.insertData([user.name, md5(user.pass + 'asd&$BH&*') ])
       console.log(res.insertId)
        await  userModel.findDataByName(user.name)
        .then((result)=>{
            
          //  var res = JSON.parse(JSON.stringify(result))
            console.log(result[0]['avator'])
            ctx.session.id = res.insertId;
            ctx.session.user=user.name;
            ctx.session.avator = 'default.jpg';
            ctx.body = {
            code: 3
            };
            console.log('Registration successful')
         })
    }

})

module.exports = routerCopy the code

After registration, create a session for the user and add it to the database. Do not forget to add module.exports = router at the end to expose the interface.

Login: signin. Js

var router = require('koa-router') (); var userModel = require('.. /lib/mysql.js')
var md5 = require('md5')

router.post('/signin', async(ctx, next) => {
    console.log(ctx.request.body)
    var name = ctx.request.body.username;
    var pass = ctx.request.body.pass;

    await userModel.findDataByName(name)
        .then(result => {

            var res = JSON.parse(JSON.stringify(result))

            if (name === res[0]['name']&&(md5(pass + 'asd&$BH&*') === res[0]['pass'])) {
                    console.log('Login successful')
                    ctx.body = {
                        code: 1,
                    }
    
                    ctx.session.user = res[0]['name']
                    ctx.session.id = res[0]['id']
                    ctx.session.avator = res[0]['avator']}else if(md5(pass + 'asd&$BH&*') != res[0]['pass']){cx. Body = {code: 2 // Password error}}}).catch(err => {cx. Body = {code: 3 // Account does not exist +} console.log(err => {cx.'Incorrect user name or password! ')

        })

})

module.exports = routerCopy the code

  • Log out: signout.js

var router = require('koa-router') (); var checkUser = require('.. /midllewares/checkUser');
router.get('/signout', async(ctx, next) => {
    ctx.session = null;
    console.log('Exit successfully');
    ctx.body = true;
    ctx.redirect('/home');
    //return;
    //ctx.redirect('/home');
})

module.exports = router
module.exports = routerCopy the code

  • After logging out, we will jump to the home page. After logging in and registering, we need to register the route in our entry file index.js:

// Log in to app.use(require('./routers/signin.js').routes()) // Register app.use(require()'./routers/signup.js').routes()) // Log out of app.use(require()'./routers/signout.js').routes())Copy the code

After the login registration is completed, the following is our home page, home page part we first write the back-end interface:

  • home.js

var router = require('koa-router') (); var userModel = require('.. /lib/mysql');
router.get('/home', async(ctx, next)=>{
    let types;
    console.log(ctx.headers['accept']) // Check whether the article type parameter is included, if not, thentype=all
    if(! ctx.request.querystring){ types ='all'; await userModel.findPostByPage(1) .then(result => { //console.log(result) post = result }) await userModel.findAllPost()  .then(result=>{ postsLength = result.length })if(ctx.session.user){
            await userModel.findDataByName(ctx.session.user)
            .then(res=>{
                ctx.session.avator = res[0]['avator']; }}})else{/ / if with article type parameters according to the type of query the database types = CTX. Request. The querystring. Split ('=') [1]; console.log(types)let _sql = `select * from posts where type = "${types}" limit0, 10 `; await userModel.query(_sql) .then(result=>{ post = result; }) _sql = `select * from postswhere type = "${types}"`;
            await userModel.query(_sql)
                .then(result=>{
                    postsLength = result.length;
                })              
    }
   
    await ctx.render('home', {
    session: ctx.session,
    articles: post,
    type: types,
    postsLength: postsLength,
    postsPageLength: Math.ceil(postsLength / 10),
    
    })
})
Copy the code

The above database operation uses paging query, the first query only 10 data, so that the front end of the article list paging, we pass the total number of articles forward is also for paging query. In addition, we need to write another interface for front-end paging queries:

// Home page page, each output 10 router. Post ('/articles/page', async(ctx, next) => {
    let page = ctx.request.body.page,
        type = ctx.request.querystring.split('=') [1]; console.log(type)
    if(type= ='all'){
        await userModel.findPostByPage(page)
        .then(result=>{
            //console.log(result)
            ctx.body = result   
        }).catch(()=>{
        ctx.body = false; })}else{
        let _sql = `select * from posts where type = "${type}" limit ${(page-1)*10}, 10 `; await userModel.query(_sql) .then(result=>{ ctx.body = result; }).catch(err=>{ console.log(err); ctx.body =false; })}})Copy the code

After the interface is written, we can write the front end:

  • home.ejs

<% include header %>
<% if(! session.user){ %> <link rel="stylesheet" href="/css/login.css">
<% } %>
<link rel="stylesheet" href="/css/home.css">
<script src="./js/ajax.js"></script>
<% include nav %>

<% if(! session.user){ %> <% include login %> <% } %> <header class="article-tap padmar">
    <ul class="tip">
        <li class="all"><a href="javascript:;" class="label label-default"</a></li> <li class="javascript"><a href="javascript:;" class="label label-default">javascript</a></li>
        <li class="html"><a href="javascript:;" class="label label-default">html</a></li>
        <li class="css"><a href="javascript:;" class="label label-default">css</a></li>
        <li class="node"><a href="javascript:;" class="label label-default">node</a></li>
        <li class="other"><a href="javascript:;" class="label label-default"</a></li> </ul> </header> <article class="article-list">
        <% articles.forEach(function(post){ %>
    <div class="content">
        <a href="/personal/<%= post.name %>" target="_blank" title=" <%= post.name %> " class="post-author">
            <span class="author"><%= post.name %></span>
            <span class="times"><%= post.moment %></span> &nbsp; &nbsp; <span class="label label-info"><%= post.type %></span> 
        </a>
        <a href="/articledetail/<%= post.id %>" target="_blank">           
            <h4 class="title"><%= post.title %></h4>
        
        <div class="content-fo">
            <span class="glyphicon glyphicon-heart"></span><span><%= post.likes %></span>
            <span class="glyphicon glyphicon-comment"></span><span><%= post.comments %></span>&nbsp; &nbsp; &nbsp; <span class="pv-item""> < p style =" max-width: 100%; <span class="pv"><%= post.pv %></span></span>
        </div>
    </a>
    </div>
        <% }) %>
        <% if(! postsPageLength){ %> <div class="nothing">
                    <p><span class="glyphicon glyphicon-list-alt"> < / span > < / p > < p > there is nothing < / p > < / div > < %} % > < article > < div style ="width:50%; margin-left:25%; margin-top: 30px; text-align:center;" class="pagination" id="page"></div>	
<script src="/js/pagination.js"></script>
<script>
    window.onload = function () { 
        if('<%= type %>'= = 0) {$('.tip li').eq(0).find('a').attr('class'.'label label-info');
        }else{$(".<%=type%>").find('a').attr('class'.'label label-info');
        }
     }
    	pagination({
			selector: '#page',
			totalPage: '<%= postsPageLength %>',
			currentPage: 1,
			prev: 'Previous page',
			next: 'Next page',
			first: true,
			last: true,
			showTotalPage: trueCount: 2// the number of displays before and after the current page},function(val){// current page $. Ajax ({url:"articles/page? type=<%=type%>".type: 'POST',
				data:{
					page: val
				},
				cache: false,
				success: function (msg) {
					console.log(msg)
					if(msg ! =false) {$('.article-list').html(' ')
						$.each(msg,function(i,val){
							console.log(val.name)
							$('.article-list').append(
                                "<div class='content'>" +
                                    '<a href="/personal/' +  val.name + '" title="' +val.name + '" target="_blank" class="post-author">' +
                                       
                                            "<span class='author'>" + val.name + "</span>" + "   " +
                                            "<span class='times'>" + val.moment + '</span>' + "   " +
                                            '<span class="label label-info">' + val.type + '</span> ' +
                                            "</a>" +
                                                '<a href="/articledetail/' + val.id + '" target="_blank">' +
                                            "<h4 class='title'>" + val.title + "</h4>" +
                                        
                                        "<div class='content-fo'>" +
                                            "<span class='glyphicon glyphicon-heart'></span><span>" + val.likes + "</span>" +
                                            "<span class='glyphicon glyphicon-comment'></span><span>" + val.comments + "     " +
                                            + "<span class='pv'>" + val.pv + "</span>" + '</span>' +
                                       "</div>" +
                                       "</a>" +
                                    "</div>")})}else{
						alert('Page does not exist')}}})})let articleTap = document.getElementsByClassName('article-tap')[0].getElementsByClassName('tip') [0]; articleTap.onclick =function(e){
          //  let e = e||window.e;
            let target = e.target||e.srcElement;
            // let lis = articleTap.getElementsByTagName('li');
            // console.log(lis)
            if(target.nodeName=='A') {let type = target.parentNode.getAttribute('class');
               if(type= = ='all'){
                   window.location.href = '/home';
               }else{
                window.location.href ='/home? type=' + type;
               target.setAttribute('class'.'label label-info');
               }
            }
        }
</script>
</body>
</html>Copy the code

Using the < % if (! Session.user){%> <% include login %> <%} %> <% include login %> A JSON object represents the content of the paging component; An Ajax request implements paging loading.

The article published

The following is to introduce our article publishing function, the article publishing rich text editor I use is wangeditor.min.js, simple and easy to get started, directly introduce the compressed file can be, according to their own needs to configure options, not to say:

  • articles.ejs

<% include header %>
<link rel="stylesheet" href="/css/articles.css">
<script src="./js/wangEditor.min.js"></script>
<script src="./js/ajax.js"></script>
<% include nav %>
<div class="editor1"></div>
<div id="edit">
    <span class="edit-tips"> <span class="num"< span style = "text-align: center"derection" id="derect">
        <option value="0"> Article type </option> <option value="javascript">javascript</option>
        <option value="html">html</option>
        <option value="css">css</option>
        <option value="node">node</option>
        <option value="other"</option> </select> <a href="javascript:;" class="editorUp"><span class="glyphicon glyphicon-share-alt"Word-wrap: break-word! Important; ">< span style =" box-sizing: border-box; color: RGB (74, 74, 74)"article">
    <input type="text" placeholder="Article title" name="title" class="article-title"/>

    <div class="editor2"></div>

</div>

<script src="/js/mask.js"Var E = window.wangeditor var editor = new E();'.editor1'.'.editor2'Var editor = new E(document.getelementById)'editor') )

 editor.customConfig.uploadImgShowBase64 = true/ / use base64 save picture / / editor. The customConfig. UploadImgServer ='/upload'/ / / / upload pictures to the server will image size limit for 3 m editor customConfig. UploadImgMaxSize = 3 * 1024 * 1024; / / limit once upload at most 5 picture editor. CustomConfig. UploadImgMaxLength = 5; editor.customConfig.pasteFilterStyle =true; editor.create(); // Create rich text editor editor.txt.html(' Edit the content of the article....... '); / / statistical word var num = document. GetElementsByClassName ('num')[0],
    editType = document.getElementById('derect'),
    editContent = document.getElementsByClassName('editor2') [0]; editContent.onkeyup =function () {
        num.innerHTML = (editContent.innerHTML.length - 120) + 'word'; } / / ajax upload articles var editorUp = document. The getElementsByClassName ('editorUp')[0],
    editTitle = document.getElementsByClassName('article-title')[0],
    editTips = document.getElementsByClassName('edit-tips') [0]; editorUp.onclick =function () {
      //  console.log(editContent.innerHTML)
    
        if(editTitle.value&&editContent.innerHTML.length>132){
            console.log(editor.txt.text())
            if(editType.value==0){
          fadeout('Please select article type');
            return false;
            }
        let data = { "title": editTitle.value,"content":editor.txt.html(),"type":editType.value };
       let data2 =  JSON.stringify(data);
        
        console.log(data2);
        ajax('post'.'/articles',data2,'application/json');
        xhr.onreadystatechange = function () {
            if(xhr.readyState==4&&xhr.status>=200&&xhr.status<300){
                lettext = xhr.responseText; // Object console.log(text) returned by the serverif(text==1){
                    mask('Successful publication');
                   setTimeout(()=>{
                    window.location.href = '/home'; }, 1500); }else{
                    fadeout('Failed to publish'); }}}}else{
            fadeout('Article content and title cannot be empty');
            // editTips.innerHTML = 'Article content and title cannot be empty';
            // editTips.style.animation = 'fadeout 2s'; }}function fadeout(text){
        editTips.innerHTML = text;
        editTips.style.opacity = 1;
        setTimeout(()=>{
            editTips.style.opacity = 0;
        },1000)
    }

</script>
<% include footer %>Copy the code

If you insert an image, use base64 to save the image, because the content (including the tag) is stored directly in the database as a string. Note that I have introduced a mask.js file, which encapsulates a mask function for the pop-up message. The setTimeout() timer delays deletion to avoid unnecessary labels on the page. It’s kind of like a singleton pattern. Article published background is very simple, I will not explain, now published articles can see the content on our home page.

  • mask.js

function mask(text) {
    
    let div1 = document.createElement('div');
    div1.setAttribute('class'.'login-form-mask');
    let div2 = document.createElement('div');
    div2.setAttribute('class'.'tip-box');
    div2.innerHTML = text;
    document.body.appendChild(div1);
    document.body.appendChild(div2);
    setTimeout(()=>{
        let dd = document.getElementsByClassName('.login-form-mask') [0]; console.log(dd); document.body.removeChild(document.getElementsByClassName('login-form-mask') [0]); document.body.removeChild(document.getElementsByClassName('tip-box') [0]); }}, 1500)Copy the code

Although the article has been published, we have not yet written comments and collection functions, so let’s start with the following. First, we should write our article details page:

  • articledetail.ejs

<% include header %>
<link rel="stylesheet" href="/css/login.css">
<link rel="stylesheet" href="/css/articledetail.css">
<script src="/js/ajax.js"></script>
<% include nav %>
<% if(! session.user){ %> <% include login %> <% } %> <div class="article-list padmar">
    <div class="post-head">
        <span class="post-type label label-info">
            <%= article.type %>
        </span>
        <a href="/personal/<%= article.name %>" class="author">
            <img src="/images/<%= article.avator %>" alt="" class="img-circle">
            <h4><%= article.name %></h4>
            <span><%= article.moment %></span>
        </a>
        <% if(session.user){ %>
            <% if(session.user === article.name){ %>
        <a href="/" class="editself"> <a href="/delete? id=<%= article.id %>" class="delete"> Delete </a> <%} %> <%if(session.user ! = article.name){ %> <span class="follow btn btn-default"> concerned < / span > < %} % > < %} % > < / div > < value > < ul class ="comment-taps">
            <li class="list"><a href="javascript:;" title="Thumb up"><span class="glyphicon glyphicon-heart-empty"></span></a></li>
            <li style="text-align: center; color: gray;" class="likes"><%= article.likes %></li>
            <li class="list"><a href="#comment" title="Comment"><span class="glyphicon glyphicon-comment"></span></a></li>
            <li class="list"><a href="javascript:;" title="Collection"><span class="glyphicon glyphicon-tasks"></span></a></li>
        </ul>
    </aside>
    <h1 class="article-title"><%= article.title %><span class="article-pv glyphicon glyphicon-folder-open">&nbsp; <%= article.pv %></span></h1> <section class="content-section">
            <%- article.content %>
    </section>

</div>

<div class="comment padmar"> < %if(session.user){ %>   
    <div class="create-comment">
         <form >
             <textarea name="comment" id="comment" cols="30" rows="5"></textarea>
             <input type="button" class="btn btn-success" value="Comment">
         </form>
     </div>
     <% } %>
     <% if(commentPageLenght) { %>
     <div class="comment-list">
        <% commentPage.forEach(function(val){ %>
       <div class="comment-item">
         <h4><a href="/personal/<%= val['name'] %>"><img src="/images/<%= val['avator'] %>" alt="<%= val['name'] %>"><%= val['name'] %></a></h4>
         <span><%= val['moment'] %></span>
         <div class="comment-content">
                 <%= val['content'] % > < %if(session.user == val['name']){ %>
                    <a href="javascript:deleteComment('/deleteComment/<%= article.id %>/<%= val['id'] %>',document.getElementsByClassName('deleteComment')[0])" class="deleteComment"> delete < / a > < %} % > < / div > < / div > < %}) % > < / div > < div style ="display:block; width:50%; margin:auto; margin-top: 30px;" class="pagination" id="page"></div>
     <% }else{ %>
        <p style="text-align:center; line-height:80px; fon-size:30px; color:gray"</p> <%} %> < div> <script SRC ="/js/pagination.js"></script>
<script src="/js/mask.js"></script>
<script>
   $(document).ready(function(){
        
        var userName = "<%- session.user %>"
     
		pagination({
			selector: '#page',
			totalPage: '<%= commentPageLenght %>',
			currentPage: 1,
			prev: 'Previous page',
			next: 'Next page',
			first: true,
			last: true,
			showTotalPage:true, count: 2// the number displayed in front of the current page},function(val){var _comment =' '
			$.ajax({
				url: "/article/<%= article.id %>/commentPage".type: 'POST',
				data:{
					page: val
				},
				cache: false,
				success: function (msg) {
					console.log(msg)
					_comment = ' '
					if(msg ! ='error') {$('.comment-list').html(' ')
						$.each(msg,function(i,val){
                
                            _comment +=
                            " <div class='comment-item'>" + 
                            "<h4><a href='/personal/" + val.name + "' >" + "<img src='images/" + val.avator + "' alt=" + val.name + "' >" + val.name + "</a></h4>" +
                            "<span>" + val.moment + "</span>" + 
                            "<div class='comment-content'>" ;
                                if(val.name == userName){
                                    _comment +=  val.content + "<a href='javascript:deleteComment('/deleteComment/<%= article.id%>/'" +  val.id +",this)' class='deleteComment'>" + "Delete < / a >" +
                            "</div>" +
                            "</div>"
                                }else{
                                    _comment +=  val.content + 
                            "</div>" +
                            "</div>"
                                }
                                
                        })
                        console.log( _comment)
						$('.comment-list').append(_comment)
					}else{
						alert('Page does not exist')}}}})}) / / thumb up var postTaps = document. The getElementsByClassName ('comment-taps') [0]; var addHeart = postTaps.getElementsByTagName('li')[0],
        gly = addHeart.getElementsByClassName('glyphicon')[0],
        likes = postTaps.getElementsByClassName('likes') [0]; var collects = postTaps.getElementsByTagName('li') [3];'<% if(session.user){ %>'
        window.onload = function(){
            console.log('<%- session.id%>') 
            '<% if(session.user ! = article.name){ %>'
            var follow = document.getElementsByClassName('follow') [0];if('<%= follow %>'= ='<%= session.id%>'){// follow follow.innerhtml ='Concerned'
            }
            follow.onclick = function() {if(follow.innerHTML==='Focus on the author'){
                    ajax('get'.'/follow/<%= article.uid %>? flag=1',null);
                    xhr.onreadystatechange = function () {
                 if(xhr.readyState==4&&xhr.status>=200&&xhr.status<300){
                    console.log(xhr.responseText)
                     if(xhr.responseText){
                         follow.innerHTML = 'Concerned';
                         console.log('ok')}}}}else{
                    ajax('get'.'/follow/<%= article.uid %>? flag=2',null);
                    xhr.onreadystatechange = function () {
                 if(xhr.readyState==4&&xhr.status>=200&&xhr.status<300){
                     console.log(xhr.responseText)
                     if(xhr.responseText){
                         follow.innerHTML = 'Focus on the author'
                         console.log('ok2')}}}}}'< %} % >'
            console.log('<%= likes %>') 
            if('<%= likes %>'= = ='<%= session.user %>'){// have liked gly.setattribute ('class'.'glyphicon '+'glyphicon-heart');
            }
        }

    addHeart.onclick = function () {
         if(gly.getAttribute('class').indexOf('glyphicon-heart-empty')>-1){
             ajax('get'.'/addHeart/<%= article.id %>? flag=1',null);
             xhr.onreadystatechange = function () {
                 if(xhr.readyState==4&&xhr.status>=200&&xhr.status<300){
                     if(xhr.responseText){
                         gly.setAttribute('class'.'glyphicon '+'glyphicon-heart'); likes.innerHTML = parseInt(likes.innerHTML) + 1; }}}}else{// Unlike ajax('get'.'/addHeart/<%= article.id %>? flag=2',null);
             console.log('<%= likes %>')
             xhr.onreadystatechange = function () {
                 if(xhr.readyState==4&&xhr.status>=200&&xhr.status<300){
                     if(xhr.responseText){
                         gly.setAttribute('class'.'glyphicon '+'glyphicon-heart-empty'); likes.innerHTML = parseInt(likes.innerHTML) - 1; }}}}} // Favorites // Cancel favorites or favoriteslet cotitle = collects.getElementsByTagName('a') [0];if('<%= collects %>'= ='<%= session.user %>'){
                cotitle.setAttribute('title'.'Unbookmark');
    }
        collects.onclick = function () {
            if(cotitle.getAttribute('title') = ='collection'){
              // cotitle.setAttribute('title'.'collection');
        ajax('get'.'/collects/<%= article.id %>? flag=1',null);
        xhr.onreadystatechange = function () {
            if(xhr.readyState==4&&xhr.status>=200&&xhr.status<300){
                if(xhr.responseText){
                   alert('Collection successful');
                   cotitle.setAttribute('title'.'Unbookmark'); }}}}else{
        ajax('get'.'/collects/<%= article.id %>? flag=2',null);
        xhr.onreadystatechange = function() {if(xhr.readyState==4&&xhr.status>=200&&xhr.status<300){
                if(xhr.responseText){
                    alert('Cancelled successfully');
                    cotitle.setAttribute('title'.'collection'); }}}}} // Commentslet comment = document.getElementsByClassName('create-comment')[0].getElementsByClassName('btn') [0];let Allow = true;
        comment.onclick = function() {let comments = document.getElementById('comment');
            if(comments.value==' '){
                comments.value = 'Please enter a comment! ';
                setTimeout(()=>{
                    comments.value = ' '; }, 500).return 0;
            }
            if(! Allow)return 0;
            Allow = false;
            // + '&articleId=' + '<%= article.id %>'
            ajax('post'.'/comment/<%= article.id %>'."comments=" + comments.value,'application/x-www-form-urlencoded');
            xhr.onreadystatechange = function() {if(xhr.readyState==4&xhr.status>=200&&xhr.status<300){
                    if(xhr.responseText){
                        mask('Review success! ');
                        setTimeout(()=>{
                            window.location.reload();
                        },1500)
                        Allow = true; }}}} // Delete commentsfunction deleteComment(url,item){
            ajax('get',url,null);
            console.log(item)
            xhr.onreadystatechange = function() {if(xhr.readyState==4&xhr.status>=200&&xhr.status<300){
                    if(xhr.responseText){
                        console.log('Comment deleted successfully');
                        mask('Deleted successfully! ');
                        console.log(item)
                        console.log(item.parentNode)
                        item.parentNode.parentNode.outerHTML = ' '; }}}}'<% }else{ %>'
    addHeart.onclick = function(){
        alert("Please log in first.")
    }
    collects.onclick = function(){
        alert("Please log in first.")}'< %} % >'
   
</script>
</body>
</html>Copy the code

Article details page is the most complex page, involving the page view, comment, like, collection, follow users and other functions, so you can see the above code is very long, good friends can help me sort out this module aha ha.

Although it looks like a lot of content, but slowly analysis is not difficult, in a word, there is a login to the permission, no login to nothing, according to the session to determine whether the user is logged in and is the author of the article. And send it according to the background information to determine whether the user has thumb up or collect article: the if (‘ % > < % = follow ‘= =’ % > < % = session. The id ‘) if (‘ < % = “likes” % > = = = ‘< % = session. The user % >’) {/ / thumb up.

The better logic here would be to ask the background to determine whether the user has liked or liked when the user clicks the “Like” button, so that the background doesn’t have to deal with so much logic all at once when the page loads. However, for convenience, my logic checks out all the logical information and sends it to the front end at once when the page is requested.

The comments are also paginated, and the loop renders the comment list to determine if the comment was posted by the user:

The < %if(session.user == val['name']){ %>
                    <a href="javascript:deleteComment('/deleteComment/<%= article.id %>/<%= val['id'] %>',document.getElementsByClassName('deleteComment')[0])" class="deleteComment"> Delete </a> <%} %>Copy the code

Take a look at our background code:

/ Single article page router.get('/articledetail/:postId', async(ctx, next) => {
    let comments,
        article,
        pageOne,
        article_pv,
        collects,
        follow,
        likes; 
    let postId = ctx.params.postId;
    console.log(postId,'potid')
    await userModel.findPostById(postId) 
        .then(result => {
            
            article = result;
    
            article_pv = parseInt(result[0]['pv'])
            article_pv += 1
       
        }).catch(err=>{
            console.log(err);
            ctx.body = false;
        })

    await userModel.updatePostPv([article_pv, postId])
    await userModel.findCommentByPage(1,postId)
        .then(result => {
            commentPage = result
        }).catch(err=>{
            console.log(err);
            ctx.body = false;
        })
    await userModel.findCommentById(postId)
        .then(result => {
            comments = result
            console.log('comment', Math.ceil(comments.length/10))
        }).catch(err=>{
            console.log(err);
            ctx.body = false;
        })
    
    if(ctx.session.user! =postId){ await userModel.findFollowByUserId([ctx.session.id,article[0]['uid']])
        .then(result=>{
           // console.log(result[0])
            if(result[0]! =undefined){ // console.log(result[0]) follow = result[0]['uid'];
            }else{
                follow = null;
            }
        }).catch(err=>{
            console.log(err)
        })
    }
    await userModel.findLikeByPostId([ctx.session.user,article[0]['id']])
        .then((result)=>{
            if(result[0]! {// console.log(result[0]) = result[0] {// console.log(result[0])'name']}else{
                likes = null;
            }
        })

    await userModel.findCollectionByData([ctx.session.id,article[0]['id']])
        .then((result)=>{
            if(result[0]! =undefined){ // console.log(result[0]) collects = result[0]['name']
            //    console.log(collects)
            }else{
                collects = null;
            }
        })
    await ctx.render('articledetail', {
        session: ctx.session,
        article: article[0],
        likes: likes,
        collects :collects,
        follow,
        commentLenght: comments.length,
        commentPageLenght: Math.ceil(comments.length/10),
        commentPage:commentPage
    })

})
Copy the code

First, the user enters the page, the page view of the article is increased by one, and then the comment table of the article is queried according to the article ID, followed by the query of the user’s concern table, like table and favorite table. Finally, all the query results are sent to the front end through rendering EJS template.

The following is the background code of each function of our article details page:

// Delete the article route.get ()'/delete',async(ctx,next)=>{
    let articleId = ctx.query.id;
    console.log(articleId)
    await userModel.deletePost(articleId)
        .then(()=>{
            ctx.redirect('/home');
            console.log('Deletion succeeded')
        }).catch(err=>{
            console.log(err)
            ctx.body = false; })}) // Focus on router.get()'/follow/:articleAuthor',async(ctx,next)=>{
    console.log(ctx.params.articleAuthor)
    let flag = ctx.query.flag,
        fwid = ctx.params.articleAuthor;
      
    if(flag==1){
        await userModel.insertFollow([ctx.session.id,fwid])
            .then(()=>{
                console.log('Focus on success');
                ctx.body = true;
            }).catch(err=>{
                console.log(err);
                ctx.body = false; })}else{
        await userModel.deleteFollow([ctx.session.id,fwid])
            .then(()=>{
                console.log('Cancelled successfully') ctx.body = 1; }).catch(err=>{ console.log(err); ctx.body = 0; })}}) // Like router.get()'/addHeart/:articleId',async(ctx,next)=>{
    console.log(ctx.query.flag)
    let flag = ctx.query.flag,
        likes,
        likeId,
        articleId = ctx.params.articleId;
        await userModel.findPostById(ctx.params.articleId)
        .then((result)=>{
            likes = parseInt(result[0]['likes']);
        })
    if(flag==1){
        likes += 1;
        await userModel.insertLikes([ctx.session.user,articleId])
            .then(()=>{
                console.log('thumb up OK');
            }).catch((err)=>{
                console.log(err);
            })

      await userModel.updatePostLike([likes,articleId])
        .then(()=>{
            ctx.body = true;
            console.log('Like success')
        }).catch((err)=>{
            console.log(err)
            ctx.body = false; })}else if(flag = = 2) {/ / cancel praise await userModel. FindLikeByPostId ([CTX. Session. The user, articleId]), then ((result) = > {likeId = result [0] ['id'];
            }).catch(err=>{
                console.log(err);    
            });
        await userModel.poseLikes(likeId)
            .then(()=>{
                console.log('Unliked successfully');
            }).catch((err)=>{
                console.log(err);   
            })
             
        likes -= 1;
        await userModel.updatePostLike([likes,articleId])
            .then(()=>{
                ctx.body = true;
                console.log('Unliked')
            }).catch((err)=>{
                console.log(err)
                ctx.body = false; })}}) // Unbookmark route.get ()'/collects/:articleId',async(ctx,next)=>{
        let flag = ctx.query.flag,
            articleId = ctx.params.articleId,
            collects,
            collectId;
            await userModel.findPostById(articleId)
            .then((result)=>{
                collects = result[0]['collection'];
            }).catch(err=>{
                console.log(err)
            })
            if(flag==1){
                await userModel.insertCollection([ctx.session.id,articleId])
                    .then(()=>{
                        console.log('Collection succeeded 1')
                    }).catch((err)=>{
                        console.log(err)
                    })
                
                    collects++;
                await userModel.updatePostCollection([collects,articleId])
                    .then(()=>{
                        console.log('Collection successful')
                        ctx.body = true;
                    }).catch(err=>{
                        console.log(err)
                        ctx.body = false; })}else{
                await userModel.findCollectionByNaId([ctx.session.id,articleId])
                    .then(result=>{
                        collectId = result[0]['id'];
                    }).catch(err=>{
                        console.log(err)
                    })
                
                await userModel.deleteCollection(collectId)
                    .then(()=>{
                        console.log('Cancelled successfully 2')
                    }).catch(err=>{
                        console.log(err)
                    })
                    
                    collects--;
                await userModel.updatePostCollection([collects,articleId])
                    .then(()=>{
                        console.log('Cancelled successfully 3')
                        ctx.body = true; }).catch(err=>{ console.log(err); })}}) // Comment router.post()'/comment/:articleId', async(ctx,next)=>{
    console.log('test')
    console.log(ctx.request.body.comments)
       let articleId = ctx.params.articleId,
           content = ctx.request.body.comments,
           name = ctx.session.user,
           avator = ctx.session.avator;
         //  moment = moment().format('YYYY-MM-DD HH:mm');
       let comments = 0;
           await userModel.insertComment([name,content,moment().format('YYYY-MM-DD HH:mm'),articleId,avator])
               .then(result=>{
                   console.log(result[0]);
               }).catch(err=>{
                   console.log(err);
               });
           await userModel.findPostById(articleId)
               .then(result=>{
                  // console.log(result[0]);
                  console.log(result[0]['comments'])
                   comments = parseInt(result[0]['comments']) + 1;

               }).catch(err=>{
                   console.log(err);
               });
           await userModel.updatePostComment([comments,articleId])
               .then(result=>{
                   console.log(result);
                   ctx.body = true;
               }).catch(err=>{
                   console.log(err);
                   ctx.body = false; }); }) // Comment page router.post('/article/:articleId/commentPage', async(ctx,next)=>{
     let articleId = parseInt(ctx.params.articleId),
        page = parseInt(ctx.request.body.page);
        console.log(articleId,page)
        await userModel.findCommentByPage(page,articleId)
            .then(result=>{
                ctx.body = result;
                console.log(result);
            }).catch(err=>{
                ctx.body = 'error'; console.log(err); })}) // Delete the comment route.get ()'/deleteComment/:articleId/:commentId', async(ctx,next)=>{
     let commentId = ctx.params.commentId;
     letarticleId = ctx.params.articleId, comment = 0; await userModel.deleteComment(commentId) .then(result=>{ console.log(result); }).catch(err=>{console.log(err); ctx.body =false;
        })
     await userModel.findPostById(articleId)
        .then(result=>{
            console.log(result[0]['comments']);
            comment = parseInt(result[0]['comments']) - 1; }).catch(err=>{ console.log(err); }) await userModel.updatePostComment([comment,articleId]) .then(result=>{ console.log(result); ctx.body =true;
        }).catch(err=>{
            console.log(err);
            ctx.body = false; })})Copy the code



Due to the busy study, the follow-up content will be updated continuously…