├── .gitattributes
├── .gitignore
├── README.md
├── app.js
├── config.js
├── controllers
├── app
│ ├── comment.js
│ ├── content.js
│ ├── index.js
│ └── page.js
└── server
│ ├── category.js
│ ├── comment.js
│ ├── content.js
│ ├── file.js
│ ├── index.js
│ ├── log.js
│ ├── me.js
│ ├── message.js
│ ├── notification.js
│ ├── page.js
│ ├── role.js
│ ├── tag.js
│ └── user.js
├── libs
├── core.js
├── crypto.js
├── mailer.js
└── uploader.js
├── middlewares
├── action.js
└── jwt.js
├── models
├── category.js
├── comment.js
├── content.js
├── file.js
├── log.js
├── message.js
├── notification.js
├── option.js
├── page.js
├── role.js
├── tag.js
└── user.js
├── package-lock.json
├── package.json
├── public
├── actions.js
└── assets
│ ├── app
│ ├── css
│ │ └── style.css
│ └── images
│ │ ├── favicon.ico
│ │ └── video-thumb.png
│ ├── plugins
│ └── mditor
│ │ ├── css
│ │ ├── mditor.min.css
│ │ └── mditor.min.css.map
│ │ ├── font
│ │ ├── 674f50d287a8c48dc19ba404d20fe713.eot
│ │ ├── 912ec66d7572ff821749319396470bde.svg
│ │ ├── af7ae505a9eed503f8b8e6982036873e.woff2
│ │ ├── b06871f281fee6b241d60582ae9369b9.ttf
│ │ └── fee66e712a8a08eef5805a46892932ad.woff
│ │ ├── index.html
│ │ └── js
│ │ ├── mditor.min.js
│ │ └── mditor.min.js.map
│ └── server
│ ├── css
│ ├── main.css
│ └── style.css
│ ├── img
│ └── favicon.ico
│ └── js
│ └── application.js
├── routes
├── app
│ ├── comment.js
│ ├── content.js
│ ├── index.js
│ └── page.js
└── server
│ ├── category.js
│ ├── comment.js
│ ├── content.js
│ ├── file.js
│ ├── index.js
│ ├── log.js
│ ├── me.js
│ ├── message.js
│ ├── notification.js
│ ├── page.js
│ ├── role.js
│ ├── tag.js
│ └── user.js
├── services
├── base.js
├── category.js
├── comment.js
├── content.js
├── file.js
├── log.js
├── message.js
├── notification.js
├── page.js
├── role.js
├── tag.js
└── user.js
└── views
├── app
├── contact.hbs
├── content
│ ├── item.hbs
│ └── list.hbs
├── index.hbs
├── info.hbs
└── page
│ ├── item.hbs
│ └── list.hbs
├── layouts
├── app_layout.hbs
├── layout-blank.hbs
└── layout.hbs
├── partials
├── header.hbs
└── sidebar.hbs
└── server
├── category
├── add.hbs
├── edit.hbs
├── item.hbs
└── list.hbs
├── comment
├── item.hbs
└── list.hbs
├── content
├── add.hbs
├── edit.hbs
└── list.hbs
├── error.hbs
├── file
├── add.hbs
└── list.hbs
├── index.hbs
├── info.hbs
├── install.hbs
├── log
├── item.hbs
└── list.hbs
├── me
├── edit.hbs
└── item.hbs
├── message
├── item.hbs
└── list.hbs
├── notification
├── item.hbs
└── list.hbs
├── page
├── item.hbs
└── list.hbs
├── role
├── add.hbs
├── edit.hbs
├── item.hbs
└── list.hbs
├── tag
├── add.hbs
├── edit.hbs
├── item.hbs
└── list.hbs
└── user
├── add.hbs
├── edit.hbs
├── forget.hbs
├── item.hbs
├── list.hbs
├── login.hbs
└── register.hbs
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.js linguist-language=javascript
2 | *.css linguist-language=javascript
3 | *.html linguist-language=javascript
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # 忽略 node_modules/ 目录下的所有文件
2 | node_modules/
3 | /.idea
4 | /public/uploaded/
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## 说明/Description
4 |
5 | 用 node.js 搭建属于自己的blog, UI风格我自己设计成简约的,你可以修改成自己喜欢的。
6 | 没有采用前后端分离和当前流行的MVVM框架,前端也没有模块化和工程化,采用了传统的后端渲染页面方式,一方面项目主要是用来熟悉后端的开发流程和模式,二是为了更好的SEO。
7 | 对于想从前端转向全栈开发的人员,这是一个很好的实践和参考,也是学习node.js很好的案例,项目有一些未使用的服务接口,你可以根据自己的需求去继续扩展。
8 | ***
9 | Use node.js to build your own blog,UI style,I designed my own UI style to be simple,you can modify it to your own liking.
10 | The project does not use the way of front and back separation, nor does it use the popular MVVM framework, such as react and Vue,the front end has not been modularized and engineered,the project uses the traditional backend rendering page,the first is to make front-end developers familiar with the development process and mode of the back-end, and second, for better SEO.
11 | This is a good practice and reference for those who want to move from front-end development to full-stack development. It is also a good case for learning node.js,the project has some unused service interfaces, you can continue to expand according to your needs.
12 |
13 | ## 演示地址/Demo site
14 |
15 | 个人博客[blog.frogo.cn](http://blog.frogo.cn),该博客是部署在阿里云的centOS6.5上面,部署教程可参照 [在阿里云ECS CentOS7上部署基于MongoDB+Node.js的博客](http://blog.frogo.cn/content/5b5ed2810a7a1d0a11922bd5)
16 |
17 | Personal Blog [blog.frogo.cn](http://blog.frogo.cn)。
18 |
19 | ## 主要技术列表/Main technology list
20 |
21 | - 服务端:Node.js `>=7.9.0 `
22 | - 数据库:MongoDB `>=3.4`
23 | - 数据库操作工具:mongoose`4.9`
24 | - WEB框架:Express `4.0`
25 | - 模板引擎:express-handlebars `>=3.0`
26 | - JS和UI库:JQuery`>=2.0` Bootstrap`3.3.7`
27 | ***
28 | - server:Node.js `>=7.9.0 `
29 | - database:MongoDB `>=3.4`
30 | - Database operation tool:mongoose`4.9`
31 | - WEB framework:Express `4.0`
32 | - Template engine:express-handlebars `>=3.0`
33 | - UI library:JQuery`>=2.0` Bootstrap`3.3.7`
34 |
35 |
36 |
37 | ## 目录结构/Directory Structure
38 |
39 | 后端采用传统的MVC结构,models是对象模型(就是数据格式),views是视图层,controllers是操作数据的层,所有的请求通过routes(路由)分发。
40 |
41 | The Back-End uses the traditional `MVC` structure, `models` are the object model (that is, the data format), `views` are the view layer, `controllers` are the layers of the operational data, and all requests are distributed through routes.
42 | ```tree
43 | ├── models (M)
44 | ├── views (V)
45 | │ ├── app
46 | │ ├── layout
47 | │ ├── partials
48 | │ └── server
49 | ├── controllers (C)
50 | │ ├── app
51 | │ └── server
52 | ├── routes (路由)
53 | │ ├── app
54 | │ └── server
55 | ├── services (后端服务接口)
56 | ├── libs (一些功能函数,诸如邮件,加密, some functions, such as mail, encryption)
57 | ├── middlewares (中间件)
58 | ├── node_modules
59 | ├── public
60 | │ ├── assets (前端一些静态文件)
61 | │ │ ├── app
62 | │ │ │ ├── pugins
63 | │ │ │ └── server
64 | │ │ ├── uploaded (图片上传目录)
65 | │ │ │ ├── files
66 | │ │ │ └── tmp
67 | ├── .gitignore
68 | ├── app.js
69 | ├── config.js
70 | ├── package-lock.json
71 | ├── package.json
72 | ├── readme.md
73 | ```
74 | ## 安装运行和调试 / Installation & Run & Debugging
75 |
76 | 1. 请在Linux或者windows系统下确保安装了正确版本的Node.js 和 MongoDB, MongoDB教程自行学习。
77 | 2. 进入blog目录 `cd blog`
78 | 3. 安装依赖包 npm install
79 | 4. 打开MongoDB 终端,创建一个名叫blog的数据库
80 | 5. 修改config.js里面的数据库连接,一般是url: 'mongodb://localhost:27017/blog'
81 | 6. npm run dev 进入开发环境
82 | 7. 浏览器打开 按照提示安装初始化
83 | 8. 调试可以打开chrome,地址栏输入 chrome://inspect ,打开`Open dedicated DevTools for Node`会出来一个调试窗口,当然也可以使用WebStorm,打开debug模式去调试。
84 | ***
85 | 1. Please ensure that the correct version of Node.js and MongoDB are installed under Linux or Windows. MongoDB tutorial learns by yourself.
86 | 2. Go to the *blog* directory `cd blog`
87 | 3. Install node.js dependency package `npm install`
88 | 4. Open the MongoDB terminal and create a database called blog.
89 | 5. Modify the database connection in config.js, usually url: 'mongodb://localhost:27017/blog'
90 | 6. `npm run dev` enters the development environment
91 | 7. The browser opens Follow the prompts to install the initialization.
92 | 8. You can open the chrome browser to debug the code of the node.js server, enter chrome://inspect in the address bar, open `Open dedicated DevTools for Node` will come up with a debugging window, Of course, you can also use WebStorm to open the debug mode to debug.
93 |
94 | ## 截图 / ScreenShot
95 |
96 | 
97 |
98 | 
99 |
100 | 
101 |
102 |
--------------------------------------------------------------------------------
/config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let appPath = process.cwd();
4 | let config = {
5 | port: 7000,
6 | env: process.env.NODE_ENV || 'development', // development production
7 | //mongodb配置信息
8 | mongodb: {
9 | uri: 'mongodb://username:password@121.40.123.2:27085/blog',
10 | options: {}
11 | },
12 | //redis服务,用来session维持,非必须
13 | redis: {
14 | host: '', // 127.0.0.1 配置此项表示启用redis,需保证redis服务已启用
15 | port: 6379, // 6379
16 | pass: ''
17 | },
18 | //找回密码hash过期时间
19 | findPasswordTill: 24 * 60 * 60 * 1000,
20 | // session secret
21 | sessionSecret: 'SessionSecret',
22 | // jsonwebtoken config
23 | jwt: {
24 | secret: 'JWTSecret',
25 | options: {
26 | expiresIn: '10h'
27 | }
28 | },
29 | title: '',
30 | //后台相关配置
31 | admin: {
32 | dir: 'admin', //后台访问路径,如http://localhost/admin配置为admin
33 | role: {//默认角色?
34 | admin: 'admin',
35 | user: 'user'
36 | }
37 | },
38 | upload: {
39 | tmpDir: appPath + '/public/uploaded/tmp',
40 | uploadDir: appPath + '/public/uploaded/files',
41 | uploadUrl: '/uploaded/files/',
42 | maxPostSize: 100 * 1024 * 1024, // 100M
43 | minFileSize: 1,
44 | maxFileSize: 50 * 1024 * 1024, // 50M
45 | acceptFileTypes: /.+/i,
46 | storage: {
47 | type: 'local',//保存类型,如果保存到本地可省略或local, 目前支持7牛:qiniu
48 | options: {
49 | accessKey: 'your key',
50 | secretKey: 'your secret',
51 | bucket: 'your bucket',
52 | domain: 'http://yourdomain.qiniudn.com', // 域名,包含http,如http://yourdomain.qiniudn.com
53 | timeout: 3600000, // default rpc timeout: one hour, optional
54 | }
55 | }
56 | },
57 | stopForumSpam: false,
58 | // 是否启动用户注册校验TODO:
59 | userVerify: {
60 | enable: false,
61 | type: 'admin' // mail | admin, 默认admin
62 | },
63 | // 邮箱配置,找回密码、用户注册使�?
64 | mail: {
65 | // 发信人邮�?
66 | from: '66118181@qq.com',
67 | // nodemailer config see https://nodemailer.com/about/
68 | nodemailer: {
69 | // https://nodemailer.com/smtp/
70 | service: 'gmail',
71 | host: '',
72 | port: '',
73 | auth: {
74 | user: '',
75 | pass: ''
76 | }
77 | }
78 |
79 | },
80 | // google analytics
81 | ga: '',
82 | // 短信服务配置TODO:
83 | sms: {
84 |
85 | }
86 | };
87 |
88 | module.exports = config;
89 |
--------------------------------------------------------------------------------
/controllers/app/comment.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let mongoose = require('mongoose')
4 | let Content = mongoose.model('Content')
5 | let Comment = mongoose.model('Comment')
6 | let gravatar = require('gravatar')
7 | let config = require('../../config')
8 | let _ = require('lodash')
9 | let xss = require('xss')
10 | let core = require('../../libs/core')
11 |
12 | //添加
13 | exports.add = function(req, res) {
14 | if (req.method === 'GET') {
15 |
16 | } else if (req.method === 'POST') {
17 | let obj = _.pick(req.body, 'content', 'from', 'reply', 'name', 'email', 'website');
18 | obj.ip = core.getIp(req);
19 | obj.content = xss(obj.content)
20 | if (req.session.user) {
21 | obj.author = req.session.user._id;
22 | }
23 | Content.findById(obj.from, function(err, content) {
24 | let comment = new Comment(obj);
25 | comment.save(function(err, result) {
26 | if(err || !result) {
27 | return res.json({
28 | success: false,
29 | message: '出错啦'
30 | });
31 | }
32 | if(!content.comments) {
33 | content.comments = [];
34 | }
35 | content.comments.push(result._id);
36 | content.save();
37 | if(obj.reply) {
38 | Comment.findById(obj.reply, function(err, comment) {
39 | if(!comment.comments) {
40 | comment.comments = [];
41 | }
42 | comment.comments.push(result._id);
43 | comment.save();
44 | });
45 | }
46 | let json = _.assign({}, _.pick(result, 'id', 'content', 'created', 'name', 'email', 'reply', 'from', 'ip'), {
47 | avatar: gravatar.url(result.email || '', {s: '40',r: 'x',d: 'retro'}, true)
48 | });
49 | res.json({
50 | success: true,
51 | message: '评论成功',
52 | data: json
53 | });
54 | });
55 | });
56 |
57 | }
58 | };
59 | //删除
60 | exports.del = function(req, res) {
61 | if(!req.session.user) {
62 | return res.render('app/info.hbs', { layout:'app_layout',
63 | message: '请先登录'
64 | });
65 | }
66 |
67 | };
68 | //删除
69 | exports.one = function(req, res) {
70 | if(!req.session.user) {
71 | return res.render('app/info.hbs', { layout:'app_layout',
72 | message: '请先登录'
73 | });
74 | }
75 |
76 | };
77 |
78 | exports.list = function(req, res) {
79 | let condition = {};
80 | Comment.count(condition, function(err, total) {
81 | let query = Comment.find({}).populate('author').populate('from');
82 | //分页
83 | let pageInfo = core.createPage(req.query.page, total);
84 | query.skip(pageInfo.start);
85 | query.limit(pageInfo.pageSize);
86 | query.sort({created: -1});
87 | query.exec(function(err, results) {
88 | res.render('app/comment', {
89 | comments: results,
90 | pageInfo: pageInfo
91 | });
92 | })
93 | })
94 | /*Comment.find({}).populate('author').exec(function(err, results) {
95 | //console.log(results);
96 | res.render('app/comment', {
97 | comments: results
98 | });
99 | })*/
100 | }
--------------------------------------------------------------------------------
/controllers/app/content.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let mongoose = require('mongoose')
4 | let Comment = mongoose.model('Comment')
5 | let Content = mongoose.model('Content')
6 | let Category = mongoose.model('Category')
7 | let core = require('../../libs/core')
8 | let strip = require('strip');
9 |
10 | //列表
11 | /*exports.list = function(req, res) {
12 | let condition = {};
13 | let category = req.query.category;
14 | if(category) {
15 | condition.category = category;
16 | }
17 | //查数据总数
18 | Content.count(condition, function(err, total) {
19 | let query = Content.find(condition).populate('author', 'username name email');
20 | //分页
21 | let pageInfo = core.createPage(req.query.page, total, 30);
22 | //console.log(pageInfo);
23 | query.skip(pageInfo.start);
24 | query.limit(pageInfo.pageSize);
25 | query.sort({created: -1});
26 | query.exec(function(err, results) {
27 | //console.log(err, results);
28 | res.render('app/content/list.hbs', {
29 | layout: 'app_layout',
30 | title: '内容列表',
31 | contents: results,
32 | pageInfo: pageInfo
33 | });
34 | });
35 | });
36 |
37 | };*/
38 | //单条
39 | exports.one = function(req, res) {
40 | let id = req.params.id;
41 | Content.findById(id).populate('tags').populate('author').populate('category').populate('comments').populate('gallery').exec(function(err, result) {
42 | //console.log(result);
43 | if(!result) {
44 | return res.render('app/info.hbs', { layout:'app_layout',
45 | message: '该内容不存在'
46 | });
47 | }
48 | result.visits = result.visits + 1;
49 | result.save();
50 | res.render('app/content/item.hbs', {
51 | layout:'app_layout',
52 | title: result.title,
53 | description:result.content,
54 | keywords: result.keywords,
55 | content: result
56 | });
57 | });
58 | };
--------------------------------------------------------------------------------
/controllers/app/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let mongoose = require('mongoose')
4 | let Content = mongoose.model('Content')
5 | let Message = mongoose.model('Message')
6 | let Category = mongoose.model('Category')
7 | let _ = require('lodash')
8 | let File = mongoose.model('File')
9 | let config = require('../../config')
10 | let core = require('../../libs/core')
11 | let contentService = require('../../services/content')
12 | exports.index = async function(req, res) {
13 | let condition = {};
14 | let key = req.query.key;
15 | if(key) {
16 | let _key = key.replace(/([\(\)\[])/g, '\\$1');//正则bugfix
17 | let k = '[^\s]*' + _key + '[^\s]*';
18 | let reg = new RegExp(k, 'gi');
19 | condition.title = reg;
20 | }
21 |
22 | try {
23 | let total = await contentService.count(condition)
24 | let pageInfo = core.createPage(req.query.page, total);
25 | let contents = await contentService.find(condition, null, {
26 | populate: 'author gallery',
27 | skip: pageInfo.start,
28 | limit: pageInfo.pageSize,
29 | sort: {
30 | created: -1
31 | }
32 | })
33 |
34 | let newest = await contentService.find(condition, null, {
35 | limit: 10,
36 | sort: {
37 | created: -1
38 | }
39 | })
40 |
41 | let hotest = await contentService.find(condition, null, {
42 | limit: 10,
43 | sort: {
44 | visits: -1
45 | }
46 | })
47 | res.render('app/index.hbs', {
48 | layout: 'app_layout',
49 | contents: contents,
50 | pageInfo: pageInfo,
51 | key: key,
52 | total: total,
53 | newest: newest,
54 | hotest: hotest
55 | });
56 |
57 | } catch (e) {
58 | console.log(e)
59 | res.render('app/info.hbs', { layout:'app_layout',
60 | message: '系统开小差了,请稍等'
61 | });
62 | }
63 | };
64 |
65 | exports.contact = function(req, res) {
66 | if(req.method === 'GET') {
67 | res.render('app/contact.hbs', {
68 | layout: 'app_layout',
69 | Path: 'contact'
70 | });
71 | } else if (req.method === 'POST') {
72 | let obj = _.pick(req.body, 'name', 'email', 'content');
73 | obj.ip = core.getIp(req);
74 | let contact = new Message(obj);
75 | contact.save(function(err, result) {
76 | console.log(err, result);
77 | if (err) {
78 | return res.render('app/info.hbs', { layout:'app_layout',
79 | message: err.message
80 | });
81 | } else {
82 | res.render('app/info.hbs', { layout:'app_layout',
83 | message: '提交成功!'
84 | });
85 | }
86 | })
87 |
88 | }
89 |
90 | }
91 |
92 |
--------------------------------------------------------------------------------
/controllers/app/page.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let mongoose = require('mongoose')
4 | let Page = mongoose.model('Page')
5 | let config = require('../../config')
6 | let core = require('../../libs/core')
7 |
8 | //列表
9 | exports.list = function(req, res) {
10 | let condition = {};
11 | /*if(req.Roles && req.Roles.indexOf('admin') < 0) {
12 | condition.author = req.session.user._id;
13 | }*/
14 | Page.count(condition, function(err, total) {
15 | let query = Page.find(condition).populate('author');
16 | //分页
17 | let pageInfo = core.createPage(req.query.page, total);
18 | //console.log(pageInfo);
19 | query.skip(pageInfo.start);
20 | query.limit(pageInfo.pageSize);
21 | query.sort({created: -1});
22 | query.exec(function(err, results) {
23 | //console.log(err, results);
24 | res.render('app/page/list.hbs', {
25 | layout: 'app_layout',
26 | //title: '列表',
27 | pages: results,
28 | pageInfo: pageInfo
29 | });
30 | })
31 | })
32 | };
33 |
34 | //单条
35 | exports.one = function(req, res) {
36 | let id = req.params.id;
37 | Page.findById(id).populate('author').exec(function(err, result) {
38 | console.log(result);
39 | if(!result) {
40 | return res.render('app/info.hbs', {
41 | layout: 'app_layout',
42 | message: '该留言不存在'
43 | });
44 | }
45 | res.render('app/page/item.hbs', {
46 | layout: 'app_layout',
47 | title: result.title,
48 | page: result
49 | });
50 | });
51 | };
--------------------------------------------------------------------------------
/controllers/server/comment.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let mongoose = require('mongoose')
4 | let Comment = mongoose.model('Comment')
5 | let core = require('../../libs/core')
6 | let backPath = 'comment'
7 | //列表
8 | exports.list = function(req, res) {
9 | let condition = {};
10 | if(req.Roles && req.Roles.indexOf('admin') < 0) {
11 | condition.author = req.session.user._id;
12 | }
13 | Comment.count(condition, function(err, total) {
14 | let query = Comment.find(condition).populate('author').populate('from');
15 | //分页
16 | let pageInfo = core.createPage(req.query.page, total);
17 | //console.log(pageInfo);
18 | query.skip(pageInfo.start);
19 | query.limit(pageInfo.pageSize);
20 | query.sort({created: -1});
21 | query.exec(function(err, results) {
22 | //console.log(err, results);
23 | res.render('server/comment/list.hbs', {
24 | title: '评论列表',
25 | Menu:'comment',
26 | comments: results,
27 | pageInfo: pageInfo
28 | });
29 | })
30 | })
31 |
32 | };
33 | //单条
34 | exports.one = function(req, res) {
35 | let id = req.params.id;
36 | Comment.findById(id).populate('author', 'username name email').populate('from').exec(function(err, result) {
37 | console.log(result);
38 | if(!result) {
39 | return res.render('server/info.hbs', { layout:'layout-blank',
40 | message: '该评论不存在' ,backPath:backPath
41 | });
42 | }
43 | res.render('server/comment/item.hbs', {
44 | title: result.name,
45 | Menu:'comment',
46 | comment: result
47 | });
48 | });
49 | };
50 | //删除
51 | exports.del = function(req, res) {
52 | let id = req.params.id;
53 | Comment.findById(id).populate('author').populate('from').exec(function(err, result) {
54 | if(!result) {
55 | return res.render('server/info.hbs', { layout:'layout-blank',
56 | message: '评论不存在' ,backPath:backPath
57 | });
58 | }
59 |
60 | let isAdmin = req.Roles && req.Roles.indexOf('admin') > -1;
61 | let isOwner = result.from && ((result.from.author + '') === req.session.user._id);
62 |
63 | if(!isAdmin && !isOwner) {
64 | return res.render('server/info.hbs', { layout:'layout-blank',
65 | message: '没有权限' ,backPath:backPath
66 | });
67 | }
68 | console.log(result)
69 | result.remove(function(err) {
70 | if (req.xhr) {
71 | return res.json({
72 | status: !err
73 | });
74 | }
75 | if(err) {
76 | return res.render('server/info.hbs', { layout:'layout-blank',
77 | message: '删除失败' ,backPath:backPath
78 | });
79 | }
80 | res.render('server/info.hbs', { layout:'layout-blank',
81 | message: '删除成功' ,backPath:backPath
82 | })
83 | });
84 | });
85 | };
--------------------------------------------------------------------------------
/controllers/server/file.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let mongoose = require('mongoose')
4 | let fs = require('fs')
5 | let path = require('path')
6 | let File = mongoose.model('File')
7 | let _ = require('lodash')
8 | let config = require('../../config')
9 | let core = require('../../libs/core')
10 | let backPath = 'file'
11 | console.log(config.upload)
12 | let uploader = require('../../libs/uploader')(config.upload);
13 | //列表
14 | exports.list = function(req, res) {
15 | //console.log(req.cookies['XSRF-TOKEN'])
16 | let condition = {};
17 | if(req.Roles && req.Roles.indexOf('admin') < 0) {
18 | condition.author = req.session.user._id;
19 | }
20 | File.count(condition, function(err, total) {
21 | let query = File.find(condition).populate('author');
22 | //分页
23 | let pageInfo = core.createPage(req.query.page, total);
24 | console.log(pageInfo)
25 | query.skip(pageInfo.start);
26 | query.limit(pageInfo.pageSize);
27 | query.sort({created: -1});
28 | query.exec(function(err, results) {
29 | console.log(results)
30 | res.render('server/file/list.hbs', {
31 | files: results,
32 | pageInfo: pageInfo,
33 | Menu: 'file'
34 | });
35 | });
36 | })
37 | };
38 | //添加
39 | exports.add = function(req, res) {
40 | if (req.method === 'GET') {
41 | res.render('server/file/add.hbs', {
42 | Menu: 'file'
43 | });
44 | } else if (req.method === 'POST') {
45 | //console.log(req.files);
46 | //console.log(req.body);
47 | console.log(req.headers.host)
48 | uploader.post(req, res, function (result) {
49 | console.log('上传结果', result);
50 |
51 | if(!result || !result.files) {
52 | return;
53 | }
54 | let id = req.body.id;
55 | //如果是修改文件,则不保存到服务器
56 | if(id) {
57 | console.log('修改文件');
58 | File.findById(id).populate('author').exec(function(err, file) {
59 | let isAdmin = req.Roles && req.Roles.indexOf('admin') > -1;
60 | let isAuthor = file.author && ((file.author._id + '') === req.session.user._id);
61 |
62 | if(!isAdmin && !isAuthor) {
63 | return res.render('server/info.hbs', { layout:'layout-blank',
64 | message: '没有权限' ,backPath:backPath
65 | });
66 | }
67 | //TODO: 删除之前的文件 uploader.delete()?
68 | fs.unlink(config.upload.uploadDir + '/' + file.name, function(err) {
69 | console.log('删除成功', config.upload.uploadDir + '/' + file.name)
70 | });
71 | // todo: 更新文件信息
72 |
73 | res.json(result);
74 | });
75 | return;
76 | }
77 | let len = result.files.length;
78 | let json = {
79 | files: []
80 | };
81 | result.files.forEach(function(item) {
82 | if(req.session.user) {
83 | item.author = req.session.user._id;
84 | }
85 | if(item.url.indexOf('%20')){
86 | item.url=item.url.replace("%20"," ");
87 | }
88 | //这里还可以处理url
89 | let fileObj = item;//_.pick(item, 'name', 'size', 'type', 'url');
90 | console.log(fileObj);
91 | let file = new File(fileObj);
92 | file.save(function(err, obj) {
93 | if(err || !obj) {
94 | console.log('保存file失败', err, obj);
95 | return;
96 | }
97 | len --;
98 | item._id = obj._id;
99 | json.files.push(item);
100 | if(len === 0) {
101 | console.log(json)
102 | res.json(json);
103 | }
104 | });
105 | });
106 | });
107 | }
108 | };
109 | //删除
110 | exports.del = function(req, res) {
111 | let id = req.params.id;
112 | File.findById(id).populate('author').exec(function(err, result) {
113 | if(!result) {
114 | return res.render('server/info.hbs', { layout:'layout-blank',
115 | message: '文件不存在' ,backPath:backPath
116 | });
117 | }
118 | let isAdmin = req.Roles && req.Roles.indexOf('admin') > -1;
119 | let isAuthor = result.author && ((result.author._id + '') === req.session.user._id);
120 |
121 | if(!isAdmin && !isAuthor) {
122 | return res.render('server/info.hbs', { layout:'layout-blank',
123 | message: '没有权限' ,backPath:backPath
124 | });
125 | }
126 | //console.log(result);
127 | let url = result.url;
128 | uploader.delete(url, function(e) {
129 | if (e) {
130 | console.log('文件删除失败')
131 | return res.json({
132 | success: false,
133 | error: e
134 | });
135 | }
136 | result.remove(function(err) {
137 | if(req.xhr) {
138 | return res.json({
139 | success: !err,
140 | error: err
141 | });
142 | }
143 | if(err) {
144 | return res.render('server/info.hbs', { layout:'layout-blank',
145 | message: '删除失败' ,backPath:backPath
146 | });
147 | }
148 | res.render('server/info.hbs', { layout:'layout-blank',
149 | message: '删除成功' ,backPath:backPath
150 | });
151 | })
152 | })
153 | });
154 | };
--------------------------------------------------------------------------------
/controllers/server/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let mongoose = require('mongoose')
4 | let User = mongoose.model('User')
5 | let Content = mongoose.model('Content')
6 | let Category = mongoose.model('Category')
7 | let Comment = mongoose.model('Comment')
8 | let File = mongoose.model('File')
9 | let Role = mongoose.model('Role')
10 | let userController = require('./user')
11 | let config = require('../../config')
12 | let core = require('../../libs/core')
13 |
14 | //后台首页
15 | exports.index = function(req, res) {
16 | if(!req.session.user) {
17 | let path = core.translateAdminDir('/user/login');
18 | return res.redirect(path);
19 | }
20 | let obj = {}
21 | let filter = {};
22 | if(req.Roles && req.Roles.indexOf('admin') < 0) {
23 | filter = {author: req.session.user._id};
24 | }
25 | Promise.all([
26 | Content.find(filter).count().exec(),
27 | Category.find(filter).count().exec(),
28 | Comment.find(filter).count().exec(),
29 | User.find(filter).count().exec(),
30 | Role.find(filter).count().exec(),
31 | File.find(filter).count().exec()
32 | ]).then((result) => {
33 | //console.log(result)
34 | res.render('server/index.hbs', {
35 | title: '后台',
36 | data: {
37 | content: result[0],
38 | category: result[1],
39 | comment: result[2],
40 | user: result[3],
41 | role: result[4],
42 | file: result[5]
43 | }
44 | });
45 | }).catch((e) => {
46 |
47 | })
48 | };
49 |
50 | //初始化后台,安装初始数据
51 | exports.install = function(req, res) {
52 | if(req.session.user) {
53 | let path = core.translateAdminDir('');
54 | return res.redirect(path);
55 | }
56 | //检查是否已经有用户
57 | User.find({}, function(err, results) {
58 | console.log(err, results);
59 | if(err) {
60 | return;
61 | }
62 | if(results.length < 1) {
63 | //满足条件
64 | if(req.method === 'GET') {
65 | res.render('server/install.hbs', {
66 | title: '初始化'
67 | });
68 | } else if(req.method === 'POST') {
69 | let createUser = function(obj) {
70 | let user = new User(obj);
71 | user.save(function() {
72 | res.render('server/info.hbs', { layout:'layout-blank',
73 | message: '初始化完成'
74 | });
75 | });
76 | };
77 | let obj = req.body;
78 | obj.status = 101;//系统默认管理员
79 | //检查是否有角色,没有的话创建
80 | Role.find({status: 201}, function(err, roles) {
81 | console.log('查找role', err, roles)
82 | if(roles.length < 1) {
83 | console.log('没有角色 ' + config.admin.role.admin);
84 | let role = new Role({
85 | name: config.admin.role.admin,
86 | actions: [],
87 | status: 201//系统默认管理员角色
88 | });
89 | role.save(function(err, result) {
90 | console.log('role result', result);
91 | obj.roles = [role._id];
92 | createUser(obj);
93 | });
94 | //创建普通角色
95 | new Role({
96 | name: config.admin.role.user,
97 | actions: [],
98 | status: 202//系统默认用户角色
99 | }).save();
100 | }else{
101 | obj.roles = [roles[0]._id];
102 | createUser(obj);
103 | }
104 | })
105 | }
106 | }else{
107 | //已经初始化过,跳过
108 | let path = core.translateAdminDir('');
109 | res.redirect(path);
110 | }
111 | })
112 | };
--------------------------------------------------------------------------------
/controllers/server/log.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let mongoose = require('mongoose')
4 | let Log = mongoose.model('Log')
5 | let core = require('../../libs/core')
6 |
7 | //列表
8 | exports.list = function(req, res) {
9 | let condition = {};
10 | if(req.Roles && req.Roles.indexOf('admin') < 0) {
11 | condition.author = req.session.user._id;
12 | }
13 | Log.count(condition, function(err, total) {
14 | let query = Log.find(condition);
15 | //分页
16 | let pageInfo = core.createPage(req.query.page, total);
17 | //console.log(pageInfo);
18 | query.skip(pageInfo.start);
19 | query.limit(pageInfo.pageSize);
20 | query.sort({created: -1});
21 | query.exec(function(err, results) {
22 | //console.log(err, results);
23 | res.render('server/log/list.hbs', {
24 | title: '日志列表',
25 | Menu:'log',
26 | logs: results,
27 | pageInfo: pageInfo
28 | });
29 | })
30 | })
31 | };
32 |
33 | //单条
34 | exports.one = function(req, res) {
35 | let id = req.params.id;
36 | Log.findById(id).populate('author', 'username name').exec(function(err, result) {
37 | console.log(result);
38 | if(!result) {
39 | return res.render('server/info.hbs', { layout:'layout-blank',
40 | message: '该页面不存在'
41 | });
42 | }
43 | res.render('server/log/item.hbs', {
44 | title: '日志详情',
45 | Menu:'log',
46 | log: result
47 | });
48 | });
49 | };
50 | //删除
51 | exports.del = function(req, res) {
52 | let id = req.params.id;
53 | Log.findById(id).populate('author').exec(function(err, result) {
54 | if(!result) {
55 | return res.render('server/info.hbs', { layout:'layout-blank',
56 | message: '留言不存在'
57 | });
58 | }
59 | let isAdmin = req.Roles && req.Roles.indexOf('admin') > -1;
60 | let isAuthor = result.author && ((result.author._id + '') === req.session.user._id);
61 |
62 | if(!isAdmin && !isAuthor) {
63 | return res.render('server/info.hbs', { layout:'layout-blank',
64 | message: '没有权限'
65 | });
66 | }
67 | console.log(result)
68 | result.remove(function(err) {
69 | if (req.xhr) {
70 | return res.json({
71 | status: !err
72 | });
73 | }
74 | if(err) {
75 | return res.render('server/info.hbs', { layout:'layout-blank',
76 | message: '删除失败'
77 | });
78 | }
79 | res.render('server/info.hbs', { layout:'layout-blank',
80 | message: '删除成功'
81 | })
82 | });
83 | });
84 | };
85 |
--------------------------------------------------------------------------------
/controllers/server/me.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let mongoose = require('mongoose')
4 | let User = mongoose.model('User')
5 | let Role = mongoose.model('Role')
6 | let userController = require('./user')
7 | let _ = require('lodash')
8 | let config = require('../../config')
9 | let core = require('../../libs/core')
10 | const ACTIONS = require('../../public/actions')
11 |
12 | //管理员资料
13 | exports.init = function(req, res) {
14 | let id = req.session.user._id;
15 | User.findById(id).populate('roles').exec(function(err, user) {
16 | if (!user) {
17 | return res.render('server/info.hbs', { layout:'layout-blank',
18 | message: '出错了'
19 | });
20 | }
21 |
22 | let actions = [];
23 | if (req.Roles.indexOf('admin') > -1) {
24 | actions = ACTIONS;
25 | } else {
26 | actions = ACTIONS.filter(function(item) {
27 | let items = item.actions.filter(function(act) {
28 | return req.Actions.indexOf(act.value) > -1;
29 | });
30 | if (items.length > 0) {
31 | return item;
32 | }
33 | })
34 | }
35 | res.render('server/me/item.hbs', {
36 | title: '我的资料',
37 | user: user,
38 | ACTIONS: actions
39 | });
40 | });
41 | };
42 | //修改管理员信息
43 | exports.edit = function(req, res) {
44 | let id = req.session.user._id;
45 | if(req.method === 'GET') {
46 | User.findById(id).populate('roles').exec(function(err, user) {
47 | let data = _.pick(user, 'name', 'mobile', 'gender', 'birthday');
48 | res.render('server/me/edit.hbs', {
49 | title: '修改资料',
50 | user: data
51 | });
52 | });
53 | } else if(req.method === 'POST') {
54 | let obj = req.body;
55 | User.findById(id).populate('roles').exec(function(err, user) {
56 | _.assign(user, _.pick(obj, 'name', 'mobile', 'gender', 'birthday'));
57 | user.save(function(err, result) {
58 | console.log(err, result);
59 | if(err || !result) {
60 | return res.render('server/info.hbs', { layout:'layout-blank',
61 | message: '修改失败'
62 | });
63 | }
64 | req.session.user = result;
65 | res.locals.User = user;
66 | res.render('server/info.hbs', { layout:'layout-blank',
67 | message: '修改成功'
68 | });
69 | })
70 | });
71 | }
72 | };
73 | //修改密码
74 | exports.updatePassword = function(req, res) {
75 | if(req.method === 'GET') {
76 |
77 | } else if(req.method === 'POST') {
78 | let obj = req.body;
79 | let oldPassword = obj.oldpassword;
80 | let password = obj.password;
81 | let id = req.session.user._id;
82 | User.findById(id).exec(function(err, user) {
83 | if (user.authenticate(oldPassword)) {
84 | user.password = password;
85 | user.token_version ++;
86 | user.save(function(err, result) {
87 | //console.log('fffffffffffff', result);
88 | userController.reload(result._id, function(err, user) {
89 | req.session.user = user;
90 | res.locals.User = user;
91 | res.render('server/info.hbs', { layout:'layout-blank',
92 | message: '密码修改成功'
93 | });
94 | });
95 | });
96 | } else {
97 | res.render('server/info.hbs', { layout:'layout-blank',
98 | message: '原密码不正确'
99 | });
100 | }
101 | });
102 | }
103 | };
--------------------------------------------------------------------------------
/controllers/server/message.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let mongoose = require('mongoose')
4 | let Message = mongoose.model('Message')
5 | let core = require('../../libs/core')
6 | let backPath = 'message';
7 | //列表
8 | exports.list = function(req, res) {
9 | let condition = {};
10 | /*if(req.Roles && req.Roles.indexOf('admin') < 0) {
11 | condition.author = req.session.user._id;
12 | }*/
13 | Message.count(condition, function(err, total) {
14 | let query = Message.find(condition);
15 | //分页
16 | let pageInfo = core.createPage(req.query.page, total);
17 | //console.log(pageInfo);
18 | query.skip(pageInfo.start);
19 | query.limit(pageInfo.pageSize);
20 | query.sort({created: -1});
21 | query.exec(function(err, results) {
22 | //console.log(err, results);
23 | res.render('server/message/list.hbs', {
24 | title: '留言列表',
25 | Menu: 'message',
26 | messages: results,
27 | pageInfo: pageInfo
28 | });
29 | })
30 | })
31 |
32 | };
33 | //单条
34 | exports.one = function(req, res) {
35 | let id = req.params.id;
36 | Message.findById(id).exec(function(err, result) {
37 | console.log(result);
38 | if(!result) {
39 | return res.render('server/info.hbs', { layout:'layout-blank',
40 | message: '该留言不存在' ,backPath:backPath
41 | });
42 | }
43 | res.render('server/message/item.hbs', {
44 | title: result.name + '的留言',
45 | Menu: 'message',
46 | message: result
47 | });
48 | });
49 | };
50 | //删除
51 | exports.del = function(req, res) {
52 | let id = req.params.id;
53 | Message.findById(id).exec(function(err, result) {
54 | if(!result) {
55 | return res.render('server/info.hbs', { layout:'layout-blank',
56 | message: '留言不存在' ,backPath:backPath
57 | });
58 | }
59 | /*if(req.Roles && req.Roles.indexOf('admin') === -1) {
60 | return res.render('server/info.hbs', { layout:'layout-blank',
61 | message: '没有权限'
62 | });
63 | }*/
64 | console.log(result)
65 | result.remove(function(err) {
66 | if (req.xhr) {
67 | return res.json({
68 | status: !err
69 | });
70 | }
71 | if(err) {
72 | return res.render('server/info.hbs', { layout:'layout-blank',
73 | message: '删除失败' ,backPath:backPath
74 | });
75 | }
76 | res.render('server/info.hbs', { layout:'layout-blank',
77 | message: '删除成功' ,backPath:backPath
78 | })
79 | });
80 | });
81 | };
--------------------------------------------------------------------------------
/controllers/server/page.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let mongoose = require('mongoose')
4 | let Page = mongoose.model('Page')
5 | let _ = require('lodash')
6 | let core = require('../../libs/core')
7 | let backPath = 'page'
8 | //列表
9 | exports.list = function(req, res) {
10 | let condition = {};
11 | if(req.Roles && req.Roles.indexOf('admin') < 0) {
12 | condition.author = req.session.user._id;
13 | }
14 | Page.count(condition, function(err, total) {
15 | let query = Page.find(condition);
16 | //分页
17 | let pageInfo = core.createPage(req.query.page, total);
18 | //console.log(pageInfo);
19 | query.skip(pageInfo.start);
20 | query.limit(pageInfo.pageSize);
21 | query.sort({created: -1});
22 | query.exec(function(err, results) {
23 | //console.log(err, results);
24 | res.render('server/page/list.hbs', {
25 | title: '单页面',
26 | Menu:'page',
27 | pages: results,
28 | pageInfo: pageInfo
29 | });
30 | })
31 | })
32 | };
33 |
34 | //单条
35 | exports.one = function(req, res) {
36 | let id = req.params.id;
37 | Page.findById(id).populate('author').exec(function(err, result) {
38 | console.log(result);
39 | if(!result) {
40 | return res.render('server/info.hbs', { layout:'layout-blank',
41 | message: '该页面不存在' ,backPath:backPath
42 | });
43 | }
44 | res.render('server/page/item.hbs', {
45 | title: result.title,
46 | page: result
47 | });
48 | });
49 | };
50 | //删除
51 | exports.del = function(req, res) {
52 | let id = req.params.id;
53 | Page.findById(id).populate('author').exec(function(err, result) {
54 | if(!result) {
55 | return res.render('server/info.hbs', { layout:'layout-blank',
56 | message: '留言不存在' ,backPath:backPath
57 | });
58 | }
59 | let isAdmin = req.Roles && req.Roles.indexOf('admin') > -1;
60 | let isAuthor = result.author && ((result.author._id + '') === req.session.user._id);
61 |
62 | if(!isAdmin && !isAuthor) {
63 | return res.render('server/info.hbs', { layout:'layout-blank',
64 | message: '没有权限' ,backPath:backPath
65 | });
66 | }
67 | console.log(result)
68 | result.remove(function(err) {
69 | if (req.xhr) {
70 | return res.json({
71 | status: !err
72 | });
73 | }
74 | if(err) {
75 | return res.render('server/info.hbs', { layout:'layout-blank',
76 | message: '删除失败' ,backPath:backPath
77 | });
78 | }
79 | res.render('server/info.hbs', { layout:'layout-blank',
80 | message: '删除成功' ,backPath:backPath
81 | })
82 | });
83 | });
84 | };
85 |
86 | //发送
87 | exports.add = function(req, res) {
88 | let obj = _.pick(req.body, 'title', 'flag', 'description', 'content');
89 | if (req.session.user) {
90 | obj.author = req.session.user._id;
91 | }
92 | let page = new Page(obj);
93 | page.save(function(err) {
94 | if (err) {
95 | /*return res.render('server/info.hbs', { layout:'layout-blank',
96 | message: '发送失败'
97 | });*/
98 | console.log(err);
99 | return res.json({
100 | message: '创建失败'
101 | })
102 | }
103 | /*return res.render('server/info.hbs', { layout:'layout-blank',
104 | message: '发送成功'
105 | });*/
106 | return res.json({
107 | message: '创建成功'
108 | })
109 | });
110 | };
--------------------------------------------------------------------------------
/controllers/server/tag.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let mongoose = require('mongoose')
4 | let Tag = mongoose.model('Tag')
5 | let _ = require('lodash')
6 | let core = require('../../libs/core')
7 | let backPath = 'tag'
8 | //列表
9 | exports.list = function(req, res) {
10 | let condition = {};
11 | if(req.Roles && req.Roles.indexOf('admin') < 0) {
12 | condition.author = req.session.user._id;
13 | }
14 | Tag.count(condition, function(err, total) {
15 | let query = Tag.find(condition).populate('author');
16 | //分页
17 | let pageInfo = core.createPage(req.query.page, total);
18 | //console.log(pageInfo);
19 | query.skip(pageInfo.start);
20 | query.limit(pageInfo.pageSize);
21 | query.sort({created: -1});
22 | query.exec(function(err, results) {
23 | //console.log(err, results);
24 | res.render('server/tag/list.hbs', {
25 | title: '标签',
26 | tags: results,
27 | pageInfo: pageInfo,
28 | Menu: 'tag'
29 | });
30 | })
31 | })
32 |
33 | };
34 | //单条
35 | exports.one = function(req, res) {
36 | let id = req.params.id;
37 | Tag.findById(id).populate('author', 'username name email').exec(function(err, result) {
38 | console.log(result);
39 | if(!result) {
40 | return res.render('server/info.hbs', { layout:'layout-blank',
41 | message: '该分类不存在' ,backPath:backPath
42 | });
43 | }
44 | res.render('server/tag/item.hbs', {
45 | Menu:"tag",
46 | title: result.name,
47 | tag: result
48 | });
49 | });
50 | };
51 | //添加
52 | exports.add = function(req, res) {
53 |
54 | if (req.method === 'GET') {
55 | res.render('server/tag/add.hbs', {
56 | Menu: 'tag'
57 | });
58 | } else if (req.method === 'POST') {
59 |
60 | let obj = _.pick(req.body, 'name', 'description');
61 | if (req.session.user) {
62 | obj.author = req.session.user._id;
63 | }
64 | console.log("ss")
65 | console.log(req.body)
66 | console.log(obj)
67 | let tag = new Tag(obj);
68 | tag.save(function(err, tag) {
69 | if (req.xhr) {
70 | return res.json({
71 | status: !err
72 | })
73 | }
74 | if (err) {
75 | return res.render('server/info.hbs', { layout:'layout-blank',
76 | message: '创建失败' ,backPath:backPath
77 | });
78 | }
79 | res.render('server/info.hbs', { layout:'layout-blank',
80 | message: '创建成功' ,backPath:backPath
81 | });
82 | });
83 | }
84 | };
85 | exports.edit = function(req, res) {
86 | if(req.method === 'GET') {
87 | let id = req.params.id;
88 | Tag.findById(id).populate('author').exec(function(err, result) {
89 | let isAdmin = req.Roles && req.Roles.indexOf('admin') > -1;
90 | let isAuthor = result.author && ((result.author._id + '') === req.session.user._id);
91 |
92 | if(!isAdmin && !isAuthor) {
93 | return res.render('server/info.hbs', { layout:'layout-blank',
94 | message: '没有权限' ,backPath:backPath
95 | });
96 | }
97 | res.render('server/tag/edit.hbs', {
98 | Menu:'tag',
99 | tag: result
100 | });
101 | });
102 | } else if(req.method === 'POST') {
103 | let id = req.params.id;
104 | let obj = _.pick(req.body, 'name', 'description');
105 | Tag.findById(id).populate('author').exec(function(err, result) {
106 | let isAdmin = req.Roles && req.Roles.indexOf('admin') > -1;
107 | let isAuthor = result.author && ((result.author._id + '') === req.session.user._id);
108 |
109 | if(!isAdmin && !isAuthor) {
110 | return res.render('server/info.hbs', { layout:'layout-blank',
111 | message: '没有权限' ,backPath:backPath
112 | });
113 | }
114 | _.assign(result, obj);
115 | result.save(function(err, tag) {
116 | if (req.xhr) {
117 | return res.json({
118 | status: !err
119 | })
120 | }
121 | if(!err) {
122 | res.render('server/info.hbs', { layout:'layout-blank',
123 | message: '更新成功' ,backPath:backPath
124 | });
125 | }
126 | });
127 | });
128 | }
129 | };
130 | //删除
131 | exports.del = function(req, res) {
132 | let id = req.params.id;
133 | Tag.findById(id).populate('author').exec(function(err, result) {
134 | if(!result) {
135 | return res.render('server/info.hbs', { layout:'layout-blank',
136 | message: '分类不存在' ,backPath:backPath
137 | });
138 | }
139 | let isAdmin = req.Roles && req.Roles.indexOf('admin') > -1;
140 | let isAuthor = result.author && ((result.author._id + '') === req.session.user._id);
141 |
142 | if(!isAdmin && !isAuthor) {
143 | return res.render('server/info.hbs', { layout:'layout-blank',
144 | message: '没有权限' ,backPath:backPath
145 | });
146 | }
147 | result.remove(function(err) {
148 | if (req.xhr) {
149 | return res.json({
150 | status: !err
151 | })
152 | }
153 | if(err) {
154 | return res.render('server/info.hbs', { layout:'layout-blank',
155 | message: '删除失败' ,backPath:backPath
156 | });
157 | }
158 | res.render('server/info.hbs', { layout:'layout-blank',
159 | message: '删除成功' ,backPath:backPath
160 | })
161 | });
162 | });
163 | };
--------------------------------------------------------------------------------
/libs/core.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let fs = require('fs')
4 | let path = require('path')
5 | let config = require('../config')
6 | let qs = require('qs')
7 | let _ = require('lodash')
8 | let axios = require('axios')
9 |
10 |
11 | // recursively walk modules path and callback for each file
12 | let walk = function(modulesPath, excludeDir, callback) {
13 | fs.readdirSync(modulesPath).forEach(function(file) {
14 | let newPath = path.join(modulesPath, file);
15 | let stat = fs.statSync(newPath);
16 | if (stat.isFile() && /(.*)\.(js|coffee)$/.test(file)) {
17 | callback(newPath);
18 | } else if (stat.isDirectory() && file !== excludeDir) {
19 | walk(newPath, excludeDir, callback);
20 | }
21 | });
22 | };
23 | exports.walk = walk;
24 |
25 | exports.stringify = function(obj) {
26 | return qs.stringify(obj);
27 | };
28 | //包装admin路径
29 | exports.translateAdminDir = function(p) {
30 | let newPath = (config.admin.dir ? '/' + config.admin.dir : '') + (p || '');
31 | return newPath;
32 | };
33 | //分页 params: 当前页, 总条数, 每页条数
34 | exports.createPage = function(page = 0, total = 0, pageSize = 10) {
35 | let totalPage = Math.max(Math.ceil(total / pageSize), 1); //获取总页数
36 | let currentPage = page < 1 ? 1 : page > totalPage ? totalPage : page; //获取当前页数
37 | let start = pageSize * (currentPage - 1); //计算开始位置
38 | return {
39 | start: start,
40 | pageSize: pageSize,
41 | totalPage: totalPage,
42 | currentPage: currentPage
43 | };
44 | };
45 |
46 | //获取用户的所有角色,去重
47 | exports.getRoles = function(user) {
48 | let result = [];
49 | if (user && user.roles) {
50 | user.roles.forEach(function(role) {
51 | result.push(role.name);
52 | });
53 | }
54 | return result;
55 | };
56 | //获取用户的所有权限,去重
57 | exports.getActions = function(user) {
58 | let result = [];
59 | if (user && user.roles) {
60 | user.roles.forEach(function(role) {
61 | result = result.concat(role.actions);
62 | });
63 | }
64 | return _.uniq(result);
65 | };
66 |
67 | //获取用户的所有权限,去重
68 | exports.getRoleStatus = function(user) {
69 | let result = [];
70 | if (user && user.roles) {
71 | user.roles.forEach(function(role) {
72 | result.push(role.status);
73 | });
74 | }
75 | return result;
76 | };
77 |
78 | //给字符组后面加上斜杠,主要应用在目录拼接
79 | exports.prettySlash = function(str) {
80 | return str.substr(-1) === '/' ? str : str + '/';
81 | };
82 | //根据文件类型输出icon
83 | exports.fileToIcon = function(type) {
84 | let suffix = '';
85 | switch (type) {
86 | case 'text/html':
87 | case 'text/javascript':
88 | suffix = '-code-o';
89 | break;
90 | case 'application/pdf':
91 | suffix = '-pdf-o';
92 | break;
93 | case 'application/zip':
94 | suffix = '-zip-o';
95 | break;
96 | case 'application/msword':
97 | suffix = '-word-o';
98 | break;
99 | case 'image/png':
100 | case 'iamge/jpeg':
101 | suffix = '-photo-o';
102 | break;
103 | case 'audio/amr':
104 | suffix = '-audio-o';
105 | break;
106 | case 'video/mp4':
107 | suffix = '-video-o';
108 | break;
109 | }
110 | return 'fa-file' + suffix;
111 | };
112 | exports.getIp = function(req) {
113 | return req.headers['x-forwarded-for'] || req.connection.remoteAddress || req.socket.remoteAddress || req.connection.socket.remoteAddress || req.ip;
114 | };
115 |
116 | exports.stopForumSpam = function(obj = {}) {
117 | return new Promise((resolve, reject) => {
118 | axios('http://api.stopforumspam.org/api?json', {
119 | params: {
120 | username: obj.username,
121 | email: obj.email,
122 | ip: obj.ip
123 | }
124 | }).then((res) => {
125 | resolve(res.data)
126 | }).catch((error) => {
127 | reject(error)
128 | })
129 | })
130 |
131 | }
--------------------------------------------------------------------------------
/libs/crypto.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | //加密组件
4 | let crypto = require('crypto');
5 | /**
6 | * md5 hash
7 | *
8 | * @param str
9 | * @returns md5 str
10 | */
11 | exports.md5 = function md5(str) {
12 | let md5sum = crypto.createHash('md5');
13 | md5sum.update(str);
14 | str = md5sum.digest('hex');
15 | return str;
16 | };
17 |
18 |
19 | /**
20 | * 加密函数
21 | * @param str 源串
22 | * @param secret 因子
23 | * @returns str
24 | */
25 | exports.encrypt = function encrypt(str, secret) {
26 | let cipher = crypto.createCipher('aes192', secret);
27 | let enc = cipher.update(str, 'utf8', 'hex');
28 | enc += cipher.final('hex');
29 | return enc;
30 | };
31 |
32 | /**
33 | * 解密
34 | * @param str
35 | * @param secret
36 | * @returns str
37 | */
38 | exports.decrypt = function decrypt(str, secret) {
39 | let decipher = crypto.createDecipher('aes192', secret);
40 | let dec = decipher.update(str, 'hex', 'utf8');
41 | dec += decipher.final('utf8');
42 | return dec;
43 | };
44 |
45 | exports.random = function(len) {
46 | return crypto.randomBytes(len || 16).toString('hex');
47 | }
48 |
49 | exports.hashPassword = function(password, secret) {
50 | if (!password) return '';
51 | let encrypred;
52 | try {
53 | encrypred = crypto.createHmac('sha1', secret).update(password).digest('hex');
54 | return encrypred;
55 | } catch (err) {
56 | return '';
57 | }
58 | };
--------------------------------------------------------------------------------
/libs/mailer.js:
--------------------------------------------------------------------------------
1 | const sendmail = require('sendmail')()
2 | const nodemailer = require('nodemailer')
3 | const config = require('../config')
4 |
5 | class Mailer {
6 | constructor(options = {}) {
7 | if (options.nodemailer) {
8 | this.transporter = nodemailer.createTransport(config.mail.nodemailer)
9 | }
10 | }
11 | /**
12 | * 描述
13 | from: '',
14 | to: '',
15 | subject: '',
16 | html: '',
17 | **/
18 | send(obj, callback) {
19 | return new Promise((resolve, reject) => {
20 | if (this.transporter) {
21 | this.transporter.sendMail({
22 | from: obj.from,
23 | to: obj.to,
24 | subject: obj.subject,
25 | html: obj.html
26 | }, function(err, info) {
27 | if (!err) {
28 | resolve(info)
29 | } else {
30 | reject(err)
31 | }
32 | if (callback) {
33 | callback(err, info)
34 | }
35 | console.log(err, info, 'nodemailer')
36 | })
37 | } else {
38 | sendmail({
39 | from: obj.from,
40 | to: obj.to,
41 | subject: obj.subject,
42 | html: obj.html
43 | }, function(err, info) {
44 | if (!err) {
45 | resolve(info)
46 | } else {
47 | reject(err)
48 | }
49 | if (callback) {
50 | callback(err, info)
51 | }
52 | console.log(err, info, 'sendmail')
53 | })
54 | }
55 | })
56 |
57 | }
58 | }
59 |
60 | module.exports = Mailer
--------------------------------------------------------------------------------
/libs/uploader.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | //上传文件
4 | //用法
5 | // let upload = require('./upload')({
6 | // maxFileSize: 30000
7 | // });
8 | // upload.post(req, res, callback);
9 | //todo: use multer https://cnodejs.org/topic/564f32631986c7df7e92b0db
10 | let _ = require('lodash')
11 | let config = require('../config')
12 | let qn = require('qn')
13 |
14 | let path = require('path')
15 | let fs = require('fs')
16 | let existsSync = fs.existsSync || path.existsSync
17 | let mkdirp = require('mkdirp')
18 | let nameCountRegexp = /(?:(?: \(([\d]+)\))?(\.[^.]+))?$/
19 |
20 | module.exports = function(opts) {
21 | let options = _.assign({}, config.upload, opts);
22 | // 检查上传目录是否存在
23 | function checkExists(dir) {
24 | fs.exists(dir, function(exists) {
25 | if (!exists) {
26 | mkdirp(dir, function(err) {
27 | if (err) console.error(err)
28 | else console.log("默认目录不存在,已自动创建: [" + dir + "]");
29 | });
30 | }
31 | });
32 | }
33 | checkExists(options.tmpDir);
34 | checkExists(options.uploadDir);
35 | //已上传的文件防重名
36 | let nameCountFunc = function(s, index, ext) {
37 | return '_' + ((parseInt(index, 10) || 0) + 1) + '_' + (ext || '');
38 | };
39 | let safeName = function(_name) {
40 | // Prevent directory traversal and creating hidden system files:
41 | let name = path.basename(_name).replace(/^\.+/, '');
42 | // Prevent overwriting existing files:
43 | while (existsSync(options.uploadDir + '/' + name)) {
44 | name = name.replace(nameCountRegexp, nameCountFunc);
45 | }
46 | return name;
47 | };
48 | //构造文件url
49 | let initUrls = function(host, name) {
50 | let baseUrl = (options.useSSL ? 'https:' : 'http:') +
51 | '//' + host + options.uploadUrl;
52 | let url = baseUrl + encodeURIComponent(name);
53 | return url;
54 | };
55 | let validate = function(file) {
56 | let error = '';
57 | if (options.minFileSize && options.minFileSize > file.size) {
58 | error = 'File is too small';
59 | } else if (options.maxFileSize && options.maxFileSize < file.size) {
60 | error = 'File is too big';
61 | } else if (!options.acceptFileTypes.test(file.name)) {
62 | error = 'Filetype not allowed';
63 | }
64 | return !error;
65 | };
66 | //七牛云存储
67 | let client = null;
68 | if (options.storage.type === 'qiniu') {
69 | client = qn.create(options.storage.options);
70 | }
71 | let Uploader = {};
72 | Uploader.delete = function(url, callback) {
73 | let fileName = path.basename(decodeURIComponent(url))
74 | console.log('删除文件', url, fileName);
75 | if (fileName[0] !== '.') {
76 | if(url.indexOf(options.storage.options.domain) > -1) {
77 | try {
78 | client.delete(fileName, function(err) {
79 | callback && callback.call(null, err);
80 | })
81 | } catch(e) {
82 | console.log('删除7牛图片失败', e);
83 | callback && callback.call(null, '删除7牛图片失败');
84 | }
85 | } else {
86 | fs.unlink(options.uploadDir + '/' + fileName, function (err) {
87 | callback && callback.call(null, err);
88 | });
89 | }
90 | } else {
91 | callback && callback.call(null, '文件类型错误');
92 | }
93 | };
94 | Uploader.post = function(req, res, callback) {
95 | let files = req.files.files;
96 | let len = files.length;
97 | if (len < 1) {
98 | return;
99 | }
100 | let result = [];
101 | //七牛
102 | if (options.storage.type === 'qiniu') {
103 | files.forEach(function(file) {
104 | if (!validate(file)) {
105 | fs.unlink(file.path);
106 | return;
107 | }
108 | client.uploadFile(file.path, function(err, qf) {
109 | //console.log(qf);
110 | try {
111 | fs.unlink(file.path);
112 | } catch (e) {
113 | console.log(e);
114 | }
115 | result.push({
116 | url: qf.url,
117 | md_url: qf.url + '?imageView/1/w/300',
118 | sm_url: qf.url + '?imageView/1/w/100',
119 | name: file.name,
120 | size: file.size,
121 | type: file.type
122 | });
123 | len--;
124 | if (len <= 0) {
125 | callback.call(null, {
126 | files: result
127 | });
128 | }
129 | });
130 | });
131 | } else {
132 | //本地上传
133 | files.forEach(function(file) {
134 | if (!validate(file)) {
135 | fs.unlink(file.path);
136 | return;
137 | }
138 | let sName = safeName(file.name);
139 | fs.renameSync(file.path, options.uploadDir + '/' + sName);
140 |
141 |
142 | let host = req.headers['x-forwarded-host'] || req.headers.host//如果设置了代理转发,则先获取转发的主机地址
143 | console.log('头部信息1',req.headers)
144 | console.log('头部信息2',req.header)
145 | console.log('主机名:',host)
146 | result.push({
147 | // url: initUrls(host, sName),
148 | url: options.uploadUrl + encodeURIComponent(sName),
149 | name: sName,
150 | size: file.size,
151 | type: file.type
152 | })
153 | });
154 | callback.call(null, {
155 | files: result
156 | });
157 | }
158 | };
159 | return Uploader;
160 | };
161 |
--------------------------------------------------------------------------------
/middlewares/action.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let _ = require('lodash')
4 |
5 | // 检查权限中间件
6 | exports.checkAction = function(actionName) {
7 | //console.log(actionName)
8 | return function (req, res, next) {
9 | console.log(req.Actions, 'action middleware ')
10 | //console.log(req.user, 'user ++++++++++++++++')
11 | if (req.Roles && req.Roles.indexOf('admin') > -1) {
12 | return next();
13 | }
14 | let result = false;
15 |
16 | if (_.isArray(actionName)) {
17 | result = _.intersection(req.Actions, actionName).length === actionName.length;
18 | } else if(_.isString(actionName)) {
19 | result = req.Actions.indexOf(actionName) > -1;
20 | }
21 |
22 | if (result) {
23 | return next();
24 | } else {
25 | if (req.jwt) {
26 | return res.json({
27 | success: false,
28 | msg: '无权访问'
29 | })
30 | }
31 |
32 | if (req.xhr) {
33 | res.json({
34 | success: false,
35 | msg: '无权访问'
36 | })
37 | } else {
38 | res.render('server/info.hbs', { layout:'layout-blank',
39 | message: '无权访问'
40 | });
41 | }
42 | }
43 | };
44 | }
--------------------------------------------------------------------------------
/middlewares/jwt.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let jwt = require('jsonwebtoken')
4 | let config = require('../config')
5 | let core = require('../libs/core')
6 | let userService = require('../services/user')
7 | let roleService = require('../services/role')
8 | let UserCache = {}
9 | exports.verify = async function (req, res, next) {
10 | req.jwt = true;
11 | let token
12 | if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') {
13 | token = req.headers.authorization.split(' ')[1];
14 | } else if (req.query && req.query.token) {
15 | token = req.query.token;
16 | }
17 | let data = null
18 | let error
19 | // 缓存用户信息,读取
20 | try {
21 | data = jwt.verify(token, config.jwt.secret)
22 | console.log(data, 'jwt decode ++++++++++++')
23 | let uid = data.user.id
24 | let version = data.user.token_version
25 | if (!UserCache[uid]) {
26 | let user = await userService.findById(uid)
27 | // TODO 已删除用户容错
28 | let roleArray = []
29 | if (user.roles && user.roles.length > 0) {
30 | for (let i = 0, len = user.roles.length; i < len; i ++) {
31 | let item = user.roles[i]
32 | roleArray.push(await roleService.findById(item))
33 | }
34 | }
35 | user.roles = roleArray
36 | let roles = core.getRoles(user);
37 | let actions = core.getActions(user);
38 | req.Roles = roles;
39 | req.Actions = actions;
40 | req.user = user
41 | UserCache[uid] = user;
42 | //console.log(uid, user, '---------------------------')
43 | } else {
44 | console.log('已保存')
45 | let user = UserCache[uid]
46 | if (user.token_version !== version) {
47 | // token中途过期处理
48 | req.Roles = null
49 | req.Actions = null
50 | req.user = null
51 | UserCache[uid] = null
52 | data = null
53 | error = 'token过期,请重新登录'
54 | return
55 | }
56 | let roles = core.getRoles(user);
57 | let actions = core.getActions(user);
58 | //console.log(roles, actions, '----------------')
59 | req.Roles = roles;
60 | req.Actions = actions;
61 | req.user = user
62 | }
63 |
64 | } catch (e) {
65 | error = e.message
66 | }
67 | if (error) {
68 | return res.json({
69 | success: !error,
70 | data: data,
71 | error: '请登录后操作'
72 | });
73 | }
74 | next()
75 | }
--------------------------------------------------------------------------------
/models/category.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * 模块依赖
5 | */
6 | let mongoose = require('mongoose')
7 | let Schema = mongoose.Schema
8 |
9 | /**
10 | * 分类模型
11 | */
12 | let CategorySchema = new Schema({
13 | name: {
14 | type: String,
15 | required: true
16 | },
17 | subName:{
18 | type: String,
19 | },
20 | flag: {
21 | type: String,
22 | unique: true
23 | },
24 | author: {
25 | type: Schema.ObjectId,
26 | ref: 'User'
27 | },
28 | description: String,
29 | created: {
30 | type: Date,
31 | default: Date.now
32 | },
33 | status: {
34 | type: Number,
35 | default: 0
36 | },
37 | parent: {
38 | type: Schema.ObjectId,
39 | ref: 'Category'
40 | }
41 | });
42 | CategorySchema.methods = {
43 |
44 | };
45 |
46 | mongoose.model('Category', CategorySchema);
47 |
48 | /*
49 | * 分类嵌套格式获取方法示例
50 | *
51 | let items = [
52 | {
53 | id: 1,
54 | name: '顶级分类1'
55 | },{
56 | id: 2,
57 | name: '顶级分类1'
58 | },{
59 | id: 3,
60 | name: '顶级分类1',
61 | parent: 1
62 | },{
63 | id: 4,
64 | name: '顶级分类1',
65 | parent: 2
66 | },{
67 | id: 5,
68 | name: '顶级分类1',
69 | parent: 3
70 | },{
71 | id: 6,
72 | name: '顶级分类1',
73 | parent: 5
74 | },{
75 | id: 7,
76 | name: '顶级分类1',
77 | parent: 5
78 | },{
79 | id: 8,
80 | name: '顶级分类1',
81 | parent: 2
82 | },{
83 | id: 9,
84 | name: '顶级分类1'
85 | }
86 | ]
87 |
88 | function getItems(obj, items) {
89 | let result = items.filter(function(item) {
90 | return item.parent === obj.id;
91 | })
92 | return result;
93 | }
94 |
95 | function nestedItem(items) {
96 | let result = [];
97 | items.forEach(function(item) {
98 | let res = getItems(item, items)
99 | console.log(res)
100 | if (res.length > 0) {
101 | item.items = res;
102 | }
103 | if (!item.parent) {
104 | result.push(item)
105 | }
106 | })
107 | return result;
108 | }
109 |
110 | let res = nestedItem(items);
111 |
112 |
113 |
114 | console.log(JSON.stringify(res, null, 4))*/
--------------------------------------------------------------------------------
/models/comment.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * 模块依赖
5 | */
6 | let mongoose = require('mongoose')
7 | let Schema = mongoose.Schema
8 |
9 | /**
10 | * 评论模型
11 | */
12 | let CommentSchema = new Schema({
13 | content: {
14 | type: String,
15 | required: true
16 | },
17 | from: {//文章id
18 | type: Schema.ObjectId,
19 | ref: 'Content'
20 | },
21 | reply: {//回复评论的id
22 | type: Schema.ObjectId,
23 | ref: 'Comment'
24 | },
25 | author: {
26 | type: Schema.ObjectId,
27 | ref: 'User'
28 | },
29 | //匿名用户信息
30 | name: {
31 | type: String
32 | },
33 | email: {
34 | type: String
35 | },
36 | website: {
37 | type: String
38 | },
39 | created: {
40 | type: Date,
41 | default: Date.now
42 | },
43 | comments: [{
44 | type: Schema.ObjectId,
45 | ref: 'Comment'
46 | }],
47 | ip: {//回复ip
48 | type: String
49 | },
50 | status: {
51 | type: Number,
52 | default: 0
53 | }
54 | });
55 |
56 | /*CommentSchema.pre('save', function(next) {
57 | if (!this.isNew) return next();
58 | if (!this.title) {
59 | next(new Error('Invalid password'));
60 | } else {
61 | next();
62 | }
63 | });*/
64 |
65 | CommentSchema.methods = {
66 |
67 | };
68 |
69 | mongoose.model('Comment', CommentSchema);
--------------------------------------------------------------------------------
/models/content.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * 模块依赖
5 | */
6 | let mongoose = require('mongoose')
7 | let Schema = mongoose.Schema
8 |
9 | /**
10 | * 内容模型
11 | */
12 | let ContentSchema = new Schema({
13 | title: {
14 | type: String,
15 | required: true
16 | },
17 | summary: {
18 | type: String
19 | },
20 | content: {
21 | type: String,
22 | required: true
23 | },
24 | keywords:{
25 | type: String
26 | },
27 | gallery: [{
28 | type: Schema.ObjectId,
29 | ref: 'File'
30 | }],
31 | thumb:{
32 | type: Schema.ObjectId
33 | },
34 | author: {
35 | type: Schema.ObjectId,
36 | ref: 'User'
37 | },
38 | category: {
39 | type: Schema.ObjectId,
40 | ref: 'Category'
41 | },
42 | tags: [{
43 | type: Schema.ObjectId,
44 | ref: 'Tag'
45 | }],
46 | created: {
47 | type: Date,
48 | default: Date.now
49 | },
50 | visits: {
51 | type: Number,
52 | default: 0
53 | },
54 | comments: [{
55 | type: Schema.ObjectId,
56 | ref: 'Comment'
57 | }],
58 | status: {
59 | type: Number,
60 | default: 0
61 | },
62 | //喜欢
63 | like: {
64 | type: Number,
65 | default: 0
66 | },
67 | //置顶,权重由数值决定
68 | up: {
69 | type: Number,
70 | default: 0
71 | },
72 |
73 | });
74 |
75 | /*ContentSchema.pre('save', function(next) {
76 | if (!this.isNew) return next();
77 | if (!this.title) {
78 | next(new Error('Invalid password'));
79 | } else {
80 | next();
81 | }
82 | });*/
83 |
84 | ContentSchema.methods = {
85 |
86 | };
87 |
88 | mongoose.model('Content', ContentSchema);
--------------------------------------------------------------------------------
/models/file.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * 模块依赖
5 | */
6 | let mongoose = require('mongoose')
7 | let Schema = mongoose.Schema
8 |
9 | /**
10 | * 文件模型
11 | */
12 | let FileSchema = new Schema({
13 | name: {
14 | type: String
15 | },
16 | url: {
17 | type: String
18 | },
19 | md_url: {
20 | type: String
21 | },
22 | sm_url: {
23 | type: String
24 | },
25 | size: Number,
26 | type: String,
27 | description: String,
28 | created: {
29 | type: Date,
30 | default: Date.now
31 | },
32 | author: {
33 | type: Schema.ObjectId,
34 | ref: 'User'
35 | },
36 | status: {
37 | type: Number,
38 | default: 0
39 | }
40 | });
41 | FileSchema.methods = {
42 |
43 | };
44 |
45 | mongoose.model('File', FileSchema);
--------------------------------------------------------------------------------
/models/log.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * 模块依赖
5 | */
6 | let mongoose = require('mongoose')
7 | let Schema = mongoose.Schema
8 |
9 | /**
10 | * 标签模型
11 | */
12 | let LogSchema = new Schema({
13 | type: {
14 | type: String
15 | },
16 | action: {
17 | type: String
18 | },
19 | status: {
20 | type: String
21 | },
22 | ip: {
23 | type: String
24 | },
25 | ua: {
26 | type: String
27 | },
28 | message: {
29 | type: String
30 | },
31 | created: {
32 | type: Date,
33 | default: Date.now
34 | },
35 | author: {
36 | type: Schema.ObjectId,
37 | ref: 'User'
38 | }
39 | });
40 | LogSchema.methods = {
41 |
42 | };
43 |
44 | mongoose.model('Log', LogSchema);
--------------------------------------------------------------------------------
/models/message.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * 模块依赖
5 | */
6 | let mongoose = require('mongoose')
7 | let Schema = mongoose.Schema
8 |
9 | /**
10 | * 客户留言模型
11 | */
12 | let MessageSchema = new Schema({
13 | name: {
14 | type: String,
15 | required: '请输入姓名'
16 | },
17 | email: {
18 | type: String
19 | },
20 | mobile: {
21 | type: String
22 | },
23 | address: {
24 | type: String
25 | },
26 | content: String,
27 | created: {
28 | type: Date,
29 | default: Date.now
30 | },
31 | ip: {//用户ip
32 | type: String
33 | },
34 | status: {
35 | type: Number,
36 | default: 0
37 | }
38 | });
39 | MessageSchema.methods = {
40 |
41 | };
42 |
43 | mongoose.model('Message', MessageSchema);
--------------------------------------------------------------------------------
/models/notification.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * 模块依赖
5 | */
6 | let mongoose = require('mongoose')
7 | let Schema = mongoose.Schema
8 |
9 | /**
10 | * 通知模型
11 | */
12 | let NotificationSchema = new Schema({
13 | content: {
14 | type: String,
15 | required: true
16 | },
17 | from: {//发送人
18 | type: Schema.ObjectId,
19 | ref: 'User'
20 | },
21 | to: [{//接收人
22 | type: Schema.ObjectId,
23 | ref: 'User'
24 | }],
25 | // 是否系统广播,系统广播只更新read字段, unread默认为全员
26 | broadcast: {
27 | type: Boolean,
28 | default: false
29 | },
30 | // TODO 提醒类型,比如有人看了给你的帖子点赞,有人加你为好友等,待开发
31 | type: {
32 | type: String,
33 | default: ''
34 | },
35 | // 已读用户
36 | read: [{
37 | type: Schema.ObjectId,
38 | ref: 'User'
39 | }],
40 | // 未读用户
41 | unread: [{
42 | type: Schema.ObjectId,
43 | ref: 'User'
44 | }],
45 | // 已删除用户
46 | deleted: [{
47 | type: Schema.ObjectId,
48 | ref: 'User'
49 | }],
50 | created: {
51 | type: Date,
52 | default: Date.now
53 | },
54 | status: {
55 | type: Number,
56 | default: 0
57 | }
58 | });
59 |
60 | /*NotificationSchema.pre('save', function(next) {
61 | if (!this.isNew) return next();
62 | if (!this.title) {
63 | next(new Error('Invalid password'));
64 | } else {
65 | next();
66 | }
67 | });*/
68 | /*NotificationSchema.virtual('compare').set(function(password) {
69 |
70 | }).get(function() {
71 | if (this.broadcast) {
72 | return this.read
73 | } else {
74 | return this.unread
75 | }
76 | });*/
77 |
78 | NotificationSchema.methods = {
79 |
80 | };
81 |
82 | mongoose.model('Notification', NotificationSchema);
--------------------------------------------------------------------------------
/models/option.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * 模块依赖
5 | */
6 | let mongoose = require('mongoose')
7 | let Schema = mongoose.Schema
8 |
9 | /**
10 | * 选项模型
11 | */
12 | let OptionSchema = new Schema({
13 | name: {
14 | type: String,
15 | unique: true
16 | },
17 | value: {
18 | type: String
19 | },
20 | type: {
21 | type: String //user global system ...
22 | },
23 | description: String,
24 | created: {
25 | type: Date,
26 | default: Date.now
27 | },
28 | author: {
29 | type: Schema.ObjectId,
30 | ref: 'User'
31 | },
32 | status: {
33 | type: Number,
34 | default: 0
35 | }
36 | });
37 | OptionSchema.methods = {
38 |
39 | };
40 |
41 | mongoose.model('Option', OptionSchema);
--------------------------------------------------------------------------------
/models/page.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * 模块依赖
5 | */
6 | let mongoose = require('mongoose')
7 | let Schema = mongoose.Schema
8 |
9 | /**
10 | * 页面模型
11 | */
12 | let PageSchema = new Schema({
13 | title: {
14 | type: String,
15 | required: true,
16 | unique: true
17 | },
18 | flag: {
19 | type: String
20 | },
21 | description: {
22 | type: String
23 | },
24 | content: {
25 | type: String,
26 | required: true
27 | },
28 | author: {
29 | type: Schema.ObjectId,
30 | ref: 'User'
31 | },
32 | created: {
33 | type: Date,
34 | default: Date.now
35 | },
36 | status: {
37 | type: Number,
38 | default: 0
39 | }
40 | });
41 |
42 | /*PageSchema.pre('save', function(next) {
43 | if (!this.isNew) return next();
44 | if (!this.title) {
45 | next(new Error('Invalid password'));
46 | } else {
47 | next();
48 | }
49 | });*/
50 |
51 | PageSchema.methods = {
52 |
53 | };
54 |
55 | mongoose.model('Page', PageSchema);
--------------------------------------------------------------------------------
/models/role.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * 模块依赖
5 | */
6 | let mongoose = require('mongoose')
7 | let Schema = mongoose.Schema
8 |
9 | /**
10 | * 角色模型
11 | */
12 | let RoleSchema = new Schema({
13 | name: {
14 | type: String,
15 | required: true,
16 | unique: true
17 | },
18 | actions: Array,//['read', 'write', 'guest', 'createUser']
19 | description: String,
20 | created: {
21 | type: Date,
22 | default: Date.now
23 | },
24 | author: {
25 | type: Schema.ObjectId,
26 | ref: 'User'
27 | },
28 | status: {
29 | type: Number,
30 | default: 0
31 | }
32 | });
33 | RoleSchema.methods = {
34 |
35 | };
36 |
37 | mongoose.model('Role', RoleSchema);
--------------------------------------------------------------------------------
/models/tag.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * 模块依赖
5 | */
6 | let mongoose = require('mongoose')
7 | let Schema = mongoose.Schema
8 |
9 | /**
10 | * 标签模型
11 | */
12 | let TagSchema = new Schema({
13 | name: {
14 | type: String,
15 | required: true
16 | },
17 | description: {
18 | type: String
19 | },
20 | created: {
21 | type: Date,
22 | default: Date.now
23 | },
24 | author: {
25 | type: Schema.ObjectId,
26 | ref: 'User'
27 | },
28 | status: {
29 | type: Number,
30 | default: 0
31 | }
32 | });
33 | TagSchema.methods = {
34 |
35 | };
36 |
37 | mongoose.model('Tag', TagSchema);
--------------------------------------------------------------------------------
/models/user.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | /**
3 | * 模块依赖
4 | */
5 |
6 | let crypto = require('crypto')
7 | let _ = require('lodash')
8 | let mongoose = require('mongoose')
9 | let Schema = mongoose.Schema
10 |
11 |
12 | /**
13 | * 用户模型
14 | */
15 | let UserSchema = new Schema({
16 | username: {
17 | type: String,
18 | required: '请输入用户名',
19 | unique: true
20 | },
21 |
22 | email: {
23 | type: String,
24 | required: '请输入邮箱',
25 | unique: true
26 | },
27 |
28 | mobile: {
29 | type: String
30 | },
31 |
32 | name: {
33 | type: String,
34 | required: '请输入姓名'
35 | },
36 |
37 | avatar: {
38 | type: String
39 | },
40 |
41 | gender: {
42 | type: String,
43 | enum: ['男', '女', '保密']
44 | },
45 |
46 | birthday: {
47 | type: Date,
48 | default: Date.now
49 | },
50 |
51 | description: {
52 | type: String
53 | },
54 |
55 | address: {
56 | type: String
57 | },
58 |
59 |
60 |
61 | roles: [{
62 | type: Schema.ObjectId,
63 | ref: 'Role'
64 | }],
65 |
66 | last_login_date: Date,
67 |
68 | last_login_ip: String,
69 |
70 | position: {
71 | type: Array,
72 | index: '2dsphere'
73 | },
74 |
75 | reg_ip: String,//注册ip
76 |
77 | created: {
78 | type: Date,
79 | default: Date.now
80 | },
81 |
82 | author: {
83 | type: Schema.ObjectId,
84 | ref: 'User'
85 | },
86 |
87 | status: {
88 | type: Number,
89 | default: 0
90 | },
91 |
92 | rank: {
93 | type: Number,
94 | default: 0
95 | },
96 |
97 | //找回密码
98 | forget: {
99 | hash: String,
100 | till: Date
101 | },
102 |
103 | questions: [{
104 | q: String,
105 | a: String
106 | }],
107 |
108 | salt: String,
109 |
110 | hashed_password: String,
111 | activeKey: String,
112 | isActive: {
113 | type: Boolean,
114 | default: false
115 | },
116 | token: String,
117 | // 用来标记token变更比如修改密码后需要重新登录
118 | token_version: {
119 | type: Number,
120 | default: 0
121 | }
122 | });
123 |
124 | /**
125 | * Virtuals
126 | */
127 | UserSchema.virtual('password').set(function(password) {
128 | this._password = password;
129 | this.salt = this.makeSalt();
130 | this.hashed_password = this.hashPassword(password);
131 | }).get(function() {
132 | return this._password;
133 | });
134 |
135 | /**
136 | * Validations
137 | */
138 | UserSchema.path('name').validate(function(name) {
139 | return (typeof name === 'string' && name.length >= 1 && name.length <= 50);
140 | }, '名字在1-50个字符之间');
141 |
142 | UserSchema.path('email').validate(function(email) {
143 | return (typeof email === 'string' && email.length > 0);
144 | }, 'Email不能为空');
145 | UserSchema.path('email').validate(function(email) {
146 | return /^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/.test(email);
147 | }, 'Email格式不正确');
148 |
149 | UserSchema.path('username').validate(function(username) {
150 | return (typeof username === 'string' && username.length >= 4 && username.length <= 20);
151 | }, '用户名为4-20个字符');
152 | UserSchema.path('username').validate(function(username) {
153 | return /^\w+$/.test(username);
154 | }, '用户名只能为a-zA-Z0-9_');
155 |
156 |
157 | /**
158 | * Pre-save hook
159 | */
160 | /*UserSchema.pre('save', function(next) {
161 | if (!this.isNew) return next();
162 |
163 | if (!validatePresenceOf(this.password) && !this.provider)
164 | next(new Error('Invalid password'));
165 | else
166 | next();
167 | });*/
168 |
169 |
170 | /**
171 | * Methods
172 | */
173 | UserSchema.methods = {
174 |
175 | /**
176 | * HasRole - check if the user has required role
177 | *
178 | * @param {String} plainText
179 | * @return {Boolean}
180 | * @api public
181 | */
182 | //通过角色名判断权限
183 | hasRole: function(role) {
184 | let roles = [];
185 | this.roles.forEach(function(item) {
186 | roles = _.union(roles, item.name);
187 | });
188 | return (roles.indexOf(role) !== -1);
189 | },
190 | //通过动作判断权限
191 | hasAction: function(action) {
192 | let actions = [];
193 | this.roles.forEach(function(item) {
194 | actions = _.union(actions, item.actions);
195 | });
196 | return (actions.indexOf(action) !== -1);
197 | },
198 | roleToObj: function() {
199 | let roles = [];
200 | let actions = [];
201 | this.roles.forEach(function(item) {
202 | roles = _.union(roles, item.name);
203 | actions = _.union(actions, item.actions);
204 | });
205 | return {
206 | _roles: roles,
207 | _actions: actions
208 | };
209 | },
210 | /**
211 | * Authenticate - check if the passwords are the same
212 | *
213 | * @param {String} plainText
214 | * @return {Boolean}
215 | * @api public
216 | */
217 | authenticate: function(plainText) {
218 | return this.hashPassword(plainText) === this.hashed_password;
219 | },
220 | makeSalt: function() {
221 | return Math.round((new Date().valueOf() * Math.random())) + '';
222 | },
223 | hashPassword: function(password) {
224 | if (!password) return '';
225 | let encrypred;
226 | try {
227 | encrypred = crypto.createHmac('sha1', this.salt).update(password).digest('hex');
228 | return encrypred;
229 | } catch (err) {
230 | return '';
231 | }
232 | }
233 | };
234 |
235 | mongoose.model('User', UserSchema);
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "F_Blog",
3 | "version": "0.0.1",
4 | "private": true,
5 | "scripts": {
6 | "dev": "cross-env NODE_ENV=development nodemon --inspect ./app.js --ignore public/ --ignore views/",
7 | "server": "cross-env NODE_ENV=production node app"
8 | },
9 | "dependencies": {
10 | "axios": "^0.16.0",
11 | "body-parser": "~1.17.1",
12 | "compression": "^1.6.2",
13 | "connect-multiparty": "^1.0.5",
14 | "connect-redis": "^3.2.0",
15 | "consolidate": "^0.14.5",
16 | "cookie-parser": "~1.4.3",
17 | "csurf": "^1.8.3",
18 | "debug": "~2.6.3",
19 | "express": "~4.15.2",
20 | "express-handlebars": "^3.0.0",
21 | "express-session": "^1.2.0",
22 | "gravatar": "^1.0.6",
23 | "jade": "~1.11.0",
24 | "jsonwebtoken": "^7.3.0",
25 | "lodash": "^4.17.4",
26 | "marked": "^0.3.3",
27 | "mkdirp": "^0.5.0",
28 | "moment": "^2.18.0",
29 | "mongoose": "^4.9.2",
30 | "morgan": "~1.8.1",
31 | "nodemailer": "^4.0.1",
32 | "qn": "^0.2.2",
33 | "qs": "^2.3.3",
34 | "sendmail": "^1.1.1",
35 | "serve-favicon": "^2.4.2",
36 | "strip": "0.0.7",
37 | "xss": "^0.3.3"
38 | },
39 | "devDependencies": {
40 | "cross-env": "^4.0.0",
41 | "nodemon": "^1.11.0"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/public/assets/app/images/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frogo/blog/4bf19545155e6dfbf764a0f0ee50cce9da5b56c1/public/assets/app/images/favicon.ico
--------------------------------------------------------------------------------
/public/assets/app/images/video-thumb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frogo/blog/4bf19545155e6dfbf764a0f0ee50cce9da5b56c1/public/assets/app/images/video-thumb.png
--------------------------------------------------------------------------------
/public/assets/plugins/mditor/css/mditor.min.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":[],"names":[],"mappings":"","file":"css/mditor.min.css","sourceRoot":""}
--------------------------------------------------------------------------------
/public/assets/plugins/mditor/font/674f50d287a8c48dc19ba404d20fe713.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frogo/blog/4bf19545155e6dfbf764a0f0ee50cce9da5b56c1/public/assets/plugins/mditor/font/674f50d287a8c48dc19ba404d20fe713.eot
--------------------------------------------------------------------------------
/public/assets/plugins/mditor/font/af7ae505a9eed503f8b8e6982036873e.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frogo/blog/4bf19545155e6dfbf764a0f0ee50cce9da5b56c1/public/assets/plugins/mditor/font/af7ae505a9eed503f8b8e6982036873e.woff2
--------------------------------------------------------------------------------
/public/assets/plugins/mditor/font/b06871f281fee6b241d60582ae9369b9.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frogo/blog/4bf19545155e6dfbf764a0f0ee50cce9da5b56c1/public/assets/plugins/mditor/font/b06871f281fee6b241d60582ae9369b9.ttf
--------------------------------------------------------------------------------
/public/assets/plugins/mditor/font/fee66e712a8a08eef5805a46892932ad.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frogo/blog/4bf19545155e6dfbf764a0f0ee50cce9da5b56c1/public/assets/plugins/mditor/font/fee66e712a8a08eef5805a46892932ad.woff
--------------------------------------------------------------------------------
/public/assets/plugins/mditor/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | mditor
8 |
9 |
10 |
11 |
21 |
22 |
23 |
24 |
25 |
26 |
29 |
30 |
31 |
32 |
33 |
34 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/public/assets/server/css/main.css:
--------------------------------------------------------------------------------
1 | .mgt10{margin-top:10px}
2 | .mgr5{margin-right:5px}
3 | .pdr20{padding-right:20px}
4 | .dis_none{display:none}
5 | #header{border-bottom:1px solid #ddd;z-index:200;background:#fff;min-height:80px;margin-right:0}
6 | #header .brand{float:left;width:240px;min-height:79px;padding:0 0 0 10px;position:relative;background:#fff}
7 | #header .logo{color:#504E4E;padding:23px 0 0 17px;font-size:1.7em;text-transform:uppercase;display:inline-block}
8 | #header .logo span{font-weight:700}
9 |
10 | #header .user-nav{float:right;padding-top:23px;padding-right:20px}
11 | #header .user-nav ul li{display:inline-block;vertical-align:middle;font-size:15px}
12 | #header .user-nav ul li .profile-photo{display:inline-block;overflow:hidden;vertical-align:middle}
13 | #header .user-nav ul li.dropdown.settings .dropdown-menu{width:125px}
14 | #header .dropdown.settings .dropdown-menu>li>a:hover{color:#555;text-decoration: none}
15 | #header li.dropdown ul.dropdown-menu.alert>li h1{-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;-ms-border-radius:3px 3px 0 0;-o-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0;background-color:#e84c3d;margin:0;font-size:13px;padding:10px;font-weight:400;color:#fff}
16 | #header .user-nav ul li.dropdown.settings{line-height:39px;margin-right:10px;padding:0 10px}
17 | #header .user-nav ul li.dropdown.settings .dropdown-menu>li>a{padding:5px 10px;font-size:12px}
18 | #header .user-nav ul li.dropdown.settings .dropdown-menu{-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;left:-70px;top:48px;background-color:#F6F6F6;border-color:#e7e8ec;-webkit-box-shadow:0 0 5px rgba(0,0,0,.1);-moz-box-shadow:0 0 5px rgba(0,0,0,.1);-o-box-shadow:0 0 5px rgba(0,0,0,.1);box-shadow:0 0 5px rgba(0,0,0,.1)}
19 | #header .dropdown.settings .dropdown-menu>li{display:block}
20 | #container{width:100%;height:100%;z-index:0;}
21 | .main-content-wrapper{margin-left:240px;margin-right:0;min-height:800px}
22 | .main-content-wrapper #main-content{display:inline-block;padding:15px 15px 0;width:100%}
23 | .sidebar{width:240px;height:100%;background:#fff;position:absolute;border-right:1px solid #ddd;z-index:100}
24 | .sidebar #leftside-navigation ul{margin:-2px 0 0;padding:0}
25 | .sidebar #leftside-navigation ul li{list-style-type:none;border-bottom:1px solid rgba(255,255,255,.05)}
26 | .sidebar #leftside-navigation ul li a{color:#666;text-decoration:none;display:block;padding:18px 0 18px 25px;font-size:14px;outline:0;-webkit-transition:all 200ms ease-in;-moz-transition:all 200ms ease-in;-o-transition:all 200ms ease-in;-ms-transition:all 200ms ease-in;transition:all 200ms ease-in}
27 | .sidebar #leftside-navigation ul li a span{display:inline-block;padding-left:13px}
28 | .sidebar #leftside-navigation ul li a:hover,#leftside-navigation ul li.active>a{color:#666}
29 | .pagination li.disabled{display:none}
30 | .pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{color:#504E4E;background:#dfdfdf;border-color:#dfdfdf}
31 | .pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{color:#504E4E}
32 | .pagination>li>a,.pagination>li>span{color:#504E4E}
33 | .gallery{padding-bottom:55px}
34 | .gallery:after{content:'';clear:both;display:table}
35 | .gallery-item{width:100px;height:100px;background-size:cover;float:left;margin:0 15px 0 0;text-align:center;border:1px solid #dfdfdf;position:relative;display:-webkit-flex;-webkit-flex-direction:row;-webkit-align-items:center;-webkit-justify-content:center;}
36 | .table .gallery-item{overflow: hidden}
37 | .gallery-item .file-holder{font-size:100px}
38 | .gallery-item .setThumb,.gallery-item .insertContent{position: absolute;left:0}
39 | .gallery-item .setThumb{bottom: -36px}
40 | .gallery-item .insertContent{bottom: -50px}
41 |
42 | .gallery-item img{max-width:100%;max-height:100%}
43 | .gallery-remove{position:absolute;right:-10px;top:-14px;font-size:20px;line-height:0;color:#ccc}
44 | .media-list{border-left:4px solid #eee;padding:0px 15px;margin:10px 0}
45 | .form-control{color:#717171;outline:0;height:18px;padding:6px 11px;line-height:18px;font-size:13px;background-color:#fafafa;min-height:36px;filter:none!important;-webkit-box-shadow:none!important;-moz-box-shadow:none!important;box-shadow:none!important;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;-o-border-radius:3px;border-radius:3px;-webkit-transition:all .2s linear;-moz-transition:all .2s linear;-o-transition:all .2s linear;-ms-transition:all .2s linear;transition:all .2s linear}
46 | .table>thead>tr>th,.table>tbody>tr>td{border-bottom:1px solid #f1f2f7}
47 | a{color:#555;}
48 | a:hover{color:#444}
49 | a:focus, a:hover {color:#444;outline: none}
--------------------------------------------------------------------------------
/public/assets/server/css/style.css:
--------------------------------------------------------------------------------
1 | body{font-family:'微软雅黑' Monaco}
2 | .preview img{max-width:200px;max-height:200px}
3 | a.btn-danger{color:#fff}
4 |
5 | *{font:12px/1.5 '微软雅黑',Verdana,Helvetica,Arial,sans-serif;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-box-sizing:border-box;padding:0;margin:0;list-style:none;box-sizing:border-box}
6 | body,html{height:100%;overflow:hidden}
7 | body{background:#f1f1f1;background-size:cover}
8 | a{color:#27A9E3;text-decoration:none;cursor:pointer}
9 | /* 初始化密码 登录 忘记密码 找回密码 */
10 | .login.install{max-width:420px;}
11 | .login .box{padding:30px 30px;background-color:#ffffff;border-radius:4px;box-sizing:border-box}
12 | .login{margin:150px auto 0 auto;margin-left:auto;margin-right:auto;max-width:320px;}
13 | .login h4{margin:0;padding:0 0 40px 0;font-weight:700}
14 | .login h6{margin:0 0 20px 0}
15 | .login a{color:#666}
16 | .login label{ font-size:12px; font-weight:normal}
17 | .login .help{padding:10px}
18 | input[type=text],input[type=file],input[type=password],input[type=email],select{border:1px solid #DCDEE0;vertical-align:middle;border-radius:3px;height:40px;padding:0px 16px;font-size:14px;color:#555555;outline:none;width:100%}
19 | input[type=text]:focus,input[type=file]:focus,input[type=password]:focus,input[type=email]:focus,select:focus{border:1px solid #27A9E3}
20 | input[type=submit],input[type=button]{display:inline-block;vertical-align:middle;padding:8px 20px;margin:0px;font-size:16px;line-height:24px;text-align:center;white-space:nowrap;vertical-align:middle;cursor:pointer;color:#ffffff;background-color:#0a1922;border-radius:3px;border:none;-webkit-appearance:none;outline:none;width:100%}
21 | hr.hr15{height:15px;border:none;margin:0px;padding:0px;width:100%}
22 | hr.hr20{height:20px;border:none;margin:0px;padding:0px;width:100%}
23 | input:-webkit-autofill {
24 | -webkit-box-shadow: 0 0 0px 1000px white inset !important;
25 | }
26 |
27 | /* 成功提示 错误提示 */
28 | .info-container{
29 | width: 100%;
30 | height: 100%;
31 | position: absolute;
32 | -webkit-transition: all 3s ease-in-out;
33 | -moz-transition: all 3s ease-in-out;
34 | -o-transition: all 3s ease-in-out;
35 | -ms-transition: all 3s ease-in-out;
36 | transition: all 3s ease-in-out;
37 | color: #fff;
38 | background:#f1f1f1
39 | /* background: #405d8c;
40 | background: -moz-radial-gradient(center,ellipse cover,#50a3a2 0,#53e3a6 100%);
41 | background: -webkit-gradient(radial,center center,0,center center,100%,color-stop(0%,#50a3a2),color-stop(100%,#53e3a6));
42 | background: -webkit-radial-gradient(center,ellipse cover,#50a3a2 0,#53e3a6 100%);
43 | background: -o-radial-gradient(center,ellipse cover,#50a3a2 0,#53e3a6 100%);
44 | background: -ms-radial-gradient(center,ellipse cover,#50a3a2 0,#53e3a6 100%);
45 | background: radial-gradient(ellipse at center,#50a3a2 0,#53e3a6 100%);
46 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#50a3a2',endColorstr='#53e3a6',GradientType=1)*/}
47 | .info-container .info-message{padding:15px;width:400px;margin:50px auto 0;color:#555;background:#fff;border-radius:5px}
--------------------------------------------------------------------------------
/public/assets/server/img/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frogo/blog/4bf19545155e6dfbf764a0f0ee50cce9da5b56c1/public/assets/server/img/favicon.ico
--------------------------------------------------------------------------------
/public/assets/server/js/application.js:
--------------------------------------------------------------------------------
1 | var app = function() {
2 | var e = function() {
3 | g()
4 | },
5 | g = function() {
6 | $('#myModal').on('show.bs.modal', function (e) {
7 | var $realtedTarget = $(e.relatedTarget),
8 | dataUrl = $realtedTarget.attr('data-url'),
9 | dataType = $realtedTarget.attr('data-type'),
10 | dataToken = $realtedTarget.attr('data-token')
11 |
12 | $(this).attr('data-url',dataUrl)
13 | $(this).attr('data-type',dataType)
14 | $(this).attr('data-token',dataToken)
15 | if($realtedTarget.hasClass('gallery-remove')){
16 | if($(this).find('.deleteOriginal').hasClass('dis_none')){
17 | $(this).find('.deleteOriginal').removeClass('dis_none')
18 | }
19 | }
20 | $(this).find('button.confirm').off('click')
21 | $(this).find('button.confirm').on('click',$realtedTarget,h)
22 | //判断是否绑定事件,保证绑定1次
23 | /* var objEvt = $._data($(this).find('button.confirm')[0], 'events');
24 | if (!objEvt || !objEvt['click']) { }*/
25 | })
26 | },
27 | h= function(e) {
28 | var $modal = $(e.currentTarget).parents('.modal') ,
29 | url = $modal.attr('data-url'),
30 | dataType = $modal.attr('data-type'),
31 | token = $modal.attr('data-token'),
32 | item = e.data.parents('.gallery-item'),
33 | checked = $modal.find('.deleteOriginal input')[0].checked;
34 |
35 | if(dataType || dataType === 'ajax'){
36 | if(checked){
37 | $.post(url, {
38 | _csrf: token
39 | }, function(json) {
40 | if(json.success) {
41 | item.remove();
42 | $modal.modal('hide')
43 | } else{
44 | alert('删除失败')
45 | $modal.modal('hide')
46 | }
47 | });
48 | }else{
49 | item.remove();
50 | $modal.modal('hide')
51 | }
52 |
53 | } else {
54 | window.location.href = url;
55 | }
56 | };
57 | return {
58 | init: e
59 | }
60 | }();
61 | $(document).ready(function() {
62 | app.init()
63 | });
64 |
65 |
--------------------------------------------------------------------------------
/routes/app/comment.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let express = require('express')
4 | let router = express.Router()
5 | let comment = require('../../controllers/app/comment')
6 |
7 | //评论
8 | router.use(function(req, res, next) {
9 | res.locals.Path = 'comment';
10 | next();
11 | });
12 | router.route('/').get(comment.list);
13 | router.route('/add').post(comment.add);
14 | router.route('/:id').get(comment.one).delete(comment.del);
15 |
16 | module.exports = function(app) {
17 | app.use('/comment', router);
18 | };
19 |
--------------------------------------------------------------------------------
/routes/app/content.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let express = require('express')
4 | let router = express.Router()
5 | let content = require('../../controllers/app/content')
6 |
7 | //
8 | router.use(function(req, res, next) {
9 | res.locals.Path = 'content';
10 | next();
11 | });
12 | /*router.route('/').get(content.list);*/
13 | router.route('/:id').get(content.one);
14 |
15 | module.exports = function(app) {
16 | app.use('/content', router);
17 | };
18 |
--------------------------------------------------------------------------------
/routes/app/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let express = require('express')
4 | let router = express.Router()
5 | let index = require('../../controllers/app/index')
6 |
7 | //首页
8 | router.route('/contact').all(index.contact);
9 | router.route('/').all(index.index);
10 |
11 | module.exports = function(app) {
12 | app.use('/', router);
13 | };
14 |
--------------------------------------------------------------------------------
/routes/app/page.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let express = require('express')
4 | let router = express.Router()
5 | let page = require('../../controllers/app/page')
6 |
7 | //首页
8 | router.route('/:id').all(page.one);
9 | router.route('/').all(page.list);
10 |
11 | module.exports = function(app) {
12 | app.use('/page', router);
13 | };
14 |
--------------------------------------------------------------------------------
/routes/server/category.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let express = require('express')
4 | let router = express.Router()
5 | let core = require('../../libs/core')
6 | let action = require('../../middlewares/action')
7 | let category = require('../../controllers/server/category')
8 |
9 | //权限判断
10 | router.use(function(req, res, next) {
11 | console.log('分类页: ' + Date.now());
12 | res.locals.Path = 'category';
13 | if(!req.session.user) {
14 | let path = core.translateAdminDir('/user/login');
15 | return res.redirect(path);
16 | }
17 | next();
18 | });
19 | //内容列表
20 | router.route('/').get(action.checkAction('CATEGORY_INDEX'), category.list);
21 | //添加内容
22 | router.route('/add').all(action.checkAction('CATEGORY_CREATE'), category.add);
23 | //单条信息
24 | router.route('/:id').get(action.checkAction('CATEGORY_DETAIL'), category.one);
25 | //更新信息
26 | router.route('/:id/edit').all(action.checkAction('CATEGORY_UPDATE'), category.edit);
27 | //删除信息
28 | router.route('/:id/del').all(action.checkAction('CATEGORY_DELETE'), category.del);
29 |
30 | module.exports = function(app) {
31 | let path = core.translateAdminDir('/category');
32 | app.use(path, router);
33 | };
34 |
--------------------------------------------------------------------------------
/routes/server/comment.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let express = require('express')
4 | let router = express.Router()
5 | let core = require('../../libs/core')
6 | let action = require('../../middlewares/action')
7 | let comment = require('../../controllers/server/comment')
8 |
9 | //权限判断
10 | router.use(function(req, res, next) {
11 | console.log('评论页: ' + Date.now());
12 | res.locals.Path = 'comment';
13 | if(!req.session.user) {
14 | let path = core.translateAdminDir('/user/login');
15 | return res.redirect(path);
16 | }
17 | next();
18 | });
19 | //内容列表
20 | router.route('/').get(action.checkAction('COMMENT_INDEX'), comment.list);
21 | //单条信息
22 | router.route('/:id').get(action.checkAction('COMMENT_DETAIL'), comment.one);
23 | //删除信息
24 | router.route('/:id/del').all(action.checkAction('COMMENT_DELETE'), comment.del);
25 |
26 | module.exports = function(app) {
27 | let path = core.translateAdminDir('/comment');
28 | app.use(path, router);
29 | };
30 |
--------------------------------------------------------------------------------
/routes/server/content.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let express = require('express')
4 | let router = express.Router()
5 | let core = require('../../libs/core')
6 | let action = require('../../middlewares/action')
7 | let content = require('../../controllers/server/content')
8 |
9 | //权限判断
10 | router.use(function(req, res, next) {
11 | console.log('内容页: ' + Date.now());
12 | res.locals.Path = 'content';
13 | if(!req.session.user) {
14 | let path = core.translateAdminDir('/user/login');
15 | return res.redirect(path);
16 | }
17 | next();
18 | });
19 |
20 | //内容列表
21 | router.route('/').get(action.checkAction('CONTENT_INDEX'), content.list);
22 | //添加内容
23 | router.route('/add').all(action.checkAction('CONTENT_CREATE'), content.add);
24 | //更新信息
25 | router.route('/:id/edit').all(action.checkAction('CONTENT_UPDATE'), content.edit);
26 | //删除信息
27 | router.route('/:id/del').all(action.checkAction('CONTENT_DELETE'), content.del);
28 |
29 |
30 | module.exports = function(app) {
31 | let path = core.translateAdminDir('/content');
32 | app.use(path, router);
33 | };
34 |
--------------------------------------------------------------------------------
/routes/server/file.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let express = require('express')
4 | let router = express.Router()
5 | let core = require('../../libs/core')
6 | let action = require('../../middlewares/action')
7 | let file = require('../../controllers/server/file')
8 |
9 | //文件
10 | router.use(function(req, res, next) {
11 | console.log('文件页: ' + Date.now());
12 | res.locals.Path = 'file';
13 | if(!req.session.user) {
14 | let path = core.translateAdminDir('/user/login');
15 | return res.redirect(path);
16 | }
17 | next();
18 | });
19 | //内容列表
20 | router.route('/').get(action.checkAction('FILE_INDEX'), file.list);
21 | //添加内容
22 | router.route('/add').all(action.checkAction('FILE_CREATE'), file.add);
23 | //单条信息
24 | /*router.route('/:id').get(action.checkAction('FILE_DETAIL'), file.one);*/
25 | //更新信息
26 | /*router.route('/:id/edit').all(action.checkAction('FILE_UPDATE'), file.edit);*/
27 | //删除信息
28 | router.route('/:id/del').all(action.checkAction('FILE_DELETE'), file.del);
29 |
30 |
31 | module.exports = function(app) {
32 | let path = core.translateAdminDir('/file');
33 | app.use(path, router);
34 | };
35 |
--------------------------------------------------------------------------------
/routes/server/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | /**
3 | ** 这个页面文件名比较特殊,为了能在最后加载所以前面加了个z,对应controller是index.js
4 | **
5 | */
6 | let express = require('express')
7 | let router = express.Router()
8 | let core = require('../../libs/core')
9 | let index = require('../../controllers/server/index')
10 |
11 | //首页
12 | router.use(function(req, res, next) {
13 | console.log('首页: ' + Date.now());
14 | res.locals.Path = 'index';
15 | next();
16 | });
17 | router.get('/', index.index);
18 | router.route('/install').all(index.install);
19 |
20 | module.exports = function(app) {
21 | let path = core.translateAdminDir('/');
22 | app.use(path, router);
23 | };
24 |
--------------------------------------------------------------------------------
/routes/server/log.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let express = require('express')
4 | let router = express.Router()
5 | let core = require('../../libs/core')
6 | let action = require('../../middlewares/action')
7 | let log = require('../../controllers/server/log')
8 |
9 | //权限判断
10 | router.use(function(req, res, next) {
11 | console.log('通知页: ' + Date.now());
12 | res.locals.Path = 'log';
13 | if(!req.session.user) {
14 | let path = core.translateAdminDir('/user/login');
15 | return res.redirect(path);
16 | }
17 | next();
18 | });
19 |
20 | //单条信息
21 | router.route('/:id').get(action.checkAction('LOG_DETAIL'), log.one);
22 | //删除信息
23 | router.route('/:id/del').all(action.checkAction('LOG_DELETE'), log.del);
24 | //内容列表
25 | router.route('/').get(action.checkAction('LOG_INDEX'), log.list);
26 |
27 | module.exports = function(app) {
28 | let path = core.translateAdminDir('/log');
29 | app.use(path, router);
30 | };
31 |
--------------------------------------------------------------------------------
/routes/server/me.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let express = require('express')
4 | let router = express.Router()
5 | let core = require('../../libs/core')
6 | let me = require('../../controllers/server/me')
7 |
8 | //首页
9 | router.use(function(req, res, next) {
10 | console.log('管理员信息: ' + Date.now());
11 | res.locals.Path = 'me';
12 | if(!req.session.user) {
13 | let path = core.translateAdminDir('/user/login');
14 | return res.redirect(path);
15 | }
16 | next();
17 | });
18 | router.get('/', me.init);
19 | router.route('/edit').all(me.edit);
20 | router.route('/updatepwd').all(me.updatePassword);
21 |
22 | module.exports = function(app) {
23 | let path = core.translateAdminDir('/me');
24 | app.use(path, router);
25 | };
26 |
--------------------------------------------------------------------------------
/routes/server/message.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let express = require('express')
4 | let router = express.Router()
5 | let core = require('../../libs/core')
6 | let action = require('../../middlewares/action')
7 | let message = require('../../controllers/server/message')
8 |
9 | //权限判断
10 | router.use(function(req, res, next) {
11 | console.log('评论页: ' + Date.now());
12 | res.locals.Path = 'message';
13 | if(!req.session.user) {
14 | let path = core.translateAdminDir('/user/login');
15 | return res.redirect(path);
16 | }
17 | next();
18 | });
19 | //内容列表
20 | router.route('/').get(action.checkAction('MESSAGE_INDEX'), message.list);
21 | //单条信息
22 | router.route('/:id').get(action.checkAction('MESSAGE_DETAIL'), message.one);
23 | //删除信息
24 | router.route('/:id/del').all(action.checkAction('MESSAGE_DELETE'), message.del);
25 |
26 | module.exports = function(app) {
27 | let path = core.translateAdminDir('/message');
28 | app.use(path, router);
29 | };
30 |
--------------------------------------------------------------------------------
/routes/server/notification.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let express = require('express')
4 | let router = express.Router()
5 | let core = require('../../libs/core')
6 | let action = require('../../middlewares/action')
7 | let notification = require('../../controllers/server/notification')
8 |
9 | //权限判断
10 | router.use(function(req, res, next) {
11 | console.log('通知页: ' + Date.now());
12 | res.locals.Path = 'notification';
13 | if(!req.session.user) {
14 | let path = core.translateAdminDir('/user/login');
15 | return res.redirect(path);
16 | }
17 | next();
18 | });
19 |
20 | //已发出
21 | router.route('/sent').get(action.checkAction('NOTIFICATION_INDEX'), notification.sent);
22 | router.route('/received').get(action.checkAction('NOTIFICATION_INDEX'), notification.received);
23 | //发信
24 | router.route('/add').all(action.checkAction('NOTIFICATION_CREATE'), notification.add);
25 | //单条信息
26 | router.route('/:id').get(action.checkAction('NOTIFICATION_DETAIL'), notification.one);
27 | //删除信息
28 | router.route('/:id/del').all(action.checkAction('NOTIFICATION_DELETE'), notification.del);
29 | //内容列表
30 | router.route('/').get(action.checkAction('NOTIFICATION_INDEX'), notification.list);
31 |
32 | module.exports = function(app) {
33 | let path = core.translateAdminDir('/notification');
34 | app.use(path, router);
35 | };
36 |
--------------------------------------------------------------------------------
/routes/server/page.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let express = require('express')
4 | let router = express.Router()
5 | let core = require('../../libs/core')
6 | let action = require('../../middlewares/action')
7 | let page = require('../../controllers/server/page')
8 |
9 | //权限判断
10 | router.use(function(req, res, next) {
11 | console.log('通知页: ' + Date.now());
12 | res.locals.Path = 'page';
13 | if(!req.session.user) {
14 | let path = core.translateAdminDir('/user/login');
15 | return res.redirect(path);
16 | }
17 | next();
18 | });
19 |
20 | //发信
21 | router.route('/add').all(action.checkAction('PAGE_CREATE'), page.add);
22 | //单条信息
23 | router.route('/:id').get(action.checkAction('PAGE_DETAIL'), page.one);
24 | //删除信息
25 | router.route('/:id/del').all(action.checkAction('PAGE_DELETE'), page.del);
26 | //内容列表
27 | router.route('/').get(action.checkAction('PAGE_INDEX'), page.list);
28 |
29 | module.exports = function(app) {
30 | let path = core.translateAdminDir('/page');
31 | app.use(path, router);
32 | };
33 |
--------------------------------------------------------------------------------
/routes/server/role.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let express = require('express')
4 | let router = express.Router()
5 | let core = require('../../libs/core')
6 | let action = require('../../middlewares/action')
7 | let role = require('../../controllers/server/role')
8 |
9 | //权限判断
10 | router.use(function(req, res, next) {
11 | console.log('角色页: ' + Date.now());
12 | res.locals.Path = 'role';
13 | if(!req.session.user) {
14 | let path = core.translateAdminDir('/user/login');
15 | return res.redirect(path);
16 | }
17 | next();
18 | });
19 | //内容列表
20 | router.route('/').get(action.checkAction('ROLE_INDEX'), role.list);
21 | //添加内容
22 | router.route('/add').all(action.checkAction('ROLE_CREATE'), role.add);
23 | //单条信息
24 | router.route('/:id').get(action.checkAction('ROLE_DETAIL'), role.one);
25 | //更新信息
26 | router.route('/:id/edit').all(action.checkAction('ROLE_UPDATE'), role.edit);
27 | //删除信息
28 | router.route('/:id/del').all(action.checkAction('ROLE_DELETE'), role.del);
29 |
30 |
31 | module.exports = function(app) {
32 | let path = core.translateAdminDir('/role');
33 | app.use(path, router);
34 | };
35 |
--------------------------------------------------------------------------------
/routes/server/tag.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let express = require('express')
4 | let router = express.Router()
5 | let core = require('../../libs/core')
6 | let action = require('../../middlewares/action')
7 | let tag = require('../../controllers/server/tag')
8 |
9 | //权限判断
10 | router.use(function(req, res, next) {
11 | console.log('标签页: ' + Date.now());
12 | res.locals.Path = 'tag';
13 | if(!req.session.user) {
14 | let path = core.translateAdminDir('/user/login');
15 | return res.redirect(path);
16 | }
17 | next();
18 | });
19 | //标签列表
20 | router.route('/').get(action.checkAction('TAG_INDEX'), tag.list);
21 | //添加标签
22 | router.route('/add').all(action.checkAction('TAG_CREATE'), tag.add);
23 | //单条信息
24 | router.route('/:id').get(action.checkAction('TAG_DETAIL'), tag.one);
25 | //更新信息
26 | router.route('/:id/edit').all(action.checkAction('TAG_UPDATE'), tag.edit);
27 | //删除信息
28 | router.route('/:id/del').all(action.checkAction('TAG_DELETE'), tag.del);
29 |
30 | module.exports = function(app) {
31 | let path = core.translateAdminDir('/tag');
32 | app.use(path, router);
33 | };
34 |
--------------------------------------------------------------------------------
/routes/server/user.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let express = require('express')
4 | let router = express.Router()
5 | let core = require('../../libs/core')
6 | let action = require('../../middlewares/action')
7 | let user = require('../../controllers/server/user')
8 |
9 | //权限判断
10 | router.use(function(req, res, next) {
11 | console.log('用户页: ' + Date.now());
12 | res.locals.Path = 'user';
13 | next();
14 | });
15 | //登录
16 | router.route('/login').all(user.checkInstall, user.login);
17 | //注册
18 | router.route('/register').all(user.register);
19 | //查询
20 | router.route('/query').all(user.authenticate, user.query);
21 |
22 | //注销
23 | router.route('/logout').all(user.logout);
24 | //忘记密码
25 | router.route('/forget').all(user.forget);
26 |
27 |
28 | //权限判断
29 | router.use(function(req, res, next) {
30 | if(!req.session.user) {
31 | let path = core.translateAdminDir('/user/login');
32 | return res.redirect(path);
33 | }
34 | next();
35 | });
36 | //用户列表
37 | router.route('/').get(action.checkAction('USER_INDEX'), user.list);
38 | //添加
39 | router.route('/add').all(action.checkAction('USER_CREATE'), user.add);
40 | //单个用户
41 | router.route('/:id').get(action.checkAction('USER_DETAIL'), user.one);
42 | //编辑用户信息
43 | router.route('/:id/edit').all(action.checkAction('USER_UPDATE'), user.edit);
44 | //删除用户
45 | router.route('/:id/del').all(action.checkAction('USER_DELETE'), user.del);
46 |
47 |
48 |
49 | module.exports = function(app) {
50 | let path = core.translateAdminDir('/user');
51 | app.use(path, router);
52 | };
53 |
--------------------------------------------------------------------------------
/services/base.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let _ = require('lodash')
4 |
5 | /**
6 | * 基础服务
7 | **/
8 | let services = function(Model) {
9 | return {
10 | count: function(_condition) {
11 | let condition = _condition || {};
12 | return new Promise(function(resolve, reject) {
13 | Model.count(condition, function(err, total) {
14 | if (err) {
15 | reject(err)
16 | } else {
17 | resolve(total)
18 | }
19 | })
20 | })
21 | },
22 | find: function(condition = {}, fields = null, options = {}) {
23 | return new Promise(function(resolve, reject) {
24 | Model.find(condition, fields, options, function(err, result) {
25 | if (err) {
26 | reject(err)
27 | } else {
28 | resolve(result)
29 | }
30 | })
31 | });
32 | },
33 | findOne: function(condition = {}, projection = null, options = {}) {
34 | return new Promise(function(resolve, reject) {
35 | Model.findOne(condition, projection, options, function(err, result) {
36 | if (err) {
37 | reject(err);
38 | } else {
39 | resolve(result)
40 | }
41 | })
42 | })
43 | },
44 | findById: function(id, populates = []) {
45 | return new Promise(function(resolve, reject) {
46 | let query = Model.findById(id)
47 | if (populates && populates.length > 0) {
48 | populates.forEach(function(item) {
49 | query = query.populate(item);
50 | })
51 | }
52 | query.exec(function(err, result) {
53 | if (err) {
54 | reject(err)
55 | } else {
56 | resolve(result)
57 | }
58 | });
59 | })
60 | },
61 | create: function(obj) {
62 | return new Promise(function(resolve, reject) {
63 | let user = new Model(obj);
64 | user.save(function(err, result) {
65 | if (err) {
66 | reject(err)
67 | } else {
68 | resolve(result)
69 | }
70 | });
71 | })
72 | },
73 | update: function(condition = {}, doc = {}, options = {}) {
74 | return new Promise(function(resolve, reject) {
75 | Model.update(condition, doc, options, function(err, result) {
76 | if (err) {
77 | reject(err)
78 | } else {
79 | resolve(result)
80 | }
81 | })
82 | })
83 | },
84 | findOneAndUpdate: function(condition = {}, doc = {}, options = {}) {
85 |
86 | },
87 | findByIdAndUpdate: function(id, obj, options) {
88 | return new Promise(function(resolve, reject) {
89 | Model.findByIdAndUpdate(id, obj, options, function(err, result) {
90 | if (err) {
91 | reject(err)
92 | } else {
93 | resolve(result);
94 | }
95 | })
96 | })
97 | },
98 | updateById: function(id, obj, options) {
99 | return this.findByIdAndUpdate(id, obj, options)
100 | },
101 | remove: function(condition = {}) {
102 | return new Promise(function(resolve, reject) {
103 | Model.remove(condition, function(err, result) {
104 | if (err) {
105 | reject(err)
106 | } else {
107 | resolve(result)
108 | }
109 | })
110 | })
111 | },
112 | findOneAndRemove: function(condition) {
113 |
114 | },
115 | findByIdAndRemove: function(id, options = null) {
116 | return new Promise(function(resolve, reject) {
117 | Model.findByIdAndRemove(id, options, function(err, result) {
118 | if (err) {
119 | reject(err)
120 | } else {
121 | resolve(result)
122 | }
123 | })
124 | })
125 | },
126 | removeById: function(id) {
127 | return this.findByIdAndRemove(id)
128 | },
129 |
130 | }
131 | }
132 |
133 | module.exports = services;
--------------------------------------------------------------------------------
/services/category.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 分类服务
3 | **/
4 | 'use strict';
5 |
6 | let mongoose = require('mongoose');
7 | let _ = require('lodash');
8 | let Category = mongoose.model('Category');
9 |
10 |
11 | let baseServices = require('./base')(Category);
12 |
13 | let services = {
14 | findBySome: function(id, populates) {
15 | return new Promise(function(resolve, reject) {
16 | let query = Category.findById(id)
17 | if (populates && populates.length > 0) {
18 | populates.forEach(function(item) {
19 | query = query.populate(item);
20 | })
21 | }
22 | query.exec(function(err, result) {
23 | if (err) {
24 | reject(err)
25 | } else {
26 | resolve(result)
27 | }
28 | });
29 | })
30 | }
31 | };
32 |
33 | module.exports = _.assign({}, baseServices, services);
--------------------------------------------------------------------------------
/services/comment.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 评论服务
3 | **/
4 | 'use strict';
5 |
6 | let mongoose = require('mongoose');
7 | let _ = require('lodash');
8 | let Comment = mongoose.model('Comment');
9 |
10 |
11 | let baseServices = require('./base')(Comment);
12 |
13 | let services = {
14 | findBySome: function(id, populates) {
15 | return new Promise(function(resolve, reject) {
16 | let query = Comment.findById(id)
17 | if (populates && populates.length > 0) {
18 | populates.forEach(function(item) {
19 | query = query.populate(item);
20 | })
21 | }
22 | query.exec(function(err, result) {
23 | if (err) {
24 | reject(err)
25 | } else {
26 | resolve(result)
27 | }
28 | });
29 | })
30 | }
31 | };
32 |
33 | module.exports = _.assign({}, baseServices, services);
--------------------------------------------------------------------------------
/services/content.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 内容服务
3 | **/
4 | 'use strict';
5 |
6 | let mongoose = require('mongoose');
7 | let _ = require('lodash');
8 | let Content = mongoose.model('Content');
9 |
10 |
11 | let baseServices = require('./base')(Content);
12 |
13 | let services = {
14 | findBySome: function(id, populates) {
15 | return new Promise(function(resolve, reject) {
16 | let query = Content.findById(id)
17 | if (populates && populates.length > 0) {
18 | populates.forEach(function(item) {
19 | query = query.populate(item);
20 | })
21 | }
22 | query.exec(function(err, result) {
23 | if (err) {
24 | reject(err)
25 | } else {
26 | resolve(result)
27 | }
28 | });
29 | })
30 | }
31 | };
32 |
33 | module.exports = _.assign({}, baseServices, services);
34 |
35 |
--------------------------------------------------------------------------------
/services/file.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 文件服务
3 | **/
4 | 'use strict';
5 |
6 | let mongoose = require('mongoose');
7 | let _ = require('lodash');
8 | let File = mongoose.model('File');
9 |
10 |
11 | let baseServices = require('./base')(File);
12 |
13 | let services = {
14 | findBySome: function(id, populates) {
15 | return new Promise(function(resolve, reject) {
16 | let query = File.findById(id)
17 | if (populates && populates.length > 0) {
18 | populates.forEach(function(item) {
19 | query = query.populate(item);
20 | })
21 | }
22 | query.exec(function(err, result) {
23 | if (err) {
24 | reject(err)
25 | } else {
26 | resolve(result)
27 | }
28 | });
29 | })
30 | }
31 | };
32 |
33 | module.exports = _.assign({}, baseServices, services);
--------------------------------------------------------------------------------
/services/log.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 页面服务
3 | **/
4 | 'use strict';
5 |
6 | let mongoose = require('mongoose');
7 | let _ = require('lodash');
8 | let Log = mongoose.model('Log');
9 |
10 |
11 | let baseServices = require('./base')(Log);
12 |
13 | let services = {
14 | add: function(obj) {
15 | return this.create(_.pick(obj, 'type', 'action', 'status', 'ip', 'ua', 'message', 'author'));
16 | }
17 | };
18 |
19 | module.exports = _.assign({}, baseServices, services);
--------------------------------------------------------------------------------
/services/message.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 留言服务
3 | **/
4 | 'use strict';
5 |
6 | let mongoose = require('mongoose');
7 | let _ = require('lodash');
8 | let Message = mongoose.model('Message');
9 |
10 |
11 | let baseServices = require('./base')(Message);
12 |
13 | let services = {
14 | findBySome: function(id, populates) {
15 | return new Promise(function(resolve, reject) {
16 | let query = Message.findById(id)
17 | if (populates && populates.length > 0) {
18 | populates.forEach(function(item) {
19 | query = query.populate(item);
20 | })
21 | }
22 | query.exec(function(err, result) {
23 | if (err) {
24 | reject(err)
25 | } else {
26 | resolve(result)
27 | }
28 | });
29 | })
30 | }
31 | };
32 |
33 | module.exports = _.assign({}, baseServices, services);
--------------------------------------------------------------------------------
/services/notification.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 通知服务
3 | **/
4 | 'use strict';
5 |
6 | let mongoose = require('mongoose');
7 | let _ = require('lodash');
8 | let Notification = mongoose.model('Notification');
9 |
10 |
11 | let baseServices = require('./base')(Notification);
12 |
13 | let services = {
14 | findBySome: function(id, populates) {
15 | return new Promise(function(resolve, reject) {
16 | let query = Notification.findById(id)
17 | if (populates && populates.length > 0) {
18 | populates.forEach(function(item) {
19 | query = query.populate(item);
20 | })
21 | }
22 | query.exec(function(err, result) {
23 | if (err) {
24 | reject(err)
25 | } else {
26 | resolve(result)
27 | }
28 | });
29 | })
30 | }
31 | };
32 |
33 | module.exports = _.assign({}, baseServices, services);
--------------------------------------------------------------------------------
/services/page.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 页面服务
3 | **/
4 | 'use strict';
5 |
6 | let mongoose = require('mongoose');
7 | let _ = require('lodash');
8 | let Page = mongoose.model('Page');
9 |
10 |
11 | let baseServices = require('./base')(Page);
12 |
13 | let services = {
14 | findBySome: function(id, populates) {
15 | return new Promise(function(resolve, reject) {
16 | let query = Page.findById(id)
17 | if (populates && populates.length > 0) {
18 | populates.forEach(function(item) {
19 | query = query.populate(item);
20 | })
21 | }
22 | query.exec(function(err, result) {
23 | if (err) {
24 | reject(err)
25 | } else {
26 | resolve(result)
27 | }
28 | });
29 | })
30 | }
31 | };
32 |
33 | module.exports = _.assign({}, baseServices, services);
--------------------------------------------------------------------------------
/services/role.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 角色服务
3 | **/
4 | 'use strict';
5 |
6 | let mongoose = require('mongoose');
7 | let _ = require('lodash');
8 | let Role = mongoose.model('Role');
9 |
10 |
11 | let baseServices = require('./base')(Role);
12 |
13 | let services = {
14 | findBySome: function(id, populates) {
15 | return new Promise(function(resolve, reject) {
16 | let query = Role.findById(id)
17 | if (populates && populates.length > 0) {
18 | populates.forEach(function(item) {
19 | query = query.populate(item);
20 | })
21 | }
22 | query.exec(function(err, result) {
23 | if (err) {
24 | reject(err)
25 | } else {
26 | resolve(result)
27 | }
28 | });
29 | })
30 | }
31 | };
32 |
33 | module.exports = _.assign({}, baseServices, services);
--------------------------------------------------------------------------------
/services/tag.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 标签服务
3 | **/
4 | 'use strict';
5 |
6 | let mongoose = require('mongoose');
7 | let _ = require('lodash');
8 | let Tag = mongoose.model('Tag');
9 |
10 |
11 | let baseServices = require('./base')(Tag);
12 |
13 | let services = {
14 | findBySome: function(id, populates) {
15 | return new Promise(function(resolve, reject) {
16 | let query = Tag.findById(id)
17 | if (populates && populates.length > 0) {
18 | populates.forEach(function(item) {
19 | query = query.populate(item);
20 | })
21 | }
22 | query.exec(function(err, result) {
23 | if (err) {
24 | reject(err)
25 | } else {
26 | resolve(result)
27 | }
28 | });
29 | })
30 | }
31 | };
32 |
33 | module.exports = _.assign({}, baseServices, services);
--------------------------------------------------------------------------------
/services/user.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 用户服务
3 | **/
4 | 'use strict';
5 |
6 | let mongoose = require('mongoose');
7 | let _ = require('lodash');
8 | let moment = require('moment')
9 | let config = require('../config');
10 | let core = require('../libs/core');
11 | let User = mongoose.model('User');
12 | let RoleService = require('./role');
13 |
14 |
15 | let baseServices = require('./base')(User);
16 |
17 | let services = {
18 | login: function(id, populates) {
19 | return new Promise(function(resolve, reject) {
20 |
21 | })
22 | },
23 | register: function(obj) {
24 | return new Promise(function(resolve, reject) {
25 | if (!obj) {
26 | return reject(null)
27 | }
28 | //默认角色
29 | RoleService.read({status: 202}, function(err, role) {
30 | console.log('role', role);
31 | if(err || !role) {
32 | console.log('注册失败, 未开放角色:' + config.admin.role.user)
33 | }
34 | obj.roles = [role._id];
35 | obj.reg_ip = core.getIp(req);
36 | let user = new User(obj);
37 | user.save(function(err, result) {
38 | console.log(result);
39 | if (err) {
40 | console.log(err);
41 |
42 | } else {
43 | console.log('注册成功')
44 | }
45 |
46 | });
47 | });
48 | })
49 | },
50 | trend: function() {
51 | let now = new Date()
52 | let lastMonth = moment().subtract(3, 'month').format()
53 | return new Promise(function(resolve, reject) {
54 | User.aggregate({
55 | $match: {
56 | created: {'$gt': new Date(lastMonth)}
57 | }
58 | }, {
59 | $project: {
60 | d: {$add: ['$created', 28800000]}
61 | }
62 | }, {
63 | $project: {
64 | day: {$dateToString: {format: '%Y-%m-%d', date: '$d'}}
65 | }
66 | }, {
67 | $group: {
68 | _id: '$day',
69 | total: {$sum: 1}
70 | }
71 | }, {
72 | $sort: {
73 | _id: -1
74 | }
75 | }).exec((err, res) => {
76 | if (err) {
77 | reject(err)
78 | } else {
79 | resolve(res)
80 | }
81 | })
82 | })
83 | }
84 | };
85 |
86 | module.exports = _.assign({}, baseServices, services);
87 |
--------------------------------------------------------------------------------
/views/app/contact.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/views/app/content/list.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 标题 |
8 | 作者 |
9 | 发布时间 |
10 |
11 |
12 |
13 | {{#each contents}}
14 |
15 | {{title}} |
16 | {{#if author}}{{author.name}}{{/if}}
17 | |
18 | {{dateFormat created 'yyyy-MM-dd hh:mm:ss'}} |
19 |
20 | {{/each}}
21 |
22 |
23 |
24 |
25 | {{#if pageInfo}}
26 | {{#compare pageInfo.totalPage '>' 1}}
27 |
28 | {{/compare}}
29 | {{/if}}
30 |
31 |
32 |
--------------------------------------------------------------------------------
/views/app/index.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{#if key}}
3 | 搜索 "{{key}}" 共有 {{total}} 条结果
4 | {{/if}}
5 | {{#each contents}}
6 |
7 |
8 | {{#if gallery}}
9 | {{#if thumb}}
10 | {{#each gallery}}
11 | {{#compare ../thumb '==' id}}
12 |
13 |

14 |
15 | {{/compare}}
16 | {{/each}}
17 | {{/if}}
18 | {{/if}}
19 |
20 |
21 |
22 | {{strip100 content}}...
23 |
24 |
25 |
26 | {{dateFromNow created}} - {{visits}} 热度 - {{#if comments}} {{comments.length}} {{else}} 0 {{/if}} 评论
27 |
28 |
29 |
30 | {{/each}}
31 | {{#if pageInfo}}
32 | {{#compare pageInfo.totalPage '>' 1}}
33 |
34 | {{/compare}}
35 | {{/if}}
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/views/app/info.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{message}}
3 |
4 |
5 |
--------------------------------------------------------------------------------
/views/app/page/item.hbs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/views/app/page/list.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 标题 |
8 | 作者 |
9 | 发布时间 |
10 |
11 |
12 |
13 |
14 | {{#each pages}}
15 |
16 | {{title}} |
17 | {{#if author}}{{author.name}}{{/if}}
18 | |
19 | {{dateFormat created 'yyyy-MM-dd hh:mm:ss'}} |
20 |
21 | {{/each}}
22 |
23 |
24 |
25 |
26 |
27 |
28 | {{#if pageInfo}}
29 | {{#compare pageInfo.totalPage '>' 1}}
30 |
31 | {{/compare}}
32 | {{/if}}
33 |
34 |
35 |
--------------------------------------------------------------------------------
/views/layouts/app_layout.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{title}}
5 |
6 |
7 |
8 |
9 |
10 | {{#each cssList}}
11 |
12 | {{/each}}
13 |
14 |
15 |
16 |
38 |
39 |
40 | {{{body}}}
41 |
42 |
43 |
53 |
54 |
55 |
56 | {{#each jsList}}
57 |
58 | {{/each}}
59 | {{{_sections.scripts}}}
60 |
61 |
--------------------------------------------------------------------------------
/views/layouts/layout-blank.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {{title}}
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | {{{body}}}
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/views/layouts/layout.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 管理后台 - {{title}}
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | {{#each cssList}}
21 |
22 | {{/each}}
23 |
24 |
25 |
26 | {{> header}}
27 |
28 |
29 | {{> sidebar}}
30 |
31 |
32 |
33 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | {{#each jsList}}
48 |
49 | {{/each}}
50 |
51 |
52 | {{{_sections.scripts}}}
53 |
54 |
55 |
56 |
57 |
58 |
59 |
63 |
64 |
确认删除?
65 |
67 |
68 |
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/views/partials/header.hbs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/views/partials/sidebar.hbs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/views/server/category/add.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 分类列表
6 | 添加分类
7 |
8 |
9 |
38 |
39 |
--------------------------------------------------------------------------------
/views/server/category/edit.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 分类列表
6 | 添加分类
7 |
8 |
9 |
38 |
39 |
--------------------------------------------------------------------------------
/views/server/category/item.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 分类列表
6 | 添加分类
7 |
8 |
9 |
10 |
名称:{{category.name}}
11 |
副名称:{{category.subName}}
12 |
标记(别名):{{category.flag}}
13 |
父分类:{{category.flag}}
14 |
日期:{{category.created}}
15 |
描述:{{category.description}}
16 |
17 |
18 |
--------------------------------------------------------------------------------
/views/server/category/list.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
添加分类
4 |
5 |
6 |
7 |
8 | 名称 |
9 | 副名稱 |
10 | 标记 |
11 | 创建者 |
12 | 创建时间 |
13 | 操作 |
14 |
15 |
16 |
17 |
18 | {{#each categorys}}
19 | {{name}} |
20 | {{subName}} |
21 | {{flag}} |
22 | {{author.name}} |
23 | {{dateFormat created 'yyyy-MM-dd hh:mm:ss'}} |
24 | 编辑
25 |
26 | 删除
31 |
32 |
33 | |
34 |
35 | {{/each}}
36 |
37 |
38 | {{#if pageInfo}}
39 | {{#compare pageInfo.totalPage '>' 1}}
40 |
41 | {{/compare}}
42 | {{/if}}
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/views/server/comment/item.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 评论列表
6 |
7 |
8 |
9 |
{{comment.content}}
10 |
来自:{{#if comment.from}}{{comment.from.title}}{{/if}}
11 |
12 |
{{comment.created}}
13 |
作者:{{#if comment.author}}{{comment.author}}{{else}}匿名{{/if}}
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/views/server/comment/list.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 内容 |
9 | 作者 |
10 | 来自 |
11 | IP |
12 | 时间 |
13 | 操作 |
14 |
15 |
16 |
17 | {{#each comments}}
18 |
19 | {{content}} |
20 | {{#if author }}{{author.name}} {{else}}匿名{{/if}} |
21 | {{#if from }}{{from.title}} {{/if}}
22 | |
23 | {{ip}} |
24 | {{dateFormat created 'yyyy-MM-dd hh:mm:ss'}} |
25 |
26 | 删除
30 | |
31 |
32 | {{/each}}
33 |
34 |
35 | {{#if pageInfo}}
36 | {{#compare pageInfo.totalPage '>' 1}}
37 |
38 | {{/compare}}
39 | {{/if}}
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/views/server/content/add.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{css '/assets/plugins/mditor/css/mditor.min.css'}}
3 | {{css 'http://cdn.jsdelivr.net/jquery.fileupload/9.9.0/css/jquery.fileupload.css'}}
4 |
5 | {{js '/assets/plugins/mditor/js/mditor.min.js' }}
6 | {{js 'http://cdn.jsdelivr.net/jquery.fileupload/9.9.0/js/vendor/jquery.ui.widget.js' }}
7 | {{js 'http://cdn.jsdelivr.net/load-image/1.11.1/js/load-image.min.js' }}
8 | {{js 'http://cdn.jsdelivr.net/jquery.fileupload/9.9.0/js/jquery.fileupload.js' }}
9 | {{js 'http://cdn.jsdelivr.net/jquery.fileupload/9.9.0/js/jquery.fileupload-process.js' }}
10 | {{js 'http://cdn.jsdelivr.net/jquery.fileupload/9.9.0/js/jquery.fileupload-image.js' }}
11 | {{js 'http://cdn.jsdelivr.net/jquery.fileupload/9.9.0/js/jquery.fileupload-audio.js' }}
12 | {{js 'http://cdn.jsdelivr.net/jquery.fileupload/9.9.0/js/jquery.fileupload-video.js' }}
13 | {{js 'http://cdn.jsdelivr.net/jquery.fileupload/9.9.0/js/jquery.fileupload-validate.js' }}
14 |
15 |
16 |
17 |
18 |
19 |
20 | 文章列表
21 |
22 |
23 |
58 |
59 |
60 |
61 | {{#section 'scripts'}}
62 |
144 | {{/section}}
145 |
--------------------------------------------------------------------------------
/views/server/content/list.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 新增文章
7 |
8 |
9 |
10 |
11 |
12 | {{#each contents}}
13 |
14 |
15 | {{title}}
16 |
17 | |
18 |
19 | {{author.name}}
20 | |
21 | {{dateFormat created 'yyyy-MM-dd hh:mm:ss'}} |
22 | 编辑
23 | 删除
27 | |
28 |
29 | {{/each}}
30 |
31 |
32 |
33 | {{#if pageInfo}}
34 | {{#compare pageInfo.totalPage '>' 1}}
35 |
36 |
39 |
40 | {{/compare}}
41 | {{/if}}
42 |
43 |
44 |
--------------------------------------------------------------------------------
/views/server/error.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
{{message}}
7 |
8 | {{error.status}}
9 |
10 |
11 | {{error.stack}}
12 |
13 |
14 |
15 | {{#if backPath}}
16 | 返回
17 | {{else}}
18 | 返回
19 | {{/if}}
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/views/server/file/add.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{css 'http://cdn.jsdelivr.net/jquery.fileupload/9.9.0/css/jquery.fileupload.css'}}
3 | {{js 'http://cdn.jsdelivr.net/jquery.fileupload/9.9.0/js/vendor/jquery.ui.widget.js' }}
4 | {{js 'http://cdn.jsdelivr.net/load-image/1.11.1/js/load-image.min.js' }}
5 | {{js 'http://cdn.jsdelivr.net/jquery.fileupload/9.9.0/js/jquery.fileupload.js' }}
6 | {{js 'http://cdn.jsdelivr.net/jquery.fileupload/9.9.0/js/jquery.fileupload-process.js' }}
7 | {{js 'http://cdn.jsdelivr.net/jquery.fileupload/9.9.0/js/jquery.fileupload-image.js' }}
8 | {{js 'http://cdn.jsdelivr.net/jquery.fileupload/9.9.0/js/jquery.fileupload-audio.js' }}
9 | {{js 'http://cdn.jsdelivr.net/jquery.fileupload/9.9.0/js/jquery.fileupload-video.js' }}
10 | {{js 'http://cdn.jsdelivr.net/jquery.fileupload/9.9.0/js/jquery.fileupload-validate.js' }}
11 |
12 |
13 |
14 |
15 | 文件列表
16 |
17 |
31 |
32 |
33 |
34 |
35 |
36 | {{#section 'scripts'}}
37 |
79 | {{/section}}
80 |
--------------------------------------------------------------------------------
/views/server/file/list.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
添加文件
4 |
5 |
6 |
7 | 名称 |
8 | 创建人 |
9 | 类型 |
10 | 创建时间 |
11 | 操作 |
12 |
13 |
14 |
15 | {{#each files}}
16 |
17 |
18 |
31 | |
32 | {{author.username}}
33 | |
34 | {{type}} |
35 | {{dateFormat created 'yyyy-MM-dd hh:mm:ss'}} |
36 |
37 | 删除
41 | |
42 |
43 | {{/each}}
44 |
45 |
46 | {{#if pageInfo}}
47 | {{#compare pageInfo.totalPage '>' 1}}
48 |
49 | {{/compare}}
50 | {{/if}}
51 |
52 |
53 |
--------------------------------------------------------------------------------
/views/server/index.hbs:
--------------------------------------------------------------------------------
1 |
2 |
9 |
--------------------------------------------------------------------------------
/views/server/info.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
{{message}}
10 |
11 | {{#if backPath}}
12 | 返回
13 | {{else}}
14 | 返回
15 | {{/if}}
16 |
17 |
18 |
--------------------------------------------------------------------------------
/views/server/install.hbs:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/views/server/log/item.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
日志列表
4 |
5 |
类型:{{log.type}}
6 |
动作:{{log.action}}
7 |
状态:{{log.status}}
8 |
ip:{{log.ip}}
9 |
ua:{{log.ua}}
10 |
用户:{{log.author}}
11 |
内容:{{log.message}}
12 |
日期:{{dateFormat log.created 'yyyy-MM-dd hh:mm:ss'}}
13 |
14 |
15 |
--------------------------------------------------------------------------------
/views/server/log/list.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 类型 |
8 | 动作 |
9 | 状态 |
10 | ip |
11 | 信息 |
12 | 时间 |
13 | 操作 |
14 |
15 |
16 |
17 | {{#each logs}}
18 |
19 | {{type}} |
20 | {{action}} |
21 | {{status}} |
22 | {{ip}} |
23 | {{message}} |
24 | {{dateFormat created 'yyyy-MM-dd hh:mm:ss'}} |
25 | 删除 |
26 |
27 | {{/each}}
28 |
29 |
30 |
31 | {{#if pageInfo}}
32 | {{#compare pageInfo.totalPage '>' 1}}
33 |
34 | {{/compare}}
35 | {{/if}}
36 |
37 |
38 |
39 |
40 | {{#section 'scripts'}}
41 |
60 | {{/section}}
--------------------------------------------------------------------------------
/views/server/me/edit.hbs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/views/server/me/item.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
我的信息
4 |
5 | -
6 |
7 |

8 |
9 |
13 |
14 |
15 | -
16 |
17 |
用户名
18 |
{{user.username}}
19 |
20 |
21 | -
22 |
23 |
名字
24 |
{{user.name}}
25 |
26 |
27 | -
28 |
29 |
Email
30 |
{{user.email}}
31 |
32 |
33 | -
34 |
35 |
手机号
36 |
{{user.mobile}}
37 |
38 |
39 | -
40 |
41 |
生日
42 |
{{user.birthday}}
43 |
44 |
45 | -
46 |
47 |
性别
48 |
{{user.gender}}
49 |
50 |
51 | -
52 |
53 |
注册日期
54 |
{{user.created}}
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/views/server/message/item.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 留言列表
5 |
6 |
7 |
内容:{{message.content}}
8 |
日期:{{message.created}}
9 |
作者:{{#if message.name}}{{message.name}}{{/if}}
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/views/server/message/list.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 内容 |
8 | 姓名 |
9 | 邮箱 |
10 | IP |
11 | 时间 |
12 | 操作 |
13 |
14 |
15 |
16 | {{#each messages}}
17 |
18 | {{content}} |
19 | {{#if name}}{{name}} {{else}} 匿名{{/if}}
20 | |
21 | {{email}} |
22 | {{ip}} |
23 | {{dateFormat created 'yyyy-MM-dd hh:mm:ss'}} |
24 |
25 |
26 | 删除
30 |
31 | |
32 |
33 | {{/each}}
34 |
35 |
36 |
37 | {{#if pageInfo}}
38 | {{#compare pageInfo.totalPage '>' 1}}
39 |
40 | {{/compare}}
41 | {{/if}}
42 |
43 |
44 |
--------------------------------------------------------------------------------
/views/server/notification/item.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 全部私信
5 | 已发私信
6 | 已收私信
7 |
8 |
9 |
内容:{{notification.content}}
10 |
来自: {{#each notification.from}}{{name}}{{/each}}
11 |
发给:{{#each notification.to}}{{name}}{{/each}}
12 |
已读:{{#each notification.read}}{{name}}{{/each}}
13 |
未读:{{#each notification.unread}}{{name}}{{/each}}
14 |
日期:{{dateFormat notification.created 'yyyy-MM-dd hh:mm:ss'}}
15 |
16 |
17 |
--------------------------------------------------------------------------------
/views/server/page/item.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 页面列表
5 | 新增页面
6 |
7 |
8 |
内容:{{page.content}}
9 |
日期:{{page.created}}
10 |
作者:{{#if page.author}}{{page.author.name}}{{/if}}
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/views/server/page/list.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 新增页面
5 |
6 |
7 |
8 |
9 |
10 | 标题 |
11 | 内容 |
12 | 时间 |
13 | 操作 |
14 |
15 |
16 |
17 | {{#each pages}}
18 |
19 | {{title}} |
20 | {{content}} |
21 | {{dateFormat created 'yyyy-MM-dd hh:mm:ss'}} |
22 |
23 | 删除
27 | |
28 |
29 | {{/each}}
30 |
31 |
32 | {{#if pageInfo}}
33 | {{#compare pageInfo.totalPage '>' 1}}
34 |
35 | {{/compare}}
36 | {{/if}}
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | {{#section 'scripts'}}
46 |
96 | {{/section}}
97 |
--------------------------------------------------------------------------------
/views/server/role/add.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 角色列表
5 |
6 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/views/server/role/edit.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 角色列表
5 | 添加角色
6 |
7 |
40 |
41 |
42 |
43 |
44 | {{#section 'scripts'}}
45 |
47 | {{/section}}
48 |
--------------------------------------------------------------------------------
/views/server/role/item.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 角色列表
5 | 添加角色
6 |
7 |
8 |
名称:{{role.name}}
9 |
创建人: {{#if role.name}}{{role.name}} {{else}}无{{/if}}
10 |
11 |
权限:{{role.actions}}
12 |
13 |
日期:{{dateFormat role.created 'yyyy-MM-dd hh:mm:ss'}}
14 |
描述:{{role.description}}
15 |
16 |
17 |
--------------------------------------------------------------------------------
/views/server/role/list.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 添加角色
6 |
7 |
8 |
9 |
10 |
11 | 名称 |
12 | 创建人 |
13 | 创建时间 |
14 | 操作 |
15 |
16 |
17 |
18 | {{#each roles}}
19 |
20 | {{name}} |
21 | {{#if author}}{{author.name}}{{else}}无{{/if}}
22 | |
23 | {{dateFormat created 'yyyy-MM-dd hh:mm:ss'}} |
24 |
25 | 编辑
26 |
27 | 删除
32 |
33 | |
34 |
35 | {{/each}}
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | {{#section 'scripts'}}
45 |
68 | {{/section}}
69 |
70 |
--------------------------------------------------------------------------------
/views/server/tag/add.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 标签列表
5 |
6 |
18 |
19 |
20 |
21 |
22 | {{#section 'scripts'}}
23 |
27 | {{/section}}
--------------------------------------------------------------------------------
/views/server/tag/edit.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 标签列表
5 | 添加标签
6 |
7 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/views/server/tag/item.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 标签列表
5 | 添加标签
6 |
7 |
8 |
名称:{{tag.name}}
9 |
日期:{{dateFormat tag.created 'yyyy-MM-dd hh:mm:ss'}}
10 |
描述:{{tag.description}}
11 |
12 |
13 |
--------------------------------------------------------------------------------
/views/server/tag/list.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 添加标签
5 |
6 |
7 |
8 |
9 | 名称 |
10 | 创建者 |
11 | 创建时间 |
12 | 操作 |
13 |
14 |
15 |
16 | {{#each tags}}
17 |
18 | {{name}} |
19 | {{#if author}}{{author.name}}{{else}}无{{/if}}
20 | |
21 | {{dateFormat created 'yyyy-MM-dd hh:mm:ss'}} |
22 | 编辑
23 |
24 | 删除
29 |
30 |
31 | |
32 |
33 | {{/each}}
34 |
35 |
36 | {{#if pageInfo}}
37 | {{#compare pageInfo.totalPage '>' 1}}
38 |
39 | {{/compare}}
40 | {{/if}}
41 |
42 |
43 |
--------------------------------------------------------------------------------
/views/server/user/add.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
44 |
45 |
--------------------------------------------------------------------------------
/views/server/user/edit.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
48 |
49 |
--------------------------------------------------------------------------------
/views/server/user/forget.hbs:
--------------------------------------------------------------------------------
1 | {{#if type}}
2 |
18 | {{else}}
19 |
30 | {{/if}}
--------------------------------------------------------------------------------
/views/server/user/item.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 用户列表
8 | 用户添加
9 |
10 |
11 |
12 |
18 |
24 |
30 |
36 |
42 |
48 |
56 |
64 |
70 |
76 |
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/views/server/user/list.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 添加用户
9 |
10 |
11 |
12 |
13 |
14 | 用户名 |
15 | 姓名 |
16 | 角色 |
17 | 创建人 |
18 | 创建日期 |
19 | 操作 |
20 |
21 |
22 |
23 | {{#each users}}
24 |
25 | {{username}} |
26 | {{name}} |
27 | {{#each roles}}{{name}}{{/each}} |
28 | {{#each author}}{{name}}{{/each}} |
29 | {{dateFormat created 'yyyy-MM-dd hh:mm:ss'}} |
30 |
31 | 编辑
32 |
33 |
34 | 删除
39 |
40 |
41 | |
42 |
43 | {{/each}}
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/views/server/user/login.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
23 |
24 |
25 |
26 |
27 | {{css '/assets/server/css/style.css'}}
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/views/server/user/register.hbs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------