自定义nodeJs脚手架相关配置

Jiafeng

分类: Nodejs 323 0

代码地址:https://github.com/zjiafeng/userinfo/tree/master/nodeJs/16server

1、引入所需模块(模块版本默认最新) cnpm isntall

    "art-template": "^4.13.2",
    "jsonwebtoken": "^8.5.1",
    "koa": "^2.12.0",
    "koa-art-template": "^1.1.1",
    "koa-bodyparser": "^4.3.0",
    "koa-generic-session": "^2.0.4",
    "koa-redis": "^4.0.1",
    "koa-router": "^9.0.1",
    "koa-static": "^5.0.0",
    "md5": "^2.2.1",
    "mongodb": "^3.5.9",
    "nodemailer": "^6.4.8",
    "svg-captcha": "^1.4.0"

2、新建入口文件 配置各个模块

/**引入所需模块 */
const Koa = require('koa'),
    Router = require('koa-router'),
    render = require('koa-art-template'),
    path = require('path'),
    svgCaptcha = require('svg-captcha'),
    session = require('koa-generic-session'),
    Redis = require('koa-redis'),
    bodyParser = require('koa-bodyparser'),
    serve = require('koa-static');

/**实例化 */
const app = new Koa();
const router = new Router();

// 配置koa-art-template 模板引擎
render(app, {
    root: path.join(__dirname, 'views'), // 模板引擎位置
    extname: '.html', // 文件后缀名
    debug: process.env.NODE_ENV !== 'production' //是否开启调试模式
});

//引入子模块
var api=require('./routes/api.js');
var index=require('./routes/index.js');

//配置路由
router.use('/api',api);   /*在模块里面暴露路由并且启动路由*/
router.use(index);

// 路由中间件 (拦截)
app.use(async (ctx, next) => {
    await next();
    if (ctx.status == 404) {
        ctx.body = '404. 抱歉,您访问的资源不存在'
    } else {
        // console.log(ctx.url)
    }
})

app.use(bodyParser()); // 必须放到启动路由前调用
app.use(serve(__dirname + '/public')); //启动koa-static
// 一些session和redis相关配置
app.keys = ['keys', 'keykeys']; 
app.use(session({
    store: new Redis()
}));
app.use(router.routes()).use(router.allowedMethods()); //启动路由 必须放在其他模块后
app.listen(3000);

3、新建公共文件目录 util/conf.js

/**mongodb数据库配置文件 */

// 定义连接数据库的地址 集合名称
const app = {
    dbUrl: 'mongodb://localhost:27017/',
    dbName: 'server01',
    smtp: {
        get host() {
            return 'smtp.qq.com'
        },
        get user() {
            return '2621275142@qq.com' // qq邮箱名
        },
        get pass() {
            return 'jkhrgzgopqkbdibe' // qq邮箱授权码
        },
        // 邮箱验证码
        get code() {
            return () => {
                return Math.random()
                    .toString(16)
                    .slice(2, 6)
                    .toUpperCase()
            }
        },
        // 定义验证码过期时间rules,5分钟内邮箱
        get expire() {
            return () => {
                return new Date().getTime() + 5 * 60 * 1000
            }
        }
    },
    redis: {
        get host() {
            return '127.0.0.1'
        },
        get port() {
            return 6379
        }
    }
}

module.exports = app;

4、新建公共文件目录 util/db.js 对数据操作进行封装

const MongoClient = require('mongodb').MongoClient; //引入mongodb数据库模块
const ObjectID = require('mongodb').ObjectID;
var Config = require('./conf'); //引入配置文件

const client = new MongoClient(Config.dbUrl, { useNewUrlParser: true, useUnifiedTopology: true });

class Db {
    static getInstance() {   /*1、单例  多次实例化实例不共享的问题*/
        if (!Db.instance) {
            Db.instance = new Db();
        }
        return Db.instance;
    }
    constructor() {
        this.dbClient = ''; /*属性 放db对象*/
        this.connect();
    }
    connect() { //连接数据库
        let _that = this;
        return new Promise((resolve, reject) => {
            if (!_that.dbClient) { /**解决数据库多次连接问题 */
                client.connect((err, client) => {
                    if (err) {
                        reject(err)
                    } else {
                        _that.dbClient = client.db(Config.dbName);
                        resolve(_that.dbClient)
                    }
                })
            } else {
                resolve(_that.dbClient)
            }
        })
    }
    find(collectionName, json) {
        return new Promise((resolve, reject) => {
            this.connect().then((db) => {
                var result = db.collection(collectionName).find(json);
                result.toArray(function (err, docs) {
                    if (err) {
                        reject(err);
                        return;
                    }
                    resolve(docs);
                })
            })
        })
    }
    updata(collectionName, jsonOld, jsonNew) {
        return new Promise((resolve, reject) => {
            this.connect().then((db) => {
                db.collection(collectionName).updateMany(jsonOld, {$set:jsonNew}, (err, docs) => {
                    if (err) {
                        reject(err);
                        return
                    }
                    resolve(docs);
                })
            })
        })
    }
    insert(collectionName, json) {
        return new Promise((resolve, reject) => {
            this.connect().then((db) => {
                db.collection(collectionName).insertOne(json, (err, docs) => {
                    if (err) {
                        reject(err);
                        return
                    }
                    resolve(docs)
                })
            })
        })
    }
    remove(collectionName, json) {
        return new Promise((resolve, reject) => {
            this.connect().then((db) => {
                db.collection(collectionName).removeOne(json, (err, docs) => {
                    if (err) {
                        reject(err);
                        return
                    }
                    resolve(docs)
                })
            })
        })
    }
    getObjectId(id) { //mongodb中查询_id把字符串转换为对象
        return new ObjectID(id);
    }
}

module.exports = Db.getInstance();

5、新建公共文件目录 util/tool.js

var md5 = require('md5'), jwt = require('jsonwebtoken');

let tools = {
    md5(str) {
        return md5(str);
    },
    createToken(user_id) {
        const token = jwt.sign(
            {
                user_id: user_id
            },
            'zhangjf',
            {
                expiresIn: '1h'
            }
        )

        return token
    }
}

module.exports = tools;

6、新建公共文件目录 util/checkToken.js jwt验证token

const jwt = require('jsonwebtoken');
//检查token是否过期
module.exports = async ( ctx, next ) => {
    //拿到token
    const authorization = ctx.get('Authorization');
    if (authorization === '') {
        ctx.throw(401, 'no token detected in http headerAuthorization');
    }
    const token = authorization.split(' ')[1];
    let tokenContent;
    try {
        tokenContent = await jwt.verify(token, 'zhangjf');//如果token过期或验证失败,将抛出错误
    } catch (err) {
        ctx.throw(401, 'invalid token');
    }
    await next();
};

7、登录接口模块的具体操作

var router = require('koa-router')();
var tools = require('../../util/tool.js');
var Db = require('../../util/db.js');
var nodeMailer = require('nodeMailer');
var Config = require('../../util/conf'); //引入配置文件
var Redis = require('koa-redis'); 
var checkToken = require('../../util/checkToken');
// 获取redis的客户端
const Store = new Redis().client

// 发送验证码
router.post('/verify', async (ctx, next) => {
    const username = ctx.request.body.username;
    const saveExpire = await Store.hget(`nodemail:${username}`, 'expire') // 拿到过期时间

    console.log(ctx.request.body)
    console.log('当前时间:', new Date().getTime())
    console.log('过期时间:', saveExpire)

    // 检验已存在的验证码是否过期,以限制用户频繁发送验证码
    if (saveExpire && new Date().getTime() - saveExpire < 0) {
        ctx.body = {
            code: -1,
            msg: '发送过于频繁,请稍后再试'
        }
        return
    }

    // QQ邮箱smtp服务权限校验
    const transporter = nodeMailer.createTransport({
        /**
         *  端口465和587用于电子邮件客户端到电子邮件服务器通信 - 发送电子邮件。
         *  端口465用于smtps SSL加密在任何SMTP级别通信之前自动启动。
         *  端口587用于msa
         */
        host: Config.smtp.host,
        port: 587,
        secure: false, // 为true时监听465端口,为false时监听其他端口
        auth: {
            user: Config.smtp.user,
            pass: Config.smtp.pass
        }
    })

    // 邮箱需要接收的信息
    const ko = {
        code: Config.smtp.code(),
        expire: Config.smtp.expire(),
        email: ctx.request.body.email,
        username: ctx.request.body.username
    }

    // 邮件中需要显示的内容
    const mailOptions = {
        from: `"认证邮件" <${Config.smtp.user}>`, // 邮件来自
        to: ko.email, // 邮件发往
        subject: '邀请码', // 邮件主题 标题
        html: `用户${ko.username},您正在注册****,您的邀请码是${ko.code}` // 邮件内容
    }

    // 执行发送邮件
    await transporter.sendMail(mailOptions, (err, info) => {
        if (err) {
            return console.log('发送邮件失败')
        } else {
            console.log(`nodemail:${ko.username}`, 'code', ko.code, 'expire', ko.expire, 'email', ko.email);
            Store.hmset(`nodemail:${ko.username}`, 'code', ko.code, 'expire', ko.expire, 'email', ko.email)
        }
    })

    ctx.body = {
        code: 0,
        msg: '验证码已发送,请注意查收,可能会有延时,有效期5分钟'
    }
})

// 注册接口
router.post('/register', async (ctx) => {
    let { username, password, email, code } = ctx.request.body
    console.log(ctx.request.body);
    if (code) {
        const saveCode = await Store.hget(`nodemail:${username}`, 'code') // 拿到已存储的真实的验证码
        const saveExpire = await Store.hget(`nodemail:${username}`, 'expire') // 过期时间

        console.log(ctx.request.body)
        console.log('redis中保存的验证码:', saveCode)
        console.log('当前时间:', new Date().getTime())
        console.log('过期时间:', saveExpire)

        // 用户提交的验证码是否等于已存的验证码
        if (code === saveCode) {
            if (new Date().getTime() - saveExpire > 0) {
                ctx.body = {
                    code: -1,
                    msg: '验证码已过期,请重新申请'
                }
                return
            }
        } else {
            ctx.body = {
                code: -1,
                msg: '请填写正确的验证码'
            }
            return
        }
    } else {
        ctx.body = {
            code: -1,
            msg: '请填写验证码'
        }
        return
    }

    // 用户名是否已经被注册
    const user = await Db.find('userlist', {"username":username})
    if (user.length) {
        ctx.body = {
            code: -1,
            msg: '该用户名已被注册'
        }
        return
    }
    // 如果用户名未被注册,则写入数据库
    const newUser = await Db.insert('userlist',{
        username,
        password,
        email,
        token: tools.createToken(this.username) // 生成一个token 存入数据库
    })
    // console.log(newUser);
    // 如果用户名被成功写入数据库,则返回注册成功
    if (newUser.ok = 1) {
        ctx.body = {
            code: 0,
            msg: '注册成功',
        }
    } else {
        ctx.body = {
            code: -1,
            msg: '注册失败'
        }
    }
})

// 登录接口
router.post('/login',async (ctx)=>{
    let {username, password} = ctx.request.body;
    result = await Db.find('userlist',{username});
    console.log(result);
    if(!result){
        ctx.body = {
            code: -1,
            msg: '用户名不存在'
        }
    }else if(result[0].password != password){
        ctx.body = {
            code: -1,
            msg: '用户密码错误'
        }
    }else if(result[0].password == password){
        let token = tools.createToken(username)
        // console.log(token)
        try {
            await Db.updata('userlist',{username:username},{token:token});
            ctx.body = {
                code: 0,
                msg: '登录成功',
                data: [
                    {username,token}
                ]
            }
        } catch (error) {
            ctx.body = {
                code: -1,
                msg: '登录失败,请重新登录'
            }
        }
    }
})

// 获取所有用户列表
router.get('/getAlluser',checkToken, async (ctx)=>{
    ctx.body = '用户查找'
    try {
        let newArr = [];
        let result = await Db.find('userlist',{});
        result.map((value,index)=>{
            newArr.push({
                username: value.username,
                email: value.email
            })
        })
        console.log(result);
        ctx.body = {
            code: 0,
            msg: '用户查询成功',
            data: [{newArr}]
        }
    } catch (error) {
        ctx.body = {
            code: -1,
            msg: '查找失败',
            result: err
        }
    }
})
module.exports = router.routes();
  • 0人 Love
  • 0人 Haha
  • 0人 Wow
  • 0人 Sad
  • 0人 Angry
koa、nodejs

作者简介: Jiafeng

共 0 条评论关于 “自定义nodeJs脚手架相关配置”

Loading...