diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1dcef2d --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules +.env \ No newline at end of file diff --git a/Controllers/blog-controller.js b/Controllers/blog-controller.js new file mode 100644 index 0000000..8359bc7 --- /dev/null +++ b/Controllers/blog-controller.js @@ -0,0 +1,60 @@ +const Blogs = require('../Models/blog.js') + +function addBlog (req,res,next){ + Blogs.create({ + title:req.body.title, + blog: req.body.blog + },function(err,result){ + res.send(result) + }) +} + +function listBlogs (req,res,next){ + Blogs.find({},function(err,result){ + res.send(result) + }) +} + +function getBlog (req,res,next){ + Blogs.findOne({ + _id: req.params.id + },function(err,result){ + res.send(result) + }) +} + +// function userBlogs (req,res,next){ +// // Blogs.find({ +// // user_id: +// // },function(err,result){ +// // res.send(result) +// // }) +// } + +function deleteBlog(req,res,next){ + Blogs.remove({ + _id:req.params.id + },function(err,result){ + res.send(`Delete Success!`) + }) +} + +function updateBlog (req,res,next){ + Blogs.findOne({ + _id:req.params.id + },function(err,result){ + Blogs.updateOne({ + _id:req.params.id + },{ + title:req.body.title || result.title, + blog: req.body.blog || result.blog + },function(err,result){ + res.send(`Update Succes!`) + }) + }) +} + + +module.exports={ + addBlog,listBlogs,deleteBlog,getBlog,updateBlog +} \ No newline at end of file diff --git a/Controllers/user-controller.js b/Controllers/user-controller.js new file mode 100644 index 0000000..67ec409 --- /dev/null +++ b/Controllers/user-controller.js @@ -0,0 +1,80 @@ +const Users = require('../Models/user.js') +const jwt = require('jsonwebtoken') +const bcrypt = require('bcrypt') +const saltRounds = 10 +const salt = bcrypt.genSaltSync(saltRounds) +require('dotenv').config() + + +function signup (req,res,next){ + let hash = bcrypt.hashSync(req.body.password,salt) + Users.create({ + username:req.body.username, + password: hash, + email: req.body.email + },function(err,result){ + res.send(result) + }) +} + +function login (req,res,next){ + Users.findOne({ + username: req.body.username + },function(err,result){ + if(result.length < 1){ + res.send('invalid username!') + } + else{ + if(bcrypt.compare(req.body.password,result.password)){ + let token = jwt.sign({_id:result._id,username:result.username,email:result.email},process.env.SECRET) + res.send(token) + } + else{ + res.send('invalid password!') + } + } + }) +} + +function deleteUser (req,res,next){ + Users.remove({ + _id: req.params.id + },function(err,result){ + res.send(result) + }) +} + +function updateUser (req,res,next){ + let hash = bcrypt.hashSync(req.body.password,salt) + Users.findOne({ + _id: req.params.id + },function(err,result){ + Users.updateOne({ + _id: req.params.id + },{ + username: req.body.username || result.username, + password: hash || result.password, + email: req.body.email || result.email + },function(err,result){ + res.send(result) + }) + }) +} + +function getUser (req,res,next){ + Users.findOne({ + _id:req.params.id + },function(err,result){ + res.send(result) + }) +} + +function listUsers (req,res,next){ + Users.find({},function(err,result){ + res.send(result) + }) +} + +module.exports = { + getUser,updateUser,deleteUser,login,signup,listUsers +} \ No newline at end of file diff --git a/Models/blog.js b/Models/blog.js new file mode 100644 index 0000000..917bb26 --- /dev/null +++ b/Models/blog.js @@ -0,0 +1,12 @@ +const mongoose = require('mongoose') +const Schema = mongoose.Schema + +var blogSchema = new Schema({ + title: String, + blog: String, + user_id: {type:Schema.Types.ObjectId,ref:'User'} +}) + +var blog = mongoose.model('Blogs',blogSchema) + +module.exports = blog \ No newline at end of file diff --git a/Models/user.js b/Models/user.js new file mode 100644 index 0000000..3c85d4c --- /dev/null +++ b/Models/user.js @@ -0,0 +1,12 @@ +const mongoose = require('mongoose') +const Schema = mongoose.Schema + +var userSchema = new Schema ({ + username: String, + password: String, + email: String +}) + +var User = mongoose.model('Users',userSchema) + +module.exports = User \ No newline at end of file diff --git a/app.js b/app.js new file mode 100644 index 0000000..46b7faa --- /dev/null +++ b/app.js @@ -0,0 +1,60 @@ +var express = require('express'); +var path = require('path'); +var favicon = require('serve-favicon'); +var logger = require('morgan'); +var cookieParser = require('cookie-parser'); +var bodyParser = require('body-parser'); +const cors = require('cors') +const mongoose = require('mongoose') +var index = require('./routes/index'); +var users = require('./routes/users'); +var app = express(); +var db_config = { + development:'mongodb://blog:blog@ds163181.mlab.com:63181/blog-tdd', + test: 'mongodb://blog:blog@ds163181.mlab.com:63181/blog-tdd-test' +} +var current_env = app.settings.env //developnent / test / production +mongoose.connect(db_config[current_env],function(err,res){ + if(err){ + console.log('error database',err); + } + else{ + console.log('connected to database', db_config[current_env]); + } +}) +// var db = mongoose.createConnection(db_config) +// view engine setup +app.set('views', path.join(__dirname, 'views')); +app.set('view engine', 'jade'); + +// uncomment after placing your favicon in /public +//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); +app.use(cors()) +app.use(logger('dev')); +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({ extended: false })); +app.use(cookieParser()); +app.use(express.static(path.join(__dirname, 'public'))); + +app.use('/', index); +app.use('/users', users); + +// catch 404 and forward to error handler +app.use(function(req, res, next) { + var err = new Error('Not Found'); + err.status = 404; + next(err); +}); + +// error handler +app.use(function(err, req, res, next) { + // set locals, only providing error in development + res.locals.message = err.message; + res.locals.error = req.app.get('env') === 'development' ? err : {}; + + // render the error page + res.status(err.status || 500); + res.render('error'); +}); + +module.exports = app; diff --git a/bin/www b/bin/www new file mode 100755 index 0000000..aa1db4f --- /dev/null +++ b/bin/www @@ -0,0 +1,90 @@ +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var app = require('../app'); +var debug = require('debug')('blog-tdd:server'); +var http = require('http'); + +/** + * Get port from environment and store in Express. + */ + +var port = normalizePort(process.env.PORT || '3000'); +app.set('port', port); + +/** + * Create HTTP server. + */ + +var server = http.createServer(app); + +/** + * Listen on provided port, on all network interfaces. + */ + +server.listen(port); +server.on('error', onError); +server.on('listening', onListening); + +/** + * Normalize a port into a number, string, or false. + */ + +function normalizePort(val) { + var port = parseInt(val, 10); + + if (isNaN(port)) { + // named pipe + return val; + } + + if (port >= 0) { + // port number + return port; + } + + return false; +} + +/** + * Event listener for HTTP server "error" event. + */ + +function onError(error) { + if (error.syscall !== 'listen') { + throw error; + } + + var bind = typeof port === 'string' + ? 'Pipe ' + port + : 'Port ' + port; + + // handle specific listen errors with friendly messages + switch (error.code) { + case 'EACCES': + console.error(bind + ' requires elevated privileges'); + process.exit(1); + break; + case 'EADDRINUSE': + console.error(bind + ' is already in use'); + process.exit(1); + break; + default: + throw error; + } +} + +/** + * Event listener for HTTP server "listening" event. + */ + +function onListening() { + var addr = server.address(); + var bind = typeof addr === 'string' + ? 'pipe ' + addr + : 'port ' + addr.port; + debug('Listening on ' + bind); +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..bbb8d99 --- /dev/null +++ b/package.json @@ -0,0 +1,40 @@ +{ + "name": "blog-tdd", + "version": "0.0.0", + "private": true, + "scripts": { + "start": "nodemon ./bin/www" + }, + "dependencies": { + "bcrypt": "^1.0.2", + "body-parser": "~1.17.1", + "chai": "^4.0.1", + "chai-http": "^3.0.0", + "cookie-parser": "~1.4.3", + "cors": "^2.8.3", + "debug": "~2.6.3", + "dotenv": "^4.0.0", + "express": "~4.15.2", + "jade": "~1.11.0", + "jsonwebtoken": "^7.4.1", + "mocha": "^3.4.2", + "mongoose": "^4.10.4", + "morgan": "~1.8.1", + "serve-favicon": "~2.4.2" + }, + "description": "", + "main": "app.js", + "directories": { + "test": "test" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/stedyyulius/blog-tdd.git" + }, + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/stedyyulius/blog-tdd/issues" + }, + "homepage": "https://github.com/stedyyulius/blog-tdd#readme" +} diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css new file mode 100644 index 0000000..9453385 --- /dev/null +++ b/public/stylesheets/style.css @@ -0,0 +1,8 @@ +body { + padding: 50px; + font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; +} + +a { + color: #00B7FF; +} diff --git a/routes/index.js b/routes/index.js new file mode 100644 index 0000000..2d00ee0 --- /dev/null +++ b/routes/index.js @@ -0,0 +1,13 @@ +var express = require('express'); +var router = express.Router(); +const BlogController = require('../Controllers/blog-controller.js') + +/* GET home page. */ +router.get('/blog',BlogController.listBlogs) +router.post('/blog',BlogController.addBlog) +router.delete('/blog/:id',BlogController.deleteBlog) +router.put('/blog/:id',BlogController.updateBlog) +router.get('/blog/:id',BlogController.getBlog) +// router.get('/blog/user',BlogController.userBlogs) + +module.exports = router; diff --git a/routes/users.js b/routes/users.js new file mode 100644 index 0000000..4f4001b --- /dev/null +++ b/routes/users.js @@ -0,0 +1,13 @@ +var express = require('express'); +var router = express.Router(); +const UserController = require('../Controllers/user-controller.js') +/* GET users listing. */ +router.get('/',UserController.listUsers) +router.get('/:id',UserController.getUser) +router.put('/:id',UserController.updateUser) +router.delete('/:id',UserController.deleteUser) +router.post('/signup',UserController.signup) +router.post('/login',UserController.login) + + +module.exports = router; diff --git a/test/test.js b/test/test.js new file mode 100644 index 0000000..ade53a0 --- /dev/null +++ b/test/test.js @@ -0,0 +1,215 @@ +const chai = require('chai') +const chaiHttp = require('chai-http') +chai.use(chaiHttp) +const server = require('../app.js') +const Blogs = require('../Models/blog.js') +const Users = require ('../Models/user.js') +const should = chai.should() +var id = "" +var user_id = "" + + +describe('Blogs',function(){ + beforeEach(function(done){ + var createBlog = new Blogs({ + title: 'the legacy of test', + blog: 'once upon a time' + }) + createBlog.save((err,data)=>{ + id = data._id + done() + }) + }) + + +afterEach(function(done){ + Blogs.remove({},function(err){ + done() + }) +}) + +describe('Get all blogs in GET /blog',function(){ + it('should return all blogs',function(done){ + chai.request(server) + .get('/blog') + .end((err,res)=>{ + res.should.have.status(200) + res.body.should.be.a('array') + // res.body.length.should.equal(1) + done() + }) + }) + // it('should return error') +}) + +describe('Create new Blog in POST /blog',function(){ + it('should return one blog created',function(done){ + chai.request(server) + .post('/blog') + .send({ + title: 'asli', + blog: 'uda pass woi' + }) + .end((err,res)=>{ + console.log(res.body); + res.should.have.status(200) + res.body.should.be.a('object') + res.body.should.have.property('title') + res.body.should.have.property('blog') + done() + }) + }) + // it('should return error') +}) + +describe('Delete Blog in DELETE /blog/:id',function(){ + it('should delete one blog',function(done){ + chai.request(server) + .delete(`/blog/${id}`) + .end((err,res)=>{ + res.should.have.status(200) + res.body.should.be.a('object') + done() + }) + }) + }) + +describe('Edit Blog in PUT /blog/:id',function(){ + it('should update one blog',function(done){ + chai.request(server) + .put(`/blog/${id}`) + .end((err,res)=>{ + res.should.have.status(200) + res.body.should.be.a('object') + done() + }) + }) + }) + +describe('Get one Blog in GET /blog/:id',function(){ + it('should get one blog',function(done){ + chai.request(server) + .get(`/blog/${id}`) + .end((err,res)=>{ + console.log(res.body); + res.should.have.status(200) + res.body.should.be.a('object') + done() + }) + }) + }) +}) + +describe('Users',function(){ + beforeEach(function(done){ + var createUsers = new Users({ + username: 'testguy', + password: 'testguy', + email: 'test@gmail.com' + }) + createUsers.save((err,res)=>{ + user_id = res._id + done() + }) + }) + + afterEach(function(done){ + Users.remove({},function(err){ + done() + }) + }) + + describe(`list all users in database in GET /users`,function(){ + it('should return all users', function(done){ + chai.request(server) + .get('/users') + .end((err,res)=>{ + console.log(`1 `+res.body); + res.should.have.status(200) + res.body.should.be.a('array') + done() + }) + }) + }) + + describe('Get one user in GET /users/:id',function(){ + it('should return one user', function(done){ + chai.request(server) + .get(`/users/${user_id}`) + .end((err,res)=>{ + console.log(`2 `+ res.body); + res.should.have.status(200) + res.body.should.be.a('object') + done() + }) + }) + }) + + describe('Delete one user in DELETE /users/:id',function(){ + it('should delete one user',function(done){ + chai.request(server) + .delete(`/users/${user_id}`) + .end((err,res)=>{ + res.should.have.status(200) + res.body.should.be.a('object') + done() + }) + }) + }) + + describe('Edit one user in PUT /users/:id',function(){ + it('should edit one user',function(done){ + chai.request(server) + .put(`/users/${user_id}`) + .send({ + username: 'timo', + password: 'timo', + email: 'timo' + }) + .end((err,res)=>{ + res.should.have.status(200) + res.body.should.be.a('object') + done() + }) + }) + }) + + describe('Sign Up user in POST /users/signup',function(){ + it('should register a user',function(done){ + chai.request(server) + .post(`/users/signup`) + .send({ + username: 'tester', + password: 'tester', + email: 'testguy@gmail.com' + }) + .end((err,res)=>{ + res.should.have.status(200) + res.body.should.be.a('object') + res.body.should.have.property('username') + res.body.should.have.property('password') + res.body.should.have.property('email') + done() + }) + }) + }) + + describe('Log in a user in POST /users/login',function(){ + it('should login a user and develop a token',function(done){ + chai.request(server) + .post('/users/signup') + .send({ + username: 'testguy', + password: 'testguy' + }) + .end((err,res)=>{ + console.log(`login `+ res.body); + res.should.have.status(200) + res.body.should.be.a('object') + res.body.should.have.property('username') + res.body.should.have.property('password') + done() + }) + }) + }) +}) diff --git a/views/error.jade b/views/error.jade new file mode 100644 index 0000000..51ec12c --- /dev/null +++ b/views/error.jade @@ -0,0 +1,6 @@ +extends layout + +block content + h1= message + h2= error.status + pre #{error.stack} diff --git a/views/index.jade b/views/index.jade new file mode 100644 index 0000000..3d63b9a --- /dev/null +++ b/views/index.jade @@ -0,0 +1,5 @@ +extends layout + +block content + h1= title + p Welcome to #{title} diff --git a/views/layout.jade b/views/layout.jade new file mode 100644 index 0000000..15af079 --- /dev/null +++ b/views/layout.jade @@ -0,0 +1,7 @@ +doctype html +html + head + title= title + link(rel='stylesheet', href='/stylesheets/style.css') + body + block content