Recently, I want to make a Node.js application to initially implement the Node.js skills I have learned. After thinking about it, I would like to make a small chat application. I am afraid that I don’t have enough energy in the later stage of blog (before DOING phper, I gave up 😅 in the middle of doing blog because I thought more and more about its functions). Since I have never done WebSocket related business before, I also want to practice in this aspect. I expect to make a small function with single chat and group chat, simple circle of friends and so on. At the same time, I also want to practice in this aspect because I have never used Mongo before.

In short, this small application is mainly personal exploration of Node “full stack” preliminary test water, but also want to share with me as still vegetables front-end.


Initial functions to be implemented

  • Registration & login required

  • Notify the current user online or offline information to the user and other users

  • Distinguish message types, online information, and messages from clients

  • The message is sent to the target user

  • Initial implementation of a dialogue between two users

    For the moment, assume that you have user user1, and user2 implements the conversation between the two users

Required plug-ins and dependencies

Express 4.17.1

Glob 7.1.6

Mongoose 5.9.14

Ejs 3.1.2

Nodemon 2.0.4

Ws 7.3.0

Front-end Websocket communication implementation

const ws = new WebSocket('ws: / / 192.168.31.200:8000') // Connect to websokect Server
let userInfo = JSON.parse(sessionStorage.getItem("userInfo")); // Current user information
const JSONToString = function(json) {
    return JSON.stringify(json)
}
// Connect to the server and submit the current user information to the server
ws.onopen = (a)= > {
    // ws.send(' I'm online '); // ws.send(' I'm online '
    ws.send(JSON.stringify({
        sender: userInfo.name
    }))
}
// Receive messages from the server
ws.onmessage = (msg) = > {
    // Handle logic based on the MSG type returned by server, notify other users, render messages, etc
    // MSG. MsgType is classified as notice message
    //TODO
}
// Server communication error handling
ws.onerror = err= > {
    console.log(err)
    //TODO
}
// Offline logic
ws.onclose = (a)= > {
    ws.send(JSON.stringify({
         sender: userInfo.name,
         receiver: userInfo.name == 'user1' ? 'test2' : "user1".message: msg
    }))
    console.log('close')}// Send a message to the server. Other events call this method
function sendMsgToServer(msg) {
   // MSG temporary format
   / / {
    // sender: userInfo.name,
    // receiver: receiver,
    // message: MSG Note that there are more messages than the first onopen
    / /}
    ws.send(JSONToString(msg))
}
Copy the code

Setting up the Express Service

The directory structure

—common

|—function.js

—db

|—mongo.conf.js

— routes

|—user.js

—views

|—login.html

|—chating.html

app.js

Introduce basic modules and enable services

//app.js
const express = require('express')
const app = express()
const glob = require("glob");
require('./routes/chats') 
const {
    resolve
} = require('path');


app.listen(3000) 
console.log('Service started')
Copy the code

Configuring the Template Engine

//app.js
/* express.js: configure the engine */
app.set('views'.'./views'); // Add view path
app.engine('html'.require('ejs').renderFile); // Map the EJS template to the ".html" file
app.set('view engine'.'html'); // Set the view engine


/* express.js: configure the engine */
glob.sync(resolve('./views'."**/*.html")).forEach((item, i) = > {
    let htmlRelativePath = item.split('/views') [1]
    let pagePath = htmlRelativePath.replace('.html'.' ')
    app.get(pagePath, function (request, response) {
        let viewPath = pagePath.replace('/'.' ')
        response.render(viewPath)
    })
})
Copy the code

Express parses the configuration required for jSON-formatted request parameters

//app.js
app.use(express.json()) 
app.use(express.urlencoded({
    extended: true
})) 
Copy the code

Add the routing

//app.js
const userRouter = require('./routes/user')
app.use('/', userRouter)
Copy the code

Mongo basic configuration

const mongoose = require('mongoose') / / into the mongoose
const url = "mongodb://localhost:27017/chat"; // Address of the local database
mongoose.connect(url)
const db = mongoose.connection;
db.on('error'.console.error.bind(console.'connection error:'));
db.once('open'.function () {
    console.log("Successful connection to " + url)
});

var Schema = mongoose.Schema 

let user = {
    name: String.password: String.headImg: String
}

var userSchema = Schema(user)
var User = mongoose.model('users', userSchema); // Compile the schema into the model constructor


module.exports = {
    mongoose,
    User
}
// This configuration is still a little rough, later to modify 😥
Copy the code

User module function realization

//user.js
const express = require('express')
const router = express.Router()
const ObjectID = require('mongodb').ObjectID;
const {
    sendJson,
    throwError
} = require('.. /common/function')
const {
    mongoose,
    User
} = require(".. /db/mongo.conf")

// Don't worry about this for the moment, because at this point I only focus on the main function 😅
const checkUserExit = function (params) {
    return new Promise(function (resolve, reject) {
        User.findOne(params, function (error, res) {
            if(res) {
            	resolve(res)                
            }
        })
    })
}

/ / register
router.post('/register'.function (request, response) {
    let params = request.body
    const user = new User(params)
    checkUserExit({
        name: params.name
    }).then(res= > {
        if (res) {
            response.send(sendJson(0.'Username already exists'))}else {
            user.save(function (error, res) {
                if (error) {
                    response.send(throwError())
                } else {
                    response.send(sendJson(1.'Registration successful'))}})}})/ / login
router.post('/login'.function (request, response) {
    let params = request.body
    User.findOne({
        name: params.name
    }, function (error, res) {
        if(! res) { response.send(sendJson(0.'User does not exist'))}else {
            if(params.password ! = res.password) { response.send(sendJson(0.'Wrong username or password'))}else {
                response.send(sendJson(1.'User authentication succeeded',params))
            }
        }
    })
})

module.exports = router
Copy the code

At present, public method encapsulation is not much, or to achieve normal flow

//function.js 
const getJsonStr = function (params) {
     return JSON.stringify(params)
 }

 function sendJson(status, msg, data, params) {
     return getJsonStr({
         status: status,
         message: msg,
         data: data || null})}function throwError(params) {
     return getJsonStr({
         status: 0.msg: 'Service error'})}module.exports.sendJson = sendJson
 module.exports.throwError = throwError
Copy the code

Websocket Server basic implementation

Step 1 Start the service

const webSocket = require('ws'); // Import the WS server module
const ws = new webSocket.Server({
    port: 8000
}); // Create a server with port 8000

const {
    JSONToString,
    getTime
} = require('.. /common/function')
var clients = {}  // Records the information about the current online user
var userList = [] // Only the current online user name is stored
Copy the code

Step 2 Connect to the service and interact with the client

ws.on('connection', (client) => { // Connect to the client
    // The user goes online
    client.on('message', (msg) => {
        let userMsg = JSON.parse(msg)
        let {
            sender,
            receiver,
            message
        } = userMsg
        client.name = sender;
        Observer() // Update basic data in real time
        if (message) {
            // Send data output
            sendMessageToClient(sender, receiver, message)
        } else {
            // Notifications go live
            noticeOnlineOrOffLine(sender, true)}})// An error message was reported
    client.on('error', (err= > {
        if (err) {
            console.log(err)
            // I haven't decided what to do}}))// Offline information
    client.on('close', () = > {console.log('users' + client.name + 'Message service closed')
        noticeOnlineOrOffLine(client.name, false)})})Copy the code

Step 3. Send the MESSAGE to the specified user

/** * * @param {*String} sender * @param {*String} receiver * @param {*Object} message * @param {*Boolean} isOnline */
const sendMessageToClient = function (sender, receiver, message) {
    let messageInfo = {
        sender: sender,
        message: message,
        msgType: "message".timestamp: getTime(),
        userList: userList
    }
    // If the receiver is online, send to it
    if (receiver) {
        messageInfo.receiver = receiver
        clients[receiver].send(JSONToString(messageInfo))
    }
    clients[sender].send(JSONToString(messageInfo))
    console.log('Send a message to a client', JSONToString(messageInfo))
}
Copy the code

Step 4 Notify other users of the current online status

/** * * @param {*String} currentUser * @param {*Boolean} isOnline */
const noticeOnlineOrOffLine = function (currentUser, isOnline) {
    for (var key in clients) {
        // You need to update the friends list of other users
        let noticeUserMessage = {}
        let exceptCurrentUserList = userList.filter(el= >el ! = currentUser) noticeUserMessage =Object.assign(onlineOrOffLineNoticeMsg(key, isOnline), {
            userList: isOnline ? userList : exceptCurrentUserList
        })
        let isOnlineMsg = isOnline ? 'online' : 'off'
        console.log('users: + currentUser + isOnlineMsg + ', message :' + JSONToString(noticeUserMessage))
        clients[key].send(JSONToString(noticeUserMessage))
    }
    if(! isOnline) {deleteclients[currentUser]; }}Copy the code
// Offline message template
const onlineOrOffLineNoticeMsg = function (receiver, isOnline) {
    return {
        receiver: receiver,
        msgType: 'notice'.message: isOnline ? receiver + 'Online' : receiver + 'Offline'.timestamp: getTime()
    }
}
Copy the code

At this point, the main function of this small application is basically perfect, the first step of the long March, ha ha 😁, due to the current just to chat the process go through, even the interface is literally written a few div (and not can not use, manual dog head), you may have found that mongo has not been used to chat in the process 🤣, Because the current mongo enabled posture is not deep enough, I am afraid to dig a hole for myself, and I will do data after further planning.

The main function that needs to be improved in the later stage is group chat (select the kind of fixed users, not everyone’s chat room), followed by the realization of the circle of friends function, involving data storage, graphic processing and so on, but also need to plan and polish, will be further updated.

Finally, due to my limited level, and may use a relatively bad way of business implementation, I hope not to mislead beginners, also please various gods for correction, suggestions and exchanges.

Attached github address tiny-chat