preface

This blog- Node project is based on KOA, adopts the mainstream idea of front – end separation, and provides RESTful API interface

Functional description

Implemented functions

  • The article management
  • Comments on the management
  • Comment response Management
  • Menu management
  • Functional management
  • Link management
  • Message management
  • The project management
  • Motivational Language Management
  • Statistical management
  • Label management
  • Upload File Management
  • User management
  • Role management
  • API Document Management

The node technology

  • Bcryptjs (password encryption)
  • Connect-history-api-fallback (support front-end history mode)
  • Jsonwebtoken (provides token user authentication)
  • Koa (Node framework)
  • Koa-body (parsing JSON and form data)
  • Koa-logger (Logging)
  • Koa-router (Routing Middleware)
  • Koa-static (Static Resource Middleware)
  • Koa2-cors (Cross-domain processing)
  • Moment (Time format processing)
  • Mongoose (mongodb operation)
  • Nodemailer (mail sending)
  • Svg-captcha (Verification Code)

The project structure

- apiDesc API documentation in the form of comments, - ApidOC generates API documents in the form of annotations - config-connect mongodb database connection - Constant constant data - globalHandle Route Interception verification token - JWT Token generation and validation encapsulation-utils Response, database operation request, email sending, captchas and time formatting - baseController encapsulation Resolve class cannot get this - functionOper function list Add, Delete, change, and search - index Import all routing interfaces - link Add, Delete, change, and search - menu Menu function add, delete, modify view and permission list tree structure data - message add, delete, modify view - project add, delete, modify view - replyComment replyComment add, delete, modify view - role add, delete, modify view, obtain and set the role permission list, and import and remove users in batches - Statement Front end Blog displays inspirational statements Add/modify Query - statistics Statistics of visitors, users, articles, and messages by year, month, day, week or period and ranking - tag Article tags add/delete/modify query - upload Upload resources Add/delete, query, and download - user For example, you can add, delete, modify, and search users, register and send emails, and obtain verification codes. N/A Routers Route request n/A Models Specifies the mode type, and defines the field attributes and verification of documents. N/A mongodb Backs up the mongodb data set. Json apidoc file configuration, output name, version, sequence, etc. - app.js initialization and configurationCopy the code

Master file app


const Koa = require('koa')
const app = new Koa()
const json = require('koa-json')
const onerror = require('koa-onerror')
const bodyparser = require('koa-body')
const cors = require('koa2-cors')
// const history = require('connect-history-api-fallback')
const router = require('./routers')
const logger = require("koa-logger"); 
const utils = require('./config/utils')

// cross-domain processing
app.use(
  cors({
      origin: function(ctx) { // Set requests from the specified domain to be allowed
          return The '*'; // only requests for http://localhost:3000 are allowed
      },
      maxAge: 5.// Specifies the validity period of this precheck request, in seconds.
      credentials: false.// Whether cookies can be sent
      allowMethods: ['GET'.'POST'.'PUT'.'DELETE'.'OPTIONS'].// Sets the allowed HTTP request methods
      allowHeaders: ['Content-Type'.'Authorization'.'Accept'].// Set all header fields supported by the server
      exposeHeaders: ['WWW-Authenticate'.'Server-Authorization'] // Set to get additional custom fields
  })
)
app.use(logger());
// error handler
onerror(app)
// Use history mode on the front end
// app.use(history({
// htmlAcceptHeaders: ['text/html', 'application/xhtml+xml']
// }))
// middlewares
app.use(bodyparser({
  multipart:true.jsonLimit: '30mb'.formLimit: '30mb',
}))
app.use(json())
app.use(require('koa-static')(__dirname + '/static'))
app.use(require('koa-static')(__dirname + '/views'))
app.use(require('koa-static')(__dirname + '/apidoc'))

// logger
// app.use(async (ctx, next) => {
// const start = new Date()
// await next()
// const ms = new Date() - start
// console.log(`${ctx.method} ${ctx.url} - ${ms}ms`)
// })

/ / configuration
require("./config/globalHandle")(app)   / / check the token
require("./config/connect")   // MongoDB database connection

// Initialize the route
router(app)
// error-handling
app.on('error'.(err, ctx) = > {
  utils.severErr(err, ctx)
});

module.exports = app

Copy the code

Token interception verification


const JwtUtil = require('./jwt');
const CONSTANT = require('./constant');
const HTTP_CODE = CONSTANT.HTTP_CODE
const utils = require('./utils');
module.exports = app= > {
  let whitelist = [
    '/blogAdmin/user/login'.'/blogAdmin/user/register'.'/blogAdmin/user/getCode'.'/blogAdmin/user/resetPwd'.'/blogAdmin/user/sendEmail'.'/blogAdmin/file/down'.'/blogAdmin/file/down/'.'/blogPage/user/login'.'/blogPage/statistics/tagList'.'/blogPage/statistics/articleArchive'.'/blogPage/statistics/randomMessage'.'/blogPage/statistics/randomArticle'.'/blogPage/article/list'.'/blogPage/article/detail'.'/blogPage/comment/list'.'/blogPage/statement/list'.'/blogPage/message/list'.'/blogPage/project/list'.'/blogPage/link/list'
  ]
  app.use( async(ctx, next)=> {
    let {request:req, response:res} = ctx
    let url = req.url.indexOf('? ') > -1? req.url.split('? ') [0]:req.url
    if(! whitelist.includes(url)) {let token = req.headers.authorization;
        if(token){
            let jwt = new JwtUtil(token);
            let result = jwt.verifyToken();
            req.tokenMessage = result
            // If the test passes next, otherwise return the login information is incorrect
            if (result == 'err') {
               utils.responseClient(ctx, HTTP_CODE.unauthorized, 'Login has expired, please log in again'.null, HTTP_CODE.unauthorized)
            } else {
              awaitnext(); }}else{
            utils.responseClient(ctx, HTTP_CODE.unauthorized, 'Token does not exist'.null, HTTP_CODE.unauthorized)
        }
    } else {
      let token = req.headers.authorization;
      if(token){
        let jwt = new JwtUtil(token);
        let result = jwt.verifyToken();
        req.tokenMessage = result || ' ';
      }
      awaitnext(); }}); }Copy the code

Models Data model

User User model. The model has the default value default and verifies required, VALIDATE, and match. Enumeration enum


const mongoose = require('mongoose');
const moment = require('moment');
const Schema = mongoose.Schema;
const userSchema = new Schema({
  name: {
    type: String.required: true.validate: (val) = > {
      return val.length < 10}},email: {
    type: String.required: true.match: /^[a-z0-9]+([._\\-]*[a-z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.) Tobacco on {1} [a - z0-9] + $/ I
  },
  phone: {
    type: String.match: / ^ 1 [3 4 5 6 7 | | | | | | 8, 9] [0-9] \ d {4, 8} $/
  },
  password: {
    type: String.select: false.// This field information will not be displayed in the returned object data
    required: true
  },
  info: {
    type: String.validate: (val) = > {
      return val.length < 40}},status: {
    type: String.default: '1'.enum: ['0'.'1']},avatarId: {
    type: String.default: ' '
  },
  mark: {
    type: String.default: 'xxxxxx4xxxyxxxxxx'.replace(/[xy]/g.function (c) {
        let r = Math.random() * 16 | 0,
            v = c == 'x' ? r : (r & 0x3 | 0x8);
        return v.toString(16); })},roleId: { 
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Role',},createTime: {
    type: String.default: () = > moment().format('YYYY-MM-DD HH:mm:ss')},updateTime: {
    type: String.default: () = > moment().format('YYYY-MM-DD HH:mm:ss')}}, {versionKey: false.collection: 'user'  // Generate a custom name for the collection, plural by default
  // timestamps: { createdAt: 'createTime', updatedAt: 'updateTime' }
})

module.exports = mongoose.model('User', userSchema);

Copy the code

Routers routing

Index.js loads all files in the Routers one by one

/* * Load all routing interfaces */
const fs = require('fs')
module.exports = (app) = >{
  fs.readdirSync(__dirname).forEach(file= >{
    if(file === 'index.js') {return
    }
    const router = require(`. /${file}`)
    app.use(router.routes()).use(router.allowedMethods())
  })
}

Copy the code

Gets the Article article routing module


const Router = require('koa-router')
const router =  new Router()
const { articleList, articleDetail, articleAdd, articleLike, articleUpdate, articleDel} = require('.. /controllers/article')

const baseUrl = '/blogAdmin'
const basePageUrl = '/blogPage'

router.get(baseUrl + '/article/list', articleList)
router.get(baseUrl + '/article/detail', articleDetail)
router.post(baseUrl + '/article/add', articleAdd)
router.put(baseUrl + '/article/like', articleLike)
router.put(baseUrl + '/article/update', articleUpdate)
router.delete(baseUrl + '/article/del/:id', articleDel)
router.get(basePageUrl + '/article/list', articleList)
router.get(basePageUrl + '/article/detail', articleDetail)

module.exports = router

Copy the code

Controllers control module

BaseController Basic module

Resolve this pointing problem in class


class BaseController {  
  resolve(){  
      return new Proxy(this, {  
          get(target, name) {
              return target[name].bind(target)  
          }  
      })  
  }  
}

module.exports = BaseController

Copy the code

The article module

  • The list query performs fuzzy matching, paging, and sorting based on parameters. If a role is not a super user, only the list of articles belonging to that account can be retrieved

  • Use populate to get the entire object

  • Use inc to decrease or increase the number, inc to decrease or increase the number, inc to decrease or increase the number, inc to pull or $push to remove or add array elements, such as adding article level comments, to modify the corresponding data in the article model


const BaseController = require('./baseController')
const Article = require(".. /models/article");
const Tag = require(".. /models/tag");
const Comment = require(".. /models/comment");
const History = require(".. /models/history");
const ReplyComment = require(".. /models/replyComment");
const CONSTANT = require('.. /config/constant');
const RES_CODE = CONSTANT.RES_CODE
const ROLE_TYPE = CONSTANT.ROLE_TYPE
const utils = require('.. /config/utils');

class ArticleCtl extends BaseController{
  constructor() {
    super()}// List of articles
  async articleList(ctx){
    let req = ctx.request
    let conditions =  utils.blurSelect(req.query)   // fuzzy query
    let pageObj =  utils.pageSelect(req.query)  // paging query
    if(req.url.indexOf('blogPage') = = = -1) {// Data filtering interception and platform judgment
      let userMessage = req.tokenMessage.userMessage
      if(userMessage.roleId ! == ROLE_TYPE.superRole){ conditions['createUser.mark'] = userMessage.mark
      }
    }else{
      conditions.status = '1'
    }
    if(! pageObj.sort){/ / sorting
      pageObj.sort = {
        createTime: '1'}}let count = await Article.countDocuments(conditions)
    let fields = {
      _id: 1.title: 1.description: 1.imgId: 1.status: 1.meta: 1.createUser: 1.createTime: 1,}let docs = await Article.find(conditions, fields, pageObj).populate([
      { path: 'tags'.select: '_id name bgColor' },
      { path: 'createUser'.select: '_id name mark'}])if (docs) {
      let data = {
        count,
        data: docs
      }
      utils.responseClient(ctx, RES_CODE.reqSuccess, "Obtaining article list succeeded", data)
    } else {
      utils.responseClient(ctx, RES_CODE.dataFail, "Failed to get list of articles")}}// Article details
  async articleDetail(ctx){
    let req = ctx.request;
    let tokenMessage = req.tokenMessage;
    let articleId = req.query.id;
    let doc = await Article.findByIdAndUpdate(articleId, {$inc: {'meta.viewTotal': 1}}, {new: true}).populate([
      { path: 'tags'.select: '_id name bgColor' },
      { path: 'linkUser'.select: '_id avatarId name' },
      { 
        path: 'commentList'.populate: {path: 'createUser'.select: '_id avatarId name'}  // The article comment page requires a separate review of the comment list
      },
      { path: 'createUser'.select: '_id avatarId name' },
      { path: 'updateUser'.select: '_id avatarId name'}])if(doc){
      utils.responseClient(ctx, RES_CODE.reqSuccess, "Obtaining article details succeeded", doc)
      if(tokenMessage && tokenMessage.userMessage){
        this.historyHandle({
          userId: tokenMessage.userMessage.id,
          articleId,
          type: '1'}}})else{
      utils.responseClient(ctx, RES_CODE.dataFail, "Failed to obtain article details")}}// Add a new article
  async articleAdd(ctx){
    let req = ctx.request
    let body = req.body
    let conditions =  utils.completeSelect(body)
    letuserMessage = req.tokenMessage.userMessage conditions.tags = conditions.tags? conditions.tags.split(', '):[]
    conditions.createUser = userMessage.id
    conditions.updateUser = userMessage.id
    conditions['meta.txtTotal'] = utils.getPostWordCount(body.content)
    let newArticle = new Article(conditions)
    let docs = await Article.findOne({
      title: body.title
    })
    if (docs) {
      utils.responseClient(ctx, RES_CODE.dataAlready, "Article title already exists")}else {
      let doc = awaitnewArticle.save() doc? utils.responseClient(ctx, RES_CODE.reqSuccess,"Article added success"):utils.responseClient(ctx, RES_CODE.dataFail, "Article added failure")}}// The article was liked
  async articleLike(ctx){
    let req = ctx.request
    let userMessage = req.tokenMessage.userMessage
    let {type, id} = req.body
    if(type === '1') {/ / thumb up
      let docs = await Article.findById(id)
      if(docs){
        let likeTotal = docs.meta.likeTotal + 1;
        // Determine if you have already liked it
        if(docs.linkUser.includes(userMessage.id)){
          return utils.responseClient(ctx, RES_CODE.dataAlready, "Like it already, don't like it twice.")
        }
        docs.linkUser.push(userMessage.id);
        let doc = await Article.findByIdAndUpdate(id, {'meta.likeTotal': likeTotal, linkUser: docs.linkUser}, {new: true})
        if(doc){
          utils.responseClient(ctx, RES_CODE.reqSuccess, "The article was liked.")
          this.historyHandle({
            userId: userMessage.id,
            articleId: id,
            type: '2'})}else{
          utils.responseClient(ctx, RES_CODE.dataFail, "Failed to like the article.")}}else{
        utils.responseClient(ctx, RES_CODE.dataFail, "Failed to get article")}}else{
      // Unlike
      let docs = await Article.findById(id)
      if(docs){
        let likeTotal = docs.meta.likeTotal - 1;
        if(! docs.linkUser.includes(userMessage.id)){return utils.responseClient(ctx, RES_CODE.dataNot, "No likes yet.")}let linkUser = docs.linkUser.filter((item) = >{
          returnitem.toString() ! == userMessage.id })let doc = await Article.findByIdAndUpdate(id, {'meta.likeTotal': likeTotal, linkUser}, {new: true})
        if(doc){
          utils.responseClient(ctx, RES_CODE.reqSuccess, "Unliked successful.")
          await History.findOneAndRemove({
            type: '2'.articleId: id
          })
        }else{
          utils.responseClient(ctx, RES_CODE.dataFail, "Unlike failed")}}else{
        utils.responseClient(ctx, RES_CODE.dataFail, "Failed to get article")}}}// Update the article
  async articleUpdate(ctx){
    let req = ctx.request
    let body = req.body
    let conditions =  utils.completeSelect(body)
    if(conditions.tags){
      conditions.tags = conditions.tags.split(', ')}let doc = await Article.find({
      title: body.title
    })
    if(doc.length === 1 && doc[0]._id.toString() ! = body.id){return utils.responseClient(ctx, RES_CODE.dataAlready, "Article title already exists")
    }
    conditions.updateTime = utils.currentDayDate()
    conditions.updateUser = req.tokenMessage.userMessage.id
    if(body.content){
      conditions['meta.txtTotal'] = utils.getPostWordCount(body.content)
    }
    let docs = await Article.findByIdAndUpdate(body.id, conditions, {new: true}) docs? utils.responseClient(ctx, RES_CODE.reqSuccess,"Updated article successfully"):utils.responseClient(ctx, RES_CODE.dataFail, "Update article failed")}// Delete the article
  async articleDel (ctx){
    let id = ctx.params.id
    let docs = await Article.findByIdAndRemove(id)
    if(docs){
      utils.responseClient(ctx, RES_CODE.reqSuccess, "Article deleted successfully")
      await Comment.deleteMany({articleId: id})
      await ReplyComment.deleteMany({articleId: id})
      await History.deleteMany({articleId: id})
    }else{
      utils.responseClient(ctx, RES_CODE.dataFail, "Failed to delete article")}}// Article operation history
  async historyHandle(historyData){
    let historyResult = await History.findOne(historyData)
    if(historyResult){
      let updateTimeObj = {
        updateTime: utils.currentDayDate()
      }
      await History.findByIdAndUpdate(historyResult._id, updateTimeObj, {new: true})}else{
      newHistory(historyData).save(); }}}module.exports = new ArticleCtl().resolve()

Copy the code

User modules


// Use the map structure to cache random verification codes and mailbox verification codes. Set the validity period to 10 minutes
// Mailbox verification code authentication
const emailCodeList = new Map(a);// Cache email verification code information list
emailCodeFind(ctx) {
  let { email, emailCode } = ctx.request.body;
  let getEmailCode = emailCodeList.get(email)
  let result = false;
  if (getEmailCode) {
    if (getEmailCode.email === email && getEmailCode.code === emailCode) {
      let diffTime = utils.timeDiff(getEmailCode.createTime, utils.currentDayDate())
      if (diffTime <= 10) {
        result = true;
      } else {
        emailCodeList.delete(email)
        utils.responseClient(ctx, RES_CODE.timeOver, "Mailbox verification code has expired." + (diffTime - 10) + 'minutes')}}else {
      utils.responseClient(ctx, RES_CODE.codeFail, "Email verification code error")}}else {
    utils.responseClient(ctx, RES_CODE.codeFail, "Email verification code error")}return result
}

// Use bcrypt to encrypt the password
pwdBcrypt(password) {
  return new Promise((resolve, reject) = > {
    bcrypt.genSalt(10.function (error, salt) {
      bcrypt.hash(password, salt, function (err, hashPassword) {
        //store hash in your password DB.
        if (err) {
          reject(err)
          throw new Error('Encryption failed'); } resolve(hashPassword) }); })})}// Token stores the required user information
async login(ctx){
  let req = ctx.request
  const { name, password } = req.body;
  if (this.randomCodeFind(req)) {
    return utils.responseClient(ctx, RES_CODE.randomFail, "Random captcha error or expiration of 10 minutes")}// Query the database
  let user = await User.findOne({ $or: [{ name }, { email: name }] }, "+password").populate([
    { path: 'roleId'}])if(! user){return utils.responseClient(ctx, RES_CODE.dataFail, "Mailbox or username does not exist")}if (user.status === '0') {
    return utils.responseClient(ctx, RES_CODE.statusFail, "This user is disabled.")}let isMatch = await bcrypt.compare(password, user.password)
  if (isMatch) {
    let userMessage = {
      id: user._id,
      name: user.name,
      avatarId: user.avatarId,
      functionList: user.roleId ? user.roleId.functionList : [],
      roleId: user.roleId ? user.roleId._id : null.mark: user.mark
    };
    let tokenMessage = new JwtUtil({ userMessage });
    let token = tokenMessage.generateToken();
    user.password = null;
    let data = {
      user: user,
      token
      // token: "Bearer " + token
    }
    utils.responseClient(ctx, RES_CODE.reqSuccess, "Login successful", data)
    let accessTime = utils.currentDayDate().split(' ') [0]
    let doc = await AccessUser.find({ userName:  user.name})
    if (doc.length > 0) {
      let timeArr = []
      doc.forEach((item) = >{
        timeArr.push(item.accessTime.split(' ') [0])})if(! timeArr.includes(accessTime)) {let newAccessUser = new AccessUser({
          userName: user.name
        })
        newAccessUser.save()
      }
    } else {
      let newAccessUser = new AccessUser({
        userName: user.name
      })
      newAccessUser.save()
    }
  } else {
    utils.responseClient(ctx, RES_CODE.pwdFail, "Password error")}}Copy the code

Comment on top


// Top comments, sorted by isTop and topUpdateTime
async commentSticky(ctx){
  let req = ctx.request
  let { commentId, isTop} = req.body
  isTop = isTop === '1' || isTop == 'true'?true:false
  let updateResult = await Comment.findByIdAndUpdate(commentId, { isTop, topUpdateTime: utils.currentDayDate() }, { new: true}) updateResult? utils.responseClient(ctx, RES_CODE.reqSuccess,"Top replacement successful", updateResult):utils.responseClient(ctx, RES_CODE.dataFail, "Top replacement failed")}Copy the code

Menu module


// Menu function Returns flat data structure to tree data structure
treeData(source){
  let cloneData = JSON.parse(JSON.stringify(source))    // Deep clone source data
  return cloneData.filter(father= >{               
    let branchArr = cloneData.filter(child= >father._id == child.parentId)    // Returns the subseries set for each term
    branchArr.length>0 ? father.children = branchArr : ' '   // If there are children, add a children attribute to the parent and assign it to the parent
    return father.parentId=='0';      // return to the first layer})}// Intercepts data permissions
let userMessage = req.tokenMessage.userMessage
if(! userMessage.functionList.includes('5e834ff2fb69305aa091e836')) {return utils.responseClient(ctx, RES_CODE.dataFail, "No permission for this function")}Copy the code

The statistics module


// Statistics by day, week, month, year or custom time period
// Count the number of articles
async articleStatistics(ctx){
  let req = ctx.request
  let { type, startTime, endTime } = req.query
  startTime = startTime || utils.currentDayDate('day')
  let options = {}
  if (type === 'day') {
    options.startTime = startTime
    options.endTime = startTime
    options.substrData = ["$createTime".11.2]}else if (type === 'week') {
    let weekArr = utils.weekFirstLast(startTime)
    options.startTime = weekArr[0]
    options.endTime = weekArr[1]
    options.substrData = ["$createTime".0.10]}else if (type === 'month') {
    let monthArr = utils.monthFirstLast(startTime)
    options.startTime = monthArr[0]
    options.endTime = monthArr[1]
    options.substrData = ["$createTime".0.10]}else if (type === 'year') {
    let yearArr = utils.yearFirstLast(startTime)
    options.startTime = yearArr[0]
    options.endTime = yearArr[1]
    options.substrData = ["$createTime".5.2]}else {
    options.startTime = startTime
    options.endTime = endTime
    options.substrData = ["$createTime".0.10]}let data = await Article.aggregate([
    {
      $match: {
        status: '1'.createTime: { '$gte': options.startTime + '00:00:00'.'$lt': options.endTime + "23:59:59"}}}, {$project: {
        hour: {
          $substr: options.substrData
        }
      },
    },
    {
      $group: {
        _id: "$hour".count: { $sum: 1}}}, {$sort: { _id: 1} } ]) data? utils.responseClient(ctx, RES_CODE.reqSuccess,"Get article statistics", data):utils.responseClient(ctx, RES_CODE.dataFail, "Failed to get article statistics")}$time = 1970-01-0t00:00:00.000z; $time = 1970-01-0t00:00:00.000z
async articleList(ctx){
  let req = ctx.request
  let { type } = req.query
  let date = null
  if(type === 'day'){
    date = {
      $substr: ["$createTime".11.2]}}else if(type === 'month'){
    date = {
      $substr: ["$createTime".5.2]}}else{
    date = {
      $isoDayOfWeek: {
        $dateFromString: {
          dateString: "$createTime"}}}}let data = await Article.aggregate([
    {
      $match: {
        status: '1'}}, {$project: {
        date,
        createTime: 1}}, {$group: {
        _id: "$date".count: { $sum: 1}}}, {$sort: { count: -1} } ]) data? utils.responseClient(ctx, RES_CODE.reqSuccess,"Get article ranking statistics", data):utils.responseClient(ctx, RES_CODE.dataFail, "Failed to obtain article ranking statistics")}$lookup query, $group field must be placed in _id
async tagList(ctx){
  let data = await Article.aggregate([
    {
      $match: {
        status: '1'}}, {$lookup: {
        from: 'tag'.localField: 'tags'.foreignField: '_id'.as: 'articleTag'}}, {$unwind: "$articleTag"
    },
    {
      $project: {
        title: 1.'articleTag._id': 1.'articleTag.name': 1.'articleTag.description': 1.'articleTag.bgColor': 1}}, {$group: {
        _id: {
          "id": "$articleTag._id"."name": "$articleTag.name"."description": "$articleTag.description"."bgColor": "$articleTag.bgColor"
        },
        count: { $sum: 1}}}, {$sort: { count: -1} } ]) data? utils.responseClient(ctx, RES_CODE.reqSuccess,"Get article tag statistics", data):utils.responseClient(ctx, RES_CODE.dataFail, "Failed to get article tag statistics")}Copy the code

File upload and download


// File upload
async uploadFile(ctx){
  let file = ctx.request.files.file
  if(file){
    // Create a readable stream
    const reader = fs.createReadStream(file.path);
    let timeValue = utils.timeValue() 
    let filePath = path.join(__dirname, '.. /static/img/') + `${timeValue}.${file.type.split('/') [1]}`;
    let imgUrl = ctx.protocol + ': / /'+ ctx.headers.host + '/blogAdmin/file/down? downId=' + timeValue
    // Create a writable stream
    const upStream = fs.createWriteStream(filePath);
    // The readable stream writes to the writable stream through the pipe
    reader.pipe(upStream);
    const newSource = new Source({
      sourceId: timeValue,
      name: file.name,
      type: file.type,
      url: imgUrl
    })
    let source = awaitnewSource.save() source? utils.responseClient(ctx, RES_CODE.reqSuccess,"Upload successful", source):utils.responseClient(ctx, RES_CODE.dataFail, "Upload failed")}else{
    utils.responseClient(ctx, RES_CODE.dataFail, "Failed to get file")}}// File download
async downFile(ctx) {
  let req = ctx.request
  let sourceId = req.query.downId
  let source = await Source.findOne({
    sourceId
  })
  if(source){
    let pathUrl = `static/img/${source.sourceId}.${source.type.split('/') [1]}`
    // Download the file
    ctx.attachment(pathUrl)
    await send(ctx, pathUrl)
  }else{
    utils.responseClient(ctx, RES_CODE.dataFail, "Failed to obtain resource")}}Copy the code

instructions

  • The default super administrator account is test and the password is 123456

  • Nodeman is used in the development environment. If an error occurs, the application will be disconnected. Pm2 is used in the production environment

  • NPM install [email protected] -g (0.26.0 test request cannot be hidden), NPM run apidoc generates apidOC file, local access is http://localhost:3000/, Online for sdjblog. Cn: 3000 /

Building installation

Mysql > install mongodb, install mongodb according to the tutorial download, and then configure: \mongod --dbpath D:\mongodb\data\db, dbpath D:\mongodb\data\db, Check whether the installation is successful. 3. mongod -config " D:\mongodb\bin\mongod.cfg" -install -serviceName "MongoDB" D:\mongodb\bin run./mongo or configure system variables to create a superuser using mongo: Use admin db.createuser ({user:"admin", PWD :"123456",roles:["root"]}) Use blogNode db.createuser ({user:"admin", PWD :"123456",roles:[{role:"dbOwner",db:"blogNode"}]}) (dbOwner: CFG/mongod. CFG/mongod. CFG/mongod. NPM install -g nodemon install NPM install -g nodemon install NPM install NPM run dev, default port 3000Copy the code

Project Address:

Front Desk Presentation: https://gitee.com/sdj_work/blog-page (Vue/Nuxt/ UNI-app)

Management background: https://gitee.com/sdj_work/blog-admin (Vue/React)

Back-end Node: https://gitee.com/sdj_work/blog-node (Express/Koa)

Blog: https://sdjBlog.cn/

Project series:

Vue+Nuxt blog display

Vue+ UniAPP blog display

Vue+ElementUI backstage blog management

Node + KOA + mongodb blog interface development

Node + Express + mongodb blog interface development