Skip to content

Commit 5d229a5

Browse files
authoredNov 1, 2017
Merge pull request #66 from SoftwareEngineeringDaily/develop
Merging develop into master
2 parents 99b2fe9 + 9f6bb22 commit 5d229a5

15 files changed

+185
-187
lines changed
 

‎.env.example ‎.env.docker_example

File renamed without changes.

‎.env.local_example

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
NODE_ENV=development
2+
PORT=4040
3+
JWT_SECRET=insert-random-secret-string-here
4+
5+
MONGO_HOST=mongodb://localhost/express-mongoose-es6-rest-api-development
6+
MONGO_HOST_TEST=mongodb://localhost/express-mongoose-es6-rest-api-development-test
7+
MONGO_PORT=27017
8+
9+
RACCOON_REDIS_URL=localhost
10+
RACCOON_REDIS_PORT=6379
11+
#RACCOON_REDIS_AUTH=
12+
13+
DEBUG=express-mongoose-es6-rest-api:*
14+
15+
FACEBOOK_ID=insert-facebook-app-id-here
16+
FACEBOOK_SECRET=insert-facebook-app-secret-here

‎README.md

+15-7
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
1-
## Set up
2-
- Install and run a local redis client (docker coming soon)
3-
- Install and run a local mongo client (docker coming soon)
4-
- cp .env.example .env
5-
- npm install or yarn install
6-
- npm start or yarn start
1+
[![logo](https://i.imgur.com/3OtP3p8.png)](https://softwareengineeringdaily.com/)
2+
3+
[![Build Status](https://travis-ci.org/SoftwareEngineeringDaily/software-engineering-daily-api.svg?branch=travis-fix)](https://travis-ci.org/SoftwareEngineeringDaily/software-engineering-daily-api)
4+
5+
# SEDaily-API
6+
7+
The backend services and API for the Software Engineering Daily [Android](https://github.com/SoftwareEngineeringDaily/SEDaily-Android), [iOS](https://github.com/SoftwareEngineeringDaily/se-daily-iOS), and [web front end](https://github.com/SoftwareEngineeringDaily/sedaily-front-end).
8+
9+
## Set up (local)
10+
- Install and run a local redis client
11+
- Install and run a local mongo client
12+
- `cp .env.local_example .env`
13+
- `npm install` or `yarn install`
14+
- `npm start` or `yarn start`
715
- check package.json for other builds
816
- use curl or Postman to make requests
917

1018

1119
## Using Docker
20+
- `cp .env.docker_example .env`
1221
- Run `docker-compose up`
13-

‎config/param-validation.js

+15-27
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,37 @@
11
import Joi from 'joi';
22

33
export default {
4-
// POST /api/users
5-
createUser: {
6-
body: {
7-
username: Joi.string().required(),
8-
mobileNumber: Joi.string().regex(/^[1-9][0-9]{9}$/).required()
9-
}
10-
},
11-
124
// UPDATE /api/users/:userId
135
updateUser: {
146
body: {
157
username: Joi.string().required(),
16-
mobileNumber: Joi.string().regex(/^[1-9][0-9]{9}$/).required()
8+
name: Joi.string().required(),
9+
bio: Joi.string().allow(''),
10+
website: Joi.string().allow(''),
11+
email: Joi.string().email().allow('')
1712
},
1813
params: {
1914
userId: Joi.string().hex().required()
2015
}
2116
},
22-
23-
// POST /api/users
24-
createPost: {
25-
body: {
26-
username: Joi.string().required(),
27-
mobileNumber: Joi.string().regex(/^[1-9][0-9]{9}$/).required()
28-
}
29-
},
30-
31-
// UPDATE /api/users/:userId
32-
updatePost: {
17+
// POST /api/auth/login
18+
login: {
3319
body: {
3420
username: Joi.string().required(),
35-
mobileNumber: Joi.string().regex(/^[1-9][0-9]{9}$/).required()
36-
},
37-
params: {
38-
userId: Joi.string().hex().required()
21+
password: Joi.string().required()
3922
}
4023
},
4124

42-
// POST /api/auth/login
43-
login: {
25+
// POST /api/auth/register
26+
register: {
4427
body: {
4528
username: Joi.string().required(),
46-
password: Joi.string().required()
29+
password: Joi.string().required(),
30+
// Should be required once mobile apps get updated:
31+
name: Joi.string(),
32+
bio: Joi.string().allow(''),
33+
website: Joi.string().allow(''),
34+
email: Joi.string().email().allow('')
4735
}
4836
}
4937
};

‎package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
"main": "index.js",
77
"private": false,
88
"engines": {
9-
"node": ">=4.8.0",
10-
"npm": ">=2.15.11"
9+
"node": "6.11.1",
10+
"npm": "5.5.1"
1111
},
1212
"scripts": {
1313
"launch": "npm run build && node dist/index.js",

‎server/controllers/auth.controller.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import config from '../../config/config';
55
import passport from 'passport';
66
import FacebookTokenStrategy from 'passport-facebook-token';
77
import User from '../models/user.model';
8-
8+
import _ from 'lodash';
99

1010
passport.serializeUser(function(user, done){
1111
done(null, user._id);
@@ -110,8 +110,10 @@ function register(req, res, next) {
110110
}
111111

112112
const newUser = new User();
113-
newUser.username = username;
114-
newUser.password = newUser.generateHash(password);
113+
newUser.password = User.generateHash(password);
114+
// We assign a set of "approved fields"
115+
const newValues = _.pick(req.body, User.updatableFields);
116+
Object.assign(newUser, newValues);
115117

116118
return newUser.save();
117119
})

‎server/controllers/user.controller.js

+50-50
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,44 @@
1+
import APIError from '../helpers/APIError';
2+
import httpStatus from 'http-status';
13
import User from '../models/user.model';
4+
import _ from 'lodash';
25

36
/**
47
* Load user and append to req.
58
*/
69
function load(req, res, next, id) {
710
User.get(id)
811
.then((user) => {
9-
req.user = user; // eslint-disable-line no-param-reassign
12+
delete user.password;
13+
req.userLoaded = user; // eslint-disable-line no-param-reassign
1014
return next();
1115
})
1216
.catch(e => next(e));
1317
}
14-
1518
/**
16-
* Get user
19+
* Get currently logged in user
1720
* @returns {User}
1821
*/
19-
function get(req, res) {
20-
return res.json(req.user);
22+
function me(req, res, next) {
23+
User.get(req.user._id)
24+
.then((user) => {
25+
user.password = null;
26+
return res.json(user);
27+
})
28+
.catch(e => {
29+
return next(err);
30+
});
2131
}
2232

33+
// TODO: maybe a quick version of me that only loads a shallow verison of
34+
// user id
35+
2336
/**
24-
* Create new user
25-
* @property {string} req.body.username - The username of user.
26-
* @property {string} req.body.mobileNumber - The mobileNumber of user.
37+
* Get user
2738
* @returns {User}
2839
*/
29-
function create(req, res, next) {
30-
const user = new User({
31-
username: req.body.username,
32-
mobileNumber: req.body.mobileNumber
33-
});
34-
35-
user.save()
36-
.then(savedUser => res.json(savedUser))
37-
.catch(e => next(e));
40+
function get(req, res) {
41+
return res.json(req.userLoaded);
3842
}
3943

4044
/**
@@ -44,37 +48,33 @@ function create(req, res, next) {
4448
* @returns {User}
4549
*/
4650
function update(req, res, next) {
47-
const user = req.user;
48-
user.username = req.body.username;
49-
user.mobileNumber = req.body.mobileNumber;
50-
51-
user.save()
52-
.then(savedUser => res.json(savedUser))
53-
.catch(e => next(e));
54-
}
55-
56-
/**
57-
* Get user list.
58-
* @property {number} req.query.skip - Number of users to be skipped.
59-
* @property {number} req.query.limit - Limit number of users to be returned.
60-
* @returns {User[]}
61-
*/
62-
function list(req, res, next) {
63-
const { limit = 50, skip = 0 } = req.query;
64-
User.list({ limit, skip })
65-
.then(users => res.json(users))
66-
.catch(e => next(e));
67-
}
68-
69-
/**
70-
* Delete user.
71-
* @returns {User}
72-
*/
73-
function remove(req, res, next) {
74-
const user = req.user;
75-
user.remove()
76-
.then(deletedUser => res.json(deletedUser))
77-
.catch(e => next(e));
78-
}
51+
const user = req.userLoaded;
52+
const username = req.body.username;
53+
// We gotta check a few things:
54+
// First we make sure we are the actual user we are modifying.
55+
if(!req.user || user._id != req.user._id) {
56+
let err = new APIError('Not enough permissions to modify that user.', httpStatus.UNAUTHORIZED, true); //eslint-disable-line
57+
return next(err);
58+
}
59+
// Next we are making sure the username doens't already exist:
60+
User.findOne({ username })
61+
.exec()
62+
.then((_user) => {
63+
if (_user && _user.id != user.id) {
64+
let err = new APIError('User already exists.', httpStatus.UNAUTHORIZED, true); //eslint-disable-line
65+
return next(err);
66+
}
67+
// Using _.pick to only get a few properties:
68+
// otherwise user can set themselves to verified, etc :)
69+
const newValues = _.pick(req.body, User.updatableFields);
70+
Object.assign(user, newValues);
71+
delete user.password;
72+
user.save();
73+
delete user.password; // Why doesn't this work?
74+
user.password = null;
75+
res.json(user);
76+
})
77+
.catch(e => next(e));
78+
}
7979

80-
export default { load, get, create, update, list, remove };
80+
export default {load, get, me, update};

‎server/models/comment.model.js

+4-6
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
11

22
import Promise from 'bluebird';
3-
import mongoose from 'mongoose';
3+
import mongoose, {Schema} from 'mongoose';
44
import moment from 'moment';
55
import httpStatus from 'http-status';
66
import APIError from '../helpers/APIError';
77
import Vote from './vote.model';
88

9-
// TODO
10-
// import mongoose {Schema} from 'mongoose';
119

1210
//
1311
/**
1412
* Comment Schema
1513
*/
16-
const CommentSchema = new mongoose.Schema({
14+
const CommentSchema = new Schema({
1715
id: String,
1816
content: {
1917
type: String,
@@ -24,11 +22,11 @@ const CommentSchema = new mongoose.Schema({
2422
default: Date.now
2523
},
2624
post: {
27-
type: mongoose.Schema.Types.ObjectId,
25+
type: Schema.Types.ObjectId,
2826
ref: 'Post'
2927
},
3028
author: {
31-
type: mongoose.Schema.Types.ObjectId,
29+
type: Schema.Types.ObjectId,
3230
ref: 'User'
3331
}
3432
});

‎server/models/user.model.js

+28-6
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,31 @@ import APIError from '../helpers/APIError';
1010
const UserSchema = new mongoose.Schema({
1111
username: {
1212
type: String,
13-
required: true
13+
// , required: true // Should be requied since it is also validated
1414
},
1515
password: {
1616
type: String
17+
// TODO: Should be required.
18+
},
19+
name: {
20+
type: String
21+
// , required: true // Should be requied but need to update all clients
22+
},
23+
avatarUrl: {
24+
type: String
25+
},
26+
bio: {
27+
type: String
28+
},
29+
website: {
30+
type: String
31+
},
32+
verified: {
33+
type: Boolean,
34+
default: false
35+
},
36+
email: {
37+
type: String
1738
},
1839
facebook: {
1940
id: {
@@ -52,10 +73,6 @@ const UserSchema = new mongoose.Schema({
5273
* Methods
5374
*/
5475
UserSchema.method({
55-
generateHash: function generateHash(password) {
56-
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
57-
},
58-
5976
validPassword: function validPassword(password) {
6077
return bcrypt.compareSync(password, this.password);
6178
},
@@ -82,6 +99,10 @@ UserSchema.statics = {
8299
});
83100
},
84101

102+
generateHash: function generateHash(password) {
103+
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
104+
},
105+
85106
/**
86107
* List users in descending order of 'createdAt' timestamp.
87108
* @param {number} skip - Number of users to be skipped.
@@ -94,7 +115,8 @@ UserSchema.statics = {
94115
.skip(+skip)
95116
.limit(+limit)
96117
.exec();
97-
}
118+
},
119+
updatableFields: ['username','website','bio', 'name','email']
98120
};
99121

100122
/**

‎server/routes/auth.route.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ router.route('/login')
1313
.post(validate(paramValidation.login), authCtrl.login);
1414

1515
router.route('/register')
16-
.post(validate(paramValidation.login), authCtrl.register);
16+
.post(validate(paramValidation.register), authCtrl.register);
1717

1818
/** GET /api/auth/random-number - Protected route,
1919
* needs token returned by the above as header. Authorization: Bearer {token} */

0 commit comments

Comments
 (0)
Please sign in to comment.