├── .gitignore ├── README.md ├── app.js ├── bin └── www ├── gulpfile.js ├── models ├── comment.js ├── db.js ├── post.js └── user.js ├── package.json ├── public ├── images │ ├── new.jpg │ ├── tooopen_sy_140703593676.jpg │ ├── 模板消息.png │ └── 模板消息1.png ├── javascripts │ └── blog.js ├── jsmin │ └── blog.js └── stylesheets │ └── style.css ├── routes ├── index.js └── users.js ├── settings.js └── views ├── about.ejs ├── article.ejs ├── blog.ejs ├── comment.ejs ├── edit.ejs ├── error.ejs ├── footer.ejs ├── footer_main.ejs ├── header.ejs ├── header_main.ejs ├── home.ejs ├── index.ejs ├── login.ejs ├── paging.ejs ├── post.ejs ├── reg.ejs └── user.ejs /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | npm-debug.log 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Blog 2 | 3 | Node.js 博客实例 4 | 开发环境: 5 | - nodejs 6.3.1 6 | - express 4.13.4 7 | - mongodb 3.2.9 8 | 9 | ## 安装依赖 10 | npm install 11 | 12 | ## 安装数据库 13 | 数据库:**mongodb** 14 | 15 | 监听端口号:27017 16 | 17 | ## 运行 18 | npm start 19 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var path = require('path'); 3 | var favicon = require('serve-favicon'); 4 | var logger = require('morgan'); 5 | var cookieParser = require('cookie-parser'); 6 | var bodyParser = require('body-parser'); 7 | var session = require('express-session'); 8 | var MongoStore = require('connect-mongo')(session); 9 | var flash = require('connect-flash'); 10 | 11 | var routes = require('./routes/index'); 12 | var users = require('./routes/users'); 13 | 14 | var app = express(); 15 | 16 | // view engine setup 17 | app.set('views', path.join(__dirname, 'views')); 18 | app.set('view engine', 'ejs'); 19 | 20 | //session 21 | var settings = require('./settings'); 22 | app.use(session({ 23 | resave: true, 24 | saveUninitialized: true, 25 | secret: settings.cookieSecret, 26 | key: settings.db,//cookie name 27 | cookie: {maxAge:1000*60*60*24*30},//30days 28 | store: new MongoStore({ 29 | url: 'mongodb://localhost/blog' 30 | }) 31 | })); 32 | 33 | app.use(flash()); 34 | // uncomment after placing your favicon in /public 35 | //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); 36 | app.use(logger('dev')); 37 | app.use(bodyParser.json()); 38 | app.use(bodyParser.urlencoded({ extended: false })); 39 | app.use(cookieParser()); 40 | app.use(express.static(path.join(__dirname, 'public'))); 41 | 42 | app.use('/', routes); 43 | app.use('/users', users); 44 | 45 | 46 | // catch 404 and forward to error handler 47 | app.use(function(req, res, next) { 48 | var err = new Error('Not Found'); 49 | err.status = 404; 50 | next(err); 51 | }); 52 | 53 | // error handlers 54 | 55 | // development error handler 56 | // will print stacktrace 57 | if (app.get('env') === 'development') { 58 | app.use(function(err, req, res, next) { 59 | res.status(err.status || 500); 60 | res.render('error', { 61 | message: err.message, 62 | error: err 63 | }); 64 | }); 65 | } 66 | 67 | // production error handler 68 | // no stacktraces leaked to user 69 | app.use(function(err, req, res, next) { 70 | res.status(err.status || 500); 71 | res.render('error', { 72 | message: err.message, 73 | error: {} 74 | }); 75 | }); 76 | 77 | 78 | module.exports = app; 79 | -------------------------------------------------------------------------------- /bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('blog:server'); 9 | var http = require('http'); 10 | 11 | /** 12 | * Get port from environment and store in Express. 13 | */ 14 | 15 | var port = normalizePort(process.env.PORT || '3000'); 16 | app.set('port', port); 17 | 18 | /** 19 | * Create HTTP server. 20 | */ 21 | 22 | var server = http.createServer(app); 23 | 24 | /** 25 | * Listen on provided port, on all network interfaces. 26 | */ 27 | 28 | server.listen(port); 29 | server.on('error', onError); 30 | server.on('listening', onListening); 31 | 32 | /** 33 | * Normalize a port into a number, string, or false. 34 | */ 35 | 36 | function normalizePort(val) { 37 | var port = parseInt(val, 10); 38 | 39 | if (isNaN(port)) { 40 | // named pipe 41 | return val; 42 | } 43 | 44 | if (port >= 0) { 45 | // port number 46 | return port; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | /** 53 | * Event listener for HTTP server "error" event. 54 | */ 55 | 56 | function onError(error) { 57 | if (error.syscall !== 'listen') { 58 | throw error; 59 | } 60 | 61 | var bind = typeof port === 'string' 62 | ? 'Pipe ' + port 63 | : 'Port ' + port; 64 | 65 | // handle specific listen errors with friendly messages 66 | switch (error.code) { 67 | case 'EACCES': 68 | console.error(bind + ' requires elevated privileges'); 69 | process.exit(1); 70 | break; 71 | case 'EADDRINUSE': 72 | console.error(bind + ' is already in use'); 73 | process.exit(1); 74 | break; 75 | default: 76 | throw error; 77 | } 78 | } 79 | 80 | /** 81 | * Event listener for HTTP server "listening" event. 82 | */ 83 | 84 | function onListening() { 85 | var addr = server.address(); 86 | var bind = typeof addr === 'string' 87 | ? 'pipe ' + addr 88 | : 'port ' + addr.port; 89 | debug('Listening on ' + bind); 90 | } 91 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var gulpUglify = require('gulp-uglify'); 3 | var gulpImg = require('gulp-imagemin'); 4 | 5 | function errlog(err) { 6 | console.error(err); 7 | this.emit('end'); 8 | } 9 | 10 | 11 | gulp.task('default', ['script', 'watch']); 12 | 13 | 14 | gulp.task('watch', function () { 15 | gulp.watch('public/javascripts/*.js', ['script']) 16 | }) 17 | 18 | 19 | gulp.task('script', function () { 20 | gulp.src('public/javascripts/*.js') 21 | .on('error', errlog) // 使用錯誤事件處理例外 22 | .pipe(gulpUglify()) 23 | .pipe(gulp.dest('public/jsmin')) 24 | }) 25 | 26 | gulp.task('img', function () { 27 | 28 | }) -------------------------------------------------------------------------------- /models/comment.js: -------------------------------------------------------------------------------- 1 | var mongodb = require('./db'); 2 | 3 | function Comment(day, title, comment) { 4 | this.day = day; 5 | this.title = title; 6 | this.comment = comment; 7 | } 8 | 9 | module.exports = Comment; 10 | 11 | 12 | Comment.prototype.save = function (callback) { 13 | var day = this.day, 14 | title = this.title, 15 | comment = this.comment; 16 | 17 | mongodb.open(function (err, db) { 18 | if(err){ 19 | mongodb.close(); 20 | return callback(err); 21 | }; 22 | 23 | db.collection('posts',function (err, collection) { 24 | if(err){ 25 | mongodb.close(); 26 | return callback(err); 27 | }; 28 | 29 | collection.update({ 30 | "time.day": day, 31 | "title": title, 32 | },{ 33 | $push:{"comments": comment} 34 | },function (err) { 35 | mongodb.close(); 36 | if(err){ 37 | return callback(err); 38 | }; 39 | callback(null); 40 | }); 41 | 42 | }); 43 | }); 44 | }; -------------------------------------------------------------------------------- /models/db.js: -------------------------------------------------------------------------------- 1 | var settings = require('../settings'), 2 | Db = require('mongodb').Db, 3 | Connection = require('mongodb').Connection, 4 | Server = require('mongodb').Server; 5 | 6 | 7 | module.exports = new Db(settings.db, new Server(settings.host, settings.port),{safe: true}); -------------------------------------------------------------------------------- /models/post.js: -------------------------------------------------------------------------------- 1 | var mongodb = require('./db'); 2 | var markdown = require('markdown').markdown; 3 | 4 | function Post(name, title, post) { 5 | this.name = name; 6 | this.title = title; 7 | this.post = post; 8 | } 9 | 10 | module.exports = Post; 11 | 12 | Post.prototype.save = function (callback) { 13 | //储存各种时间,方便以后拓展 14 | var date = new Date(); 15 | var time = { 16 | date: date, 17 | year : date.getFullYear(), 18 | month : date.getFullYear() + "-" + (date.getMonth() + 1), 19 | day : date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate(), 20 | minute : date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate() + " " + 21 | date.getHours() + ":" + (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()) 22 | }; 23 | //要存入数据库的文档 24 | var post = { 25 | name: this.name, 26 | time: time, 27 | title: this.title, 28 | post: this.post, 29 | comments: [], 30 | views: 0 31 | }; 32 | //打开数据库 33 | mongodb.open(function (err, db) { 34 | if(err){ 35 | return callback(err); 36 | } 37 | //读取posts集合 38 | db.collection('posts',function (err, collection) { 39 | if(err){ 40 | mongodb.close(); 41 | return callback(err); 42 | } 43 | //将文档插入posts集合 44 | collection.insert(post,{ 45 | safa: true 46 | },function (err) { 47 | mongodb.close(); 48 | if(err){ 49 | return callback(err); 50 | } 51 | callback(null); 52 | }); 53 | }); 54 | }); 55 | }; 56 | 57 | 58 | //读取所有文章及其相关信息 59 | Post.getSome =function (name, page, sortWay, search,callback) { 60 | 61 | //打开数据库 62 | mongodb.open(function (err, db) { 63 | if(err){ 64 | return callback(err); 65 | } 66 | 67 | //读取posts集合 68 | db.collection('posts',function (err, collection) { 69 | if(err){ 70 | mongodb.close(); 71 | return callback(err); 72 | } 73 | //设置query对象 74 | var query = {}; 75 | if (search) { 76 | query.title = new RegExp(search, 'i'); 77 | } 78 | //设置排序方式 79 | var sort = {}; 80 | sort[sortWay] = -1; 81 | 82 | //count返回文章数目 83 | collection.count({},function (err, total) { 84 | //跟据query对象查找文章 85 | collection.find(query, { 86 | skip: (page - 1) * 10, 87 | limit: 10 88 | }).sort(sort).toArray(function (err, docs) { 89 | mongodb.close(); 90 | if(err){ 91 | return callback(err); 92 | }; 93 | docs.forEach(function (doc) { 94 | doc.post = markdown.toHTML(doc.post); 95 | }); 96 | callback(null, docs, total); 97 | }); 98 | }); 99 | }); 100 | }); 101 | }; 102 | 103 | 104 | 105 | //获取一篇文章 106 | Post.getOne = function (day, title, callback) { 107 | //打开数据库 108 | mongodb.open(function (err, db) { 109 | if(err){ 110 | return callback('打开数据库失败!'); 111 | } 112 | //读取posts集合 113 | db.collection('posts',function (err,collection) { 114 | if(err){ 115 | mongodb.close(); 116 | return callback("读取posts集合失败!(getOne)") 117 | } 118 | //跟据用户名,发表时间及文章名进行查询 119 | //取得一篇文章 120 | collection.findOne({ 121 | "time.day": day, 122 | "title": title 123 | }, function (err, doc) { 124 | if(err){ 125 | mongodb.close(); 126 | return callback('操作数据库失败!(getOne)'); 127 | }; 128 | if(doc){ 129 | //阅读次数增加1 130 | collection.update({ 131 | "title": title, 132 | "time.day": day 133 | }, { 134 | $inc: {"views": 1} 135 | }, function (err) { 136 | mongodb.close(); 137 | if(err){ 138 | return callback("操作数据库失败!(viewTimes)"); 139 | }; 140 | }); 141 | //文章中的正文进行转换 142 | doc.post = markdown.toHTML(doc.post); 143 | // if(doc.comments){ 144 | // doc.comments.forEach(function (comment) { 145 | // comment.content = markdown.toHTML(comment.content) 146 | // console.log(comment); 147 | // }); 148 | // } 149 | callback(null, doc); 150 | }; 151 | }); 152 | }); 153 | }); 154 | }; 155 | 156 | 157 | //返回原始发表的内容(markdown 格式) 158 | Post.edit = function (name, day, title, callback) { 159 | mongodb.open(function (err, db) { 160 | if(err){ 161 | return callback(err); 162 | } 163 | db.collection('posts', function (err, collection) { 164 | collection.findOne({ 165 | "name": name, 166 | "time.day": day, 167 | "title": title 168 | },function (err, doc) { 169 | mongodb.close(); 170 | if(err){ 171 | return callback(err); 172 | }; 173 | callback(null, doc); 174 | }); 175 | }); 176 | }); 177 | }; 178 | 179 | 180 | // //增加阅读计数 181 | // Post.view = function (day, title, callback) { 182 | // mongodb.open(function (err, db) { 183 | // if(err) { 184 | // return callback("打开数据库失败!(viewTimes)") 185 | // }; 186 | // db.collection('post', function (err, collection) { 187 | // if(err) { 188 | // return callback("数据库集合获取失败!(viewTimes)"); 189 | // }; 190 | // collection.update({ 191 | // "title": title, 192 | // "time.day": day 193 | // },{ 194 | // $inc: {"views": 1} 195 | // },{ 196 | // upsert: true 197 | // },function (err) { 198 | // mongodb.close(); 199 | // if(err){ 200 | // return callback("数据库操作失败!(viewTimes)"); 201 | // }; 202 | // callback(null); 203 | // }) 204 | // }) 205 | // }) 206 | // } 207 | 208 | 209 | //更新一篇文章 210 | Post.update = function (name, day, title, post, callback) { 211 | mongodb.open(function (err, db) { 212 | if(err){ 213 | return callback(err); 214 | } 215 | db.collection('posts',function (err, collection) { 216 | if(err){ 217 | return callback(err); 218 | }; 219 | collection.update({ 220 | "name": name, 221 | "time.day": day, 222 | "title": title 223 | },{ 224 | $set: {post} 225 | },function (err) { 226 | mongodb.close(); 227 | if(err){ 228 | return callback(err); 229 | }; 230 | callback(null); 231 | }) 232 | }) 233 | }) 234 | } 235 | 236 | 237 | //删除一篇文章 238 | Post.remove = function (name, day, title, callback) { 239 | mongodb.open(function (err, db) { 240 | if(err){ 241 | return callback(err); 242 | } 243 | db.collection('posts', function (err, collection) { 244 | if(err){ 245 | return callback(err); 246 | } 247 | collection.remove({ 248 | "name": name, 249 | "time.day": day, 250 | "title": title 251 | },function (err) { 252 | mongodb.close(); 253 | if(err){ 254 | callback(err); 255 | }; 256 | callback(null); 257 | }); 258 | }); 259 | }); 260 | }; -------------------------------------------------------------------------------- /models/user.js: -------------------------------------------------------------------------------- 1 | var mongodb = require('./db'); 2 | 3 | function User(user) { 4 | this.name = user.name; 5 | this.password = user.password; 6 | this.email = user.email; 7 | }; 8 | 9 | module.exports = User; 10 | 11 | //储存用户信息 12 | User.prototype.save = function (callback) { 13 | //要储存的信息 14 | var user = { 15 | name: this.name, 16 | password: this.password, 17 | email: this.email 18 | }; 19 | //开打数据库 20 | mongodb.open(function (err, db) { 21 | if(err){ 22 | return callback(err);//错误,返回err信息 23 | } 24 | //读取users集合 25 | db.collection('users',function (err, collection) { 26 | if(err){ 27 | mongodb.close(); 28 | return callback(err)//返回err信息 29 | } 30 | //将用户数据插入users集合 31 | collection.insert(user,{ 32 | safe: true 33 | },function (err, user) { 34 | mongodb.close(); 35 | if(err){ 36 | return callback(err);//return err message 37 | } 38 | callback(null,user[0]);//success! err is null,and return doc which saved. 39 | }); 40 | }); 41 | }); 42 | }; 43 | 44 | //read uesr messages 45 | User.get = function (name, callback) { 46 | //open database 47 | mongodb.open(function (err, db) { 48 | if(err){ 49 | return callback(err);//error! return err message 50 | } 51 | //read users 集合 52 | db.collection('users',function (err, collection) { 53 | if(err){ 54 | mongodb.close(); 55 | return callback(err); 56 | } 57 | //query the doc which the value of username(key:name) be named name 58 | collection.findOne({ 59 | name: name 60 | },function (err, user) { 61 | mongodb.close(); 62 | if(err){ 63 | return callback(err); 64 | } 65 | callback(null, user);//success! return user messages 66 | }); 67 | }); 68 | }); 69 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "blog", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www" 7 | }, 8 | "dependencies": { 9 | "babel-preset-react": "^6.11.1", 10 | "babelify": "^7.3.0", 11 | "body-parser": "~1.15.1", 12 | "connect-flash": "0.1.1", 13 | "connect-mongo": "0.8.2", 14 | "connect-multiparty": "*", 15 | "cookie-parser": "~1.4.3", 16 | "debug": "~2.2.0", 17 | "ejs": "~2.4.1", 18 | "express": "~4.13.4", 19 | "express-session": "1.9.1", 20 | "markdown": "0.5.0", 21 | "mongodb": "2.0.42", 22 | "morgan": "~1.7.0", 23 | "multer": "1.1.0", 24 | "react": "^15.3.1", 25 | "react-dom": "^15.3.1", 26 | "serve-favicon": "~2.3.0" 27 | }, 28 | "devDependencies": { 29 | "gulp": "^3.9.1", 30 | "gulp-imagemin": "^3.0.3", 31 | "gulp-uglify": "^2.0.0" 32 | } 33 | } -------------------------------------------------------------------------------- /public/images/new.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pspgbhu/Node.js-Blog/f7e1fbb8b48a6a5b3c9926d18278a202211dd0d7/public/images/new.jpg -------------------------------------------------------------------------------- /public/images/tooopen_sy_140703593676.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pspgbhu/Node.js-Blog/f7e1fbb8b48a6a5b3c9926d18278a202211dd0d7/public/images/tooopen_sy_140703593676.jpg -------------------------------------------------------------------------------- /public/images/模板消息.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pspgbhu/Node.js-Blog/f7e1fbb8b48a6a5b3c9926d18278a202211dd0d7/public/images/模板消息.png -------------------------------------------------------------------------------- /public/images/模板消息1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pspgbhu/Node.js-Blog/f7e1fbb8b48a6a5b3c9926d18278a202211dd0d7/public/images/模板消息1.png -------------------------------------------------------------------------------- /public/javascripts/blog.js: -------------------------------------------------------------------------------- 1 | function uploadFile(){ 2 | var file = document.getElementById("file") 3 | var formData = new FormData(); 4 | var imgsrc = null; 5 | formData.append('file',file.files[0]); 6 | $.ajax({ 7 | url: '/upload', 8 | type: 'POST', 9 | data: formData, 10 | async: false, 11 | cache: false, 12 | contentType: false, 13 | processData: false, 14 | success: function (data){ 15 | if(200 === data.code) { 16 | $('#result').html("上传成功!"); 17 | $('#img').attr('src','http://' + data.data.headersHost + '/images/' + data.data.name); 18 | $('#addimg').show(); 19 | imgsrc = '/images/' + data.data.name 20 | } else { 21 | $('#result').html("上传失败!"); 22 | } 23 | }, 24 | error: function(){ 25 | $("#result").html("与服务器通信发生错误"); 26 | } 27 | }); 28 | return imgsrc; 29 | }; 30 | 31 | 32 | //myField 光标所在的控件名 document.Form.XXX 33 | //myValue 所要插入的值 34 | function insertAtCursor(myField, myValue) { 35 | // IE 36 | if (document.selection) 37 | { 38 | myField.focus(); 39 | sel = document.selection.createRange(); 40 | 41 | sel.text = myValue; 42 | sel.select(); 43 | } 44 | else if (myField.selectionStart || myField.selectionStart == '0') { 45 | // MOZILLA/NETSCAPE support 46 | //起始位置 47 | var startPos = myField.selectionStart; 48 | //结束位置 49 | var endPos = myField.selectionEnd; 50 | //插入信息 51 | myField.value = myField.value.substring(0, startPos) 52 | + myValue 53 | + myField.value.substring(endPos, myField.value.length); 54 | } else { 55 | 56 | //没有焦点的话直接加在TEXTAREA的最后一位 57 | myField.value += myValue; 58 | } 59 | } 60 | function iaddimg() { 61 | var mytextarea = document.getElementById('mytextarea'); 62 | var src = '![Alt text](' + uploadFile() + ')' ; 63 | insertAtCursor(mytextarea, src); 64 | // mytextarea.value += src; 65 | } 66 | 67 | 68 | window.onload = function () { 69 | 70 | if(!document.getElementById('upload')){ 71 | return false 72 | }else{ 73 | var uploada = document.getElementById('upload'); 74 | uploada.addEventListener("click",function () { 75 | uploadFile(); 76 | },false); 77 | } 78 | 79 | if(!document.getElementById('addimg')){ 80 | return false; 81 | }else{ 82 | var addimg = document.getElementById('addimg'); 83 | addimg.addEventListener('click',function () { 84 | iaddimg(); 85 | } ,false) 86 | } 87 | 88 | } -------------------------------------------------------------------------------- /public/jsmin/blog.js: -------------------------------------------------------------------------------- 1 | function uploadFile(){var e=document.getElementById("file"),t=new FormData,n=null;return t.append("file",e.files[0]),$.ajax({url:"/upload",type:"POST",data:t,async:!1,cache:!1,contentType:!1,processData:!1,success:function(e){200===e.code?($("#result").html("上传成功!"),$("#img").attr("src","http://"+e.data.headersHost+"/images/"+e.data.name),$("#addimg").show(),n="/images/"+e.data.name):$("#result").html("上传失败!")},error:function(){$("#result").html("与服务器通信发生错误")}}),n}function insertAtCursor(e,t){if(document.selection)e.focus(),sel=document.selection.createRange(),sel.text=t,sel.select();else if(e.selectionStart||"0"==e.selectionStart){var n=e.selectionStart,a=e.selectionEnd;e.value=e.value.substring(0,n)+t+e.value.substring(a,e.value.length)}else e.value+=t}function iaddimg(){var e=document.getElementById("mytextarea"),t="![Alt text]("+uploadFile()+")";insertAtCursor(e,t)}window.onload=function(){if(!document.getElementById("upload"))return!1;var e=document.getElementById("upload");if(e.addEventListener("click",function(){uploadFile()},!1),!document.getElementById("addimg"))return!1;var t=document.getElementById("addimg");t.addEventListener("click",function(){iaddimg()},!1)}; -------------------------------------------------------------------------------- /public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | /* 2 | * all 3 | */ 4 | a:hover{ 5 | text-decoration: none; 6 | } 7 | body,html{ 8 | width: 100%; 9 | height: 100%; 10 | padding: 0; 11 | margin: 0; 12 | /* font-family: 'Microsoft YaHei'; */ 13 | } 14 | body{ 15 | /* background-color: #e9e9e9; */ 16 | background-color: #e9e9e9; 17 | } 18 | .wp{ 19 | max-width: 960px; 20 | margin: auto; 21 | } 22 | .header{ 23 | margin-bottom: 20px; 24 | width: 100%; 25 | min-height: 50px; 26 | background-color: #336699; 27 | box-shadow: 0 0 10px #666; 28 | } 29 | .header a{ 30 | color: #fff; 31 | } 32 | .header h1{ 33 | font-size: 24px; 34 | line-height: 50px; 35 | margin: 0; 36 | padding: 0; 37 | } 38 | ul{ 39 | margin: 0; 40 | padding: 0; 41 | } 42 | .header ul li{ 43 | float: left; 44 | list-style: none; 45 | line-height: 50px; 46 | font-size: 18px; 47 | padding: 0 15px; 48 | margin: 0; 49 | } 50 | .header ul li a{ 51 | color: #fff; 52 | } 53 | .content{ 54 | padding: 25px 0; 55 | } 56 | .main{ 57 | margin-bottom: 200px; 58 | } 59 | /* 60 | * page-index 61 | */ 62 | .page-index{ 63 | width: 100%; 64 | min-height: 100%; 65 | background-color: #336699; 66 | overflow: auto; 67 | } 68 | .page-index h1{ 69 | display: block; 70 | position: absolute; 71 | top: 30%; 72 | height: 80px; 73 | width: 100%; 74 | margin: auto; 75 | font-size: 80px; 76 | font-weight: bold; 77 | text-align: center; 78 | color: #fff; 79 | opacity: 0; 80 | cursor: pointer; 81 | -webkit-animation: h1show 1.5s ease-out forwards; 82 | -o-animation: h1show 1.5s ease-out forwards; 83 | animation: h1show 1.5s ease-out forwards; 84 | } 85 | 86 | @-webkit-keyframes h1show { 87 | from { 88 | opacity: 0; 89 | -webkit-transform: translate(0,0); 90 | } 91 | to { 92 | opacity: 1; 93 | -webkit-transform: translate(0, 100%); 94 | } 95 | } 96 | 97 | @-o-keyframes h1show { 98 | from { 99 | opacity: 0; 100 | -o-transform: translate(0,0); 101 | } 102 | to { 103 | opacity: 1; 104 | -o-transform: translate(0, 100%); 105 | } 106 | } 107 | @-moz-keyframes h1show { 108 | from { 109 | opacity: 0; 110 | -moz-transform: translate(0,0); 111 | } 112 | to { 113 | opacity: 1; 114 | -moz-transform: translate(0, 100%); 115 | } 116 | } 117 | @keyframes h1show { 118 | from { 119 | opacity: 0; 120 | transform: translate(0,0); 121 | } 122 | to { 123 | opacity: 1; 124 | transform: translate(0, 100%); 125 | } 126 | } 127 | 128 | /* 129 | * page-main 130 | */ 131 | .blogBox{ 132 | width: 70%; 133 | float: left; 134 | } 135 | .blogBox span{ 136 | padding-right: 15px; 137 | } 138 | .blog{ 139 | color: #333; 140 | display: block; 141 | border-bottom: 1px solid #ccc; 142 | } 143 | .blog:hover{ 144 | -webkit-animation: blog .5s ease-out; 145 | -o-animation: blog .5s ease-out; 146 | animation: blog .5s ease-out; 147 | } 148 | @-webkit-keyframes blog { 149 | from { 150 | opacity: .5; 151 | -webkit-transform: -webkit-translate(-10px,0) 152 | } 153 | to { 154 | opacity: 1; 155 | -webkit-transform: -webkit-translate(0,0); 156 | } 157 | } 158 | @-o-keyframes blog { 159 | from { 160 | opacity: .5; 161 | -o-transform: -o-translate(-10px,0) 162 | } 163 | to { 164 | opacity: 1; 165 | -o-transform: -o-translate(0,0); 166 | } 167 | } 168 | @-moz-keyframes blog { 169 | from { 170 | opacity: .5; 171 | -moz-transform: -moz-translate(-10px,0) 172 | } 173 | to { 174 | opacity: 1; 175 | -moz-transform: -moz-translate(0,0); 176 | } 177 | } 178 | @keyframes blog { 179 | from { 180 | opacity: .5; 181 | transform: translate(-10px,0) 182 | } 183 | to { 184 | opacity: 1; 185 | transform: translate(0,0); 186 | } 187 | } 188 | 189 | 190 | .blog img{ 191 | max-width: 100% 192 | } 193 | .latest{ 194 | float: right; 195 | width: 30%; 196 | 197 | } 198 | .latest ul { 199 | list-style: none; 200 | text-align: left; 201 | width: 180px; 202 | margin: auto; 203 | overflow: hidden; 204 | 205 | } 206 | .latest li{ 207 | font-size: 18px; 208 | padding: 10px; 209 | } 210 | .latest h3{ 211 | padding: 10px; 212 | } 213 | /* 214 | * page-article 215 | */ 216 | .page-article h2{ 217 | border-bottom: 1px solid #ccc; 218 | } 219 | .article h2{ 220 | padding-bottom: 5px; 221 | border-bottom: 1px solid #ccc; 222 | } 223 | .article p{ 224 | padding: 16px 0; 225 | } 226 | .newComment{ 227 | max-width: 600px; 228 | } 229 | .newComment h4,.comments h4{ 230 | padding-bottom: 20px; 231 | } 232 | 233 | /* 234 | * page-blog 235 | */ 236 | .search{ 237 | width: 200px; 238 | display: inline-block; 239 | } 240 | .search-btn{ 241 | vertical-align: baseline; 242 | } 243 | .rank li{ 244 | float: left; 245 | list-style: none; 246 | padding-right: 20px; 247 | line-height: 34px; 248 | vertical-align: top; 249 | } 250 | .rank li label{ 251 | cursor: pointer; 252 | } -------------------------------------------------------------------------------- /routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | var crypto = require('crypto'); 4 | var multer = require('multer'); 5 | var upload = multer({ dest: 'uploads/' }); 6 | var User = require('../models/user.js'); 7 | var Post = require('../models/post.js'); 8 | var Comment = require('../models/comment.js') 9 | 10 | 11 | /* index page */ 12 | router.get('/',function (req, res, next) { 13 | res.render('index', {title: 'pspgbhu的主页'}); 14 | }) 15 | 16 | 17 | /* home page. */ 18 | router.get('/home', function(req, res, next) { 19 | var page = parseInt(req.query.p) || 1; 20 | var sortWay = "time"; 21 | Post.getSome(null, page, sortWay, null,function (err, posts, total) { 22 | if(err){ 23 | posts = []; 24 | } 25 | res.render('home', { 26 | title: '主页', 27 | user: req.session.user, 28 | posts: posts, 29 | page: page, 30 | isFirstPage: (page - 1) == 0, 31 | isLastPage:((page - 1) * 10 + posts.length) == total, 32 | success: req.flash('success').toString(), 33 | error: req.flash('error').toString() 34 | }); 35 | }); 36 | }); 37 | 38 | 39 | /* blog page. */ 40 | router.get('/blog',function (req, res, next) { 41 | var page = parseInt(req.query.p) || 1; 42 | var sortWay = "time"; 43 | var search = null; 44 | 45 | if(req.query.rank) { 46 | sortWay = req.query.rank; 47 | } 48 | if(req.query.search) { 49 | search = req.query.search; 50 | } 51 | 52 | Post.getSome(null, page, sortWay, search,function (err, posts, total) { 53 | if(err){ 54 | posts = []; 55 | } 56 | res.render('blog', { 57 | title: '主页', 58 | user: req.session.user, 59 | search: search, 60 | posts: posts, 61 | page: page, 62 | isFirstPage: (page - 1) == 0, 63 | isLastPage:((page - 1) * 10 + posts.length) == total, 64 | success: req.flash('success').toString(), 65 | error: req.flash('error').toString() 66 | }); 67 | }); 68 | }) 69 | 70 | 71 | // /* user */ 72 | // router.get('/u/:name', function (req, res) { 73 | // User.get(req.params.name, function (err, user) { 74 | // if(!user){ 75 | // req.flash('error', '用户不存在!'); 76 | // return res.redirect('/'); 77 | // } 78 | // Post.getSome(user.name, page, function (err, posts, total) { 79 | // if(err){ 80 | // req.flash('error', err); 81 | // return res.redirect('/'); 82 | // } 83 | // res.render('user', { 84 | // title: user.name, 85 | // posts: posts, 86 | // user : req.session.user, 87 | // page: page, 88 | // isFirstPage: (page - 1) == 0, 89 | // isLastPage: ((page - 1) * 10 + posts.length) == total, 90 | // success: req.flash('success').toString(), 91 | // error: req.flash('error').toString() 92 | // }); 93 | // }); 94 | // }); 95 | // }); 96 | 97 | 98 | /* article page */ 99 | router.post('/:day/:title',function (req, res) { 100 | var date = new Date(), 101 | time = date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate() + " " + 102 | date.getHours() + ":" + (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()); 103 | 104 | var comment = { 105 | "name": req.body.visitorName, 106 | "email": req.body.visitorEmail, 107 | "time": time, 108 | "content": req.body.visitorContent 109 | } 110 | 111 | var newComment = new Comment(req.params.day, req.params.title, comment); 112 | 113 | newComment.save(function (err) { 114 | if (err) { 115 | req.flash('error', err); 116 | return res.redirect('back'); 117 | } 118 | req.flash('success', '留言成功!'); 119 | return res.redirect('back'); 120 | }); 121 | }); 122 | 123 | router.get('/:day/:title', function (req, res) { 124 | Post.getOne(req.params.day, req.params.title, function (err, post) { 125 | if(err){ 126 | req.flash('error', err); 127 | return res.redirect('/blog') 128 | } 129 | res.render('article',{ 130 | title: req.params.title, 131 | post: post, 132 | user: req.session.user, 133 | success: req.flash('success').toString(), 134 | error: req.flash('error').toString() 135 | }); 136 | }); 137 | }); 138 | 139 | 140 | /* about page. */ 141 | router.get('/about',function (req, res, next) { 142 | res.render('about', { 143 | user: req.session.user, 144 | success: req.flash('success').toString(), 145 | error: req.flash('error').toString() 146 | }) 147 | }) 148 | 149 | 150 | /* 151 | **************************************** 152 | */ 153 | 154 | 155 | /* login page. */ 156 | router.get('/login',function (req, res, next) { 157 | res.render('login',{ 158 | title: '登录', 159 | user: req.session.user, 160 | success: req.flash('success').toString(), 161 | error: req.flash('error').toString() 162 | }) 163 | }); 164 | 165 | router.post('/login',function (req, res) { 166 | //生成密码的 md5 值 167 | var md5 = crypto.createHash('md5'), 168 | password = md5.update(req.body.password).digest('hex'); 169 | //检查用户是否存在 170 | User.get(req.body.username, function (err, user) { 171 | console.log(req.body.username) 172 | if (!user) { 173 | req.flash('error', '用户不存在!'); 174 | return res.redirect('/login');//用户不存在则跳转到登录页 175 | } 176 | //检查密码是否一致 177 | if (user.password != password) { 178 | req.flash('error', '密码错误!'); 179 | return res.redirect('/login');//密码错误则跳转到登录页 180 | } 181 | //用户名密码都匹配后,将用户信息存入 session 182 | req.session.user = user; 183 | req.flash('success', '登陆成功!'); 184 | res.redirect('/blog');//登陆成功后跳转到主页 185 | }); 186 | }); 187 | 188 | 189 | /* logout page. */ 190 | router.get('/logout',function (req, res, next) { 191 | req.session.user = null; 192 | req.flash('success',"登出成功!"); 193 | res.redirect('/') 194 | }); 195 | 196 | 197 | /* reg page. */ 198 | router.get('/reg',function (req, res, next) { 199 | res.render('reg', { 200 | title: '注册', 201 | user: req.session.user, 202 | success: req.flash('success').toString(), 203 | error: req.flash('error').toString() 204 | }); 205 | }); 206 | 207 | router.post('/reg',function (req, res) { 208 | var name = req.body.name, 209 | password = req.body.password, 210 | password_re = req.body.passwordrepeat; 211 | email = req.body.email 212 | //检验用户名密码是否为空 213 | if(name === "" || password === "" || name === null || password === null ){ 214 | req.flash('error','用户名或密码不能为空!') 215 | return res.redirect('/reg'); 216 | }; 217 | //检验邮箱是否为空 218 | if(email === "" || email === null){ 219 | req.flash('error','请输入正确的邮箱地址!') 220 | return res.redirect('/reg'); 221 | } 222 | //检验两次密码是否一致 223 | if(password != password_re){ 224 | req.flash('error','两次输入的密码不一致!'); 225 | return res.redirect('/reg');//返回注册页 226 | }; 227 | //生成密码的md5值 228 | var md5 = crypto.createHash('md5'), 229 | password = md5.update(req.body.password).digest('hex'); 230 | var newUser = new User({ 231 | name: name, 232 | password: password, 233 | email: req.body.email 234 | }); 235 | //检查用户名是否存在 236 | User.get(newUser.name, function (err, user){ 237 | if(err) { 238 | req.flash('error', err); 239 | return res.redirect('/') 240 | } 241 | if(user) { 242 | req.flash('error','用户已存在!') 243 | return res.redirect('/reg') 244 | } 245 | //如果不存在则新增用户 246 | newUser.save(function (err, user) { 247 | if(err){ 248 | req.flash('error', err) 249 | return res.redirect('/reg') 250 | } 251 | req.session.user = newUser;//用户信息存入session 252 | req.flash('success','注册成功!') 253 | res.redirect('/'); 254 | }) 255 | }) 256 | }) 257 | 258 | 259 | /* post page. */ 260 | router.get('/post',function (req, res, next) { 261 | res.render('post',{ 262 | title: '发表', 263 | user: req.session.user, 264 | success: req.flash('success').toString(), 265 | error: req.flash('error').toString() 266 | }) 267 | }) 268 | 269 | router.post('/post',function (req, res) { 270 | var currentUser = req.session.user; 271 | var title = req.body.title; 272 | var article = req.body.post; 273 | var post = new Post(currentUser.name, title, article); 274 | if(title ==="" || article ==="" || title === null || article === null){ 275 | req.flash('error','标题或正文不能为空!') 276 | res.redirect('/post'); 277 | }else{ 278 | post.save(function (err) { 279 | if(err){ 280 | req.flash('error', err) 281 | return res.redirect('/') 282 | } 283 | req.flash('success','发表成功!') 284 | res.redirect('/blog'); 285 | }); 286 | } 287 | }); 288 | 289 | 290 | /* upload page. */ 291 | var storage = multer.diskStorage({ 292 | destination: function (req, file, cb){ 293 | cb(null, './public/images') 294 | }, 295 | filename: function (req, file, cb){ 296 | cb(null, file.originalname) 297 | } 298 | }); 299 | var upload = multer({ 300 | storage: storage 301 | }); 302 | router.post('/upload', upload.single('file'), function (req, res, next) { 303 | res.json({ 304 | code : 200, 305 | data : { 306 | headersHost: req.headers.host, 307 | name: req.file.originalname 308 | } 309 | }) 310 | }); 311 | 312 | 313 | /* edit page. */ 314 | router.get('/edit/:name/:day/:title', function (req, res){ 315 | var currentUser = req.session.user; 316 | Post.edit(currentUser.name, req.params.day, req.params.title, function (err, post) { 317 | if(err){ 318 | req.flash('error',err); 319 | return res.redirect('back'); 320 | }; 321 | res.render('edit',{ 322 | title: '编辑', 323 | post: post, 324 | name: post.name, 325 | user: req.session.user, 326 | success: req.flash('success').toString(), 327 | error: req.flash('error').toString() 328 | }); 329 | }); 330 | }); 331 | 332 | router.post('/edit/:name/:day/:title',function (req, res) { 333 | var currentUser = req.session.user; 334 | Post.update(currentUser.name, req.params.day, req.params.title, req.body.post, function (err) { 335 | var url = encodeURI('/' + req.params.day + '/' + req.params.title) 336 | if(err){ 337 | req.flash('error',err); 338 | return res.redirect('/'); 339 | } 340 | req.flash('success','修改成功!') 341 | return res.redirect(url); 342 | }) 343 | }) 344 | 345 | 346 | /* remove page. */ 347 | router.get('/remove/:name/:day/:title',function (req, res) { 348 | var currentUser = req.session.user; 349 | Post.remove(req.params.name, req.params.day, req.params.title, function (err) { 350 | if(err){ 351 | req.flash('error', err); 352 | return res.redirect('back'); 353 | }; 354 | req.flash('success','删除成功!') 355 | res.redirect('/'); 356 | }); 357 | }); 358 | 359 | 360 | 361 | // function checkLogin(req, res, next) { 362 | // if(req.session.user){ 363 | // req.flash('error', '已登录!'); 364 | // res.redirect('back'); 365 | // } 366 | // next(); 367 | // } 368 | 369 | 370 | // function checkLogout(req, res, next) { 371 | // if(!req.session.user){ 372 | // req.flash('error',"未登录!"); 373 | // res.redirect('back'); 374 | // } 375 | // next(); 376 | // } 377 | 378 | 379 | module.exports = router; 380 | -------------------------------------------------------------------------------- /routes/users.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET users listing. */ 5 | router.get('/', function(req, res, next) { 6 | res.send('respond with a resource'); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /settings.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | cookieSecret: 'myblog', 3 | db: 'blog', 4 | host: 'localhost', 5 | port: 27017 6 | }; -------------------------------------------------------------------------------- /views/about.ejs: -------------------------------------------------------------------------------- 1 | <%- include header %> 2 | <%- include header_main %> 3 | 4 |

5 | email: brotherchun001@gmail.com 6 |

7 | 8 | <%- include footer_main %> 9 | <%- include footer %> -------------------------------------------------------------------------------- /views/article.ejs: -------------------------------------------------------------------------------- 1 | <%- include header %> 2 | <%- include header_main %> 3 |
4 | 5 |
6 |

<%= post.title %>

7 | 8 | <% if(user && user.name === post.name){ %> 9 | 10 |

11 | 编辑 12 | 13 | 删除 14 |

15 | 16 | <% } %> 17 | 18 |

19 | 作者:<%= post.name %> 20 | 日期:<%= post.time.minute %> 21 |

22 |
<%- post.post %>
23 |
24 | 25 | 26 | 27 |

留言板:

28 |
29 | 30 | <% if (post.comments) { %> 31 | 32 | <% post.comments.forEach(function(comment){%> 33 | 34 |
35 |
36 |

<%= comment.name %>

37 | 38 | <% if(user){ %> 39 | 40 | <%= comment.email %> 41 | 42 | <% } %> 43 | 44 |
45 |
46 | <%= comment.content %> 47 |
48 | 49 |
50 | 51 | <% }) %> 52 | <% } %> 53 | 54 |
55 | 56 | 57 | 58 |

游客留言:

59 |
60 |
61 |
62 |

昵称:

63 | 64 |
65 | 66 |
67 |

邮箱地址:

68 | 69 |

该邮箱仅方便博主与您取得联系,并且承诺不会泄露您的邮箱

70 |
71 | 72 |
73 |

留言内容:

74 | 75 |
76 | 77 | 78 |
79 |
80 | 81 | 82 |
83 | <%- include footer_main %> 84 | <%- include footer %> -------------------------------------------------------------------------------- /views/blog.ejs: -------------------------------------------------------------------------------- 1 | <%- include header %> 2 | <%- include header_main %> 3 | 4 |
5 |
6 | 19 |
20 |
21 |
22 | 23 | <% posts.forEach(function (post, index){ %> 24 | 25 | 26 |

27 | 28 |

<%= post.title %>

29 | 30 |

31 | 32 | 日期:<%= post.time.minute %> 33 | 阅读:<% if (post.views) { -%><%= post.views %>次<% } else { -%>0次<% } -%> 34 | 评论:<%= post.comments.length %>人 35 | 36 |

37 |
38 | 39 | <% }) %> 40 |
41 | 42 | <%- include footer_main %> 43 | <%- include footer %> -------------------------------------------------------------------------------- /views/comment.ejs: -------------------------------------------------------------------------------- 1 | <% include header %> 2 |
3 | <% post.comments.forEach(function (comment, index) { %> 4 |

<%= comment.name %> 5 | 回复于 <%= comment.time %>

6 |

<%- comment.content %>

7 | <% }) %> 8 | 9 |
10 | <% if (user) { %> 11 | 姓名:
12 | 邮箱:
13 | <% } else { %> 14 | 姓名:
15 | 邮箱:
16 | <% } %> 17 |
18 | 19 |
20 | <% include footer %> -------------------------------------------------------------------------------- /views/edit.ejs: -------------------------------------------------------------------------------- 1 | <%- include header %> 2 |
3 | 4 | 标题:
5 |
6 | 7 |
8 | 9 | 正文:
10 |
13 | 14 | 15 |
16 | <%- include footer %> -------------------------------------------------------------------------------- /views/error.ejs: -------------------------------------------------------------------------------- 1 |

<%= message %>

2 |

<%= error.status %>

3 |
<%= error.stack %>
4 | -------------------------------------------------------------------------------- /views/footer.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /views/footer_main.ejs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /views/header.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | pspgbhu的博客 12 | 13 | 14 | -------------------------------------------------------------------------------- /views/header_main.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 |

pspgbhu的博客

4 | 15 |
16 |
17 | 18 | <% if (success) { %> 19 |

<%= success %>

20 | <% } %> 21 | 22 | <% if (error) { %> 23 |

<%= error %>

24 | <% } %> 25 | 26 |
27 |
-------------------------------------------------------------------------------- /views/home.ejs: -------------------------------------------------------------------------------- 1 | <%- include header %> 2 | <%- include header_main %> 3 | 4 | 5 | 26 | 27 | 28 | 29 |
30 |
    31 | 32 |

    最新文章列表

    33 | 34 | <% posts.forEach(function(post, index){ %> 35 | 36 | <% if(index > 4) return false %> 37 | 38 |
  • <%=post.title %>
  • 39 | 40 | <% }) %> 41 | 42 |
43 |
44 | 45 | <%- include paging %> 46 | <%- include footer_main %> 47 | <%- include footer %> -------------------------------------------------------------------------------- /views/index.ejs: -------------------------------------------------------------------------------- 1 | <%- include header %> 2 | 7 | <%- include footer %> -------------------------------------------------------------------------------- /views/login.ejs: -------------------------------------------------------------------------------- 1 | <%- include header %> 2 |
3 | <% if (success) { %> 4 |
<%= success %>
5 | <% } %> 6 | <% if (error) { %> 7 |
<%= error %>
8 | <% } %> 9 |
10 |
11 | 12 | 13 |
14 |
15 | 16 | 17 |
18 | 19 |
20 |
21 | <%- include footer %> -------------------------------------------------------------------------------- /views/paging.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 | 21 |
-------------------------------------------------------------------------------- /views/post.ejs: -------------------------------------------------------------------------------- 1 | <% include header %> 2 | <% include header_main %> 3 |
4 | 5 |
6 | 7 | 8 |
9 | 10 |
11 | 12 | 13 |
14 | 15 |
16 | 17 | 18 |

19 | 20 |
上传照片
21 | 22 |
23 | 24 |
25 | 26 |
27 | 28 |
29 | <% include footer_main %> 30 | <% include footer %> -------------------------------------------------------------------------------- /views/reg.ejs: -------------------------------------------------------------------------------- 1 | <% include header %> 2 |
3 |
4 | 5 | 6 |
7 |
8 | 9 | 10 |
11 |
12 | 13 | 14 |
15 |
16 | 17 | 18 |
19 | 20 |
21 | <% include footer %> -------------------------------------------------------------------------------- /views/user.ejs: -------------------------------------------------------------------------------- 1 | <%- include header %> 2 | <% posts.forEach(function (post, index) { %> 3 |

<%= post.title %>

4 |

5 | 作者:<%= post.name %> | 6 | 日期:<%= post.time.minute %> 7 |

8 |

<%- post.post %>

9 | <% }) %> 10 | <%- include paging %> 11 | <%- include footer %> --------------------------------------------------------------------------------