├── .gitignore
├── resources
├── blog.png
├── show.gif
├── user.png
├── blog_o.png
├── index.png
├── login.png
├── project_str.png
└── blog.sql
├── static
├── admin
│ ├── img
│ │ ├── add.png
│ │ ├── sign_bg.png
│ │ ├── login_bg.png
│ │ ├── user_icon.png
│ │ ├── login_button.png
│ │ └── password_icon.png
│ ├── js
│ │ ├── blog.js
│ │ ├── users.js
│ │ ├── login.js
│ │ └── top.js
│ └── css
│ │ ├── index.css
│ │ ├── login.css
│ │ ├── top.css
│ │ ├── blog.css
│ │ ├── users.css
│ │ └── reset.css
└── upload
│ ├── 68efe455c4ecda9517d644751c749849.jpg
│ ├── 872ce9c96a353d082f20f20575af158f.jpg
│ └── c630494b49e1e54a713f2e4f275edd2e.jpg
├── .idea
└── vcs.xml
├── router
├── web
│ └── index.js
└── admin
│ ├── index.js
│ ├── login.js
│ ├── blog.js
│ └── users.js
├── lib
└── common.js
├── 数据字典.txt
├── views
└── admin
│ ├── index.ejs
│ ├── common
│ └── top.ejs
│ ├── login.ejs
│ ├── users.ejs
│ └── blog.ejs
├── package.json
├── server.js
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .idea/
--------------------------------------------------------------------------------
/resources/blog.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nealyang/ejs-express-mysql/HEAD/resources/blog.png
--------------------------------------------------------------------------------
/resources/show.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nealyang/ejs-express-mysql/HEAD/resources/show.gif
--------------------------------------------------------------------------------
/resources/user.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nealyang/ejs-express-mysql/HEAD/resources/user.png
--------------------------------------------------------------------------------
/resources/blog_o.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nealyang/ejs-express-mysql/HEAD/resources/blog_o.png
--------------------------------------------------------------------------------
/resources/index.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nealyang/ejs-express-mysql/HEAD/resources/index.png
--------------------------------------------------------------------------------
/resources/login.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nealyang/ejs-express-mysql/HEAD/resources/login.png
--------------------------------------------------------------------------------
/resources/project_str.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nealyang/ejs-express-mysql/HEAD/resources/project_str.png
--------------------------------------------------------------------------------
/static/admin/img/add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nealyang/ejs-express-mysql/HEAD/static/admin/img/add.png
--------------------------------------------------------------------------------
/static/admin/img/sign_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nealyang/ejs-express-mysql/HEAD/static/admin/img/sign_bg.png
--------------------------------------------------------------------------------
/static/admin/img/login_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nealyang/ejs-express-mysql/HEAD/static/admin/img/login_bg.png
--------------------------------------------------------------------------------
/static/admin/img/user_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nealyang/ejs-express-mysql/HEAD/static/admin/img/user_icon.png
--------------------------------------------------------------------------------
/static/admin/img/login_button.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nealyang/ejs-express-mysql/HEAD/static/admin/img/login_button.png
--------------------------------------------------------------------------------
/static/admin/img/password_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nealyang/ejs-express-mysql/HEAD/static/admin/img/password_icon.png
--------------------------------------------------------------------------------
/static/upload/68efe455c4ecda9517d644751c749849.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nealyang/ejs-express-mysql/HEAD/static/upload/68efe455c4ecda9517d644751c749849.jpg
--------------------------------------------------------------------------------
/static/upload/872ce9c96a353d082f20f20575af158f.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nealyang/ejs-express-mysql/HEAD/static/upload/872ce9c96a353d082f20f20575af158f.jpg
--------------------------------------------------------------------------------
/static/upload/c630494b49e1e54a713f2e4f275edd2e.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nealyang/ejs-express-mysql/HEAD/static/upload/c630494b49e1e54a713f2e4f275edd2e.jpg
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/router/web/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Nealyang on 17/2/25.
3 | */
4 | const express = require('express');
5 |
6 | module.exports = function () {
7 | var router = express.Router();
8 | router.get('/',function (req,res) {
9 | res.send('这是前端页面');
10 | });
11 | return router;
12 | };
--------------------------------------------------------------------------------
/lib/common.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Nealyang on 17/2/25.
3 | * 自己的库
4 | */
5 | var crypto = require('crypto');
6 |
7 | module.exports = {
8 | MD5_SUFFIX : 'JDSAIOEUQOIoieuoiqv#$%^&dhfja)(* %^&FGHJfyuieyfhfhak(^.^)YYa!!\(^o^)/Y(^o^)Y(*^__^*)ヘ|・∀・|ノ*~●',
9 | md5:function (pwd) {
10 | var md5 = crypto.createHash('md5');
11 | return md5.update(pwd).digest('hex');
12 | }
13 | };
--------------------------------------------------------------------------------
/数据字典.txt:
--------------------------------------------------------------------------------
1 | 1、管理员 admin_table
2 | ID int
3 | username varchar(32)
4 | password varchar(32)
5 |
6 | 2、用户 user_table
7 | ID int
8 | username varchar(32)
9 | email varchar(32)
10 | pic_header varchar(300)
11 |
12 | 3、博客 blog_table
13 | ID int
14 | title varchar(200)
15 | summary varchar(500)
16 | author varchar(32)
17 | href varchar(300)
--------------------------------------------------------------------------------
/views/admin/index.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 后台管理-首页
6 |
7 |
8 | <% include common/top.ejs%>
9 |
10 |
11 | 后台管理主要实现的是:博客的删除修改和新建(涉及到数据的CRUD),以及用户管理(涉及到图片上传).
12 | 对于前后端分离,前面登录就已经给出了实例~
13 |
14 |
15 |
16 | 喜欢的朋友可以star下,初涉Node,深知代码烂如麻,求指点,求轻喷~
17 |
18 |
19 |
20 | Nodejs技术交流群:209530601
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/router/admin/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Nealyang on 17/2/25.
3 | */
4 | const express = require('express');
5 |
6 | module.exports = function () {
7 | var router = express.Router();
8 | router.use(function (req,res,next) {
9 | if(!req.session['user_id'] && req.url != '/login'){
10 | res.redirect('/admin/login');
11 | }else{
12 | next();
13 | }
14 | });
15 | router.use('/login',require('./login')());
16 | router.use('/blog',require('./blog')());
17 | router.use('/users',require('./users')());
18 | router.get('/',function (req,res) {
19 | res.render('admin/index.ejs');
20 | });
21 | return router;
22 | };
--------------------------------------------------------------------------------
/views/admin/common/top.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 后台管理-首页
6 |
7 |
8 |
9 |
10 |
11 |
12 |
28 |
29 |
--------------------------------------------------------------------------------
/views/admin/login.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 后台管理员登录
6 |
7 |
8 |
9 |
10 |
11 |
12 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/static/admin/js/blog.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Nealyang on 17/2/24.
3 | */
4 | $(document).ready(function () {
5 | var add_button = $('.add_button');
6 | var scroll_img = $('.add_button img');
7 | var add_form_container = $('.add_form');
8 | add_button.mouseenter(function () {
9 | $(this).css({
10 | 'left':'0'
11 | });
12 | scroll_img.css({
13 | 'right':'30px',
14 | 'transform':'rotate(-90deg)'
15 | });
16 | });
17 |
18 | add_button.mouseleave(function () {
19 | $(this).css({
20 | 'left':'-50px'
21 | });
22 | scroll_img.css({
23 | 'right':'0',
24 | 'transform':'rotate(0deg)'
25 | });
26 | });
27 |
28 | add_button.click(function () {
29 | add_form_container.css({
30 | 'display':'flex'
31 | });
32 | });
33 | $('.add_form img').click(function () {
34 | add_form_container.css({
35 | 'display':'none'
36 | });
37 | })
38 |
39 | });
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ejs-express-mysql",
3 | "version": "1.0.0",
4 | "description": "ejs+express+mysql实现的后台管理",
5 | "main": "server",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/Nealyang/ejs-express-mysql.git"
12 | },
13 | "keywords": [
14 | "node",
15 | "express",
16 | "ejs",
17 | "mysql"
18 | ],
19 | "author": "Nealyang",
20 | "license": "ISC",
21 | "bugs": {
22 | "url": "https://github.com/Nealyang/ejs-express-mysql/issues"
23 | },
24 | "homepage": "https://github.com/Nealyang/ejs-express-mysql#readme",
25 | "devDependencies": {
26 | "body-parser": "^1.16.1",
27 | "consolidate": "^0.14.5",
28 | "cookie-parser": "^1.4.3",
29 | "cookie-session": "^2.0.0-beta.1",
30 | "ejs": "^2.5.6",
31 | "express": "^4.14.1",
32 | "express-router": "0.0.1",
33 | "express-static": "^1.0.3",
34 | "multer": "^1.3.0",
35 | "mysql": "^2.13.0"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/static/admin/js/users.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Nealyang on 17/2/25.
3 | */
4 | /**
5 | * Created by Nealyang on 17/2/24.
6 | */
7 | $(document).ready(function () {
8 | var add_button = $('.add_button');
9 | var scroll_img = $('.add_button img');
10 | var add_form_container = $('.add_form');
11 | add_button.mouseenter(function () {
12 | $(this).css({
13 | 'left':'0'
14 | });
15 | scroll_img.css({
16 | 'right':'30px',
17 | 'transform':'rotate(-90deg)'
18 | });
19 | });
20 |
21 | add_button.mouseleave(function () {
22 | $(this).css({
23 | 'left':'-50px'
24 | });
25 | scroll_img.css({
26 | 'right':'0',
27 | 'transform':'rotate(0deg)'
28 | });
29 | });
30 |
31 | add_button.click(function () {
32 | add_form_container.css({
33 | 'display':'flex'
34 | });
35 | });
36 | $('.add_form img').click(function () {
37 | add_form_container.css({
38 | 'display':'none'
39 | });
40 | })
41 |
42 | });
43 |
--------------------------------------------------------------------------------
/static/admin/js/login.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Nealyang on 17/2/25.
3 | */
4 | $(document).ready(function () {
5 | //设置一屏的宽高
6 | $('body').css({
7 | 'height': $(window).height() + 'px',
8 | 'width': $(window).width() + 'px'
9 | });
10 | // 点击登录
11 | $('.login_button').click(function () {
12 | var username = $('.username_div input').val(),
13 | password = $('.password_div input').val();
14 | if (username && password) {
15 | $.ajax({
16 | type: 'POST',
17 | url: '/admin/login',
18 | data: {
19 | username: username,
20 | password: password
21 | },
22 | success: function (response) {
23 | window.location.replace('/admin');
24 | },
25 | error: function (response) {
26 | if (JSON.parse(response.responseText).code == 500) {
27 | alert('服务器内部错误!');
28 | } else {
29 | alert('用户名或密码错误');
30 | }
31 | }
32 | })
33 | } else {
34 | confirm('用户名和密码为必填项~');
35 | }
36 |
37 | });
38 | });
--------------------------------------------------------------------------------
/static/admin/js/top.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Nealyang on 17/2/24.
3 | */
4 | $(document).ready(function () {
5 | var title = '首页';
6 | if(window.location.pathname.indexOf('blog') >0 ){
7 | title = '博文管理';
8 | $('.menu span:contains("博文管理")').css({
9 | 'bottom':'5px',
10 | 'box-shadow' :'4px 9px 10px #020202'
11 | });
12 | }else if(window.location.pathname.indexOf('users') >0 ){
13 | $('.menu span:contains("用户管理")').css({
14 | 'bottom':'5px',
15 | 'box-shadow' :'4px 9px 10px #020202'
16 | });
17 | title = '用户管理'
18 | }else{
19 | $('.menu span:contains("首页")').css({
20 | 'bottom':'5px',
21 | 'box-shadow' :'4px 9px 10px #020202'
22 | });
23 | }
24 | $('.menu span').mouseenter(function () {
25 | $(this).css({
26 | 'bottom':'5px',
27 | 'box-shadow' :'4px 9px 10px #020202'
28 | });
29 | });
30 | $('.menu span').mouseleave(function () {
31 | if($.trim($(this).text())!= title){
32 | $(this).css({
33 | 'bottom':'0',
34 | 'box-shadow' :'2px 2px 2px #6d5d5d'
35 | });
36 | }
37 |
38 | });
39 |
40 | });
--------------------------------------------------------------------------------
/static/admin/css/index.css:
--------------------------------------------------------------------------------
1 | .header{
2 | width: 100%;
3 | height: 120px;
4 | background: url("/admin/img/login_bg.png") no-repeat center;
5 | background-size: 100%;
6 | display: flex;
7 | align-items: center;
8 | color: #fff;
9 | }
10 | .header p{
11 | font-size: 32px;
12 | font-family: fantasy;
13 | margin-left: 5%;
14 | }
15 | .menu{
16 | height: 80px;
17 | font-family: fantasy;
18 | display: flex;
19 | align-items: center;
20 | justify-content: space-around;
21 | width: 500px;
22 | margin-left: 30%;
23 | }
24 |
25 | .menu span{
26 | font-size: 25px;
27 | display: inline-block;
28 | text-align: center;
29 | box-shadow: 2px 2px 2px #6d5d5d;
30 | background:-moz-linear-gradient(left,#a1655a,#2e4a53);/*火狐*/
31 | background:-webkit-gradient(linear, 0% 0%, 0% 100%,from(#b8c4cb), to(#f6f6f8));/*谷歌*/
32 | background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#0000ff)); /* Safari 4-5, Chrome 1-9*/
33 | background: -webkit-linear-gradient(left, #a1655a, #2e4a53); /*Safari5.1 Chrome 10+*/
34 | background: -o-linear-gradient(left, #a1655a, #2e4a53); /*Opera 11.10+*/
35 | height: 40px;
36 | line-height: 46px;
37 | padding:0px 20px;
38 | -webkit-border-radius:5px;
39 | -moz-border-radius:5px;
40 | border-radius:5px;
41 | cursor: pointer;
42 | }
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Nealyang on 17/2/25.
3 | */
4 |
5 | const express = require('express');
6 | const expressStatic = require('express-static');
7 | const bodyParser = require('body-parser');
8 | const multer = require('multer');
9 | const multerObj = multer({dest:'./static/upload'});
10 | const cookieParser = require('cookie-parser');
11 | const cookieSession = require('cookie-session');
12 | const consolidate = require('consolidate');
13 | const ejs = require('ejs');
14 |
15 | //创建服务器
16 | var server = express();
17 | server.listen(8080);
18 |
19 | //解析请求数据
20 |
21 | server.use(bodyParser({
22 | extended:false
23 | }));
24 | server.use(multerObj.any());
25 |
26 | //设置cookie,session
27 | server.use(cookieParser('Neal_signed'));
28 | (function () {
29 | var arr = [];
30 | for(var i = 0;i<10000;i++){
31 | arr.push('keys_'+Math.random());
32 | }
33 | server.use(cookieSession({
34 | name:'session_id',
35 | keys:arr,
36 | maxAge:20*60*1000//一般我会设置20分钟,这里是为了感受session过期~~带来的快感~?(●´∀`●)ノ
37 | }))
38 | })();
39 |
40 | //设置模板
41 | server.set('view engine','html');
42 | server.set('views','./views');
43 | server.engine('html',consolidate.ejs);
44 | //设置路由
45 | server.use('/admin',require('./router/admin/index')());
46 | server.use('/',require('./router/web/index')());
47 |
48 |
49 | //静态文件的请求
50 | server.use('/files',expressStatic('./static'));
51 |
--------------------------------------------------------------------------------
/static/admin/css/login.css:
--------------------------------------------------------------------------------
1 | body {
2 | background: url("/files/admin/img/login_bg.png") no-repeat center 0;
3 | background-size: 100% 100%;
4 | display: flex;
5 | align-items: center;
6 | justify-content: center;
7 | }
8 |
9 | .login_container {
10 | width: 475px;
11 | height: 459px;
12 | background: url("/files/admin/img/sign_bg.png") no-repeat center 0;
13 | background-size: 100% 100%;
14 | -webkit-animation:inner 1s;
15 | -o-animation:inner 1s;
16 | animation:inner 1s;
17 | }
18 |
19 | @keyframes inner {
20 | 0% {
21 | width: 0;
22 | height: 0;
23 | opacity: 0;
24 | }
25 | 100%{
26 | width: 475px;
27 | height: 459px;
28 | opacity: 1;
29 | }
30 | }
31 |
32 | .login_input_container {
33 | width: 75%;
34 | height: 13%;
35 | border: solid 2px rgba(80, 75, 75, 0.8);
36 | box-shadow: 1px 1px 9px #544e4e inset;
37 | border-radius: 7px;
38 | display: flex;
39 | align-items: center;
40 | }
41 |
42 | .username_div {
43 | margin: 27% auto 0;
44 | }
45 |
46 | .login_input_container img {
47 | width: 10%;
48 | height: auto;
49 | margin-left: 5%;
50 | }
51 |
52 | .login_input_container input {
53 | border: none;
54 | background: transparent;
55 | height: 68%;
56 | width: 80%;
57 | margin-left: 3%;
58 | font-size: 25px;
59 | color: #fff;
60 | }
61 |
62 | .password_div {
63 | margin: 10% auto 0;
64 | }
65 |
66 | .login_button {
67 | background: url(/files/admin/img/login_button.png) no-repeat center 0;
68 | height: 13%;
69 | width: 63%;
70 | background-size: 100% 100%;
71 | margin: 8% auto 0;
72 |
73 | }
--------------------------------------------------------------------------------
/router/admin/login.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Nealyang on 17/2/24.
3 | */
4 | const express = require('express');
5 | const mysql = require('mysql');
6 | const common = require('../../lib/common');
7 |
8 | const db = mysql.createPool({host:'localhost',port:3306,user:'root',password:' neal',database:'blog'});
9 |
10 | module.exports = function () {
11 | var router = express.Router();
12 | router.get('/',function (req,res) {
13 | res.render('admin/login.ejs');
14 | });
15 | router.post('/',function (req,res) {
16 | var username = req.body.username;
17 | var password = common.md5(req.body.password+common.MD5_SUFFIX);
18 | if(username && password){
19 | db.query('SELECT * FROM admin_table WHERE username="'+username+'"',function (err,userData) {
20 | if(err){
21 | console.error(err);
22 | res.status(500).send({code:500,data:[],msg:'database error'});
23 | }else if(userData.length == 0){
24 | res.status(400).send({code:400,data:[],msg:'parameters error'});
25 | }else{
26 | if(userData[0].password != password){
27 | res.status(400).send({code:400,data:[],msg:'username or password error'});
28 | }else{
29 | req.session['user_id'] = userData[0].ID;//注意这里是在req上面
30 | res.status(200).send({code:200,data:[],msg:'success'});
31 | }
32 | }
33 | })
34 | }else{
35 | res.status(400).send({code:400,data:[],msg:'parameters error'});
36 | }
37 | });
38 | return router;
39 | };
40 |
--------------------------------------------------------------------------------
/static/admin/css/top.css:
--------------------------------------------------------------------------------
1 | .header{
2 | width: 100%;
3 | height: 120px;
4 | background: url("/files/admin/img/login_bg.png") no-repeat center;
5 | background-size: 100%;
6 | display: flex;
7 | align-items: center;
8 | color: #fff;
9 | }
10 | .header p{
11 | font-size: 32px;
12 | font-family: fantasy;
13 | margin-left: 5%;
14 | }
15 | .menu{
16 | height: 80px;
17 | font-family: fantasy;
18 | display: flex;
19 | align-items: center;
20 | justify-content: space-around;
21 | width: 500px;
22 | margin-left: 30%;
23 | }
24 |
25 | .menu span{
26 | color: #fff;
27 | font-size: 25px;
28 | display: inline-block;
29 | text-align: center;
30 | box-shadow: 2px 2px 2px #6d5d5d;
31 | background:-moz-linear-gradient(left,#a1655a,#2e4a53);/*火狐*/
32 | background:-webkit-gradient(linear, 0% 0%, 0% 100%,from(#b8c4cb), to(#f6f6f8));/*谷歌*/
33 | background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#0000ff)); /* Safari 4-5, Chrome 1-9*/
34 | background: -webkit-linear-gradient(left, #a1655a, #2e4a53); /*Safari5.1 Chrome 10+*/
35 | background: -o-linear-gradient(left, #a1655a, #2e4a53); /*Opera 11.10+*/
36 | height: 40px;
37 | line-height: 46px;
38 | padding:0px 20px;
39 | -webkit-border-radius:5px;
40 | -moz-border-radius:5px;
41 | border-radius:5px;
42 | cursor: pointer;
43 | position: relative;
44 | bottom:0;
45 | -webkit-transition: bottom .3s;
46 | -moz-transition: bottom .3s;
47 | -ms-transition: bottom .3s;
48 | -o-transition: bottom .3s;
49 | transition: bottom .3s,box-shadow .3s;
50 | }
51 | .on_select{
52 | box-shadow: 4px 9px 10px #020202;
53 | }
--------------------------------------------------------------------------------
/views/admin/users.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 后台管理-用户管理
6 |
7 |
8 |
9 |
10 |
11 | <% include common/top.ejs%>
12 |
15 |
25 | <%if(typeof modData != 'undefined'){%>
26 |
37 | <%}%>
38 |
39 |
40 | | ID |
41 | 名字 |
42 | 邮箱 |
43 | 头像 |
44 | 操作 |
45 |
46 | <% for(var i = 0;i
47 |
48 | | <%=usersData[i].ID%> |
49 | <%=usersData[i].username%> |
50 | <%=usersData[i].email%> |
51 |  |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | |
60 |
61 | <%}%>
62 |
63 |
64 |
--------------------------------------------------------------------------------
/static/admin/css/blog.css:
--------------------------------------------------------------------------------
1 | .add_button{
2 | height: 50px;
3 | width: 100px;
4 | background:-moz-linear-gradient(left,#a1655a,#2e4a53);/*火狐*/
5 | background:-webkit-gradient(linear, 0% 0%, 0% 100%,from(#b8c4cb), to(#f6f6f8));/*谷歌*/
6 | background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#0000ff)); /* Safari 4-5, Chrome 1-9*/
7 | background: -webkit-linear-gradient(left, #a1655a, #2e4a53); /*Safari5.1 Chrome 10+*/
8 | background: -o-linear-gradient(left, #a1655a, #2e4a53); /*Opera 11.10+*/
9 | border-top-right-radius: 25px;
10 | border-bottom-right-radius: 25px;
11 | display: flex;
12 | align-items: center;
13 | flex-direction: row-reverse;
14 | position: fixed;
15 | left:-50px;
16 | top:300px;
17 | cursor: pointer;
18 | -webkit-transition: left .5s;
19 | -moz-transition: left .5s;
20 | -ms-transition: left .5s;
21 | -o-transition: left .5s;
22 | transition: left .5s;
23 | }
24 | .add_button img{
25 | width: 50%;
26 | transform: rotate(0deg);
27 | position: relative;
28 | right:0;
29 | -webkit-transition: right .5s,transform,.5s;
30 | -moz-transition: right .5s,transform,.5s;
31 | -ms-transition: right .5s,transform,.5s;
32 | -o-transition: right .5s,transform,.5s;
33 | transition: right .5s,transform,.5s;
34 | }
35 | .form_bg{
36 | background-color: #000;
37 | opacity: .5;
38 | height: 100%;
39 | width: 100%;
40 | position: absolute;
41 | top:0;
42 | }
43 | .add_form{
44 | justify-content: center;
45 | display: none;
46 | }
47 | .add_form img{
48 | width: 50px;
49 | height: auto;
50 | position: fixed;
51 | z-index: 2;
52 | transform: rotate(45deg);
53 | right: 37%;
54 | margin-top: 33px;
55 | }
56 | .add_form form{
57 | position: fixed;
58 | z-index: 2;
59 | text-align: center;
60 | color: #fff;
61 | font-size: 20px;
62 | margin-top: 100px;
63 | line-height: 55px;
64 | }
65 | table{
66 | width: 100%;
67 | }
68 | table .id{
69 | width: 7%;
70 | }
71 | table .title{
72 | width: 25%;
73 | }
74 | table .author{
75 | width: 8%;
76 | }
77 | table .summary{
78 | width: 30%;
79 |
80 | }
81 | table .href{
82 | width: 20%;
83 | }
84 | table tr:first-child{
85 | background-color: #ddd;
86 | height: 30px;
87 | line-height: 30px;
88 | }
89 | table tr{
90 | text-align: center;
91 | border-bottom: solid 2px #ddd;
92 | }
93 | table td{
94 | padding-bottom: 20px;
95 | padding-top: 20px;
96 | }
--------------------------------------------------------------------------------
/views/admin/blog.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 后台管理-博客管理
6 |
7 |
8 |
9 |
10 |
11 | <% include common/top.ejs %>
12 |
15 |
26 | <%if(typeof modData != 'undefined'){%>
27 |
39 | <%}%>
40 |
41 |
42 | | ID |
43 | 标题 |
44 | 作者 |
45 | 摘要 |
46 | 链接 |
47 | 操作 |
48 |
49 | <% for(var i = 0;i
50 |
51 | | <%=formData[i].ID%> |
52 | <%=formData[i].title%> |
53 | <%=formData[i].author%> |
54 | <%=formData[i].summary%> |
55 | <%=formData[i].href%> |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | |
64 |
65 | <%}%>
66 |
67 |
68 |
--------------------------------------------------------------------------------
/static/admin/css/users.css:
--------------------------------------------------------------------------------
1 | .add_button{
2 | height: 50px;
3 | width: 100px;
4 | background:-moz-linear-gradient(left,#a1655a,#2e4a53);/*火狐*/
5 | background:-webkit-gradient(linear, 0% 0%, 0% 100%,from(#b8c4cb), to(#f6f6f8));/*谷歌*/
6 | background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#0000ff)); /* Safari 4-5, Chrome 1-9*/
7 | background: -webkit-linear-gradient(left, #a1655a, #2e4a53); /*Safari5.1 Chrome 10+*/
8 | background: -o-linear-gradient(left, #a1655a, #2e4a53); /*Opera 11.10+*/
9 | border-top-right-radius: 25px;
10 | border-bottom-right-radius: 25px;
11 | display: flex;
12 | align-items: center;
13 | flex-direction: row-reverse;
14 | position: fixed;
15 | left:-50px;
16 | top:300px;
17 | cursor: pointer;
18 | -webkit-transition: left .5s;
19 | -moz-transition: left .5s;
20 | -ms-transition: left .5s;
21 | -o-transition: left .5s;
22 | transition: left .5s;
23 | }
24 | .add_button img{
25 | width: 50%;
26 | transform: rotate(0deg);
27 | position: relative;
28 | right:0;
29 | -webkit-transition: right .5s,transform,.5s;
30 | -moz-transition: right .5s,transform,.5s;
31 | -ms-transition: right .5s,transform,.5s;
32 | -o-transition: right .5s,transform,.5s;
33 | transition: right .5s,transform,.5s;
34 | }
35 | .form_bg{
36 | background-color: #000;
37 | opacity: .5;
38 | height: 100%;
39 | width: 100%;
40 | position: absolute;
41 | top:0;
42 | }
43 | .add_form{
44 | justify-content: center;
45 | display: none;
46 | }
47 | .add_form img{
48 | width: 50px;
49 | height: auto;
50 | position: fixed;
51 | z-index: 2;
52 | transform: rotate(45deg);
53 | right: 37%;
54 | margin-top: 33px;
55 | }
56 | .add_form form{
57 | position: fixed;
58 | z-index: 2;
59 | text-align: center;
60 | color: #fff;
61 | font-size: 20px;
62 | margin-top: 100px;
63 | line-height: 55px;
64 | }
65 | table{
66 | width: 100%;
67 | }
68 | table .id{
69 | width: 7%;
70 | }
71 | table .title{
72 | width: 25%;
73 | }
74 | table .author{
75 | width: 8%;
76 | }
77 | table .summary{
78 | width: 30%;
79 |
80 | }
81 | table .href{
82 | width: 20%;
83 | }
84 | table tr:first-child{
85 | background-color: #ddd;
86 | height: 30px;
87 | line-height: 30px;
88 | }
89 | table tr{
90 | text-align: center;
91 | border-bottom: solid 2px #ddd;
92 | }
93 | table td{
94 | padding-bottom: 20px;
95 | padding-top: 20px;
96 | }
97 | table img{
98 | width:40px;
99 | height: auto;
100 | }
--------------------------------------------------------------------------------
/resources/blog.sql:
--------------------------------------------------------------------------------
1 | /*
2 | Navicat Premium Data Transfer
3 |
4 | Source Server : neal
5 | Source Server Type : MySQL
6 | Source Server Version : 50717
7 | Source Host : localhost
8 | Source Database : blog
9 |
10 | Target Server Type : MySQL
11 | Target Server Version : 50717
12 | File Encoding : utf-8
13 |
14 | Date: 02/25/2017 12:44:35 PM
15 | */
16 |
17 | SET NAMES utf8;
18 | SET FOREIGN_KEY_CHECKS = 0;
19 |
20 | -- ----------------------------
21 | -- Table structure for `admin_table`
22 | -- ----------------------------
23 | DROP TABLE IF EXISTS `admin_table`;
24 | CREATE TABLE `admin_table` (
25 | `ID` int(11) NOT NULL AUTO_INCREMENT,
26 | `username` varchar(32) NOT NULL,
27 | `password` varchar(32) NOT NULL,
28 | PRIMARY KEY (`ID`)
29 | ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
30 |
31 | -- ----------------------------
32 | -- Records of `admin_table`
33 | -- ----------------------------
34 | BEGIN;
35 | INSERT INTO `admin_table` VALUES ('1', 'root', 'cecd14064502f3ab34cd5b0dee3545c2'), ('2', 'neal', 'b644584d66f1740887e60408b543cea0'), ('3', 'Nealyang', '9a162c584b7cad5c3eb19e9fbcbd8592');
36 | COMMIT;
37 |
38 | -- ----------------------------
39 | -- Table structure for `blog_list_table`
40 | -- ----------------------------
41 | DROP TABLE IF EXISTS `blog_list_table`;
42 | CREATE TABLE `blog_list_table` (
43 | `ID` int(11) NOT NULL AUTO_INCREMENT,
44 | `title` varchar(200) NOT NULL,
45 | `summary` varchar(500) NOT NULL,
46 | `href` varchar(300) NOT NULL,
47 | `author` varchar(32) NOT NULL,
48 | PRIMARY KEY (`ID`)
49 | ) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;
50 |
51 | -- ----------------------------
52 | -- Records of `blog_list_table`
53 | -- ----------------------------
54 | BEGIN;
55 | INSERT INTO `blog_list_table` VALUES ('2', '关于nodejs中cookie和session浅谈', 'cookie,session作用,以及在nodejs中如何操作和利用cookie和session', 'https://my.oschina.net/Nealyang/blog/844049', 'Nealyang'), ('6', 'angularJs中关于ng-class的三种使用方式说明', '在开发中我们通常会遇到一种需求:一个元素在不同的状态需要展现不同的样子。\r\n\r\n而在这所谓的样子当然就是改变其css的属性,而实现能动态的改变其属性值,必然只能是更换其class属性', 'https://my.oschina.net/Nealyang/blog/525865', 'Nealyang'), ('7', '点燃中国冰雪运动的火炬——习近平总书记关心北京冬奥会5个镜头', '筹备工作紧锣密鼓。春节前后,习近平总书记的日程中,北京冬奥会筹备工作占据着重要位置。', 'http://china.huanqiu.com/article/2017-02/10199694.html', 'Huanqiu'), ('8', '缅甸工人罢工还打砸抢,中资服装厂过去两个半月经历了什么', '一家位于缅甸仰光兰达亚工业区的中资服装厂23日遭遇数百名缅籍罢工工人及外来人员打砸破坏,7名中国籍员工在厂内被困9小时才获准离开。企业负责人说,该厂已经被占据近一个月,迄今仍被罢工者控制。', 'http://world.huanqiu.com/article/2017-02/10200088.html', '环球网');
56 | COMMIT;
57 |
58 | -- ----------------------------
59 | -- Table structure for `user_table`
60 | -- ----------------------------
61 | DROP TABLE IF EXISTS `user_table`;
62 | CREATE TABLE `user_table` (
63 | `ID` int(11) NOT NULL AUTO_INCREMENT,
64 | `username` varchar(32) NOT NULL,
65 | `email` varchar(32) NOT NULL,
66 | `pic_header` varchar(300) NOT NULL,
67 | PRIMARY KEY (`ID`)
68 | ) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;
69 |
70 | -- ----------------------------
71 | -- Records of `user_table`
72 | -- ----------------------------
73 | BEGIN;
74 | INSERT INTO `user_table` VALUES ('6', 'Nealyang', 'sunsting@163.com', '/files/upload/c630494b49e1e54a713f2e4f275edd2e.jpg'), ('7', 'Neal', 'nealyang231@outlook.com', '/files/upload/68efe455c4ecda9517d644751c749849.jpg'), ('8', '婷婷', 'sunsting@163.com', '/files/upload/872ce9c96a353d082f20f20575af158f.jpg');
75 | COMMIT;
76 |
77 | SET FOREIGN_KEY_CHECKS = 1;
78 |
--------------------------------------------------------------------------------
/static/admin/css/reset.css:
--------------------------------------------------------------------------------
1 | @charset "utf-8";
2 | /*************************
3 | *
4 | * author: Neal
5 | * version: 1.0
6 | * time: 2016-07-11
7 | *
8 | *************************/
9 | html { -webkit-overflow-scrolling: touch; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%}
10 | article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section,body,div,h1,h2,h3,h4,h5,h6,hr,p,blockquote,dl,dt,dd,ul,ol,li,pre,fieldset,legend,button,input,textarea,form,th,td { margin: 0; padding: 0; vertical-align: baseline}
11 | article,aside,details,figcaption,figure,footer,header,hgroup,nav,section,summary { display: block}
12 | audio,canvas,video { display: inline-block; *display: inline; *zoom:1}
13 | body,button,input,select,textarea { font: 12px/1.5 Tahoma,Helvetica,Arial,"\5B8B\4F53","\5FAE\8F6F\96C5\9ED1",sans-serif}
14 | h1,h2,h3,h4,h5,h6 { font-size: 100%; font-weight: normal}
15 | address,cite,dfn,em,var,i { font-style: normal}
16 | ul,ol { list-style: none outside none}
17 | a { text-decoration: none; outline: 0}
18 | a:hover { text-decoration: underline; }
19 | a:active { text-decoration: none; }
20 | pre { white-space: pre; white-space: pre-wrap; word-wrap: break-word}
21 | sub,sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline}
22 | sup { top: -0.5em}
23 | sub { bottom: -0.25em}
24 | fieldset,iframe { border: 0 none}
25 | img { border: 0 none; vertical-align: middle; -ms-interpolation-mode: bicubic; max-width: 100%;}
26 | button,input,select,textarea { font-family: inherit; font-size: 100%; vertical-align: baseline; *vertical-align: middle}
27 | button,input[type=button],input[type=submit],input[type="reset"] { -webkit-appearance: button; cursor: pointer; *overflow: visible}
28 | button[disabled],input[disabled] { cursor: default}
29 | button::-moz-focus-inner,button::-moz-focus-outer,input::-moz-focus-inner,input::-moz-focus-outer { border: 0 none; padding: 0; margin: 0}
30 | input[type=search] { -webkit-appearance: textfield; -moz-box-sizing: content-box; -webkit-box-sizing: content-box; box-sizing: content-box}
31 | input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration { -webkit-appearance: none}
32 | textarea { overflow: auto; vertical-align: top; resize: vertical}
33 | table { border-collapse: collapse; border-spacing: 0}
34 | strong,em,i { font-weight: normal}
35 |
36 | /**************** base ******************/
37 | /* display */
38 | .dn{display:none;}
39 | .di{display:inline;}
40 | .db{display:block;}
41 | .dib{display:inline-block;} /* if the element is block level(eg. div, li), using 'inline_any' instead */
42 | /* float*/
43 | .fl { float: left;}
44 | .fr { float: right;}
45 | /* text-align */
46 | .tc{text-align:center;}
47 | .tr{text-align:right;}
48 | .tl{text-align:left;}
49 | /* vertical-align */
50 | .vm{ display: inline-block; vertical-align:middle;}
51 | .vtb{ display: inline-block;vertical-align:text-bottom;}
52 | /* position */
53 | .rel{position:relative;}
54 | .abs{position:absolute;}
55 | /* font-size */
56 | .f12{font-size:12px;}
57 | .f14{font-size:14px;}
58 | .f16{font-size:16px;}
59 | .f18{font-size:18px;}
60 | .f20{font-size:20px;}
61 | .f24{font-size:24px;}
62 | /* 块状元素水平居中 */
63 | .auto{margin-left:auto; margin-right:auto;}
64 | /* 清除浮 动*/
65 | .clearfix{*zoom:1;}
66 | .clearfix:after{display:table; content:''; clear:both;}
67 | /* 单行文字溢出虚点显 示*/
68 | .ellipsis{text-overflow:ellipsis; white-space:nowrap; overflow:hidden;}
--------------------------------------------------------------------------------
/router/admin/blog.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Nealyang on 17/2/25.
3 | */
4 | const express = require('express');
5 | const common = require('../../lib/common');
6 | const mysql = require('mysql');
7 |
8 | const db = mysql.createPool({
9 | host: 'localhost',
10 | port: 3306,
11 | user: 'root',
12 | password: ' neal',
13 | database: 'blog'
14 | });
15 |
16 | module.exports = function () {
17 | var router = express.Router();
18 | router.get('/', function (req, res) {
19 | switch (req.query.action) {
20 | case 'del':
21 | //删除操作
22 | db.query('DELETE FROM blog_list_table WHERE id="'+req.query.id+'"',function (err,resultData) {
23 | if(err){
24 | console.error(err);
25 | res.status(500).send({code:500,msg:'database error'});
26 | }else{
27 | res.redirect('/admin/blog');
28 | }
29 | });
30 | break;
31 | case 'mod':
32 | //修改操作
33 | db.query('SELECT * FROM blog_list_table WHERE id="'+req.query.id+'"',function (err,modData) {
34 | if(err){
35 | console.error(err);
36 | res.status(500).send({code:500,msg:'database error'});
37 | }else if(modData.length == 0){
38 | res.status(400).send({code:400,msg:'parameters error'});
39 | }else{
40 | db.query('SELECT * FROM blog_list_table',function (err,allData) {
41 | if(err){
42 | console.error(err);
43 | res.status(500).send({code:500,msg:'database error'});
44 | }else{
45 | res.render('admin/blog.ejs',{formData:allData,modData:modData});
46 | }
47 | });
48 | }
49 | });
50 | break;
51 | default:
52 | db.query('SELECT * FROM blog_list_table', function (err, resultData) {
53 | if (err) {
54 | console.error(err);
55 | res.status(500).send({code: 500, msg: 'database error'}).end();
56 | } else {
57 | res.render('admin/blog.ejs', {formData: resultData});
58 | }
59 | });
60 | }
61 |
62 | });
63 | router.post('/', function (req, res) {
64 | //此处验证应该更加严格,比如正则
65 | var title = req.body.title.trim();
66 | var author = req.body.author.trim();
67 | var summary = req.body.summary.trim();
68 | var href = req.body.href.trim();
69 |
70 | if (title && author && summary && href) {
71 | if(req.body.modified){
72 | db.query('UPDATE blog_list_table SET title="'+title+'",author="'+author+'",summary="'+summary+'",href="'+href+'" WHERE ID="'+req.body.modified+'"',function (err,resultData) {
73 | if(err){
74 | console.error(err);
75 | res.status(500).send({code:500,msg:'database error'});
76 | }else{
77 | res.redirect('/admin/blog');
78 | }
79 | })
80 | }else{
81 | db.query('INSERT INTO blog_list_table (title,author,summary,href) VALUE("' + title + '","' + author + '","' + summary + '","' + href + '")', function (err, data) {
82 | if (err) {
83 | console.error(err);
84 | res.status(500).send({code: 500, msg: 'database error'}).end();
85 | } else {
86 | res.redirect('/admin/blog');
87 | }
88 | });
89 | }
90 | } else {
91 | res.status(400).send({code: 400, msg: 'parameters error'}).end();
92 | }
93 |
94 | });
95 | return router;
96 | };
97 |
--------------------------------------------------------------------------------
/router/admin/users.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Nealyang on 17/2/25.
3 | */
4 | const express = require('express');
5 | const common = require('../../lib/common');
6 | const mysql = require('mysql');
7 | const fs = require('fs');
8 | const pathLib = require('path');
9 |
10 | const db = mysql.createPool({
11 | host: 'localhost',
12 | user: 'root',
13 | password: ' neal',
14 | database: 'blog'
15 | });
16 |
17 | module.exports = function () {
18 | var router = express.Router();
19 | router.get('/', function (req, res) {
20 | switch (req.query.action) {
21 | case 'del'://删除操作
22 | db.query('SELECT * FROM user_table WHERE ID="' + req.query.id + '"', function (err, queryData) {
23 | if (err) {
24 | console.error(err);
25 | res.status(500).send({code: 500, msg: 'database error'});
26 | } else if (queryData.length == 0) {
27 | res.status(400).send({code: 400, msg: 'parameters error'});
28 | } else {
29 | fs.unlink(queryData[0].pic_header.replace('\/files','static'), function (err) {
30 | if (err) {
31 | console.error(err);
32 | res.status(500).send({code:500,msg:'operate err'});
33 | } else {
34 | db.query('DELETE FROM user_table WHERE ID="'+
35 | req.query.id+'"',function (err,resultData) {
36 | if(err){
37 | console.error(err);
38 | res.status(500).send({code: 500, msg: 'database error'});
39 | }else{
40 | res.redirect('/admin/users');
41 | }
42 | })
43 | }
44 | })
45 | }
46 | });
47 | break;
48 | case 'mod':
49 | db.query('SELECT * FROM user_table',function (err,allData) {
50 | if (err) {
51 | console.error(err);
52 | res.status(500).send({code: 500, msg: 'database error'});
53 | } else {
54 | db.query('SELECT * FROM user_table WHERE ID="'+req.query.id+'"',function (err,modData) {
55 | if (err) {
56 | console.error(err);
57 | res.status(500).send({code: 500, msg: 'database error'});
58 | }else if(modData.length == 0){
59 | res.status(400).send({code: 400, msg: 'parameters error'});
60 | }else {
61 | res.render('admin/users.ejs',{usersData:allData,modData:modData});
62 | }
63 | });
64 | }
65 | });
66 | break;
67 | default:
68 | db.query('SELECT * FROM user_table', function (err, allUsersData) {
69 | if (err) {
70 | console.error(err);
71 | res.status(500).send({code: 500, msg: 'database error'});
72 | } else {
73 | res.render('admin/users.ejs', {usersData: allUsersData});
74 | }
75 | });
76 | }
77 | });
78 | router.post('/', function (req, res) {
79 | var username = req.body.username;
80 | var email = req.body.email;
81 | if(req.files.length>0){
82 | var ext = pathLib.parse(req.files[0].originalname).ext;
83 | var pic_header = '/files/upload/' + req.files[0].filename + ext;
84 | }
85 |
86 | //需要进行一些校验,这里就忽略了
87 | if(req.body.modified){//修改
88 | //查看有没有新传来的头像,如果有,则删除,新建,如果没有,直接更新需要更新的内容
89 | if(req.files.length>0){
90 | //有修改头像,则进行原来头像的删除,再上传
91 | db.query('SELECT * FROM user_table WHERE ID="'+req.body.modified+'"',function (err,modData) {
92 | if (err) {
93 | console.error(err);
94 | res.status(500).send({code: 500, msg: 'database error'});
95 | }else if(modData.length == 0){
96 | res.status(400).send({code: 400, msg: 'parameters error'});
97 | }else{
98 | fs.unlink(modData[0].pic_header.replace('\/files','static'),function (err) {
99 | if(err){
100 | console.error(err);
101 | res.status(500).send({code:500,msg:'operate error'});
102 | }else{
103 | //删除成功,开始对新的文件进行重命名
104 | fs.rename(req.files[0].path, req.files[0].path + ext, function (err) {
105 | if (err) {
106 | console.error(err);
107 | res.status(500).send({code: 500, msg: 'operate error'});
108 | } else {
109 | db.query('UPDATE user_table SET username="'+
110 | username+'",email="' + email + '",pic_header="' +
111 | pic_header + '" WHERE ID="'+req.body.modified+'"',function (err,data) {
112 | if (err) {
113 | console.error(err);
114 | res.status(500).send({code: 500, msg: 'database error'});
115 | }else{
116 | res.redirect('/admin/users');
117 | }
118 | });
119 | }
120 | });
121 | }
122 | })
123 | }
124 | })
125 | }else{
126 | db.query('UPDATE user_table SET username="'+username+'",email="' + email + '" WHERE ID="'+
127 | req.body.modified+'"',function (err,data) {
128 | if (err) {
129 | console.error(err);
130 | res.status(500).send({code: 500, msg: 'database error'});
131 | }else{
132 | res.redirect('/admin/users');
133 | }
134 | });
135 | }
136 | }else{//新增
137 | fs.rename(req.files[0].path, req.files[0].path + ext, function (err) {
138 | if (err) {
139 | console.error(err);
140 | res.status(500).send({code: 500, msg: 'data error'});
141 | } else {
142 | db.query('INSERT INTO user_table (username,email,pic_header) VALUE("' + username + '","' +
143 | email + '","' + pic_header + '")', function (err, resultData) {
144 | if (err) {
145 | console.error(err);
146 | res.status(500).send({code: 500, msg: 'database error'});
147 | } else {
148 | res.redirect('/admin/users');
149 | }
150 | });
151 | }
152 | });
153 | }
154 |
155 | });
156 | return router;
157 | };
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ejs-express-mysql
2 | ==
3 |
4 | ###[基于express,MySQL,ejs实现的一个简单基本的网站后台管理应用](https://github.com/Nealyang/ejs-express-mysql)
5 |
6 | ##前言
7 | 也是这两周才正式的接触node,虽然在前端开发中我们常常说前后端分离,但是在学习过程中,个人感觉还是要刁难刁难自己的。因为用ejs来写前端页面。
8 | 项目主要实现用户的登录,session的存储和加密(准确的说是签名),数据库的CRUD,包括图片的上传,删除和修改等基本功能。
9 | 关于登录,查询等操作本应该更加的严谨,这里只做简单演示。包括一些配置文件的编写。
10 |
11 | ---
12 | 喜欢的朋友方便的话可以给个star (^-^)V
13 |
14 | 顺便推广一波nodejs技术交流群,群号:209530601
15 | ***
16 |
17 | >ejs mysql nodejs express express-router...
18 |
19 | ##效果图
20 |
21 | 登录页
22 | 
23 |
24 | 整体操作流程图
25 | 
26 |
27 | **GIF Brewery转gif真的有点。。。好吧,不吐槽了,后面会分开讲解每一步,好在基本操作还能看得清~**
28 |
29 |
30 | 后台管理首页
31 | 
32 |
33 | 博文管理
34 | 
35 |
36 | 用户管理页
37 | 
38 |
39 | 操作
40 | 
41 |
42 |
43 | ##开发准备
44 | 关于开发前期的准备,这里就不多说了,说实话,自己也没有准备啥,关于nodejs环境,MySQL配置啥的就多少了,关于本项目的[数据字典](./数据字典.txt),还有[SQL文件](./resources/blog.sql)已经在目录里了,这里主要说下后端开发的每一个步骤
45 | ##项目目录
46 | 项目目录
47 | 
48 | ##整体架构
49 | 项目重点在后端开发中,web端页面并没有涉及到,后端管理流程大致如下:
50 | * 路由控制分为admin,web,还是那句话,我们操作全部在admin中
51 | * 跳转到admin拦截所有的请求,判断用户是否登录
52 | * 未登录则重定向到登录,登陆成功后设置session。[不懂session?点击这里](https://my.oschina.net/Nealyang/blog/844049)
53 | * 登录后则可进行相关的操作,数据的增删改查等功能。
54 | ##后端开发
55 | ###后台基本架构、路由设置
56 | const express = require('express');
57 | const expressStatic = require('express-static');
58 | const bodyParser = require('body-parser');
59 | const multer = require('multer');
60 | const multerObj = multer({dest:'./static/upload'});
61 | const cookieParser = require('cookie-parser');
62 | const cookieSession = require('cookie-session');
63 | const consolidate = require('consolidate');
64 | const ejs = require('ejs');
65 |
66 | //创建服务器
67 | var server = express();
68 | server.listen(8080);
69 |
70 | //解析请求数据
71 |
72 | server.use(bodyParser({
73 | extended:false
74 | }));
75 | server.use(multerObj.any());
76 |
77 | //设置cookie,session
78 | server.use(cookieParser('Neal_signed'));
79 | (function () {
80 | var arr = [];
81 | for(var i = 0;i<10000;i++){
82 | arr.push('keys_'+Math.random());
83 | }
84 | server.use(cookieSession({
85 | name:'session_id',
86 | keys:arr,
87 | maxAge:20*60*1000//一般我会设置20分钟,这里是为了感受session过期~~带来的快感~?(●´∀`●)ノ
88 | }))
89 | })();
90 |
91 | //设置模板
92 | server.set('view engine','html');
93 | server.set('views','./views');
94 | server.engine('html',consolidate.ejs);
95 | //设置路由
96 | server.use('/admin',require('./router/admin/index')());
97 | server.use('/',require('./router/web/index')());
98 |
99 |
100 | //静态文件的请求
101 | server.use('/files',expressStatic('./static'));
102 | 我的基本架构如下,关于每一部分的功能,都已经标注。关于路由的控制在admin/index.js跟server.js大同小异,我想大家也都应该知道了。
103 | ###登录功能
104 | 登录功能这里主要说两点
105 | * 密码的md5签名(当然,大多数人说是md5加密)
106 | * session的应用
107 | 在lib中存放着自己写的一些方法,作为一个库,admin初始化有三个用户,包括u/p:root,neal,Nealyang
108 | 关于密码的签名方法主要如下:
109 | var crypto = require('crypto');
110 |
111 | module.exports = {
112 | MD5_SUFFIX : 'JDSAIOEUQOIoieuoiqv#$%^&dhfja)(* %^&FGHJfyuieyfhfhak(^.^)YYa!!\(^o^)/Y(^o^)Y(*^__^*)ヘ|・∀・|ノ*~●',
113 | md5:function (pwd) {
114 | var md5 = crypto.createHash('md5');
115 | return md5.update(pwd).digest('hex');
116 | }
117 | };
118 | MD5_SUFFIX是加密字符串,用法如路由login.js:
119 |
120 |
121 | router.post('/',function (req,res) {
122 | var username = req.body.username;
123 | var password = common.md5(req.body.password+common.MD5_SUFFIX);
124 | if(username && password){
125 | db.query('SELECT * FROM admin_table WHERE username="'+username+'"',function (err,userData) {
126 | if(err){
127 | console.error(err);
128 | res.status(500).send({code:500,data:[],msg:'database error'});
129 | }else if(userData.length == 0){
130 | res.status(400).send({code:400,data:[],msg:'parameters error'});
131 | }else{
132 | if(userData[0].password != password){
133 | res.status(400).send({code:400,data:[],msg:'username or password error'});
134 | }else{
135 | req.session['user_id'] = userData[0].ID;//注意这里是在req上面
136 | res.status(200).send({code:200,data:[],msg:'success'});
137 | }
138 | }
139 | })
140 | }else{
141 | res.status(400).send({code:400,data:[],msg:'parameters error'});
142 | }
143 | });
144 | 从上面的代码中也展现了什么时候设置session,并且值得提一下的是这里提供给前端页面的是接口,这样的话很多逻辑都放到了前端,后面我们都是通过页面渲染来输出的了。下面是所有请求的拦截判断:
145 |
146 | router.use(function (req,res,next) {
147 | if(!req.session['user_id'] && req.url != '/login'){
148 | res.redirect('/admin/login');
149 | }else{
150 | next();
151 | }
152 | });
153 |
154 | ##ejs前端页面的重点代码讲解
155 | 公共头部的引入:
156 |
157 | <% include common/top.ejs %>
158 |
159 | 查询数据库的前端展示:
160 |
161 | <% for(var i = 0;i
162 |
163 | | <%=formData[i].ID%> |
164 | <%=formData[i].title%> |
165 | <%=formData[i].author%> |
166 | <%=formData[i].summary%> |
167 | <%=formData[i].href%> |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 | |
176 |
177 | <%}%>
178 |
179 | 修改后我们通过页面给的标识来知道是否为修改的提交,毕竟这里我们没有前端逻辑的js
180 |
181 | <%if(typeof modData != 'undefined'){%>
182 |
194 | <%}%>
195 |
196 | ##博文管理
197 | 还是那句话,如果是仅仅提供前端的接口的话,这里会方便很多,然后我们用的是ejs,所以很多的逻辑都放在了后端,在get/post到请求的时候需要做很多判断
198 | get方法请求如下:
199 |
200 | router.get('/', function (req, res) {
201 | switch (req.query.action) {
202 | case 'del':
203 | //删除操作
204 | db.query('DELETE FROM blog_list_table WHERE id="'+req.query.id+'"',function (err,resultData) {
205 | if(err){
206 | console.error(err);
207 | res.status(500).send({code:500,msg:'database error'});
208 | }else{
209 | res.redirect('/admin/blog');
210 | }
211 | });
212 | break;
213 | case 'mod':
214 | //修改操作
215 | db.query('SELECT * FROM blog_list_table WHERE id="'+req.query.id+'"',function (err,modData) {
216 | if(err){
217 | console.error(err);
218 | res.status(500).send({code:500,msg:'database error'});
219 | }else if(modData.length == 0){
220 | res.status(400).send({code:400,msg:'parameters error'});
221 | }else{
222 | db.query('SELECT * FROM blog_list_table',function (err,allData) {
223 | if(err){
224 | console.error(err);
225 | res.status(500).send({code:500,msg:'database error'});
226 | }else{
227 | res.render('admin/blog.ejs',{formData:allData,modData:modData});
228 | }
229 | });
230 | }
231 | });
232 | break;
233 | default:
234 | db.query('SELECT * FROM blog_list_table', function (err, resultData) {
235 | if (err) {
236 | console.error(err);
237 | res.status(500).send({code: 500, msg: 'database error'}).end();
238 | } else {
239 | res.render('admin/blog.ejs', {formData: resultData});
240 | }
241 | });
242 | }
243 |
244 | });
245 | 这里的switch,主要是分为,查询,删除,和修改
246 | 都是些简单的CRUD操作,这里就不多细说了。不熟悉的兄弟们可以看一看,写的不好,多多提意见。
247 |
248 | 同理,post的请求,主要就是分为,文章列表的添加,和修改的两个post,代码如下:
249 |
250 | router.post('/', function (req, res) {
251 | //此处验证应该更加严格,比如正则
252 | var title = req.body.title.trim();
253 | var author = req.body.author.trim();
254 | var summary = req.body.summary.trim();
255 | var href = req.body.href.trim();
256 |
257 | if (title && author && summary && href) {
258 | if(req.body.modified){
259 | db.query('UPDATE blog_list_table SET title="'+title+'",author="'+author+'",summary="'+summary+'",href="'+href+'" WHERE ID="'+req.body.modified+'"',function (err,resultData) {
260 | if(err){
261 | console.error(err);
262 | res.status(500).send({code:500,msg:'database error'});
263 | }else{
264 | res.redirect('/admin/blog');
265 | }
266 | })
267 | }else{
268 | db.query('INSERT INTO blog_list_table (title,author,summary,href) VALUE("' + title + '","' + author + '","' + summary + '","' + href + '")', function (err, data) {
269 | if (err) {
270 | console.error(err);
271 | res.status(500).send({code: 500, msg: 'database error'}).end();
272 | } else {
273 | res.redirect('/admin/blog');
274 | }
275 | });
276 | }
277 | } else {
278 | res.status(400).send({code: 400, msg: 'parameters error'}).end();
279 | }
280 |
281 | });
282 |
283 |
284 | ##用户管理
285 | 后台的管理大概也即是这么多,用户管理和博文管理基本都是差不多的,这里重点是说下,这里用到的图片上传。
286 | 图片上传我用的是**multer**中间件,不知道的可以查下,注意用这个中间件接受图片上传时form表单的**enctype**必须要设置为multipart/form-data
287 |
288 | 关于图片上传后,默认是不包括后缀名的,所以这里我们需要用到fs模块的重命名操作,代码如下:
289 |
290 | fs.rename(req.files[0].path, req.files[0].path + ext, function (err) {
291 | if (err) {
292 | console.error(err);
293 | res.status(500).send({code: 500, msg: 'data error'});
294 | } else {
295 | db.query('INSERT INTO user_table (username,email,pic_header) VALUE("' + username + '","' +
296 | email + '","' + pic_header + '")', function (err, resultData) {
297 | if (err) {
298 | console.error(err);
299 | res.status(500).send({code: 500, msg: 'database error'});
300 | } else {
301 | res.redirect('/admin/users');
302 | }
303 | });
304 | }
305 | });
306 | 前面的变量定义主要如下:
307 |
308 | var username = req.body.username;
309 | var email = req.body.email;
310 | if(req.files.length>0){
311 | var ext = pathLib.parse(req.files[0].originalname).ext;
312 | var pic_header = '/files/upload/' + req.files[0].filename + ext;
313 | }
314 | 用到path模块对路径的解析。为了获取后缀名~
315 |
316 | 说到这,对于修改也就很简单了,就是删除原先有的那个图片,然后换上现在有的图片,具体代码如下:
317 |
318 | //需要进行一些校验,这里就忽略了
319 | if(req.body.modified){//修改
320 | //查看有没有新传来的头像,如果有,则删除,新建,如果没有,直接更新需要更新的内容
321 | if(req.files.length>0){
322 | //有修改头像,则进行原来头像的删除,再上传
323 | db.query('SELECT * FROM user_table WHERE ID="'+req.body.modified+'"',function (err,modData) {
324 | if (err) {
325 | console.error(err);
326 | res.status(500).send({code: 500, msg: 'database error'});
327 | }else if(modData.length == 0){
328 | res.status(400).send({code: 400, msg: 'parameters error'});
329 | }else{
330 | fs.unlink(modData[0].pic_header.replace('\/files','static'),function (err) {
331 | if(err){
332 | console.error(err);
333 | res.status(500).send({code:500,msg:'operate error'});
334 | }else{
335 | //删除成功,开始对新的文件进行重命名
336 | fs.rename(req.files[0].path, req.files[0].path + ext, function (err) {
337 | if (err) {
338 | console.error(err);
339 | res.status(500).send({code: 500, msg: 'operate error'});
340 | } else {
341 | db.query('UPDATE user_table SET username="'+
342 | username+'",email="' + email + '",pic_header="' +
343 | pic_header + '" WHERE ID="'+req.body.modified+'"',function (err,data) {
344 | if (err) {
345 | console.error(err);
346 | res.status(500).send({code: 500, msg: 'database error'});
347 | }else{
348 | res.redirect('/admin/users');
349 | }
350 | });
351 | }
352 | });
353 | }
354 | })
355 | }
356 | })
357 | }else{
358 | db.query('UPDATE user_table SET username="'+username+'",email="' + email + '" WHERE ID="'+
359 | req.body.modified+'"',function (err,data) {
360 | if (err) {
361 | console.error(err);
362 | res.status(500).send({code: 500, msg: 'database error'});
363 | }else{
364 | res.redirect('/admin/users');
365 | }
366 | });
367 | }
368 | }
369 |
370 | 看到这个代码是不是感觉想死的心都有了???的确,多少篇文章都说到了关于nodejs的回调地狱,但是Node 7.6 发布了,支持了async函数,JavaScript异步的写法彻底改变了。所以这个大可不必太过于担心
371 | 况且Koa不就这么的出来和投入大范围的使用了嘛,这里我们大可用express,去尽情的感受Node的魅力。
372 |
373 | ##结束语
374 | 说到这,基本的一个小小后台管理应用就完事了,是不是感觉没有想象中的那么难?写的不好,欢迎大家吐槽指教~~~
375 | 最后,欢迎愿意一起学习nodejs的朋友加入,Nodejs技术群:209530601
376 | ~~
--------------------------------------------------------------------------------