- Initial commit... A DB, some routes, and basic authentication routines...
This commit is contained in:
30
config.js
Normal file
30
config.js
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
module.exports = {
|
||||||
|
name: 'wEvent API',
|
||||||
|
env: process.env.NODE_ENV || 'development',
|
||||||
|
port: process.env.PORT || 3001,
|
||||||
|
base_url: process.env.BASE_URL || 'http://localhost:3000',
|
||||||
|
db: {
|
||||||
|
uri: process.env.MONGODB_URI || 'mongodb://127.0.0.1:27017/wEvent-dev',
|
||||||
|
},
|
||||||
|
version: '0.0.1',
|
||||||
|
assetStoreUrl: 'https://www.google.com/',
|
||||||
|
services: {
|
||||||
|
apple: {},
|
||||||
|
facebook: {
|
||||||
|
appId: '2359355590971136',
|
||||||
|
appSecret: 'a5703f7d0af8e694aec5bd4175a85d6b',
|
||||||
|
},
|
||||||
|
google: {
|
||||||
|
appId: '442412638360-p0idffou0qlpgor7agideudb1dh10mpf.apps.googleusercontent.com',
|
||||||
|
appSecret: 'a7fmS7Wc9Ssycr21WXdQ4TYl',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
security: {
|
||||||
|
jwt: {
|
||||||
|
audience: 'wEvents.io',
|
||||||
|
daysValid: 365,
|
||||||
|
issuer: 'patrons.wEvents.io',
|
||||||
|
secret: 'Th!sIs a d3v3lopm3nt server $#cr¢T.',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
66
index.js
Normal file
66
index.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
const jwt = require('jsonwebtoken');
|
||||||
|
const mongoose = require('mongoose');
|
||||||
|
const passport = require('passport');
|
||||||
|
const restify = require('restify');
|
||||||
|
|
||||||
|
const config = require('./config');
|
||||||
|
|
||||||
|
const auth = require('./strategies/auth')(passport);
|
||||||
|
|
||||||
|
const validateJsonData = require('./lib/validateType.js');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize Server
|
||||||
|
*/
|
||||||
|
const server = restify.createServer({
|
||||||
|
name: config.name,
|
||||||
|
version: config.version,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Middleware
|
||||||
|
*/
|
||||||
|
server.use(restify.plugins.acceptParser(server.acceptable));
|
||||||
|
server.use(restify.plugins.bodyParser({
|
||||||
|
hash: 'sha1',
|
||||||
|
mapParams: true,
|
||||||
|
multiples: true,
|
||||||
|
}));
|
||||||
|
server.use(restify.plugins.fullResponse());
|
||||||
|
server.use(restify.plugins.gzipResponse());
|
||||||
|
server.use(restify.plugins.queryParser({ mapParams: true }));
|
||||||
|
server.use(auth.passport.initialize());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error checking
|
||||||
|
*/
|
||||||
|
server.post('*', validateJsonData);
|
||||||
|
server.put('*', validateJsonData);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start Server, Connect to DB & Require Routes
|
||||||
|
*/
|
||||||
|
server.listen(config.port, () => {
|
||||||
|
// establish connection to mongodb
|
||||||
|
mongoose.Promise = global.Promise;
|
||||||
|
mongoose.connect(
|
||||||
|
config.db.uri,
|
||||||
|
{
|
||||||
|
useCreateIndex: true,
|
||||||
|
useFindAndModify: false,
|
||||||
|
useNewUrlParser: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const db = mongoose.connection;
|
||||||
|
|
||||||
|
db.on('error', (err) => {
|
||||||
|
console.error(err);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
db.once('open', () => {
|
||||||
|
require('./routes')(server, auth);
|
||||||
|
console.log(`Server is listening on port ${config.port}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
11
lib/validateType.js
Normal file
11
lib/validateType.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
const validateJsonData = (req, res, next) => {
|
||||||
|
if (!req.is('application/json')) {
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError("Expects 'application/json'"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = validateJsonData;
|
||||||
36
models/bid.js
Normal file
36
models/bid.js
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
const mongoose = require('mongoose');
|
||||||
|
const timestamps = require('mongoose-timestamp');
|
||||||
|
|
||||||
|
const BidSchema = new mongoose.Schema(
|
||||||
|
{
|
||||||
|
itemId: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
bidderId: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
bidAmount: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
maxAmount: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
time: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{ minimize: false },
|
||||||
|
);
|
||||||
|
|
||||||
|
BidSchema.plugin(timestamps);
|
||||||
|
|
||||||
|
const Bid = mongoose.model('Bid', BidSchema);
|
||||||
|
module.exports = Bid;
|
||||||
44
models/common/address.js
Normal file
44
models/common/address.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
const mongoose = require('mongoose');
|
||||||
|
const mongooseStringQuery = require('mongoose-string-query');
|
||||||
|
const mongooseTimestamps = require('mongoose-timestamp');
|
||||||
|
|
||||||
|
const AddressSchema = new mongoose.Schema(
|
||||||
|
{
|
||||||
|
address1: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
address2: {
|
||||||
|
type: String,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
locality: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
postalCode: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
enum: [ 'billing', 'shipping' ],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{ minimize: false },
|
||||||
|
);
|
||||||
|
|
||||||
|
AddressSchema.plugin(mongooseStringQuery);
|
||||||
|
AddressSchema.plugin(mongooseTimestamps);
|
||||||
|
|
||||||
|
module.exports = AddressSchema;
|
||||||
30
models/common/email.js
Normal file
30
models/common/email.js
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
const mongoose = require('mongoose');
|
||||||
|
const mongooseStringQuery = require('mongoose-string-query');
|
||||||
|
const mongooseTimestamps = require('mongoose-timestamp');
|
||||||
|
|
||||||
|
const EmailSchema = new mongoose.Schema(
|
||||||
|
{
|
||||||
|
user: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
domain: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{ minimize: false },
|
||||||
|
);
|
||||||
|
|
||||||
|
EmailSchema.virtual('address').get(function() {
|
||||||
|
return this.user + '@' + this.domain;
|
||||||
|
});
|
||||||
|
|
||||||
|
EmailSchema.plugin(mongooseStringQuery);
|
||||||
|
EmailSchema.plugin(mongooseTimestamps);
|
||||||
|
|
||||||
|
module.exports = EmailSchema;
|
||||||
29
models/common/phone.js
Normal file
29
models/common/phone.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
const mongoose = require('mongoose');
|
||||||
|
const mongooseStringQuery = require('mongoose-string-query');
|
||||||
|
const mongooseTimestamps = require('mongoose-timestamp');
|
||||||
|
|
||||||
|
const PhoneSchema = new mongoose.Schema(
|
||||||
|
{
|
||||||
|
countryCode: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
default: '1',
|
||||||
|
},
|
||||||
|
number: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
enum: [ 'home', 'mobile' ],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{ minimize: false },
|
||||||
|
);
|
||||||
|
|
||||||
|
PhoneSchema.plugin(mongooseStringQuery);
|
||||||
|
PhoneSchema.plugin(mongooseTimestamps);
|
||||||
|
|
||||||
|
module.exports = PhoneSchema;
|
||||||
16
models/constants.js
Normal file
16
models/constants.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
module.exports = {
|
||||||
|
ITEM_TYPES: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
enum: [
|
||||||
|
'auction',
|
||||||
|
'donation',
|
||||||
|
'event',
|
||||||
|
'raffle',
|
||||||
|
'membership',
|
||||||
|
'popup',
|
||||||
|
'ticket',
|
||||||
|
],
|
||||||
|
default: 'auction',
|
||||||
|
},
|
||||||
|
};
|
||||||
147
models/event.js
Normal file
147
models/event.js
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
const config = require('../config.js');
|
||||||
|
const mongoose = require('mongoose');
|
||||||
|
const timestamps = require('mongoose-timestamp');
|
||||||
|
|
||||||
|
const PostSchema = new mongoose.Schema(
|
||||||
|
{
|
||||||
|
author: String,
|
||||||
|
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
isPublic: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
scheduledPost: String,
|
||||||
|
|
||||||
|
sendNotification: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
notificationContent: String,
|
||||||
|
notificationLinksTo: String,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const TicketSchema = new mongoose.Schema(
|
||||||
|
{
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
price: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
|
||||||
|
capacity: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
available: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
itemId: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
startSale: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
endSale: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{ minimize: false },
|
||||||
|
);
|
||||||
|
|
||||||
|
const EventSchema = new mongoose.Schema(
|
||||||
|
{
|
||||||
|
eventDate: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
startTime: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
endTime: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
tagline: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
isTicketed: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
ticketClasses: [ TicketSchema ],
|
||||||
|
|
||||||
|
showFrom: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
showUntil: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
requireLoginToSeeAuction: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
images: [{
|
||||||
|
url: String,
|
||||||
|
}],
|
||||||
|
url: String,
|
||||||
|
|
||||||
|
posts: [ PostSchema ],
|
||||||
|
},
|
||||||
|
|
||||||
|
{ minimize: false },
|
||||||
|
);
|
||||||
|
|
||||||
|
EventSchema.plugin(timestamps);
|
||||||
|
|
||||||
|
EventSchema.path('images').get(v => `${config.assetStoreUrl}${v.url}`)
|
||||||
|
|
||||||
|
const Event = mongoose.model('Event', EventSchema);
|
||||||
|
module.exports = Event;
|
||||||
49
models/install.js
Normal file
49
models/install.js
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
const mongoose = require('mongoose');
|
||||||
|
const timestamps = require('mongoose-timestamp');
|
||||||
|
|
||||||
|
const InstallSchema = new mongoose.Schema(
|
||||||
|
{
|
||||||
|
timeZone: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
deviceType: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
trim: true,
|
||||||
|
enum: [ 'android', 'ios', 'web' ],
|
||||||
|
},
|
||||||
|
badge: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
installationId: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
email: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
appIdentifier: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
localeIdentifier: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{ minimize: false },
|
||||||
|
);
|
||||||
|
|
||||||
|
InstallSchema.plugin(timestamps);
|
||||||
|
|
||||||
|
const Install = mongoose.model('Install', InstallSchema);
|
||||||
|
module.exports = Install;
|
||||||
136
models/item.js
Normal file
136
models/item.js
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
const { ITEM_TYPES } = require('./constants.js');
|
||||||
|
|
||||||
|
const config = require('../config.js');
|
||||||
|
const mongoose = require('mongoose');
|
||||||
|
const mongooseStringQuery = require('mongoose-string-query');
|
||||||
|
const timestamps = require('mongoose-timestamp');
|
||||||
|
|
||||||
|
const ItemSchema = new mongoose.Schema(
|
||||||
|
{
|
||||||
|
eventId: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
subtitle: {
|
||||||
|
type: String,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
donor: {
|
||||||
|
type: String,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
images: [{
|
||||||
|
url: String
|
||||||
|
}],
|
||||||
|
|
||||||
|
type: ITEM_TYPES,
|
||||||
|
|
||||||
|
quantityAvailable: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
default: 1,
|
||||||
|
},
|
||||||
|
soldCount: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
|
||||||
|
currentPrice: {
|
||||||
|
required: true,
|
||||||
|
type: Number,
|
||||||
|
},
|
||||||
|
startingPrice: {
|
||||||
|
type: Number,
|
||||||
|
},
|
||||||
|
reservePrice: {
|
||||||
|
type: Number,
|
||||||
|
},
|
||||||
|
estimatedValue: {
|
||||||
|
type: Number,
|
||||||
|
},
|
||||||
|
|
||||||
|
currentWinner: {
|
||||||
|
type: String,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
bidders: [{
|
||||||
|
name: String,
|
||||||
|
}],
|
||||||
|
bidCount: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
bidIncrement: {
|
||||||
|
type: Number,
|
||||||
|
default: 10,
|
||||||
|
},
|
||||||
|
|
||||||
|
catalogNumber: {
|
||||||
|
type: Number,
|
||||||
|
},
|
||||||
|
|
||||||
|
start: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
end: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
hideBeforeStart: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
hideAfterEnd: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
notifyOnAvailable: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
isShippable: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
shippingCost: Number,
|
||||||
|
|
||||||
|
organizationTake: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
default: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{ minimize: false },
|
||||||
|
);
|
||||||
|
|
||||||
|
ItemSchema.plugin(timestamps);
|
||||||
|
|
||||||
|
ItemSchema.path('images').get(v => `${config.assetStoreUrl}${v.url}`);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* STATICS
|
||||||
|
*/
|
||||||
|
ItemSchema.statics.addBatch = function(data = [], callback = () => {}) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const Item = mongoose.model('Item', ItemSchema);
|
||||||
|
module.exports = Item;
|
||||||
63
models/organization.js
Normal file
63
models/organization.js
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
const mongoose = require('mongoose');
|
||||||
|
const timestamps = require('mongoose-timestamp');
|
||||||
|
|
||||||
|
const PeopleSchema = new mongoose.Schema(
|
||||||
|
{
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
bio: {
|
||||||
|
type: String,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
address: AddressSchema,
|
||||||
|
email: EmailSchema,
|
||||||
|
phone: PhoneSchema,
|
||||||
|
},
|
||||||
|
|
||||||
|
{ minimize: false },
|
||||||
|
);
|
||||||
|
|
||||||
|
const OrganizationSchema = new mongoose.Schema(
|
||||||
|
{
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
url: String,
|
||||||
|
address: [ AddressSchema ],
|
||||||
|
telephone: [ TelephoneSchema ],
|
||||||
|
|
||||||
|
about: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
team: [ PeopleSchema ],
|
||||||
|
board: [ PeopleSchema ],
|
||||||
|
|
||||||
|
privacyUrl: String,
|
||||||
|
tosUrl: String,
|
||||||
|
|
||||||
|
copyright: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
{ minimize: false },
|
||||||
|
);
|
||||||
|
|
||||||
|
OrganizationSchema.plugin(timestamps);
|
||||||
|
|
||||||
|
const Organization = mongoose.model('Organization', OrganizationSchema);
|
||||||
|
module.exports = Organization;
|
||||||
44
models/sale.js
Normal file
44
models/sale.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
const { ITEM_TYPES } = require('./constants.js');
|
||||||
|
|
||||||
|
const mongoose = require('mongoose');
|
||||||
|
const timestamps = require('mongoose-timestamp');
|
||||||
|
|
||||||
|
const SaleSchema = new mongoose.Schema(
|
||||||
|
{
|
||||||
|
itemId: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
userId: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
amount: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
itemType: ITEM_TYPES,
|
||||||
|
paymentToken: String,
|
||||||
|
|
||||||
|
isPaid: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
isPickedUp: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{ minimize: false },
|
||||||
|
);
|
||||||
|
|
||||||
|
SaleSchema.plugin(timestamps);
|
||||||
|
|
||||||
|
const Sale = mongoose.model('Sale', SaleSchema);
|
||||||
|
module.exports = Sale;
|
||||||
284
models/user.js
Normal file
284
models/user.js
Normal file
@@ -0,0 +1,284 @@
|
|||||||
|
const crypto = require('crypto');
|
||||||
|
const jwt = require('jsonwebtoken');
|
||||||
|
const mongoose = require('mongoose');
|
||||||
|
const timestamps = require('mongoose-timestamp');
|
||||||
|
|
||||||
|
const config = require('../config.js');
|
||||||
|
|
||||||
|
const AddressSchema = require('./common/address.js');
|
||||||
|
const PhoneSchema = require('./common/phone.js');
|
||||||
|
|
||||||
|
const LoginSchema = new mongoose.Schema(
|
||||||
|
{
|
||||||
|
method: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
enum: [ 'apple', 'facebook', 'google', 'local' ],
|
||||||
|
},
|
||||||
|
userId: {
|
||||||
|
type: String,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
accessToken: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
secret: {
|
||||||
|
type: String,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
profile: {},
|
||||||
|
},
|
||||||
|
|
||||||
|
{ minimize: false },
|
||||||
|
);
|
||||||
|
|
||||||
|
const UserSchema = new mongoose.Schema(
|
||||||
|
{
|
||||||
|
nomDeBid: {
|
||||||
|
type: String,
|
||||||
|
trim: true,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
firstName: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
lastName: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
email: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
avatar: {
|
||||||
|
type: String,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
address: [ AddressSchema ],
|
||||||
|
phone: [ PhoneSchema ],
|
||||||
|
|
||||||
|
credentials: [ LoginSchema ],
|
||||||
|
|
||||||
|
organizationIdentifier: {
|
||||||
|
type: String,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
paymentToken: {
|
||||||
|
type: String,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
isVerified: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
isAllowedToBid: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
isOrganizationEmployee: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{ minimize: false },
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PLUGINS
|
||||||
|
*/
|
||||||
|
UserSchema.plugin(timestamps);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* METHODS
|
||||||
|
*/
|
||||||
|
UserSchema.methods.authenticate = function (username, password) {
|
||||||
|
const user = this.model('User').findOne({ email: username });
|
||||||
|
const strategy = user ? user.getAuthStrategy('local') : null;
|
||||||
|
|
||||||
|
if (strategy) {
|
||||||
|
let hash = crypto.pbkdf2Sync(password, strategy.get('secret'), 10000, 512, 'sha512').toString('hex')
|
||||||
|
return strategy.get('accessToken') === hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
UserSchema.methods.generateJWT = function (props = {}) {
|
||||||
|
const { exp, iss } = props;
|
||||||
|
const today = new Date();
|
||||||
|
|
||||||
|
let expirationDate = exp;
|
||||||
|
if (!expirationDate) {
|
||||||
|
expirationDate = new Date(today);
|
||||||
|
expirationDate.setDate(today.getDate() + config.security.jwt.daysValid);
|
||||||
|
}
|
||||||
|
|
||||||
|
return jwt.sign({
|
||||||
|
sub: this._id,
|
||||||
|
iss: iss || config.security.jwt.issuer,
|
||||||
|
aud: config.security.jwt.audience,
|
||||||
|
iat: parseInt(today.getTime()),
|
||||||
|
exp: parseInt(expirationDate.getTime() / 1000, 10),
|
||||||
|
}, config.security.jwt.secret);
|
||||||
|
}
|
||||||
|
|
||||||
|
UserSchema.methods.getAuthStrategy = function (method = 'local') {
|
||||||
|
return this.credentials.filter((strategy) => {
|
||||||
|
return strategy.method === method;
|
||||||
|
}).pop() || false;
|
||||||
|
};
|
||||||
|
|
||||||
|
UserSchema.methods.getNomDeBid = function () {
|
||||||
|
return this.nomDeBid || `${this.firstName} ${this.lastName.charAt(0)}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
UserSchema.methods.isEventManager = function () {
|
||||||
|
return this.isOrganizationEmployee || false;
|
||||||
|
};
|
||||||
|
|
||||||
|
UserSchema.methods.isRegistrationVerified = function () {
|
||||||
|
return this.isVerified || false;
|
||||||
|
};
|
||||||
|
|
||||||
|
UserSchema.methods.setPassword = function (password, callback = () => {}) {
|
||||||
|
const hasLocalStrategy = !!this.credentials.length &&
|
||||||
|
!!this.credentials.filter(strategy => strategy.method === 'local').length;
|
||||||
|
|
||||||
|
const salt = crypto.randomBytes(16).toString('hex');
|
||||||
|
const accessToken = crypto.pbkdf2Sync(password, salt, 10000, 512, 'sha512').toString('hex');
|
||||||
|
|
||||||
|
const strategy = {
|
||||||
|
accessToken,
|
||||||
|
method: 'local',
|
||||||
|
secret: salt,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (hasLocalStrategy) {
|
||||||
|
this.model('User').findOneAndUpdate(
|
||||||
|
{ _id: this._id, 'credentials.method': 'local' },
|
||||||
|
{ $set: { 'credentials.$': strategy } },
|
||||||
|
{ upsert: true },
|
||||||
|
callback,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasLocalStrategy) {
|
||||||
|
this.credentials.push(strategy);
|
||||||
|
this.save(callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
UserSchema.methods.toAuthJSON = function () {
|
||||||
|
const hasNomDeBid = !!this.nomDeBid;
|
||||||
|
const nomDeBid = this.getNomDeBid();
|
||||||
|
|
||||||
|
return {
|
||||||
|
email: this.email,
|
||||||
|
token: this.generateJWT(),
|
||||||
|
user: {
|
||||||
|
nomDeBid: nomDeBid,
|
||||||
|
email: this.email,
|
||||||
|
firstName: this.firstName,
|
||||||
|
lastName: this.lastName,
|
||||||
|
avatar: this.avatar,
|
||||||
|
isAllowedToBid: this.isAllowedToBid,
|
||||||
|
isOrganizationEmployee: this.isOrganizationEmployee,
|
||||||
|
generatedNomDeBid: !hasNomDeBid,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
UserSchema.methods.validatePassword = function (password) {
|
||||||
|
const strategy = this.getAuthStrategy('local');
|
||||||
|
|
||||||
|
if (strategy) {
|
||||||
|
let hash = crypto.pbkdf2Sync(password, strategy.secret, 10000, 512, 'sha512').toString('hex');
|
||||||
|
return strategy.accessToken === hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* STATICS
|
||||||
|
*/
|
||||||
|
UserSchema.statics.findOrCreate = function (filter = {}, profile = {}, callback = () => {}) {
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
this.findOne(filter, function(err,result) {
|
||||||
|
if (err) {
|
||||||
|
callback(err, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
self.create(profile, (err, result) => callback(err, result));
|
||||||
|
}else{
|
||||||
|
callback(err, result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
UserSchema.statics.findOneAndUpdateOrCreate = function (
|
||||||
|
filter = {},
|
||||||
|
strategy = {},
|
||||||
|
profile = {},
|
||||||
|
callback = () => {},
|
||||||
|
) {
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
this.findOne(filter, function(err, result) {
|
||||||
|
if (err) {
|
||||||
|
callback(err, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
self.create(
|
||||||
|
{
|
||||||
|
strategy: [ strategy ],
|
||||||
|
...profile
|
||||||
|
},
|
||||||
|
(err, result) => callback(err, result),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const hasStrategy = !!result.credentials.length &&
|
||||||
|
!!result.credentials.filter(auth => auth.method === strategy.method).length;
|
||||||
|
|
||||||
|
if (hasStrategy) {
|
||||||
|
self.model('User').findOneAndUpdate(
|
||||||
|
{ _id: result._id, 'credentials.method': strategy.method },
|
||||||
|
{ $set: { 'credentials.$': strategy } },
|
||||||
|
{ upsert: true },
|
||||||
|
callback,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
result.credentials.push(strategy);
|
||||||
|
result.save(callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PATH OPERATIONS
|
||||||
|
*/
|
||||||
|
UserSchema.path('avatar').get(v => `${config.assetStoreUrl}${v}`);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export
|
||||||
|
*/
|
||||||
|
const User = mongoose.model('User', UserSchema);
|
||||||
|
|
||||||
|
module.exports = User;
|
||||||
28
package.json
Normal file
28
package.json
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"name": "api",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "The wEvent API and DB server",
|
||||||
|
"main": "index.js",
|
||||||
|
"author": "mike@fitz.guru",
|
||||||
|
"license": "MIT",
|
||||||
|
"notes": "b'VJ!4a{L(8T(CvG8BKGj).]",
|
||||||
|
"dependencies": {
|
||||||
|
"api-query-params": "^4.13.0",
|
||||||
|
"crypto": "^1.0.1",
|
||||||
|
"jsonwebtoken": "^8.5.1",
|
||||||
|
"mongoose": "^5.6.0",
|
||||||
|
"mongoose-find-or-create": "^1.3.1",
|
||||||
|
"mongoose-string-query": "^0.2.7",
|
||||||
|
"mongoose-timestamp": "^0.6.0",
|
||||||
|
"passport": "^0.4.0",
|
||||||
|
"passport-facebook": "^3.0.0",
|
||||||
|
"passport-google-oauth": "^2.0.0",
|
||||||
|
"passport-http-bearer": "^1.0.1",
|
||||||
|
"passport-jwt": "^4.0.0",
|
||||||
|
"passport-local": "^1.0.0",
|
||||||
|
"restify": "^8.3.3",
|
||||||
|
"restify-errors": "^8.0.0",
|
||||||
|
"restify-jwt-community": "^1.0.13",
|
||||||
|
"restify-plugins": "^1.6.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
92
routes/auth.js
Normal file
92
routes/auth.js
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
const errors = require('restify-errors');
|
||||||
|
|
||||||
|
const config = require('../config');
|
||||||
|
|
||||||
|
const handlePassportResponse = (req, res, next) => (err, passportUser, info) => {
|
||||||
|
if (err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
const isVerifiedUser = passportUser.isRegistrationVerified();
|
||||||
|
if (passportUser && isVerifiedUser) {
|
||||||
|
const user = passportUser;
|
||||||
|
user.token = passportUser.generateJWT();
|
||||||
|
return res.send({ ...user.toAuthJSON() });
|
||||||
|
} else if (passportUser && !isVerifiedUser){
|
||||||
|
return res.send({
|
||||||
|
registrationSuccess: true,
|
||||||
|
nextSteps: 'Check your email for our confirmation email, you will not be able to login without confirming.'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.send(400, info);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = function (server, auth) {
|
||||||
|
const { passport } = auth;
|
||||||
|
|
||||||
|
/* Local Auth */
|
||||||
|
server.post('/auth', (req, res, next) => {
|
||||||
|
const { body: { username = null, password = null } = {} } = req;
|
||||||
|
|
||||||
|
if (!username || !password) {
|
||||||
|
let errors = {};
|
||||||
|
|
||||||
|
if (!username) {
|
||||||
|
errors.username = 'is required';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!password) {
|
||||||
|
errors.password = 'is required';
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.send(422, { errors });
|
||||||
|
}
|
||||||
|
|
||||||
|
const callback = handlePassportResponse(req, res, next);
|
||||||
|
return passport.authenticate('local', { session: false }, callback)(req, res, next);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SERVICES
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Google */
|
||||||
|
server.get(
|
||||||
|
'/auth/google',
|
||||||
|
passport.authenticate('google', { scope: 'profile email', session: false }),
|
||||||
|
);
|
||||||
|
|
||||||
|
server.get(
|
||||||
|
'/auth/google/callback',
|
||||||
|
(req, res, next) => {
|
||||||
|
const callback = handlePassportResponse(req, res, next);
|
||||||
|
return passport.authenticate(
|
||||||
|
'google',
|
||||||
|
{ failureRedirect: '/login' },
|
||||||
|
callback,
|
||||||
|
)(req, res, next);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Facebook */
|
||||||
|
server.get(
|
||||||
|
'/auth/facebook',
|
||||||
|
passport.authenticate('facebook', {
|
||||||
|
scope: ['email', 'public_profile'],
|
||||||
|
session: false,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
server.get(
|
||||||
|
'/auth/facebook/callback',
|
||||||
|
(req, res, next) => {
|
||||||
|
const callback = handlePassportResponse(req, res, next);
|
||||||
|
return passport.authenticate(
|
||||||
|
'facebook',
|
||||||
|
{ failureRedirect: '/login' },
|
||||||
|
callback,
|
||||||
|
)(req, res, next);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
110
routes/bids.js
Normal file
110
routes/bids.js
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
const errors = require('restify-errors');
|
||||||
|
|
||||||
|
const Bid = require('../models/bid');
|
||||||
|
|
||||||
|
module.exports = function(server) {
|
||||||
|
server.post('/bids', (req, res, next) => {
|
||||||
|
if (!req.is('application/json')) {
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError("Expects 'application/json'"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = req.body || {};
|
||||||
|
|
||||||
|
let bid = new Bid(data);
|
||||||
|
bid.save(function(err) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(new errors.InternalError(err.message));
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(201);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.get('/bids', (req, res, next) => {
|
||||||
|
Bid.apiQuery(req.params, function(err, docs) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError(err.errors.name.message),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(docs);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.get('/bids/:bid_id', (req, res, next) => {
|
||||||
|
Bid.findOne({ _id: req.params.bid_id }, function(err, doc) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError(err.errors.name.message),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(doc);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.put('/bids/:bid_id', (req, res, next) => {
|
||||||
|
if (!req.is('application/json')) {
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError("Expects 'application/json'"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = req.body || {};
|
||||||
|
|
||||||
|
if (!data._id) {
|
||||||
|
data = Object.assign({}, data, { _id: req.params.bid_id });
|
||||||
|
}
|
||||||
|
|
||||||
|
Bid.findOne({ _id: req.params.bid_id }, function(err, doc) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError(err.errors.name.message),
|
||||||
|
);
|
||||||
|
} else if (!doc) {
|
||||||
|
return next(
|
||||||
|
new errors.ResourceNotFoundError(
|
||||||
|
'The resource you requested could not be found.',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Bid.update({ _id: data._id }, data, function(err) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError(err.errors.name.message),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(200, data);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.del('/bids/:bid_id', (req, res, next) => {
|
||||||
|
Bid.deleteOne({ _id: req.params.bid_id }, function(err) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError(err.errors.name.message),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(204);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
110
routes/events.js
Normal file
110
routes/events.js
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
const errors = require('restify-errors');
|
||||||
|
|
||||||
|
const Event = require('../models/event');
|
||||||
|
|
||||||
|
module.exports = function(server) {
|
||||||
|
server.post('/events', (req, res, next) => {
|
||||||
|
if (!req.is('application/json')) {
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError("Expects 'application/json'"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = req.body || {};
|
||||||
|
|
||||||
|
let event = new Event(data);
|
||||||
|
event.save(function(err) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(new errors.InternalError(err.message));
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(201);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.get('/events', (req, res, next) => {
|
||||||
|
Event.apiQuery(req.params, function(err, docs) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError(err.errors.name.message),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(docs);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.get('/events/:event_id', (req, res, next) => {
|
||||||
|
Event.findOne({ _id: req.params.event_id }, function(err, doc) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError(err.errors.name.message),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(doc);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.put('/events/:event_id', (req, res, next) => {
|
||||||
|
if (!req.is('application/json')) {
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError("Expects 'application/json'"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = req.body || {};
|
||||||
|
|
||||||
|
if (!data._id) {
|
||||||
|
data = Object.assign({}, data, { _id: req.params.event_id });
|
||||||
|
}
|
||||||
|
|
||||||
|
Event.findOne({ _id: req.params.event_id }, function(err, doc) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError(err.errors.name.message),
|
||||||
|
);
|
||||||
|
} else if (!doc) {
|
||||||
|
return next(
|
||||||
|
new errors.ResourceNotFoundError(
|
||||||
|
'The resource you requested could not be found.',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Event.update({ _id: data._id }, data, function(err) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError(err.errors.name.message),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(200, data);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.del('/events/:event_id', (req, res, next) => {
|
||||||
|
Event.deleteOne({ _id: req.params.event_id }, function(err) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError(err.errors.name.message),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(204);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
9
routes/index.js
Normal file
9
routes/index.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
module.exports = function(server, auth) {
|
||||||
|
require('./auth.js')(server, auth);
|
||||||
|
require('./bids.js')(server, auth);
|
||||||
|
require('./events.js')(server, auth);
|
||||||
|
require('./installs.js')(server, auth);
|
||||||
|
require('./items.js')(server, auth);
|
||||||
|
require('./sales.js')(server, auth);
|
||||||
|
require('./users.js')(server, auth);
|
||||||
|
};
|
||||||
110
routes/installs.js
Normal file
110
routes/installs.js
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
const errors = require('restify-errors');
|
||||||
|
|
||||||
|
const Install = require('../models/install');
|
||||||
|
|
||||||
|
module.exports = function(server) {
|
||||||
|
server.post('/installs', (req, res, next) => {
|
||||||
|
if (!req.is('application/json')) {
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError("Expects 'application/json'"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = req.body || {};
|
||||||
|
|
||||||
|
let install = new Install(data);
|
||||||
|
install.save(function(err) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(new errors.InternalError(err.message));
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(201);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.get('/installs', (req, res, next) => {
|
||||||
|
Install.apiQuery(req.params, function(err, docs) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError(err.errors.name.message),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(docs);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.get('/installs/:install_id', (req, res, next) => {
|
||||||
|
Install.findOne({ _id: req.params.install_id }, function(err, doc) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError(err.errors.name.message),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(doc);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.put('/installs/:install_id', (req, res, next) => {
|
||||||
|
if (!req.is('application/json')) {
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError("Expects 'application/json'"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = req.body || {};
|
||||||
|
|
||||||
|
if (!data._id) {
|
||||||
|
data = Object.assign({}, data, { _id: req.params.install_id });
|
||||||
|
}
|
||||||
|
|
||||||
|
Install.findOne({ _id: req.params.install_id }, function(err, doc) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError(err.errors.name.message),
|
||||||
|
);
|
||||||
|
} else if (!doc) {
|
||||||
|
return next(
|
||||||
|
new errors.ResourceNotFoundError(
|
||||||
|
'The resource you requested could not be found.',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Install.update({ _id: data._id }, data, function(err) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError(err.errors.name.message),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(200, data);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.del('/installs/:install_id', (req, res, next) => {
|
||||||
|
Install.deleteOne({ _id: req.params.install_id }, function(err) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError(err.errors.name.message),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(204);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
110
routes/items.js
Normal file
110
routes/items.js
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
const errors = require('restify-errors');
|
||||||
|
|
||||||
|
const Item = require('../models/item');
|
||||||
|
|
||||||
|
module.exports = function(server) {
|
||||||
|
server.post('/items', (req, res, next) => {
|
||||||
|
if (!req.is('application/json')) {
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError("Expects 'application/json'"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = req.body || {};
|
||||||
|
|
||||||
|
let item = new Item(data);
|
||||||
|
item.save(function(err) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(new errors.InternalError(err.message));
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(201);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.get('/items', (req, res, next) => {
|
||||||
|
Item.apiQuery(req.params, function(err, docs) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError(err.errors.name.message),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(docs);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.get('/items/:item_id', (req, res, next) => {
|
||||||
|
Item.findOne({ _id: req.params.item_id }, function(err, doc) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError(err.errors.name.message),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(doc);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.put('/items/:item_id', (req, res, next) => {
|
||||||
|
if (!req.is('application/json')) {
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError("Expects 'application/json'"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = req.body || {};
|
||||||
|
|
||||||
|
if (!data._id) {
|
||||||
|
data = Object.assign({}, data, { _id: req.params.item_id });
|
||||||
|
}
|
||||||
|
|
||||||
|
Item.findOne({ _id: req.params.item_id }, function(err, doc) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError(err.errors.name.message),
|
||||||
|
);
|
||||||
|
} else if (!doc) {
|
||||||
|
return next(
|
||||||
|
new errors.ResourceNotFoundError(
|
||||||
|
'The resource you requested could not be found.',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Item.update({ _id: data._id }, data, function(err) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError(err.errors.name.message),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(200, data);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.del('/items/:item_id', (req, res, next) => {
|
||||||
|
Item.deleteOne({ _id: req.params.item_id }, function(err) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError(err.errors.name.message),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(204);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
116
routes/sales.js
Normal file
116
routes/sales.js
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
/**
|
||||||
|
* Module Dependencies
|
||||||
|
*/
|
||||||
|
const errors = require('restify-errors');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model Schema
|
||||||
|
*/
|
||||||
|
const Sale = require('../models/sale');
|
||||||
|
|
||||||
|
module.exports = function(server) {
|
||||||
|
server.post('/sales', (req, res, next) => {
|
||||||
|
if (!req.is('application/json')) {
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError("Expects 'application/json'"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = req.body || {};
|
||||||
|
|
||||||
|
let sale = new Sale(data);
|
||||||
|
sale.save(function(err) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(new errors.InternalError(err.message));
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(201);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.get('/sales', (req, res, next) => {
|
||||||
|
Sale.apiQuery(req.params, function(err, docs) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError(err.errors.name.message),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(docs);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.get('/sales/:sale_id', (req, res, next) => {
|
||||||
|
Sale.findOne({ _id: req.params.sale_id }, function(err, doc) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError(err.errors.name.message),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(doc);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.put('/sales/:sale_id', (req, res, next) => {
|
||||||
|
if (!req.is('application/json')) {
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError("Expects 'application/json'"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = req.body || {};
|
||||||
|
|
||||||
|
if (!data._id) {
|
||||||
|
data = Object.assign({}, data, { _id: req.params.sale_id });
|
||||||
|
}
|
||||||
|
|
||||||
|
Sale.findOne({ _id: req.params.sale_id }, function(err, doc) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError(err.errors.name.message),
|
||||||
|
);
|
||||||
|
} else if (!doc) {
|
||||||
|
return next(
|
||||||
|
new errors.ResourceNotFoundError(
|
||||||
|
'The resource you requested could not be found.',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Sale.update({ _id: data._id }, data, function(err) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError(err.errors.name.message),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(200, data);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.del('/sales/:sale_id', (req, res, next) => {
|
||||||
|
Sale.deleteOne({ _id: req.params.sale_id }, function(err) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError(err.errors.name.message),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(204);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
42
routes/signup.js
Normal file
42
routes/signup.js
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
const errors = require('restify-errors');
|
||||||
|
|
||||||
|
const User = require('../models/user');
|
||||||
|
|
||||||
|
module.exports = function (server, auth) {
|
||||||
|
const { passport } = auth;
|
||||||
|
|
||||||
|
server.post('/signup', (req, res, next) => {
|
||||||
|
const { body: { user = null } = {} } = req;
|
||||||
|
|
||||||
|
let errors = {};
|
||||||
|
let errorCount = 0;
|
||||||
|
if (!user) {
|
||||||
|
errors.user = 'is required - can\'t make something from nothing...'';
|
||||||
|
errorCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorCount) {
|
||||||
|
return res.send(422, { errors });
|
||||||
|
}
|
||||||
|
|
||||||
|
User.register(user, (err, user, info) => {
|
||||||
|
if (err) {
|
||||||
|
next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info) {
|
||||||
|
res.send(200, {
|
||||||
|
registrationSuccess: false,
|
||||||
|
nextSteps: 'Please fix the problems indicated and try again.'
|
||||||
|
...info
|
||||||
|
});
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(200, {
|
||||||
|
registrationSuccess: true,
|
||||||
|
nextSteps: 'Check your email for our confirmation email, you will not be able to login without confirming.'
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
169
routes/users.js
Normal file
169
routes/users.js
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
const aqp = require('api-query-params');
|
||||||
|
const errors = require('restify-errors');
|
||||||
|
|
||||||
|
const User = require('../models/user');
|
||||||
|
const { PUBLIC, PROTECTED } = require('../strategies/selects/user');
|
||||||
|
|
||||||
|
module.exports = function (server, auth) {
|
||||||
|
server.post('/users', auth.manager, (req, res, next) => {
|
||||||
|
let { password = null, ...data } = req.body || {};
|
||||||
|
|
||||||
|
let user = new User(data);
|
||||||
|
user.save(function(err) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(new errors.InternalError(err.message));
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (password) {
|
||||||
|
user.setPassword(password);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(201);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.get('/users', auth.manager, (req, res, next) => {
|
||||||
|
const { filter } = aqp(req.query);
|
||||||
|
const select = req.user.isManager ? PROTECTED : PUBLIC;
|
||||||
|
|
||||||
|
User.find(filter, select, function (err, docs) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError(err),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(docs);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.get('/users/:user_id', auth.managerOrSelf, (req, res, next) => {
|
||||||
|
const select = req.user.isManager ? PROTECTED : PUBLIC;
|
||||||
|
|
||||||
|
User.findOne({ _id: req.params.user_id }, select, function (err, doc) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError(err),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(doc);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.put('/users/:user_id', auth.managerOrSelf, (req, res, next) => {
|
||||||
|
let data = req.body || {};
|
||||||
|
|
||||||
|
if (!data._id) {
|
||||||
|
data = Object.assign({}, data, { _id: req.params.user_id });
|
||||||
|
}
|
||||||
|
|
||||||
|
User.findOne({ _id: req.params.user_id }, function (err, doc) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError(err),
|
||||||
|
);
|
||||||
|
} else if (!doc) {
|
||||||
|
return next(
|
||||||
|
new errors.ResourceNotFoundError(
|
||||||
|
'The resource you requested could not be found.',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
User.update({ _id: data._id }, data, function (err) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError(err),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(200, data);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.put('/users/password/:user_id/:reset_token?', function (req, res, next) {
|
||||||
|
let {
|
||||||
|
currentPassword = null,
|
||||||
|
newPassword = null,
|
||||||
|
...data
|
||||||
|
} = req.body || {};
|
||||||
|
|
||||||
|
if (!newPassword) {
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError('Password cannot be empty.'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let filter = { _id: req.params.user_id };
|
||||||
|
let resetToken = req.params.reset_token || null;
|
||||||
|
if (resetToken) {
|
||||||
|
fiter.resetToken = resetToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
User.findOne(filter, function (err, user) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError(err),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return next(
|
||||||
|
new errors.ResourceNotFoundError(
|
||||||
|
'The user you requested could not be found.',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!resetToken &&
|
||||||
|
!!user.getAuthStrategy('local') &&
|
||||||
|
!user.validatePassword(currentPassword)
|
||||||
|
) {
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError(
|
||||||
|
'The current password was incorrect.',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
user.setPassword(newPassword, function (err) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError(err),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(200, data);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.del('/users/:user_id', auth.manager, (req, res, next) => {
|
||||||
|
User.deleteOne({ _id: req.params.user_id }, function (err) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return next(
|
||||||
|
new errors.InvalidContentError(err),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(204);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
5
strategies/auth/apple.js
Normal file
5
strategies/auth/apple.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
const passport = require('passport');
|
||||||
|
|
||||||
|
module.exports = function(passport) {
|
||||||
|
return passport;
|
||||||
|
};
|
||||||
46
strategies/auth/facebook.js
Normal file
46
strategies/auth/facebook.js
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
const passport = require('passport');
|
||||||
|
const FacebookStrategy = require('passport-facebook').Strategy;
|
||||||
|
|
||||||
|
const config = require('../../config');
|
||||||
|
const User = require('../../models/user');
|
||||||
|
|
||||||
|
module.exports = function(passport) {
|
||||||
|
passport.use(new FacebookStrategy(
|
||||||
|
{
|
||||||
|
clientID: config.services.facebook.appId,
|
||||||
|
clientSecret: config.services.facebook.appSecret,
|
||||||
|
callbackURL: 'http://localhost:3001/auth/facebook/callback',
|
||||||
|
profileFields: ['id', 'email', 'first_name', 'last_name', 'picture'],
|
||||||
|
},
|
||||||
|
(accessToken, refreshToken, profile, done) => {
|
||||||
|
const {
|
||||||
|
email,
|
||||||
|
first_name: firstName,
|
||||||
|
id: userId,
|
||||||
|
last_name: lastName,
|
||||||
|
picture: { data: { url = null } = {} } = {},
|
||||||
|
} = profile._json;
|
||||||
|
const avatar = url;
|
||||||
|
|
||||||
|
User.findOneAndUpdateOrCreate(
|
||||||
|
{
|
||||||
|
email,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessToken,
|
||||||
|
method: profile.provider,
|
||||||
|
userId,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
avatar,
|
||||||
|
email,
|
||||||
|
firstName,
|
||||||
|
lastName,
|
||||||
|
},
|
||||||
|
(err, user) => {
|
||||||
|
return done(err, user, { accessToken, refreshToken });
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
));
|
||||||
|
};
|
||||||
40
strategies/auth/google.js
Normal file
40
strategies/auth/google.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
const passport = require('passport');
|
||||||
|
const GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
|
||||||
|
|
||||||
|
const config = require('../../config');
|
||||||
|
const User = require('../../models/user');
|
||||||
|
|
||||||
|
module.exports = function(passport) {
|
||||||
|
passport.use(new GoogleStrategy({
|
||||||
|
clientID: config.services.google.appId,
|
||||||
|
clientSecret: config.services.google.appSecret,
|
||||||
|
callbackURL: "http://www.example.com/auth/google/callback",
|
||||||
|
},
|
||||||
|
(accessToken, refreshToken, profile, callback) => {
|
||||||
|
const googleUser = profile.getBasicProfile();
|
||||||
|
|
||||||
|
User.findOrCreate(
|
||||||
|
{
|
||||||
|
email: googleUser.getEmail(),
|
||||||
|
'credentials.method': 'google',
|
||||||
|
'credentials.userId': googleUser.getId(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
avatar: googleUser.getImageUrl(),
|
||||||
|
email: googleUser.getEmail(),
|
||||||
|
firstName: googleUser.getGivenName(),
|
||||||
|
lastName: googleUser.getFamilyName(),
|
||||||
|
credentials: [{
|
||||||
|
accessToken,
|
||||||
|
userId: googleUser.getId(),
|
||||||
|
method: 'facebook',
|
||||||
|
profile,
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
(err, user) => {
|
||||||
|
return done(err, user, { accessToken, refreshToken });
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
));
|
||||||
|
};
|
||||||
82
strategies/auth/index.js
Normal file
82
strategies/auth/index.js
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
const createRequestUserObject = (req, user) => ({
|
||||||
|
isGuest: !(user && user.id),
|
||||||
|
isManager: user && user.isEventManager(),
|
||||||
|
isSelf: user && user.id === req.params.user_id,
|
||||||
|
record: user || null,
|
||||||
|
});
|
||||||
|
|
||||||
|
const authenticateBasic = (passport) => (req, res, next) => (
|
||||||
|
passport.authenticate('jwt', { session: false }, (err, user, info) => {
|
||||||
|
if (err) {
|
||||||
|
next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
req.user = createRequestUserObject(req, user);
|
||||||
|
next();
|
||||||
|
})(req, res, next)
|
||||||
|
);
|
||||||
|
|
||||||
|
const authenticateEventManager = (passport) => (req, res, next) => (
|
||||||
|
passport.authenticate('jwt', { session: false }, (err, user, info) => {
|
||||||
|
if (err) {
|
||||||
|
next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
const record = createRequestUserObject(req, user);
|
||||||
|
|
||||||
|
if (!user || !record.isManager) {
|
||||||
|
return res.send(401);
|
||||||
|
}
|
||||||
|
|
||||||
|
req.user = record;
|
||||||
|
next();
|
||||||
|
})(req, res, next)
|
||||||
|
);
|
||||||
|
|
||||||
|
const authenticateEventManagerOrSelf = (passport) => (req, res, next) => (
|
||||||
|
passport.authenticate('jwt', { session: false }, (err, user, info) => {
|
||||||
|
if (err) {
|
||||||
|
next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
const record = createRequestUserObject(req, user);
|
||||||
|
|
||||||
|
if (user && (!record.isManager && !record.isSelf)) {
|
||||||
|
return res.send(401);
|
||||||
|
}
|
||||||
|
|
||||||
|
req.user = record;
|
||||||
|
next();
|
||||||
|
})(req, res, next)
|
||||||
|
);
|
||||||
|
|
||||||
|
const authenticateSecure = (passport) => (req, res, next) => (
|
||||||
|
passport.authenticate('jwt', { session: false }, (err, user, info) => {
|
||||||
|
if (err) {
|
||||||
|
next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return res.send(401);
|
||||||
|
}
|
||||||
|
|
||||||
|
req.user = createRequestUserObject(req, user);
|
||||||
|
next();
|
||||||
|
})(req, res, next)
|
||||||
|
);
|
||||||
|
|
||||||
|
module.exports = function (passport) {
|
||||||
|
require('./apple.js')(passport);
|
||||||
|
require('./facebook.js')(passport);
|
||||||
|
require('./google.js')(passport);
|
||||||
|
require('./jwt.js')(passport);
|
||||||
|
require('./local.js')(passport);
|
||||||
|
|
||||||
|
return {
|
||||||
|
basic: authenticateBasic(passport),
|
||||||
|
manager: authenticateEventManager(passport),
|
||||||
|
managerOrSelf: authenticateEventManagerOrSelf(passport),
|
||||||
|
passport,
|
||||||
|
secure: authenticateSecure(passport),
|
||||||
|
};
|
||||||
|
};
|
||||||
30
strategies/auth/jwt.js
Normal file
30
strategies/auth/jwt.js
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
const passport = require('passport');
|
||||||
|
const JwtStrategy = require('passport-jwt').Strategy;
|
||||||
|
const ExtractJwt = require('passport-jwt').ExtractJwt;
|
||||||
|
|
||||||
|
const config = require('../../config');
|
||||||
|
const User = require('../../models/user');
|
||||||
|
|
||||||
|
module.exports = function(passport) {
|
||||||
|
passport.use(new JwtStrategy(
|
||||||
|
{
|
||||||
|
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||||
|
secretOrKey: config.security.jwt.secret,
|
||||||
|
issuer: config.security.jwt.issuer,
|
||||||
|
audience: config.security.jwt.audience,
|
||||||
|
},
|
||||||
|
(jwt_payload, done) => {
|
||||||
|
User.findOne({ _id: jwt_payload.sub }, (err, user) => {
|
||||||
|
if (err) {
|
||||||
|
return done(err, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
return done(null, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
return done(null, false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
24
strategies/auth/local.js
Normal file
24
strategies/auth/local.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
const passport = require('passport');
|
||||||
|
const LocalStrategy = require('passport-local').Strategy;
|
||||||
|
|
||||||
|
const User = require('../../models/user');
|
||||||
|
|
||||||
|
module.exports = function(passport) {
|
||||||
|
passport.use(new LocalStrategy(
|
||||||
|
{
|
||||||
|
usernameField: 'username',
|
||||||
|
passwordField: 'password',
|
||||||
|
},
|
||||||
|
(username, password, done) => {
|
||||||
|
User.findOne({ email: username }, (err, user) => {
|
||||||
|
if (err) { return done(err); }
|
||||||
|
|
||||||
|
if (!user || !user.validatePassword(password)) {
|
||||||
|
return done(null, false, { message: 'Incorrect username or password.' });
|
||||||
|
}
|
||||||
|
|
||||||
|
return done(null, user);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
));
|
||||||
|
};
|
||||||
10
strategies/selects/user.js
Normal file
10
strategies/selects/user.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
module.exports = {
|
||||||
|
PUBLIC: {
|
||||||
|
credentials: 0,
|
||||||
|
isVerified: 0,
|
||||||
|
organizationIdentifier: 0,
|
||||||
|
paymentToken: 0,
|
||||||
|
},
|
||||||
|
PROTECTED: {},
|
||||||
|
};
|
||||||
|
|
||||||
Reference in New Issue
Block a user