该博客是本人5年前写的,主要技术栈用的是node+mongodb+swig模板引擎+vue+boot+微信小程序,通过小程序的引流,网站的用户量达到3000+,插件上传俩量达到4000+ 个。
可以实现用户注册,用户登陆,私人博客文章展示,插件上传,插件功能的预览和下载,技术书籍的电子版,前端技术相关的视频学习等功能。
相关的代码和安装流程,可以查阅本人的github,代码已经开源,下载安装就可部署此网站和管理系统。
主要实现的功能如下:
后台管理系统
- 网站用户管理
- 网站菜单管理(包括pc和h5)
- 内容管理(博客的创建、列表、编辑、删除)
- 视频管理(视频的上传、编辑、删除)
- 菜单管理(jq22功能网站)
- 资源管理(jq22功能网站)(资源的上传、pdf文件书籍的上传、zip插件的上传)
网站
- 博客列表
- 登陆入口
- 热点文章
- 静态资源链接汇总展示
- 视频列表页
- 前端资料库(类jq22),资源的列表页、资源的功能演示页、资源的下载页
手机端网站(vue)
- 纯博客网站
微信小程序
- 纯博客微信小程序
功能页面
项目要点
项目目录
项目的启动文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| var express = require('express');
var mongoose = require('mongoose');
var app = express();
app.use('/public',express.static(__dirname + "/public")); app.use('/static/css',express.static(__dirname + "/views/main/dist/static/css")); app.use('/static/js',express.static(__dirname + "/views/main/dist/static/js")); app.use('/ueditor',express.static(__dirname + "/ueditor"));
mongoose.connect('mongodb://localhost:27017/data',function(err){ if(err){ console.log("数据库连接失败"); }else{ console.log("数据库连接成功"); console.log("please open localhost:8080") app.listen(8080); } });
|
创建表、读取数据
1.模型类
1 2 3 4 5 6 7 8 9
|
var mongoose = require('mongoose');
var contentSchema = require('../schemas/contents');
module.exports = mongoose.model('Content',contentSchema);
|
2.表结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
|
var mongoose = require('mongoose'); var Schema = mongoose.Schema;
module.exports = new mongoose.Schema ({ category:{ type: mongoose.Schema.Types.ObjectId, ref:'Category' }, title: String, posted:{ type:Boolean, default:true }, description: { type: String, default:'' }, content: { type: String, default:'' }, comments:{ type:Array, default:[] }, views:{ type:Number, default:0 }, startTime: { type:Date, default:new Date() } })
|
3.数据的读取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| Category.find().then(function(categories){ Content.count().then(function(count) { pages = Math.ceil(count / limte); page = Math.min(page, pages); page = Math.max(page, 1); var skip = (page - 1) * limte; Content.find({ posted: true }).sort({_id: -1}).limit(limte).skip(skip).populate('category').then(function (contents) { var deviceAgent = req.headers["user-agent"].toLowerCase(); var agentID = deviceAgent.match(/(iphone|ipod|ipad|android)/); if(agentID){ res.render('webApp/index.html') }else{ res.render('main/index.html', { categories:categories, userInfo: req.userInfo, contents: contents, page: page, count: count, pages: pages, limte: limte }); }
}) }) })
|
swig模板引擎使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <!--文章的列表--> <ul class="contentList"> {% for content in contents %} <a href="/contentInfo?id={{content._id.toString()}}"> <li id="{{content._id.toString()}}"> <h3>{{content.title}}</h3> <div class="contentInfo"> <i class="fa fa-navicon"></i><span>{{content.category.name}}</span><i class="fa fa-clock-o"></i><span>{{content.startTime|date('Y年m月d日 H:i:s', -480, 'CCT')}}</span> <nav class="eyeNum"><i class="fa fa-eye"></i><span>{{content.views}}</span></nav> </div> <div class="content_description"> <p>{{content.description}}</p> </div> </li> </a> {% endfor%} </ul>
|
功能点实现
1.图片上传
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| router.post('/apply/upload',function(req,res,next){ var cacheFolder = './public/images/uploadcache/'; var currentUser = req.userInfo.username; var userDirPath =cacheFolder+ currentUser; if (!fs.existsSync(userDirPath)) { fs.mkdirSync(userDirPath); } var form = new formidable.IncomingForm(); form.encoding = 'utf-8'; form.uploadDir = userDirPath; form.keepExtensions = true; form.maxFieldsSize = 2 * 1024 * 1024; form.type = true; var displayUrl; form.parse(req, function(err, fields, files) { if (err) { res.send(err); return; } var extName = ''; console.log(files.upload.type) switch (files.upload.type) { case 'image/jpeg': extName = 'jpg'; break; case 'image/jpeg': extName = 'jpg'; break; case 'image/png': extName = 'png'; break; case 'image/x-png': extName = 'png'; break; } if (extName.length === 0) { res.send({ code: 202, msg: '只支持png和jpg格式图片' }); return; } else { var avatarName = '/' + Date.now() + '.' + extName; var newPath = form.uploadDir + avatarName; displayUrl = currentUser; fs.renameSync(files.upload.path, newPath); newPath = newPath.substring(1); res.send({ code: 200, msg: displayUrl, url: "https://www.songyanbin.com" + newPath }); } }); })
|
2.zip解压—实现zip插件浏览器预览插件效果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| router.post('/apply/addZip',function(req,res,next){ console.log(res) var cacheFolder = './public/source/zip/'; var currentUser = req.userInfo.username; var userDirPath =cacheFolder+ currentUser; if (!fs.existsSync(userDirPath)) { fs.mkdirSync(userDirPath); } var form = new formidable.IncomingForm(); form.encoding = 'utf-8'; form.uploadDir = userDirPath; form.keepExtensions = true; form.maxFieldsSize = 2 * 1024 * 1024; form.type = true; var displayUrl; form.parse(req, function(err, fields, files) { if (err) { res.send(err); return; } var extName = ''; console.log("类型:") console.log(files.upload.type) switch (files.upload.type) { case 'application/zip': extName = 'zip'; break; } if (extName.length === 0) { res.send({ code: 202, msg: '只支持zip资源上传' }); return; } else { var avatarName = '/' + Date.now() + '.' + extName; var newPath = form.uploadDir + avatarName; displayUrl = currentUser; fs.renameSync(files.upload.path, newPath); var jyName = avatarName.substring(1,avatarName.length - 4); newPath = newPath.substring(2); var path = userDirPath.substring(2) + '/'; console.log(userDirPath) Minizip.unzip(newPath, path + jyName, function(err) { if (err){ console.log(err); }else{ console.log('unzip successfully.'); } }); res.send({ code: 200, msg: displayUrl, url: "https://www.songyanbin.com" + '/' + newPath, lookUrl: "https://www.songyanbin.com" + '/' + path + jyName + '/' }); } }); })
|
3.用户登录注册实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
|
router.post('/user/register',function(req,res,next){ var username = req.body.username; var password = req.body.password; var repassword = req.body.repassword; var time = req.body.time; console.log("注册的时间:",time) console.log(username) console.log(password) console.log(repassword) if(username == ''){ responseData.code = 01; responseData.message = '用户名不能为空'; res.json(responseData); return; } if(username.length > 10){ responseData.code = 02; responseData.message = '用户名不能超过10个字符'; res.json(responseData); return; } if(password.length < 6){ responseData.code = 03; responseData.message = '密码不能少于6位'; res.json(responseData); return; } if(password == ''){ responseData.code = 04; responseData.message = '密码不能为空'; res.json(responseData); return; } if(repassword != password){ responseData.code = 05; responseData.message = '两次输入的密码不一致'; res.json(responseData); return; } User.findOne({ username:username }).then(function(userInfo){ if(userInfo){ responseData.code = 4; responseData.message = '用户名重复'; res.json(responseData); return; } var user = new User({ username:username, password:password, time:time }); return user.save(); }).then(function(newUserInfo){ responseData.code = 8; responseData.message = '注册成功'; res.json(responseData); }) })
router.post('/user/login',function(req,res,next){ var username = req.body.username; var password = req.body.password; if(username == ''){ responseData.code = 5; responseData.message = '用户名不能为空'; res.json(responseData); return; } if(password == ''){ responseData.code = 6; responseData.message = '密码不能为空'; res.json(responseData); return; } User.findOne({ username:username, password:password }).then(function(userInfo){ if(!userInfo){ responseData.code = 7; responseData.message = '用户名不存在,请先注册'; res.json(responseData); return; } responseData.code = 9; responseData.message = '登陆成功'; responseData.data = { username : userInfo.username }
res.cookie("account", username);
req.cookies.set('userInfo',JSON.stringify({ _id : userInfo._id, username : userInfo.username })); res.json(responseData); return; }) })
|
4.微信小程序注册
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
|
router.get('/user/WeChat/register',function(req,res,next){ var header = req.query.header; var username = req.query.username; var appid = req.query.appid; var secret = req.query.secret; var jscode = req.query.jscode; var openid; https.get('https://api.weixin.qq.com/sns/jscode2session?appid='+appid+'&secret='+secret+'&js_code='+jscode+'&grant_type=authorization_code', function(wxRes) { wxRes.on('data', function(d) { openid = JSON.parse(d.toString()).openid console.log(openid) Wechatusers.findOne({ openid:openid }).then(function(userInfo){ if(userInfo){ responseData.code = 4; responseData.message = '用户名重复'; responseData.data = userInfo; console.log("00000") console.log(responseData) res.json(responseData); return; } console.log("别走了") var wechatusers = new Wechatusers({ openid:openid, header:header, username:username }); return wechatusers.save().then(function(userInfo){ responseData.code = 38; responseData.message = '注册成功'; responseData.data = userInfo; res.json(responseData); }) }) }); }).on('error', function(e) { console.error(e); }); })
|
项目部署-[使用博客代码做你的博客网站]
1.安装node环境(node版本为12)
2.安装mongodb数据库,安装教程如下:
windows: http://shuy.cc/2023/06/30/mongodbWindow/
mac: http://shuy.cc/2020/11/12/mac%E6%9C%AC%E5%9C%B0%E6%90%AD%E5%BB%BAMongoDB/
3.在项目目录下面启动shell,运行npm i,安装项目相关依赖
4.运行node app.js,启动项目。