summaryrefslogtreecommitdiff
path: root/server/lib
diff options
context:
space:
mode:
authorJules Laplace <jules@okfoc.us>2016-10-28 18:07:56 -0400
committerJules Laplace <jules@okfoc.us>2016-10-28 18:07:56 -0400
commita9c9d6adf470d0966e6c6bef0803e298fd2d4117 (patch)
tree6ccec2a448992a5f43226532051a6df09afbc203 /server/lib
parent343b0b3dc5bb7dbe762182a486e63a4aff6ef8fc (diff)
parent9e7bacd46c1e5d0e1c24433690d421ab3f3a11f2 (diff)
merge
Diffstat (limited to 'server/lib')
-rw-r--r--server/lib/api/blueprint.js142
-rw-r--r--server/lib/api/index.js2
-rw-r--r--server/lib/api/layouts.js44
-rw-r--r--server/lib/api/profile.js1
-rw-r--r--server/lib/api/projects.js18
-rw-r--r--server/lib/api/rooms.js139
-rw-r--r--server/lib/api/subscription.js206
-rw-r--r--server/lib/auth/index.js15
-rw-r--r--server/lib/auth/mail.js2
-rw-r--r--server/lib/middleware.js84
-rw-r--r--server/lib/schemas/Blueprint.js66
-rw-r--r--server/lib/schemas/Layout.js3
-rw-r--r--server/lib/schemas/Media.js7
-rw-r--r--server/lib/schemas/Plan.js44
-rw-r--r--server/lib/schemas/Project.js4
-rw-r--r--server/lib/schemas/Subscription.js31
-rw-r--r--server/lib/schemas/User.js9
-rw-r--r--server/lib/util.js4
-rw-r--r--server/lib/views/index.js56
-rw-r--r--server/lib/views/staff.js545
-rw-r--r--server/lib/views/staff/defaults.js6
-rw-r--r--server/lib/views/staff/fields.js10
-rw-r--r--server/lib/views/staff/helpers.js59
-rw-r--r--server/lib/views/staff/index.js511
-rw-r--r--server/lib/views/staff/middleware.js476
-rw-r--r--server/lib/webhook/index.js44
-rw-r--r--server/lib/webhook/recurly-config.js6
-rw-r--r--server/lib/webhook/webhook.js178
28 files changed, 2088 insertions, 624 deletions
diff --git a/server/lib/api/blueprint.js b/server/lib/api/blueprint.js
new file mode 100644
index 0000000..e780b92
--- /dev/null
+++ b/server/lib/api/blueprint.js
@@ -0,0 +1,142 @@
+/* jshint node: true */
+
+var _ = require('lodash'),
+ crypto = require('crypto'),
+ util = require('../util'),
+ upload = require('../upload'),
+ config = require('../../../config.json'),
+ Blueprint = require('../schemas/Blueprint');
+
+var blueprint = {
+
+ user: function(req, res){
+ var offset = Number(req.query.offset) || 0
+ var limit = Math.min( Number(req.query.limit), 50 ) || 20
+ var query = { user_id: req.user._id }
+ if (req.query.tag) {
+ query.tag = req.query.tag
+ }
+ Blueprint.find(query)
+ .sort({'created_at': -1})
+ .skip(offset)
+ .limit(limit)
+ .exec(function(err, media){
+ res.json(media || [])
+ })
+ },
+
+ show: function(req, res){
+ Blueprint.findOne({ slug: req.params.slug }, function(err, doc){
+ if (doc) {
+ res.json(doc)
+ return
+ }
+ else {
+ Project.count({}, function(err, count){
+ var name = "Project #" + (count || 0)
+ res.json({ _id: "new", name: name, isNew: true })
+ })
+ }
+ })
+ },
+
+ create: function(req, res){
+ var data = util.cleanQuery(req.body)
+ data.user_id = req.user._id
+ data.created_at = new Date ()
+
+ if (data.tag) {
+ data.tag = util.sanitize(data.tag)
+ }
+
+ new Blueprint(data).save(function(err, rec){
+ if (err || ! rec) { return res.json({ error: err }) }
+ return res.json(rec)
+ })
+ },
+
+ upload: function(req, res){
+ var data = util.cleanQuery(req.body)
+ data.user_id = req.user._id
+ data.created_at = new Date ()
+ data.type = "image"
+
+ upload.put("media", req.files.image, {
+ username: req.user.username,
+ unacceptable: function(err){
+ res.json({ error: { errors: { media: { message: "Problem saving image: " + err } } } })
+ },
+ success: function(url){
+ data.url = url
+ done()
+ }
+ })
+
+ function done () {
+ new Blueprint(data).save(function(err, rec) {
+ if (err || ! rec) { return res.json({ error: err }) }
+ res.json(rec)
+ })
+ }
+ },
+
+ scale: function(req, res){
+ var _id = req.body._id
+ var data = util.cleanQuery(req.body)
+ if (! _id) { return res.json({ error: 404 }) }
+ Blueprint.findOne({ _id: _id }, function(err, doc){
+ if (! doc) { return res.json({ error: 404 }) }
+ if (String(doc.user_id) !== String(req.user._id)) { return res.json({ error: 404 }) }
+ doc.scale = data.scale
+ doc.units = data.units
+ doc.line = data.line
+ doc.save(function(err, rec){
+ if (err || ! rec) { return res.json({ error: err }) }
+ res.json(rec)
+ })
+ })
+ },
+
+ update: function(req, res){
+ var _id = req.body._id
+ var data = util.cleanQuery(req.body)
+ if (! _id) { return res.json({ error: 404 }) }
+ Blueprint.findOne({ _id: _id }, function(err, doc){
+ if (! doc) { return res.json({ error: 404 }) }
+ if (String(doc.user_id) !== String(req.user._id)) { return res.json({ error: 404 }) }
+
+ doc.name = util.sanitize(data.name)
+ doc.slug = util.slugify(data.name)
+ doc.units = util.sanitize(data.units)
+ doc.viewHeight = util.sanitizeNumber(data.viewHeight)
+ doc.wallHeight = util.sanitizeNumber(data.wallHeight)
+ doc.shapes = JSON.parse(data.shapes)
+ doc.startPosition = JSON.parse(data.startPosition)
+
+ doc.save(function(err, rec){
+ if (err || ! rec) { return res.json({ error: err }) }
+ res.json(rec)
+ })
+ })
+ },
+
+ destroy: function(req, res){
+ var _id = util.sanitize(req.body._id)
+ if (! _id || ! _id.length) {
+ res.json({ error: 404 })
+ return
+ }
+ Blueprint.findOne({ _id: _id }, function(err, doc){
+ if (! doc) { return res.json({ error: 404 }) }
+ if (String(doc.user_id) !== String(req.user._id)) {
+ return res.json({ error: "access denied" })
+ }
+ Blueprint.remove({ _id: _id }, function(err){
+ res.json({ status: "OK" })
+ })
+ })
+ }
+
+}
+
+module.exports = blueprint
diff --git a/server/lib/api/index.js b/server/lib/api/index.js
index 11e13fc..8254232 100644
--- a/server/lib/api/index.js
+++ b/server/lib/api/index.js
@@ -1,6 +1,7 @@
/* jshint node: true */
var api = {
+ blueprint: require('./blueprint'),
docs: require('./docs'),
layouts: require('./layouts'),
media: require('./media'),
@@ -8,6 +9,7 @@ var api = {
projects: require('./projects'),
rooms: require('./rooms'),
collaborator: require('./collaborator'),
+ subscription: require('./subscription'),
}
module.exports = api
diff --git a/server/lib/api/layouts.js b/server/lib/api/layouts.js
index 641e9e2..2c68f71 100644
--- a/server/lib/api/layouts.js
+++ b/server/lib/api/layouts.js
@@ -3,13 +3,26 @@
var _ = require('lodash'),
util = require('../util'),
upload = require('../upload'),
+ middleware = require('../middleware'),
config = require('../../../config.json'),
+ Blueprint = require('../schemas/Blueprint'),
Layout = require('../schemas/Layout');
var layouts = {
index: function(req, res){
- Layout.find({}, function(err, docs){
- res.json(docs)
+ Layout.find({ is_stock: true }, function(err, stock_layouts){
+ Layout.find({ user_id: req.user._id, is_stock: false }, function(err, user_layouts){
+ Blueprint.find({ user_id: req.user._id }, function(err, blueprints){
+ res.json({
+ layouts: stock_layouts,
+ user_layouts: user_layouts,
+ blueprints: blueprints,
+ user: res.locals.user,
+ layoutCount: res.locals.layoutCount,
+ projectCount: res.locals.projectCount,
+ })
+ })
+ })
})
},
@@ -37,15 +50,24 @@ var layouts = {
data.rooms = JSON.parse(data.rooms)
data.startPosition = JSON.parse(data.startPosition)
- upload.put("layouts", req.files.thumbnail, {
- unacceptable: function(err){
- res.json({ error: { errors: { thumbnail: { message: "Problem saving thumbnail: " + err } } } })
- },
- success: function(url){
- data.photo = url
- done()
- }
- })
+ Layout.findOne({ slug: data.slug }, function(err, doc){
+ if (! err || doc) {
+ data.slug = data.slug + "-" + Date.now()
+ }
+ do_upload()
+ })
+
+ function do_upload () {
+ upload.put("layouts", req.files.thumbnail, {
+ unacceptable: function(err){
+ res.json({ error: { errors: { thumbnail: { message: "Problem saving thumbnail: " + err } } } })
+ },
+ success: function(url){
+ data.photo = url
+ done()
+ }
+ })
+ }
function done() {
new Layout(data).save(function(err, doc){
diff --git a/server/lib/api/profile.js b/server/lib/api/profile.js
index 996505f..d72a2c3 100644
--- a/server/lib/api/profile.js
+++ b/server/lib/api/profile.js
@@ -32,6 +32,7 @@ var profile = {
delete data.old_password
delete data.new_password
delete data.isStaff
+ delete data.plan_level
data.updated_at = new Date ()
if (req.files.avatar) {
diff --git a/server/lib/api/projects.js b/server/lib/api/projects.js
index 1bf046f..50d3b49 100644
--- a/server/lib/api/projects.js
+++ b/server/lib/api/projects.js
@@ -37,9 +37,15 @@ var projects = {
data.slug = util.slugify(data.name) + "-" + (+new Date)
data.description = util.sanitize(data.description)
data.viewHeight = Number(data.viewHeight || 0)
- data.rooms = JSON.parse(data.rooms)
+ if (data.shapes) {
+ data.shapes = JSON.parse(data.shapes)
+ }
+ else {
+ data.rooms = JSON.parse(data.rooms)
+ }
data.walls = JSON.parse(data.walls)
data.media = JSON.parse(data.media)
+ data.sculpture = JSON.parse(data.sculpture)
data.colors = JSON.parse(data.colors)
data.startPosition = JSON.parse(data.startPosition)
data.lastPosition = JSON.parse(data.lastPosition)
@@ -100,11 +106,17 @@ var projects = {
data.updated_at = new Date ()
_.extend(doc, data)
-
- doc.rooms = JSON.parse(data.rooms)
+
+ if (data.shapes) {
+ doc.shapes = JSON.parse(data.shapes)
+ }
+ else {
+ doc.rooms = JSON.parse(data.rooms)
+ }
doc.walls = JSON.parse(data.walls)
doc.colors = JSON.parse(data.colors)
doc.media = JSON.parse(data.media)
+ doc.sculpture = JSON.parse(data.sculpture)
doc.startPosition = JSON.parse(data.startPosition)
doc.lastPosition = JSON.parse(data.lastPosition)
diff --git a/server/lib/api/rooms.js b/server/lib/api/rooms.js
index c044309..2a1d2fe 100644
--- a/server/lib/api/rooms.js
+++ b/server/lib/api/rooms.js
@@ -6,6 +6,8 @@ var Clipper = require("../../../public/assets/javascripts/rectangles/engine/room
var Builder = require("../../../public/assets/javascripts/rectangles/engine/rooms/builder.js")
var Grouper = require("../../../public/assets/javascripts/rectangles/engine/rooms/grouper.js")
var Walls = require("../../../public/assets/javascripts/rectangles/engine/rooms/_walls.js")
+var shapes = require("../../../public/assets/javascripts/rectangles/engine/shapes/shapelist.js")
+var RegionList = require("../../../public/assets/javascripts/rectangles/engine/shapes/regionlist.js")
/* jshint node: true */
@@ -21,47 +23,106 @@ var rooms = module.exports = {
if (! doc) { res.json({ status: 404 }); return }
doc = doc.toObject()
- doc.rooms.forEach(function(data){
- var rect = new Rect(data.rect.x[0], data.rect.y[0], data.rect.x[1], data.rect.y[1])
- var room = new Room({
- id: data.id,
- rect: rect,
- height: data.height
- })
- Rooms.add(room)
- })
- Rooms.clipper.solve_rects()
- Rooms.builder.build()
-
- var walls = [], mx_walls = [], mx_floor = [], mx_ceiling = []
- var collections = Rooms.grouper.collect()
- Rooms.grouper.cull(collections)
- Rooms.grouper.group(walls, collections, FRONT)
- Rooms.grouper.group(walls, collections, BACK)
- Rooms.grouper.group(walls, collections, LEFT)
- Rooms.grouper.group(walls, collections, RIGHT)
- walls.forEach(function(wall){
- wall.mx.forEach(function(mx){
- var data = mx.report()
- data.id = wall.id
- mx_walls.push(data)
- })
- })
-
- doc.mx_walls = mx_walls
- doc.mx_floor = mx_floor
- doc.mx_ceiling = mx_ceiling
-
- Rooms.forEach(function(room){
- room.mx_floor.forEach(function(mx){
- mx_floor.push( mx.report() )
- })
- room.mx_ceiling.forEach(function(mx){
- mx_ceiling.push( mx.report() )
- })
- })
+ if (doc.shapes.length) {
+ parseMxShapes(doc)
+ }
+ else {
+ parseMxRooms(doc)
+ }
res.json(doc)
})
}
}
+
+function parseMxShapes (doc) {
+ var viewHeight = doc.viewHeight
+ var wallHeight = doc.wallHeight
+
+ shapes.deserialize( doc.shapes )
+
+ var walls = [], mx_walls = [], mx_floor = [], mx_ceiling = []
+ var regions = RegionList.build()
+
+ regions.forEach(function(region){
+ var room = new Room({
+ rect: region,
+ regions: [region],
+ height: wallHeight,
+ })
+
+ room.sides = region.sides
+ region.id = Rooms.uid("room_")
+ Rooms.list[ region.id ] = room
+ Rooms.builder.build_walls(region)
+ mx_floor.push( Rooms.builder.make_floor(room, region) )
+ mx_ceiling.push( Rooms.builder.make_ceiling(room, region) )
+ })
+
+ var collections = Rooms.grouper.collect()
+ Rooms.grouper.cull(collections)
+ Rooms.grouper.group(walls, collections, FRONT)
+ Rooms.grouper.group(walls, collections, BACK)
+ Rooms.grouper.group(walls, collections, LEFT)
+ Rooms.grouper.group(walls, collections, RIGHT)
+ walls.forEach(function(wall){
+ wall.mx.forEach(function(mx){
+ var data = mx.report()
+ data.id = wall.id
+ mx_walls.push(data)
+ })
+ })
+
+ doc.mx_walls = mx_walls
+ doc.mx_floor = mx_floor
+ doc.mx_ceiling = mx_ceiling
+
+ Rooms.forEach(function(room){
+ mx_floor.push( room.mx_floor )
+ room.mx_ceiling.forEach(function(mx){
+ mx_ceiling.push( mx.report() )
+ })
+ })
+}
+
+function parseMxRooms (doc) {
+ doc.rooms.forEach(function(data){
+ var rect = new Rect(data.rect.x[0], data.rect.y[0], data.rect.x[1], data.rect.y[1])
+ var room = new Room({
+ id: data.id,
+ rect: rect,
+ height: data.height
+ })
+ Rooms.add(room)
+ })
+ Rooms.clipper.solve_rects()
+ Rooms.builder.build()
+
+ var walls = [], mx_walls = [], mx_floor = [], mx_ceiling = []
+ var collections = Rooms.grouper.collect()
+ Rooms.grouper.cull(collections)
+ Rooms.grouper.group(walls, collections, FRONT)
+ Rooms.grouper.group(walls, collections, BACK)
+ Rooms.grouper.group(walls, collections, LEFT)
+ Rooms.grouper.group(walls, collections, RIGHT)
+ walls.forEach(function(wall){
+ wall.mx.forEach(function(mx){
+ var data = mx.report()
+ data.id = wall.id
+ mx_walls.push(data)
+ })
+ })
+
+ doc.mx_walls = mx_walls
+ doc.mx_floor = mx_floor
+ doc.mx_ceiling = mx_ceiling
+
+ Rooms.forEach(function(room){
+ room.mx_floor.forEach(function(mx){
+ mx_floor.push( mx.report() )
+ })
+ room.mx_ceiling.forEach(function(mx){
+ mx_ceiling.push( mx.report() )
+ })
+ })
+} \ No newline at end of file
diff --git a/server/lib/api/subscription.js b/server/lib/api/subscription.js
new file mode 100644
index 0000000..c2fff4c
--- /dev/null
+++ b/server/lib/api/subscription.js
@@ -0,0 +1,206 @@
+/* jshint node: true */
+
+var _ = require('lodash'),
+ util = require('../util'),
+ upload = require('../upload'),
+ config = require('../../../config.json'),
+ User = require('../schemas/User'),
+ Project = require('../schemas/Project'),
+ Layout = require('../schemas/Layout'),
+ Plan = require('../schemas/Plan');
+ Subscription = require('../schemas/Subscription'),
+ Recurly = require('node-recurly'),
+ recurly = new Recurly(require('../webhook/recurly-config'));
+
+var plan_levels = {
+ free: 0,
+ basic: 1,
+ pro: 2,
+ custom: 3,
+}
+
+var subscription = module.exports = {
+ middleware: {
+ ensureSubscription: function(req, res, next){
+ Subscription.findOne({ user_id: req.user._id }, function(err, data){
+ if (err) { return next() }
+ req.subscription = data
+ next()
+ })
+ },
+ ensurePlans: function(req, res, next){
+ Plan.find({}).sort({ 'level': 1 }).exec(function (err, plans) {
+ res.locals.plans = (plans || [])
+ next()
+ })
+ },
+ },
+
+ // synchronise an account with recurly..
+ // useful when testing locally (where webhooks cannot be received)
+ // parses the XML from the subscription API into something usable
+ sync: function(req, res){
+ var subscriber = req.subscription || new Subscription ()
+ var user = req.user
+ recurly.subscriptions.listByAccount(req.user._id, function(recurlyRes){
+ var data = recurlyRes.data
+ if (recurlyRes.description !== 200) {
+ res.json({ error: "no account" })
+ return
+ }
+ var subscriptions = data.subscriptions.subscription.length ? data.subscriptions.subscription : [data.subscriptions.subscription]
+ var is_active = subscriptions.some(function(subscription_data){
+ if (subscription_data.state == "active") {
+ set_active(subscription_data)
+ return true
+ }
+ return false
+ })
+ if (! is_active) {
+ set_cancelled()
+ }
+ })
+ function set_cancelled(){
+ user.plan_type = "free"
+ user.plan_level = plan_levels["free"]
+
+ return subscriber.remove(function(){
+ user.save(function(){
+ res.json({ error: "account cancelled" })
+ })
+ })
+ }
+ function set_active(data){
+ var plan = data.plan.plan_code.split("-")
+ var plan_type = plan[0]
+ var plan_period = plan[1]
+
+ user.plan_type = plan_type
+ user.plan_level = plan_levels[plan_type]
+
+ subscriber.uuid = data.uuid
+ subscriber.user_id = user._id
+ subscriber.plan_type = plan_type
+ subscriber.plan_period = plan_period
+ subscriber.plan_level = plan_levels[plan_type]
+
+ var add_ons = data.subscription_add_ons.subscription_add_on
+ if (add_ons) {
+ if (add_ons.add_on_code) {
+ add_ons = [ add_ons ]
+ }
+ // TODO: handle multiple add-ons.. presumably this will work
+ add_ons.forEach(function(add_on){
+ var add_on_type = add_on.add_on_code.split("-")[1]
+ if (add_on_type == "basic") {
+ subscriber.basic_layouts = parseInt( add_on.quantity._ )
+ }
+ if (add_on_type == "pro") {
+ subscriber.pro_layouts = parseInt( add_on.quantity._ )
+ }
+ })
+ }
+ else {
+ subscriber.basic_layouts = 0
+ subscriber.pro_layouts = 0
+ }
+ subscriber.save(function(){
+ user.save(function(){
+ res.json({
+ subscription: subscriber,
+ plans: res.locals.plans
+ })
+ })
+ })
+ }
+ },
+
+ show: function(req, res){
+ if (req.subscription) {
+ res.json({
+ subscription: req.subscription,
+ plans: res.locals.plans
+ })
+ }
+ else {
+ res.json({
+ error: "no subscription",
+ plans: res.locals.plans,
+ })
+ }
+ },
+
+ update: function(req, res){
+ if (! req.subscription ) {
+ return res.json({ error: "no subscription" })
+ }
+ if (! (req.body.plan_type in plan_levels)) {
+ return res.json({ error: "bad input" })
+ }
+ var subscriber = req.subscription
+ var user = req.user
+
+ var plan_type = req.body.plan_type
+ var basic_layouts = Math.max(0, parseInt(req.body.basic_layouts || 0, 10))
+ var pro_layouts = Math.max(0, parseInt(req.body.pro_layouts || 0, 10))
+ if (plan_type != "pro") { pro_layouts = 0 }
+
+ if (plan_type == subscription.plan_type
+ && basic_layouts == subscriber.basic_layouts
+ && pro_layouts == subscriber.pro_layouts) {
+ return res.json(subscriber)
+ }
+
+ var data = { subscription_add_ons: [] }
+ data.plan_code = plan_type + "-monthly"
+
+ if (plan_levels[plan_type] > 0 && basic_layouts > 0) {
+ data.subscription_add_ons.push({ add_on_code: "extra-basic-layout", quantity: basic_layouts })
+ }
+
+ if (plan_type == "pro" && pro_layouts > 0) {
+ data.subscription_add_ons.push({ add_on_code: "extra-pro-layout", quantity: pro_layouts })
+ }
+
+ // data.plan_code
+ // data.subscription_add_ons = []
+ // add_on.add_on_code
+ // add_on.quantity
+ console.log(data)
+ recurly.subscriptions.update(subscriber.uuid, data, function(err, data){
+ console.log("got response from RECURLY ...")
+ if (err) {
+ console.log("error updating recurly subscription", err)
+ return res.json({ error: err })
+ }
+ subscriber.plan_type = plan_type
+ subscriber.basic_layouts = basic_layouts
+ subscriber.pro_layouts = pro_layouts
+ subscriber.save(function(){
+ user.plan_level = plan_levels[plan_type]
+ user.plan_type = plan_type
+ user.save(function(){
+ return res.json(subscriber)
+ })
+ })
+ })
+ },
+
+ destroy: function(req, res){
+ if (! req.subscription ) {
+ return res.json({ error: "no subscription" })
+ }
+ var subscriber = req.subscription
+
+ recurly.subscriptions.terminate(subscriber.uuid, "partial", function(err, data){
+ subscriber.remove(function(){
+ req.user.plan_code = 0
+ req.user.plan_type = "free"
+ req.user.save(function(){
+ res.json({ status: "OK" })
+ })
+ })
+ })
+ },
+
+}; \ No newline at end of file
diff --git a/server/lib/auth/index.js b/server/lib/auth/index.js
index a9a2400..46bff21 100644
--- a/server/lib/auth/index.js
+++ b/server/lib/auth/index.js
@@ -39,7 +39,14 @@ var auth = {
auth.mail.init()
},
-
+ initBasicAuth: function(app){
+ if (config.basicAuth) {
+ app.use(express.basicAuth(function(user, pass) {
+ return user === config.basicAuth.user &&
+ pass === config.basicAuth.pass
+ }))
+ }
+ },
initSockets: function (io, SessionStore) {
io.set('authorization', passportSocketIo.authorize({
cookieParser: express.cookieParser,
@@ -135,12 +142,12 @@ var auth = {
deserializeUser: function (id, done) {
try {
var _id = mongoose.Types.ObjectId(id)
- User.findOne({ _id: _id }, "_id displayName username photo isStaff", function (err, user) {
+ User.findOne({ _id: _id }, "_id displayName username photo isStaff plan_level", function (err, user) {
done(err, user);
});
}
catch (e) {
- User.findOne({ twitter_id: id }, "_id displayName username photo isStaff", function (err, user) {
+ User.findOne({ twitter_id: id }, "_id displayName username photo isStaff plan_level", function (err, user) {
done(err, user);
});
}
@@ -240,7 +247,7 @@ var auth = {
facebookUrl: profile.username ? "https://facebook.com/" + profile.username : ""
};
- User.findOne({twitter_id: profile.id}, function(err, data){
+ User.findOne({facebook_id: profile.id}, function(err, data){
if (! err && data) {
return done(err, data);
}
diff --git a/server/lib/auth/mail.js b/server/lib/auth/mail.js
index eac4007..990b53f 100644
--- a/server/lib/auth/mail.js
+++ b/server/lib/auth/mail.js
@@ -6,7 +6,7 @@ var email = require("emailjs"),
var mail = {
- from: 'VValls <info@vvalls.com>',
+ from: 'VValls <hello@vvalls.com>',
templates: {},
init: function(){
diff --git a/server/lib/middleware.js b/server/lib/middleware.js
index 4848ab0..0a0a9ce 100644
--- a/server/lib/middleware.js
+++ b/server/lib/middleware.js
@@ -6,11 +6,16 @@ var passport = require('passport'),
config = require('../../config.json'),
User = require('./schemas/User'),
Collaborator = require('./schemas/Collaborator'),
- Project = require('./schemas/Project');
+ Project = require('./schemas/Project'),
+ Layout = require('./schemas/Layout'),
+ Blueprint = require('./schemas/Blueprint'),
+ Plan = require('./schemas/Plan');
var middleware = {
+ plans: [],
+
enableCORS: function (req, res, next) {
res.header('Access-Control-Allow-Credentials', true);
// TODO Check https vs. http
@@ -46,7 +51,7 @@ var middleware = {
},
ensureLocals: function (req, res, next) {
- res.locals.token = req.csrfToken();
+ res.locals.token = req.csrfToken()
res.locals.logged_in = req.isAuthenticated()
res.locals.user = req.user || { _id: undefined }
res.locals.config = config
@@ -56,10 +61,31 @@ var middleware = {
res.locals.ogUrl = "http://vvalls.com/"
res.locals.ogDescription = "3D gallery space, fully customizable"
res.locals.ogAuthor = "VValls"
+ res.locals.plans = middleware.plans
res.locals.opt = {}
next()
},
-
+
+ ensureUserProjectsCount: function(req, res, next){
+ var counts = { stock: 0, basic: 0, pro: 0 }
+ res.locals.projectCounts = counts
+ Project.count({ user_id: req.user._id }, function(err, count){
+ res.locals.projectCount = count || 0
+ next()
+ })
+ },
+
+ ensureUserLayoutsCount: function(req, res, next){
+ var counts = { basic: 0, pro: 0 }
+ res.locals.layoutCounts = counts
+ if (req.user.plan_level == 0) { return next() }
+
+ Layout.count({ user_id: req.user._id }, function(err, count){
+ res.locals.layoutCount = count || 0
+ next()
+ })
+ },
+
ensureProject: function (req, res, next) {
if (req.params.slug) {
Project.findOne({ slug: req.params.slug }, function(err, project){
@@ -75,13 +101,57 @@ var middleware = {
}
next()
})
- }
+ }
else {
req.project = null
next()
}
},
+ ensureLayout: function (req, res, next) {
+ if (req.params.slug) {
+ Layout.findOne({ slug: req.params.slug }, function(err, layout){
+ if (err) {
+ console.error(err)
+ req.layout = null
+ }
+ else if (! layout) {
+ req.layout = null
+ }
+ else {
+ req.layout = layout
+ }
+ next()
+ })
+ }
+ else {
+ req.layout = null
+ next()
+ }
+ },
+
+ ensureBlueprint: function (req, res, next) {
+ if (req.params.slug) {
+ Blueprint.findOne({ slug: req.params.slug }, function(err, blueprint){
+ if (err) {
+ console.error(err)
+ req.blueprint = null
+ }
+ else if (! blueprint) {
+ req.blueprint = null
+ }
+ else {
+ req.blueprint = blueprint
+ }
+ next()
+ })
+ }
+ else {
+ req.blueprint = null
+ next()
+ }
+ },
+
ensureIsCollaborator: function(req, res, next) {
req.isCollaborator = false
req.isOwner = false
@@ -106,6 +176,12 @@ var middleware = {
})
}
},
+
+ updatePlans: function(){
+ Plan.find({}).sort({ 'level': -1 }).exec(function (err, plans) {
+ middleware.plans = plans.map(function(plan){ return plan.toObject() })
+ })
+ },
}
diff --git a/server/lib/schemas/Blueprint.js b/server/lib/schemas/Blueprint.js
new file mode 100644
index 0000000..3c3b0cc
--- /dev/null
+++ b/server/lib/schemas/Blueprint.js
@@ -0,0 +1,66 @@
+/* jshint node: true */
+
+var mongoose = require('mongoose'),
+ _ = require('lodash'),
+ util = require('../util');
+
+var BlueprintSchema = new mongoose.Schema({
+ type: {
+ type: String,
+ required: true
+ },
+ url: {
+ type: String,
+ required: true,
+ },
+ token: {
+ type: String,
+ default: ""
+ },
+ thumbnail: {
+ type: String,
+ default: ""
+ },
+ width: {
+ type: Number,
+ default: 0
+ },
+ height: {
+ type: Number,
+ default: 0
+ },
+ name: {
+ type: String,
+ default: ""
+ },
+ slug: {
+ type: String,
+// required: true,
+ validate: [function (val){
+ val = util.sanitize(val || this.displayName || "")
+// if (! val.length) return false
+ return true
+ },"{PATH} name is required"]
+ },
+ description: {
+ type: String,
+ default: ""
+ },
+ tag: { type: String, default: "" },
+ scale: { type: Number, default: 1.0 },
+
+ widthDimension: { type: Number },
+ heightDimension: { type: Number },
+ wallHeight: { type: Number },
+ units: { type: String },
+ line: { type: String },
+
+ shapes: [mongoose.Schema.Types.Mixed],
+ startPosition: mongoose.Schema.Types.Mixed,
+
+ user_id: { type: mongoose.Schema.ObjectId, index: true },
+ created_at: { type: Date },
+});
+
+module.exports = exports = mongoose.model('blueprint', BlueprintSchema)
+exports.schema = BlueprintSchema;
diff --git a/server/lib/schemas/Layout.js b/server/lib/schemas/Layout.js
index e3f2616..e15e188 100644
--- a/server/lib/schemas/Layout.js
+++ b/server/lib/schemas/Layout.js
@@ -23,9 +23,12 @@ var LayoutSchema = new mongoose.Schema({
photo: {
type: String,
},
+ media: mongoose.Schema.Types.Mixed,
rooms: [mongoose.Schema.Types.Mixed],
startPosition: mongoose.Schema.Types.Mixed,
viewHeight: { type: Number },
+ is_stock: { type: Boolean, default: false },
+ is_pro: { type: Boolean, default: false },
user_id: { type: mongoose.Schema.ObjectId, index: true },
created_at: { type: Date },
updated_at: { type: Date },
diff --git a/server/lib/schemas/Media.js b/server/lib/schemas/Media.js
index 1de354d..f37fb12 100644
--- a/server/lib/schemas/Media.js
+++ b/server/lib/schemas/Media.js
@@ -37,11 +37,12 @@ var MediaSchema = new mongoose.Schema({
type: String,
default: ""
},
- autoplay: { type: Boolean, default: false },
- loop: { type: Boolean, default: false },
- mute: { type: Boolean, default: true },
+ autoplay: { type: Boolean, default: true },
+ loop: { type: Boolean, default: true },
+ mute: { type: Boolean, default: false },
keyframe: { type: Number, default: 0.0 },
tag: { type: String, default: "" },
+ scale: { type: Number, default: 1.0 },
widthDimension: { type: Number },
heightDimension: { type: Number },
diff --git a/server/lib/schemas/Plan.js b/server/lib/schemas/Plan.js
new file mode 100644
index 0000000..388ce69
--- /dev/null
+++ b/server/lib/schemas/Plan.js
@@ -0,0 +1,44 @@
+/* jshint node: true */
+
+var mongoose = require('mongoose'),
+ _ = require('lodash'),
+ crypto = require('crypto'),
+ config = require('../../../config.json'),
+ util = require('../util');
+
+var PlanSchema = new mongoose.Schema({
+ name: { type: String },
+ slug: { type: String },
+
+ level: { type: Number },
+
+ monthly_price: { type: Number },
+ yearly_price: { type: Number },
+
+ basic_layout_monthly_price: { type: Number },
+ basic_layout_yearly_price: { type: Number },
+
+ pro_layout_monthly_price: { type: Number },
+ pro_layout_yearly_price: { type: Number },
+
+ basic_layout_limit: { type: Number },
+ pro_layout_limit: { type: Number },
+
+ stock_project_limit: { type: Number },
+ basic_project_limit: { type: Number },
+ pro_project_limit: { type: Number },
+
+ permissions: {
+ basic_editor: { type: Boolean, default: false },
+ pro_editor: { type: Boolean, default: false },
+ sculpture: { type: Boolean, default: false },
+ collaborators: { type: Boolean, default: false },
+ no_logo: { type: Boolean, default: false },
+ },
+
+ created_at: { type: Date, default: Date.now },
+ updated_at: { type: Date, default: Date.now },
+})
+
+module.exports = exports = mongoose.model('plan', PlanSchema);
+exports.schema = PlanSchema;
diff --git a/server/lib/schemas/Project.js b/server/lib/schemas/Project.js
index a923d85..687555d 100644
--- a/server/lib/schemas/Project.js
+++ b/server/lib/schemas/Project.js
@@ -28,16 +28,20 @@ var ProjectSchema = new mongoose.Schema({
type: String,
},
rooms: [mongoose.Schema.Types.Mixed],
+ shapes: [mongoose.Schema.Types.Mixed],
walls: [mongoose.Schema.Types.Mixed],
media: [mongoose.Schema.Types.Mixed],
+ sculpture: [mongoose.Schema.Types.Mixed],
colors: mongoose.Schema.Types.Mixed,
startPosition: mongoose.Schema.Types.Mixed,
lastPosition: mongoose.Schema.Types.Mixed,
viewHeight: { type: Number },
+ units: { type: String, default: "ft" },
user_id: { type: mongoose.Schema.ObjectId, index: true },
created_at: { type: Date },
updated_at: { type: Date },
featured: { type: Boolean, default: false },
+ layout_type: { type: Number, default: 0 },
});
module.exports = exports = mongoose.model('project', ProjectSchema);
diff --git a/server/lib/schemas/Subscription.js b/server/lib/schemas/Subscription.js
new file mode 100644
index 0000000..bf43e8b
--- /dev/null
+++ b/server/lib/schemas/Subscription.js
@@ -0,0 +1,31 @@
+/* jshint node: true */
+
+var mongoose = require('mongoose'),
+ _ = require('lodash'),
+ crypto = require('crypto'),
+ config = require('../../../config.json'),
+ util = require('../util');
+
+var SubscriptionSchema = new mongoose.Schema({
+ user_id: { type: mongoose.Schema.ObjectId, index: true },
+
+ plan_code: { type: String, default: "" },
+ plan_type: { type: String, default: "free" },
+ plan_period: { type: String, default: "monthly" },
+
+ uuid: { type: String },
+ basic_layouts: { type: Number, default: 0 },
+ pro_layouts: { type: Number, default: 0 },
+
+ history: [{
+ action: { type: String },
+ data: { type: String },
+ created_at: { type: Date, default: Date.now },
+ }],
+
+ created_at: { type: Date, default: Date.now },
+ updated_at: { type: Date, default: Date.now },
+})
+
+module.exports = exports = mongoose.model('subscription', SubscriptionSchema);
+exports.schema = SubscriptionSchema;
diff --git a/server/lib/schemas/User.js b/server/lib/schemas/User.js
index 180a140..829b360 100644
--- a/server/lib/schemas/User.js
+++ b/server/lib/schemas/User.js
@@ -54,7 +54,12 @@ var UserSchema = new mongoose.Schema({
type: String,
default: "",
},
-
+
+ plan_code: { type: String, default: "" },
+ plan_level: { type: Number, default: 0 },
+ plan_type: { type: String, default: "free" },
+ last_charged: { type: Date, default: null },
+
location: { type: String, default: "" },
photo: { type: String, default: "" },
bio: { type: String, default: "" },
@@ -62,6 +67,8 @@ var UserSchema = new mongoose.Schema({
twitterName: { type: String, default: "" },
facebookUrl: { type: String, default: "" },
isStaff: { type: Boolean, default: false },
+ isArtist: { type: Boolean, default: false },
+ subscription_id: { type: mongoose.Schema.ObjectId },
created_at: { type: Date },
updated_at: { type: Date },
last_seen: { type: Date },
diff --git a/server/lib/util.js b/server/lib/util.js
index e3fd1ed..86fbdcc 100644
--- a/server/lib/util.js
+++ b/server/lib/util.js
@@ -5,6 +5,7 @@ var whitespace = new RegExp('\\s', 'g')
var whitespaceHead = /^\s+/
var whitespaceTail = /\s+$/
var nonAlphanumerics = new RegExp('[^-_a-zA-Z0-9]', 'g')
+var nonNumerics = new RegExp('[^0-9]', 'g')
var consecutiveDashes = new RegExp("-+", 'g')
var entities = new RegExp("[<>&]", 'g')
@@ -19,6 +20,9 @@ util.slugify = function (s){
util.sanitize = function (s){
return (s || "").replace(entities, "")
}
+util.sanitizeNumber = function (s){
+ return (s || "").replace(nonNumerics, "")
+}
util.escape = function (s){
return (s || "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")
}
diff --git a/server/lib/views/index.js b/server/lib/views/index.js
index 8c3e63d..523f628 100644
--- a/server/lib/views/index.js
+++ b/server/lib/views/index.js
@@ -4,6 +4,7 @@ var User = require('../schemas/User'),
Project = require('../schemas/Project'),
Documentation = require('../schemas/Documentation'),
Collaborator = require('../schemas/Collaborator'),
+ Plan = require('../schemas/Plan'),
config = require('../../../config'),
marked = require('marked'),
util = require('../util'),
@@ -83,33 +84,42 @@ var views = module.exports = {
res.render('builder')
},
+ blueprint: function (req, res) {
+ res.render('blueprint')
+ },
+
modal: function (req, res) {
res.render('modal');
},
home: function (req, res) {
- // while in development, blank homepage if not logged in
-/*
- if (! req.user) {
- res.send("<html></html>")
- return
- }
-*/
- views_middleware.fetchProjects({ featured: true }, null, null, function(err, projects){
- res.render('home', {
- projects: projects || []
+ views_middleware.ensurePlans(req, res, function(err){
+ views_middleware.fetchProjects({ featured: true }, null, null, function(err, projects){
+ res.render('home', {
+ projects: projects || [],
+ })
})
})
},
demoHome: function (req, res) {
- views_middleware.fetchProjects({ featured: true }, null, null, function(err, projects){
- res.render('home', {
- projects: projects || []
+ views_middleware.ensurePlans(req, res, function(err){
+ views_middleware.fetchProjects({ featured: true }, null, null, function(err, projects){
+ res.render('home', {
+ projects: projects || [],
+ })
})
})
},
+ partials: {
+ plans: function (req, res){
+ views_middleware.ensurePlans(req, res, function(){
+ res.render('about/_plans', { logged_in: res.locals.logged_in || false })
+ })
+ },
+ },
+
docs: function (req, res){
var name = req.params.name || "about"
@@ -117,6 +127,13 @@ var views = module.exports = {
res.render('about/' + name)
return
}
+ if (name == "brochure" || name == "plans") {
+ // TODO: fetch plans
+ views_middleware.ensurePlans(req, res, function(){
+ res.render('about/' + name)
+ })
+ return
+ }
if (name == "about" || name == "index") {
res.render('about/' + name)
return
@@ -225,6 +242,19 @@ var views = module.exports = {
}
var views_middleware = {
+ ensurePlans: function(req, res, next){
+ Plan.find(function (err, plans) {
+ res.locals.plans = {}
+ plans.forEach(function(plan){
+ res.locals.plans[ plan.slug ] = plan
+ "monthly_price yearly_price basic_layout_monthly_price basic_layout_yearly_price pro_layout_monthly_price pro_layout_yearly_price".split(" ").forEach(function(key){
+ plan[key] = (plan[key]/100).toFixed(2)+""
+ })
+ })
+ next()
+ })
+ },
+
fetchProjects: function (criteria, limit, offset, next) {
limit = limit || 7
offset = offset || 0
diff --git a/server/lib/views/staff.js b/server/lib/views/staff.js
deleted file mode 100644
index 49f492b..0000000
--- a/server/lib/views/staff.js
+++ /dev/null
@@ -1,545 +0,0 @@
-/* jshint node: true */
-
-var User = require('../schemas/User'),
- Project = require('../schemas/Project'),
- Media = require('../schemas/Media'),
- Collaborator = require('../schemas/Collaborator'),
- config = require('../../../config'),
- middleware = require('../middleware'),
- util = require('../util'),
- _ = require('lodash'),
- moment = require('moment');
-
-
-var staff = module.exports = {
-
- fields: {
- user: "_id username displayName photo created_at updated_at last_seen created_ip last_ip",
- project: "_id name slug user_id privacy created_at updated_at",
- },
-
- defaults: {
- user: {
- _id: "", username: "", displayName: "",
- created_at: "", updated_at: "", created_ip: "", last_ip: "",
- },
- },
-
- middleware: {
-
- ensureUsers: function(req, res, next){
- var paginationInfo = res.locals.pagination = {}
- var criteria = req.criteria || {}
- var limit = paginationInfo.limit = Math.min( Number(req.query.limit) || 50, 200 )
- var offset = paginationInfo.offset = Number(req.query.offset) || 0
- var sort
- paginationInfo.sort = req.query.sort
- paginationInfo.sortOptions = ["date", "last_seen", "username"]
- switch (req.query.sort) {
- case 'date':
- sort = {'created_at': -1}
- break
- case 'last_seen':
- sort = {'last_seen': -1}
- break
- case 'username':
- default:
- sort = {'username': 1}
- paginationInfo.sort = "username"
- break
- }
- User.find(criteria)
- .select(staff.fields.user)
- .sort(sort)
- .skip(offset)
- .limit(limit)
- .exec(function (err, users) {
- res.locals.users = users.map(staff.helpers.user)
- next()
- })
- },
-
- ensureRecentUsers: function(req, res, next){
- var dreq = { query: { sort: 'last_seen', limit: 20, offset: 0 } }
- staff.middleware.ensureUsers(dreq, res, next)
- },
-
- ensureProjects: function(req, res, next){
- var paginationInfo = res.locals.pagination = {}
- var criteria = req.criteria || {}
- var limit = paginationInfo.limit = Math.min( Number(req.query.limit) || 50, 200 )
- var offset = paginationInfo.offset = Number(req.query.offset) || 0
- var sort
- paginationInfo.sort = req.query.sort
- paginationInfo.sortOptions = ["date", "name"]
- switch (req.query.sort) {
- default:
- case 'date':
- sort = {'updated_at': -1}
- break
- case 'name':
- paginationInfo.sort = "name"
- sort = {'slug': 1}
- break
- }
- Project.find(criteria)
- .select(staff.fields.project)
- .sort(sort)
- .skip(offset)
- .limit(limit)
- .exec(function (err, projects) {
- res.locals.projects = projects.map(staff.helpers.project)
- next()
- })
- },
-
- ensureMedia: function(req, res, next){
- var paginationInfo = res.locals.pagination = {}
- var criteria = req.criteria || {}
- var limit = paginationInfo.limit = Math.min( Number(req.query.limit) || 50, 200 )
- var offset = paginationInfo.offset = Number(req.query.offset) || 0
- var sort
- paginationInfo.sort = req.query.sort
- paginationInfo.sortOptions = ["date"]
- switch (req.query.sort) {
- default:
- case 'date':
- paginationInfo.sort = "date"
- sort = {'created_at': -1}
- break
- }
- Media.find(criteria)
- // .select(staff.fields.media)
- .sort(sort)
- .skip(offset)
- .limit(limit)
- .exec(function (err, media) {
- res.locals.media = media.map(staff.helpers.media)
- next()
- })
- },
-
- ensureRecentProjects: function(req, res, next){
- var dreq = { params: { sort: 'created_at', limit: 20, offset: 0 } }
- staff.middleware.ensureProjects(dreq, res, next)
- },
-
- ensureProjectsUsers: function(req, res, next){
- if (! res.locals.projects || ! res.locals.projects.length) { return next() }
- staff.middleware.ensureObjectsUsers(res.locals.projects, next)
- },
-
- ensureMediaUsers: function(req, res, next){
- if (! res.locals.media || ! res.locals.media.length) { return next() }
- staff.middleware.ensureObjectsUsers(res.locals.media, next)
- },
-
- ensureMediaUser: function(req, res, next){
- if (! res.locals.media) { return next() }
- staff.middleware.ensureObjectsUsers([ res.locals.media ], function(){
- res.locals.mediaUser = res.locals.media.User
- next()
- })
- },
-
- ensureObjectsUsers: function(objects, next){
- if (! objects) { return next () }
- var dedupe = {}, user_ids
- objects.forEach(function(obj){
- dedupe[ obj.user_id ] = dedupe[ obj.user_id ] || []
- dedupe[ obj.user_id ].push(obj)
- })
- user_ids = _.keys(dedupe)
- User.find({ _id: { $in: user_ids } })
- .select(staff.fields.user)
- .exec(function (err, users) {
- if (! users) { return next () }
- users.forEach(function(user){
- dedupe[user._id].forEach(function(obj){
- obj.user = user
- })
- })
- next()
- })
- },
-
- ensureProfile: function(req, res, next){
- var username = req.params.username
- if (username) {
- User.findOne({ username: username }, function (err, user) {
- if (user) {
- res.locals.profile = req.method == "GET" ? staff.helpers.user(user) : user
- }
- else {
- res.locals.profile = null
- }
- next()
- })
- }
- else {
- res.locals.profile = null
- next()
- }
- },
-
- ensureSingleMedia: function(req, res, next){
- var id = req.params.id
- if (id) {
- Media.findOne({ _id: id }, function (err, media) {
- if (media) {
- res.locals.media = req.method == "GET" ? staff.helpers.media(media) : media
- }
- else {
- res.locals.media = null
- }
- next()
- })
- }
- else {
- res.locals.media = null
- next()
- }
- },
-
- ensureUsersCount: function(req, res, next){
- User.count({}, function(err, count){
- res.locals.userCount = count || 0
- next()
- })
- },
-
- ensureProjectsCount: function(req, res, next){
- Project.count({}, function(err, count){
- res.locals.projectCount = count || 0
- next()
- })
- },
-
- ensureMediaCount: function(req, res, next){
- Media.count({}, function(err, count){
- res.locals.mediaCount = count || 0
- next()
- })
- },
-
- ensureProfileProjectCount: function(req, res, next){
- if (! res.locals.profile) { return next() }
- Project.count({ user_id: res.locals.profile._id}, function(err, count){
- res.locals.profile.projectCount = count || 0
- next()
- })
- },
-
- ensureProfileMediaCount: function(req, res, next){
- if (! res.locals.profile) { return next() }
- Media.count({ user_id: res.locals.profile._id}, function(err, count){
- res.locals.profile.mediaCount = count || 0
- next()
- })
- },
-
- ensureProfileProjects: function(req, res, next){
- if (! res.locals.profile) { return next() }
- Project.find({ user_id: res.locals.profile._id }, staff.fields.project, function(err, projects){
- res.locals.projects = projects.map(staff.helpers.project)
- next()
- })
- },
-
- ensureProfileMedia: function(req, res, next){
- if (! res.locals.profile) { return next() }
- req.criteria = { user_id: res.locals.profile._id }
- staff.middleware.ensureMedia(req, res, next)
- },
-
- ensureProject: function(req, res, next){
- res.locals.project = req.project
- next()
- },
-
- ensureProjectUser: function(req, res, next){
- if (! res.locals.project) { return next() }
- User.findOne({ _id: res.locals.project.user_id }, staff.fields.user, function(err, user){
- res.locals.projectUser = staff.helpers.user(user) || staff.defaults.user
- next()
- })
- },
-
- ensureProjectCollaborators: function(req, res, next){
- if (! res.locals.project) {
- res.locals.collaborators = []
- return next()
- }
- Collaborator.find({ project_id: res.locals.project._id}, function(err, collaborators){
- res.locals.collaborators = collaborators || []
- next()
- })
- },
- },
-
- helpers: {
- user: function(user){
- user = user.toObject()
- user.last_seen = moment( user.last_seen || user.updated_at || user.created_at ).fromNow()
- user.created_ip = util.num2ip( user.created_ip )
- user.last_ip = util.num2ip( user.last_ip )
- return user
- },
-
- project: function(project){
- project = project.toObject()
- project.date = moment( project.updated_at || project.created_at ).format("M/DD/YYYY hh:mm a")
- project.user = {}
- return project
- },
-
- media: function(media){
- media = media.toObject()
- media.date = moment( media.updated_at || media.created_at ).format("M/DD/YYYY hh:mm a")
- media.user = {}
- media.shortUrl = media.url.replace(/^http.:\/\//,"")
- return media
- }
- },
-
- route: function(app){
- app.get('/staff',
- middleware.ensureAuthenticated,
- middleware.ensureIsStaff,
-
- staff.middleware.ensureRecentUsers,
- staff.middleware.ensureUsersCount,
- staff.middleware.ensureProjectsCount,
- staff.middleware.ensureMediaCount,
-
- staff.index
- );
-
- //
- // users
-
- app.get('/staff/users',
- middleware.ensureAuthenticated,
- middleware.ensureIsStaff,
-
- staff.middleware.ensureUsersCount,
- staff.middleware.ensureUsers,
-
- staff.users.index
- );
- app.get('/staff/users/:username',
- middleware.ensureAuthenticated,
- middleware.ensureIsStaff,
-
- staff.middleware.ensureProfile,
- staff.middleware.ensureProfileProjectCount,
- staff.middleware.ensureProfileMediaCount,
- staff.middleware.ensureProfileProjects,
-
- staff.users.show
- );
- app.get('/staff/users/:username/media',
- middleware.ensureAuthenticated,
- middleware.ensureIsStaff,
-
- staff.middleware.ensureProfile,
- staff.middleware.ensureProfileMedia,
- staff.middleware.ensureProfileMediaCount,
-
- staff.users.media
- );
- app.put('/staff/users/:username/bless',
- middleware.ensureAuthenticated,
- middleware.ensureIsStaff,
-
- staff.middleware.ensureProfile,
-
- staff.users.bless
- );
-
- if (app.get('env') === 'development') {
- app.get('/staff/authorize',
- middleware.ensureAuthenticated,
- staff.users.blessSelf
- );
- }
-
- //
- // projects
-
- app.get('/staff/projects',
- middleware.ensureAuthenticated,
- middleware.ensureIsStaff,
-
- staff.middleware.ensureProjectsCount,
-
- staff.middleware.ensureProjects,
- staff.middleware.ensureProjectsUsers,
-
- staff.projects.index
- );
- app.get('/staff/projects/:slug',
- middleware.ensureAuthenticated,
- middleware.ensureIsStaff,
-
- middleware.ensureProject,
- staff.middleware.ensureProject,
- staff.middleware.ensureProjectUser,
- staff.middleware.ensureProjectCollaborators,
-
- staff.projects.show
- );
- app.put('/staff/projects/:slug/feature',
- middleware.ensureAuthenticated,
- middleware.ensureIsStaff,
-
- middleware.ensureProject,
- staff.middleware.ensureProject,
-
- staff.projects.feature
- );
-
- //
- // media
-
- app.get('/staff/media',
- middleware.ensureAuthenticated,
- middleware.ensureIsStaff,
-
- staff.middleware.ensureMediaCount,
-
- staff.middleware.ensureMedia,
- staff.middleware.ensureMediaUsers,
-
- staff.media.index
- );
- app.get('/staff/media/:id',
- middleware.ensureAuthenticated,
- middleware.ensureIsStaff,
-
- staff.middleware.ensureSingleMedia,
- staff.middleware.ensureMediaUser,
-
- staff.media.show
- );
-
- },
-
- paginate: function(req, res){
- var info = res.locals.pagination
- info.query = "sort=" + info.sort + "&limit=" + info.limit
- info.first_page = 0
- info.last_page = Math.max(0, info.max - info.limit)
- info.sortOptions = info.sortOptions
- if (info.offset > 0) {
- info.prev_page = Math.max(0, info.offset - info.limit)
- }
- else {
- info.prev_page = -1
- }
- if (info.count == info.limit && info.offset + info.limit < info.max) {
- info.next_page = info.offset + info.limit
- }
- else {
- info.next_page = -1
- }
- },
-
- index: function(req, res){
- res.render('staff/index')
- },
-
- // /staff/users/
- // /staff/users/:username
- users: {
- index: function(req, res){
- res.locals.pagination.count = res.locals.users.length
- res.locals.pagination.max = res.locals.userCount
- staff.paginate(req, res)
- res.render('staff/users/index')
- },
- show: function(req, res){
- if (res.locals.profile) {
- res.render('staff/users/show', {
- profileJSON: util.escape( JSON.stringify( res.locals.profile ) )
- })
- }
- else {
- res.render('staff/users/show_404')
- }
- },
- media: function(req, res){
- if (res.locals.profile) {
- res.locals.pagination.count = res.locals.media.length
- res.locals.pagination.max = res.locals.profile.mediaCount
- staff.paginate(req, res)
- res.render('staff/users/media')
- }
- else {
- res.render('staff/users/show_404')
- }
- },
- blessSelf: function(req, res){
- req.user.isStaff = true
- req.user.save(function(err, user){
- res.json({ state: user.isStaff })
- })
- },
- bless: function(req, res){
- res.locals.profile.isStaff = req.body.state == "true"
- res.locals.profile.save(function(err, user){
- res.json({ state: user.isStaff })
- })
- },
- },
-
- // /staff/projects/
- // /staff/projects/:name
- projects: {
- index: function(req, res){
- res.locals.pagination.count = res.locals.projects.length
- res.locals.pagination.max = res.locals.projectCount
- staff.paginate(req, res)
- res.render('staff/projects/index')
- },
- show: function(req, res){
- if (res.locals.project) {
- res.render('staff/projects/show', {
- projectJSON: util.escape( JSON.stringify( res.locals.project ) ),
- projectUserJSON: util.escape( JSON.stringify( res.locals.projectUser ) ),
- collaboratorsJSON: util.escape( JSON.stringify( res.locals.collaborators ) ),
- })
- }
- else {
- res.render('staff/projects/show_404')
- }
- },
- feature: function(req, res){
- res.locals.project.featured = req.body.state == "true"
- res.locals.project.save(function(err, project){
- res.json({ state: project.featured })
- })
- },
- },
-
- media: {
- index: function(req, res){
- res.locals.pagination.count = res.locals.media.length
- res.locals.pagination.max = res.locals.mediaCount
- staff.paginate(req, res)
- res.render('staff/media/index')
- },
- show: function(req, res){
- if (res.locals.media) {
- res.render('staff/media/show', {
- mediaJSON: util.escape( JSON.stringify( res.locals.media ) ),
- mediaUserJSON: util.escape( JSON.stringify( res.locals.mediaUser ) ),
- })
- }
- else {
- res.render('staff/media/show_404')
- }
- },
- }
-
-}
diff --git a/server/lib/views/staff/defaults.js b/server/lib/views/staff/defaults.js
new file mode 100644
index 0000000..4d86c41
--- /dev/null
+++ b/server/lib/views/staff/defaults.js
@@ -0,0 +1,6 @@
+module.exports = {
+ user: {
+ _id: "", username: "", displayName: "",
+ created_at: "", updated_at: "", created_ip: "", last_ip: "",
+ },
+}
diff --git a/server/lib/views/staff/fields.js b/server/lib/views/staff/fields.js
new file mode 100644
index 0000000..84c1efa
--- /dev/null
+++ b/server/lib/views/staff/fields.js
@@ -0,0 +1,10 @@
+module.exports = {
+ user: "_id username displayName photo created_at updated_at last_seen created_ip last_ip",
+ project: "_id name slug user_id privacy layout_type created_at updated_at",
+ layout: "_id name slug user_id layout_type is_stock created_at updated_at",
+ blueprint: "_id name slug user_id created_at updated_at",
+ plans: "monthly_price yearly_price basic_layout_monthly_price basic_layout_yearly_price " +
+ "pro_layout_monthly_price pro_layout_yearly_price " +
+ "basic_layout_limit pro_layout_limit stock_project_limit basic_project_limit pro_project_limit",
+ plans_permissions: "basic_editor pro_editor sculpture collaborators no_logo",
+}
diff --git a/server/lib/views/staff/helpers.js b/server/lib/views/staff/helpers.js
new file mode 100644
index 0000000..ff4065a
--- /dev/null
+++ b/server/lib/views/staff/helpers.js
@@ -0,0 +1,59 @@
+
+var util = require('../../util'),
+ _ = require('lodash'),
+ moment = require('moment');
+
+module.exports = {
+ user: function(user){
+ var last_seen = moment( user.last_seen || user.updated_at || user.created_at )
+ user = user.toObject()
+ user.last_seen = last_seen.format("YYYY/MM/DD HH:MM") + " " + last_seen.fromNow()
+ user.last_charged = user.last_charged && moment( user.last_charged ).format("YYYY/MM/DD HH:MM")
+ user.created_ip = util.num2ip( user.created_ip )
+ user.last_ip = util.num2ip( user.last_ip )
+ return user
+ },
+
+ project: function(project){
+ project = project.toObject()
+ project.date = moment( project.updated_at || project.created_at ).format("M/DD/YYYY hh:mm a")
+ project.user = {}
+ return project
+ },
+
+ layout: function(layout){
+ layout = layout.toObject()
+ layout.date = moment( layout.updated_at || layout.created_at ).format("M/DD/YYYY hh:mm a")
+ layout.user = {}
+ return layout
+ },
+
+ blueprint: function(blueprint){
+ blueprint = blueprint.toObject()
+ blueprint.date = moment( blueprint.updated_at || blueprint.created_at ).format("M/DD/YYYY hh:mm a")
+ blueprint.user = {}
+ return blueprint
+ },
+
+ media: function(media){
+ media = media.toObject()
+ media.date = moment( media.updated_at || media.created_at ).format("M/DD/YYYY hh:mm a")
+ media.user = {}
+ media.shortUrl = media.url.replace(/^http.?:\/\//,"")
+ return media
+ },
+
+ plan: function(plan){
+ plan = plan.toObject()
+ plan.date = moment( plan.updated_at || plan.created_at ).format("M/DD/YYYY hh:mm a")
+ plan.user = {}
+ return plan
+ },
+
+ subscription: function(subscription){
+ subscription = subscription.toObject()
+ subscription.date = moment( subscription.updated_at || subscription.created_at ).format("M/DD/YYYY hh:mm a")
+ subscription.user = {}
+ return subscription
+ },
+}
diff --git a/server/lib/views/staff/index.js b/server/lib/views/staff/index.js
new file mode 100644
index 0000000..49a0384
--- /dev/null
+++ b/server/lib/views/staff/index.js
@@ -0,0 +1,511 @@
+/* jshint node: true */
+
+var User = require('../../schemas/User'),
+ Project = require('../../schemas/Project'),
+ Media = require('../../schemas/Media'),
+ Collaborator = require('../../schemas/Collaborator'),
+ Plan = require('../../schemas/Plan'),
+ Subscription = require('../../schemas/Subscription'),
+ Layout = require('../../schemas/Layout'),
+ Blueprint = require('../../schemas/Blueprint'),
+ config = require('../../../../config'),
+ middleware = require('../../middleware'),
+ util = require('../../util'),
+ _ = require('lodash'),
+ moment = require('moment');
+
+
+var staff = module.exports = {
+
+ fields: require('./fields'),
+ defaults: require('./defaults'),
+ middleware: require('./middleware'),
+ helpers: require('./helpers'),
+
+ route: function(app){
+ app.get('/staff',
+ middleware.ensureAuthenticated,
+ middleware.ensureIsStaff,
+
+ staff.middleware.ensureRecentUsers,
+ staff.middleware.ensureUsersCount,
+ staff.middleware.ensureProjectsCount,
+ staff.middleware.ensureMediaCount,
+
+ staff.index
+ );
+
+ //
+ // users
+
+ app.get('/staff/users',
+ middleware.ensureAuthenticated,
+ middleware.ensureIsStaff,
+
+ staff.middleware.ensureUsersCount,
+ staff.middleware.ensureUsers,
+
+ staff.users.index
+ );
+ app.get('/staff/users/:username',
+ middleware.ensureAuthenticated,
+ middleware.ensureIsStaff,
+
+ staff.middleware.ensureProfile,
+ staff.middleware.ensureProfileProjectCount,
+ staff.middleware.ensureProfileMediaCount,
+ staff.middleware.ensureProfileProjects,
+
+ staff.users.show
+ );
+ app.get('/staff/users/:username/media',
+ middleware.ensureAuthenticated,
+ middleware.ensureIsStaff,
+
+ staff.middleware.ensureProfile,
+ staff.middleware.ensureProfileMedia,
+ staff.middleware.ensureProfileMediaCount,
+
+ staff.users.media
+ );
+ app.put('/staff/users/:username/bless',
+ middleware.ensureAuthenticated,
+ middleware.ensureIsStaff,
+
+ staff.middleware.ensureProfile,
+
+ staff.users.bless
+ );
+ app.put('/staff/users/:username/artist',
+ middleware.ensureAuthenticated,
+ middleware.ensureIsStaff,
+
+ staff.middleware.ensureProfile,
+
+ staff.users.make_artist
+ );
+
+ if (app.get('env') === 'development') {
+ app.get('/staff/authorize',
+ middleware.ensureAuthenticated,
+ staff.users.blessSelf
+ );
+ }
+
+ //
+ // projects
+
+ app.get('/staff/projects',
+ middleware.ensureAuthenticated,
+ middleware.ensureIsStaff,
+
+ staff.middleware.ensureProjectsCount,
+
+ staff.middleware.ensureProjects,
+ staff.middleware.ensureProjectsUsers,
+
+ staff.projects.index
+ );
+ app.get('/staff/projects/:slug',
+ middleware.ensureAuthenticated,
+ middleware.ensureIsStaff,
+
+ middleware.ensureProject,
+ staff.middleware.ensureProject,
+ staff.middleware.ensureProjectUser,
+ staff.middleware.ensureProjectCollaborators,
+
+ staff.projects.show
+ );
+ app.put('/staff/projects/:slug/feature',
+ middleware.ensureAuthenticated,
+ middleware.ensureIsStaff,
+
+ middleware.ensureProject,
+ staff.middleware.ensureProject,
+
+ staff.projects.feature
+ );
+
+ //
+ // layouts
+
+ app.get('/staff/layouts',
+ middleware.ensureAuthenticated,
+ middleware.ensureIsStaff,
+
+ staff.middleware.ensureLayoutsCount,
+
+ staff.middleware.ensureLayouts,
+ staff.middleware.ensureLayoutsUsers,
+
+ staff.layouts.index
+ );
+ app.get('/staff/layouts/:slug',
+ middleware.ensureAuthenticated,
+ middleware.ensureIsStaff,
+
+ middleware.ensureLayout,
+ staff.middleware.ensureLayout,
+ staff.middleware.ensureLayoutUser,
+
+ staff.layouts.show
+ );
+ app.put('/staff/layouts/:slug/stock',
+ middleware.ensureAuthenticated,
+ middleware.ensureIsStaff,
+
+ middleware.ensureLayout,
+ staff.middleware.ensureLayout,
+
+ staff.layouts.make_stock
+ );
+
+ //
+ // blueprints
+
+ app.get('/staff/blueprints',
+ middleware.ensureAuthenticated,
+ middleware.ensureIsStaff,
+
+ staff.middleware.ensureBlueprintsCount,
+
+ staff.middleware.ensureBlueprints,
+ staff.middleware.ensureBlueprintsUsers,
+
+ staff.blueprints.index
+ );
+ app.get('/staff/blueprints/:slug',
+ middleware.ensureAuthenticated,
+ middleware.ensureIsStaff,
+
+ middleware.ensureBlueprint,
+ staff.middleware.ensureBlueprint,
+ staff.middleware.ensureBlueprintUser,
+
+ staff.blueprints.show
+ );
+
+ //
+ // media
+
+ app.get('/staff/media',
+ middleware.ensureAuthenticated,
+ middleware.ensureIsStaff,
+
+ staff.middleware.ensureMediaCount,
+
+ staff.middleware.ensureMedia,
+ staff.middleware.ensureMediaUsers,
+
+ staff.media.index
+ );
+ app.get('/staff/media/:id',
+ middleware.ensureAuthenticated,
+ middleware.ensureIsStaff,
+
+ staff.middleware.ensureSingleMedia,
+ staff.middleware.ensureMediaUser,
+
+ staff.media.show
+ );
+
+ //
+ // plans
+
+ app.get('/staff/plans',
+ middleware.ensureAuthenticated,
+ middleware.ensureIsStaff,
+
+ staff.middleware.ensurePlans,
+
+ staff.plans.index
+ );
+ app.get('/staff/plans/new',
+ middleware.ensureAuthenticated,
+ middleware.ensureIsStaff,
+
+ staff.plans.new
+ );
+ app.post('/staff/plans/new',
+ middleware.ensureAuthenticated,
+ middleware.ensureIsStaff,
+
+ staff.plans.create
+ );
+ app.get('/staff/plans/:slug',
+ middleware.ensureAuthenticated,
+ middleware.ensureIsStaff,
+
+ staff.middleware.ensurePlan,
+
+ staff.plans.edit
+ );
+ app.post('/staff/plans/:slug',
+ middleware.ensureAuthenticated,
+ middleware.ensureIsStaff,
+
+ staff.middleware.ensurePlan,
+
+ staff.plans.update
+ );
+
+ //
+ // subscriptions
+ app.get('/staff/subscriptions',
+ middleware.ensureAuthenticated,
+ middleware.ensureIsStaff,
+
+ staff.middleware.ensureSubscriptions,
+ staff.middleware.ensureSubscriptionsUsers,
+
+ staff.subscriptions.index
+ );
+ app.get('/staff/subscriptions/:id',
+ middleware.ensureAuthenticated,
+ middleware.ensureIsStaff,
+
+ staff.middleware.ensureSubscription,
+ staff.middleware.ensureSubscriptionUser,
+
+ staff.subscriptions.show
+ );
+ },
+
+ paginate: function(req, res){
+ var info = res.locals.pagination
+ info.query = "sort=" + info.sort + "&limit=" + info.limit
+ info.first_page = 0
+ info.last_page = Math.max(0, info.max - info.limit)
+ info.sortOptions = info.sortOptions
+ if (info.offset > 0) {
+ info.prev_page = Math.max(0, info.offset - info.limit)
+ }
+ else {
+ info.prev_page = -1
+ }
+ if (info.count == info.limit && info.offset + info.limit < info.max) {
+ info.next_page = info.offset + info.limit
+ }
+ else {
+ info.next_page = -1
+ }
+ },
+
+ index: function(req, res){
+ res.render('staff/index')
+ },
+
+ // /staff/users/
+ // /staff/users/:username
+ users: {
+ index: function(req, res){
+ res.locals.pagination.count = res.locals.users.length
+ res.locals.pagination.max = res.locals.userCount
+ staff.paginate(req, res)
+ res.render('staff/users/index')
+ },
+ show: function(req, res){
+ if (res.locals.profile) {
+ res.render('staff/users/show', {
+ profileJSON: util.escape( JSON.stringify( res.locals.profile ) )
+ })
+ }
+ else {
+ res.render('staff/users/show_404')
+ }
+ },
+ media: function(req, res){
+ if (res.locals.profile) {
+ res.locals.pagination.count = res.locals.media.length
+ res.locals.pagination.max = res.locals.profile.mediaCount
+ staff.paginate(req, res)
+ res.render('staff/users/media')
+ }
+ else {
+ res.render('staff/users/show_404')
+ }
+ },
+ blessSelf: function(req, res){
+ req.user.isStaff = true
+ req.user.save(function(err, user){
+ res.json({ state: user.isStaff })
+ })
+ },
+ bless: function(req, res){
+ res.locals.profile.isStaff = req.body.state == "true"
+ res.locals.profile.save(function(err, user){
+ res.json({ state: user.isStaff })
+ })
+ },
+ make_artist: function(req, res){
+ res.locals.profile.isArtist = req.body.state == "true"
+ res.locals.profile.save(function(err, user){
+ res.json({ state: user.isArtist })
+ })
+ },
+ },
+
+ // /staff/projects/
+ // /staff/projects/:slug
+ projects: {
+ index: function(req, res){
+ res.locals.pagination.count = res.locals.projects.length
+ res.locals.pagination.max = res.locals.projectCount
+ staff.paginate(req, res)
+ res.render('staff/projects/index')
+ },
+ show: function(req, res){
+ if (res.locals.project) {
+ res.render('staff/projects/show', {
+ projectJSON: util.escape( JSON.stringify( res.locals.project ) ),
+ projectUserJSON: util.escape( JSON.stringify( res.locals.projectUser ) ),
+ collaboratorsJSON: util.escape( JSON.stringify( res.locals.collaborators ) ),
+ })
+ }
+ else {
+ res.render('staff/projects/show_404')
+ }
+ },
+ feature: function(req, res){
+ res.locals.project.featured = req.body.state == "true"
+ res.locals.project.save(function(err, project){
+ res.json({ state: project.featured })
+ })
+ },
+ },
+
+ // /staff/layouts/
+ // /staff/layouts/:slug
+ layouts: {
+ index: function(req, res){
+ res.locals.pagination.count = res.locals.layouts.length
+ res.locals.pagination.max = res.locals.layoutCount
+ staff.paginate(req, res)
+ res.render('staff/layouts/index')
+ },
+ show: function(req, res){
+ if (res.locals.layout) {
+ res.render('staff/layouts/show', {
+ })
+ }
+ else {
+ res.render('staff/layouts/show_404')
+ }
+ },
+ make_stock: function(req, res){
+ res.locals.layout.is_stock = req.body.state == "true"
+ res.locals.layout.save(function(err, layout){
+ res.json({ state: layout.is_stock })
+ })
+ },
+ },
+
+ // /staff/blueprints/
+ // /staff/blueprints/:slug
+ blueprints: {
+ index: function(req, res){
+ res.locals.pagination.count = res.locals.blueprints.length
+ res.locals.pagination.max = res.locals.blueprintCount
+ staff.paginate(req, res)
+ res.render('staff/blueprints/index')
+ },
+ show: function(req, res){
+ if (res.locals.blueprint) {
+ res.render('staff/blueprints/show', {
+ })
+ }
+ else {
+ res.render('staff/blueprints/show_404')
+ }
+ },
+ },
+
+ media: {
+ index: function(req, res){
+ res.locals.pagination.count = res.locals.media.length
+ res.locals.pagination.max = res.locals.mediaCount
+ staff.paginate(req, res)
+ res.render('staff/media/index')
+ },
+ show: function(req, res){
+ if (res.locals.media) {
+ res.render('staff/media/show', {
+ mediaJSON: util.escape( JSON.stringify( res.locals.media ) ),
+ mediaUserJSON: util.escape( JSON.stringify( res.locals.mediaUser ) ),
+ })
+ }
+ else {
+ res.render('staff/media/show_404')
+ }
+ },
+ },
+
+ plans: {
+ index: function(req, res){
+ res.locals.fields = staff.fields.plans.split(" ")
+ res.locals.permissions = staff.fields.plans_permissions.split(" ")
+ res.render('staff/plans/index')
+ },
+ new: function(req, res){
+ res.locals.plan = new Plan ()
+ res.render('staff/plans/new')
+ },
+ edit: function(req, res){
+ res.locals.plan = req.plan
+ res.render('staff/plans/edit')
+ },
+ create: function(req, res){
+ var plan = new Plan ()
+ var fields = staff.fields.plans.split(" ")
+ var permissions = staff.fields.plans_permissions.split(" ")
+
+ var data = util.cleanQuery(req.body)
+ data.name = util.sanitize(data.name)
+ data.slug = util.sanitize(data.slug.toLowerCase())
+
+ data.permissions = {}
+ permissions.forEach(function(field){
+ data.permissions[field] = data["permissions_" + field].length == 2
+ })
+
+ new Plan (data).save(function(err, doc){
+ if (err || ! doc) { return res.json({ error: err }) }
+ middleware.updatePlans()
+ res.redirect("/staff/plans/")
+ })
+ },
+ update: function(req, res){
+ var fields = staff.fields.plans.split(" ")
+ var permissions = staff.fields.plans_permissions.split(" ")
+
+ var data = util.cleanQuery(req.body)
+ data.name = util.sanitize(data.name)
+ data.slug = util.sanitize(data.slug.toLowerCase())
+
+ _.extend(req.plan, data)
+ permissions.forEach(function(field){
+ req.plan.permissions[field] = data["permissions_" + field].length == 2
+ })
+
+ req.plan.save(function(err, doc){
+ if (err || ! doc) { return res.json({ error: err }) }
+ middleware.updatePlans()
+ res.redirect("/staff/plans/")
+ })
+ },
+ },
+
+ subscriptions: {
+ index: function(req, res){
+ res.locals.pagination.count = res.locals.subscriptions.length
+ res.locals.pagination.max = res.locals.subscriptionCount
+ staff.paginate(req, res)
+ res.render('staff/subscriptions/index')
+ },
+ show: function(req, res){
+ res.render('staff/subscriptions/show')
+ },
+ },
+
+}
diff --git a/server/lib/views/staff/middleware.js b/server/lib/views/staff/middleware.js
new file mode 100644
index 0000000..03fda2e
--- /dev/null
+++ b/server/lib/views/staff/middleware.js
@@ -0,0 +1,476 @@
+
+var User = require('../../schemas/User'),
+ Project = require('../../schemas/Project'),
+ Media = require('../../schemas/Media'),
+ Collaborator = require('../../schemas/Collaborator'),
+ Plan = require('../../schemas/Plan'),
+ Subscription = require('../../schemas/Subscription'),
+ Layout = require('../../schemas/Layout'),
+ Blueprint = require('../../schemas/Blueprint'),
+ util = require('../../util'),
+ fields = require('./fields'),
+ helpers = require('./helpers'),
+ defaults = require('./defaults'),
+ _ = require('lodash'),
+ moment = require('moment');
+
+
+var middleware = module.exports = {
+
+ ensureUsers: function(req, res, next){
+ var paginationInfo = res.locals.pagination = {}
+ var criteria = req.criteria || {}
+ var limit = paginationInfo.limit = Math.min( Number(req.query.limit) || 50, 200 )
+ var offset = paginationInfo.offset = Number(req.query.offset) || 0
+ var initial = util.sanitize(req.query.initial)
+ var sort
+ paginationInfo.sort = req.query.sort
+ paginationInfo.sortOptions = ["date", "last_seen", "username"]
+ switch (req.query.sort) {
+ case 'date':
+ sort = {'created_at': -1}
+ break
+ case 'last_seen':
+ sort = {'last_seen': -1}
+ break
+ case 'username':
+ default:
+ sort = {'username': 1}
+ paginationInfo.sort = "username"
+ break
+ }
+ if (initial) {
+ if (initial == "?") {
+ criteria.username = new RegExp('^[^a-zA-Z]', "i")
+ }
+ else {
+ criteria.username = new RegExp('^' + initial, "i")
+ }
+ }
+ User.find(criteria)
+ .select(fields.user)
+ .sort(sort)
+ .skip(offset)
+ .limit(limit)
+ .exec(function (err, users) {
+ res.locals.users = users.map(helpers.user)
+ if (! res.locals.users.length) {
+ if (initial) {
+ res.locals.opt.error = "No users found starting with <b>" + initial.toUpperCase() + "</b>"
+ }
+ else {
+ res.locals.opt.error = "No users found"
+ }
+ }
+ next()
+ })
+ },
+
+ ensureRecentUsers: function(req, res, next){
+ var dreq = { query: { sort: 'last_seen', limit: 20, offset: 0 } }
+ middleware.ensureUsers(dreq, res, next)
+ },
+
+ ensureProjects: function(req, res, next){
+ var paginationInfo = res.locals.pagination = {}
+ var criteria = req.criteria || {}
+ var limit = paginationInfo.limit = Math.min( Number(req.query.limit) || 50, 200 )
+ var offset = paginationInfo.offset = Number(req.query.offset) || 0
+ var sort
+ paginationInfo.sort = req.query.sort
+ paginationInfo.sortOptions = ["date", "name"]
+ switch (req.query.sort) {
+ default:
+ case 'date':
+ sort = {'updated_at': -1}
+ break
+ case 'name':
+ paginationInfo.sort = "name"
+ sort = {'slug': 1}
+ break
+ }
+ Project.find(criteria)
+ .select(fields.project)
+ .sort(sort)
+ .skip(offset)
+ .limit(limit)
+ .exec(function (err, projects) {
+ res.locals.projects = projects.map(helpers.project)
+ next()
+ })
+ },
+
+ ensureLayouts: function(req, res, next){
+ var paginationInfo = res.locals.pagination = {}
+ var criteria = req.criteria || {}
+ var limit = paginationInfo.limit = Math.min( Number(req.query.limit) || 50, 200 )
+ var offset = paginationInfo.offset = Number(req.query.offset) || 0
+ var sort
+ paginationInfo.sort = req.query.sort
+ paginationInfo.sortOptions = ["date", "name"]
+ switch (req.query.sort) {
+ default:
+ case 'date':
+ sort = {'updated_at': -1}
+ break
+ case 'name':
+ paginationInfo.sort = "name"
+ sort = {'slug': 1}
+ break
+ }
+ Layout.find(criteria)
+ .select(fields.layout)
+ .sort(sort)
+ .skip(offset)
+ .limit(limit)
+ .exec(function (err, layouts) {
+ res.locals.layouts = layouts.map(helpers.layout)
+ next()
+ })
+ },
+
+ ensureBlueprints: function(req, res, next){
+ var paginationInfo = res.locals.pagination = {}
+ var criteria = req.criteria || {}
+ var limit = paginationInfo.limit = Math.min( Number(req.query.limit) || 50, 200 )
+ var offset = paginationInfo.offset = Number(req.query.offset) || 0
+ var sort
+ paginationInfo.sort = req.query.sort
+ paginationInfo.sortOptions = ["date", "name"]
+ switch (req.query.sort) {
+ default:
+ case 'date':
+ sort = {'updated_at': -1}
+ break
+ case 'name':
+ paginationInfo.sort = "name"
+ sort = {'slug': 1}
+ break
+ }
+ Blueprint.find(criteria)
+ .select(fields.blueprint)
+ .sort(sort)
+ .skip(offset)
+ .limit(limit)
+ .exec(function (err, blueprints) {
+ res.locals.blueprints = blueprints.map(helpers.blueprint)
+ next()
+ })
+ },
+
+ ensureMedia: function(req, res, next){
+ var paginationInfo = res.locals.pagination = {}
+ var criteria = req.criteria || {}
+ var limit = paginationInfo.limit = Math.min( Number(req.query.limit) || 50, 200 )
+ var offset = paginationInfo.offset = Number(req.query.offset) || 0
+ var sort
+ paginationInfo.sort = req.query.sort
+ paginationInfo.sortOptions = ["date"]
+ switch (req.query.sort) {
+ default:
+ case 'date':
+ paginationInfo.sort = "date"
+ sort = {'created_at': -1}
+ break
+ }
+ Media.find(criteria)
+ // .select(fields.media)
+ .sort(sort)
+ .skip(offset)
+ .limit(limit)
+ .exec(function (err, media) {
+ res.locals.media = media.map(helpers.media)
+ next()
+ })
+ },
+
+ ensureSubscriptions: function(req, res, next){
+ var paginationInfo = res.locals.pagination = {}
+ var criteria = req.criteria || {}
+ var limit = paginationInfo.limit = Math.min( Number(req.query.limit) || 50, 200 )
+ var offset = paginationInfo.offset = Number(req.query.offset) || 0
+ var sort
+ paginationInfo.sort = req.query.sort
+ paginationInfo.sortOptions = ["date", "name"]
+ switch (req.query.sort) {
+ case 'created':
+ sort = {'created_at': -1}
+ break
+ default:
+ case 'date':
+ sort = {'updated_at': -1}
+ break
+ }
+ Subscription.find(criteria)
+ .select(fields.project)
+ .sort(sort)
+ .skip(offset)
+ .limit(limit)
+ .exec(function (err, subscriptions) {
+ res.locals.subscriptions = subscriptions.map(helpers.subscription)
+ next()
+ })
+ },
+
+ ensurePlans: function(req, res, next){
+ Plan.find({}).sort({ 'level': -1 }).exec(function (err, plans) {
+ res.locals.plans = (plans || []).map(helpers.plan)
+ res.locals.plans.sort(function(a,b){ return a.monthly_price })
+ next()
+ })
+ },
+ ensurePlan: function (req, res, next) {
+ if (req.params.slug) {
+ Plan.findOne({ slug: req.params.slug }, function(err, plan){
+ if (err || ! plan) {
+ console.error(err)
+ res.redirect("/staff/plans/")
+ }
+ else {
+ req.plan = plan
+ next()
+ }
+ })
+ }
+ else {
+ res.redirect("/staff/plans/")
+ }
+ },
+
+ ensureSubscription: function (req, res, next) {
+ if (req.params.id) {
+ Subscription.findOne({ _id: req.params.id }, function(err, subscription){
+ if (err || ! subscription) {
+ console.error(err)
+ res.redirect("/staff/subscriptions/")
+ }
+ else {
+ req.subscription = subscription
+ next()
+ }
+ })
+ }
+ else {
+ res.redirect("/staff/subscriptions/")
+ }
+ },
+
+ ensureRecentProjects: function(req, res, next){
+ var dreq = { params: { sort: 'created_at', limit: 20, offset: 0 } }
+ middleware.ensureProjects(dreq, res, next)
+ },
+
+ ensureProjectsUsers: function(req, res, next){
+ if (! res.locals.projects || ! res.locals.projects.length) { return next() }
+ middleware.ensureObjectsUsers(res.locals.projects, next)
+ },
+
+ ensureLayoutsUsers: function(req, res, next){
+ if (! res.locals.layouts || ! res.locals.layouts.length) { return next() }
+ middleware.ensureObjectsUsers(res.locals.layouts, next)
+ },
+
+ ensureBlueprintsUsers: function(req, res, next){
+ if (! res.locals.blueprints || ! res.locals.blueprints.length) { return next() }
+ middleware.ensureObjectsUsers(res.locals.blueprints, next)
+ },
+
+ ensureSubscriptionsUsers: function(req, res, next){
+ if (! res.locals.subscriptions || ! res.locals.subscriptions.length) { return next() }
+ middleware.ensureObjectsUsers(res.locals.subscriptions, next)
+ },
+
+ ensureMediaUsers: function(req, res, next){
+ if (! res.locals.media || ! res.locals.media.length) { return next() }
+ middleware.ensureObjectsUsers(res.locals.media, next)
+ },
+
+ ensureSubscriptionUser: function(req, res, next){
+ if (! res.locals.subscription) { return next() }
+ middleware.ensureObjectsUsers([ res.locals.subscription ], function(){
+ next()
+ })
+ },
+
+ ensureMediaUser: function(req, res, next){
+ if (! res.locals.media) { return next() }
+ middleware.ensureObjectsUsers([ res.locals.media ], function(){
+ res.locals.mediaUser = res.locals.media.User
+ next()
+ })
+ },
+
+ ensureObjectsUsers: function(objects, next){
+ if (! objects) { return next () }
+ var dedupe = {}, user_ids
+ objects.forEach(function(obj){
+ dedupe[ obj.user_id ] = dedupe[ obj.user_id ] || []
+ dedupe[ obj.user_id ].push(obj)
+ })
+ user_ids = _.keys(dedupe)
+ User.find({ _id: { $in: user_ids } })
+ .select(fields.user)
+ .exec(function (err, users) {
+ if (! users) { return next () }
+ users.forEach(function(user){
+ dedupe[user._id].forEach(function(obj){
+ obj.user = user
+ })
+ })
+ next()
+ })
+ },
+
+ ensureProfile: function(req, res, next){
+ var username = req.params.username
+ if (username) {
+ User.findOne({ username: username }, function (err, user) {
+ if (user) {
+ res.locals.profile = req.method == "GET" ? helpers.user(user) : user
+ }
+ else {
+ res.locals.profile = null
+ }
+ next()
+ })
+ }
+ else {
+ res.locals.profile = null
+ next()
+ }
+ },
+
+ ensureSingleMedia: function(req, res, next){
+ var id = req.params.id
+ if (id) {
+ Media.findOne({ _id: id }, function (err, media) {
+ if (media) {
+ res.locals.media = req.method == "GET" ? helpers.media(media) : media
+ }
+ else {
+ res.locals.media = null
+ }
+ next()
+ })
+ }
+ else {
+ res.locals.media = null
+ next()
+ }
+ },
+
+ ensureUsersCount: function(req, res, next){
+ User.count({}, function(err, count){
+ res.locals.userCount = count || 0
+ next()
+ })
+ },
+
+ ensureProjectsCount: function(req, res, next){
+ Project.count({}, function(err, count){
+ res.locals.projectCount = count || 0
+ next()
+ })
+ },
+
+ ensureLayoutsCount: function(req, res, next){
+ Layout.count({}, function(err, count){
+ res.locals.layoutCount = count || 0
+ next()
+ })
+ },
+
+ ensureBlueprintsCount: function(req, res, next){
+ Blueprint.count({}, function(err, count){
+ res.locals.blueprintCount = count || 0
+ next()
+ })
+ },
+
+ ensureMediaCount: function(req, res, next){
+ Media.count({}, function(err, count){
+ res.locals.mediaCount = count || 0
+ next()
+ })
+ },
+
+ ensureProfileProjectCount: function(req, res, next){
+ if (! res.locals.profile) { return next() }
+ Project.count({ user_id: res.locals.profile._id}, function(err, count){
+ res.locals.profile.projectCount = count || 0
+ next()
+ })
+ },
+
+ ensureProfileMediaCount: function(req, res, next){
+ if (! res.locals.profile) { return next() }
+ Media.count({ user_id: res.locals.profile._id}, function(err, count){
+ res.locals.profile.mediaCount = count || 0
+ next()
+ })
+ },
+
+ ensureProfileProjects: function(req, res, next){
+ if (! res.locals.profile) { return next() }
+ Project.find({ user_id: res.locals.profile._id }, fields.project, function(err, projects){
+ res.locals.projects = projects.map(helpers.project)
+ next()
+ })
+ },
+
+ ensureProfileMedia: function(req, res, next){
+ if (! res.locals.profile) { return next() }
+ req.criteria = { user_id: res.locals.profile._id }
+ middleware.ensureMedia(req, res, next)
+ },
+
+ ensureProject: function(req, res, next){
+ res.locals.project = req.project
+ next()
+ },
+
+ ensureProjectUser: function(req, res, next){
+ if (! res.locals.project) { return next() }
+ User.findOne({ _id: res.locals.project.user_id }, fields.user, function(err, user){
+ res.locals.projectUser = helpers.user(user) || defaults.user
+ next()
+ })
+ },
+
+ ensureProjectCollaborators: function(req, res, next){
+ if (! res.locals.project) {
+ res.locals.collaborators = []
+ return next()
+ }
+ Collaborator.find({ project_id: res.locals.project._id}, function(err, collaborators){
+ res.locals.collaborators = collaborators || []
+ next()
+ })
+ },
+
+ ensureLayout: function(req, res, next){
+ res.locals.layout = req.layout
+ next()
+ },
+ ensureLayoutUser: function(req, res, next){
+ if (! res.locals.layout) { return next() }
+ User.findOne({ _id: res.locals.layout.user_id }, fields.user, function(err, user){
+ res.locals.layoutUser = helpers.user(user) || defaults.user
+ next()
+ })
+ },
+
+ ensureBlueprint: function(req, res, next){
+ res.locals.blueprint = req.blueprint
+ next()
+ },
+ ensureBlueprintUser: function(req, res, next){
+ if (! res.locals.blueprint) { return next() }
+ User.findOne({ _id: res.locals.blueprint.user_id }, fields.user, function(err, user){
+ res.locals.blueprintUser = helpers.user(user) || defaults.user
+ next()
+ })
+ },
+
+
+} \ No newline at end of file
diff --git a/server/lib/webhook/index.js b/server/lib/webhook/index.js
new file mode 100644
index 0000000..a5f23ac
--- /dev/null
+++ b/server/lib/webhook/index.js
@@ -0,0 +1,44 @@
+var config = require('../../../config.json'),
+ http = require('http'),
+ express = require('express'),
+ bodyParser = require('body-parser'),
+ mongoose = require('mongoose');
+
+var http = require('http'),
+ express = require('express'),
+ bodyParser = require('body-parser'),
+ multer = require('multer'),
+ MongoStore = require('connect-mongo')(express),
+ passport = require('passport'),
+ path = require('path'),
+ mongoose = require('mongoose');
+
+var webhook = require('./webhook');
+
+var app = express()
+var server
+var DATABASE_URI = process.env.MONGOLAB_URI || ('mongodb://' + config.databaseHost + '/vvalls')
+
+var site = {}
+
+site.init = function(){
+ mongoose.connect(DATABASE_URI, {}, site.ready);
+}
+
+site.ready = function(){
+ app.set('port', config.webhookPort);
+ // app.use(bodyParser());
+ app.use(express.query());
+ app.set('env', config.env.production ? "production" : "development")
+ app.get('env') === 'development' && app.use(express.errorHandler());
+
+ server = http.createServer(app)
+ server.listen(app.get('port'), function () {
+ console.log('Webhook server listening on port ' + app.get('port'));
+ });
+
+ app.get('/', function(req,res){ res.send('hello@vvalls.com') })
+ webhook.route(app)
+}
+
+site.init()
diff --git a/server/lib/webhook/recurly-config.js b/server/lib/webhook/recurly-config.js
new file mode 100644
index 0000000..3d7e1c5
--- /dev/null
+++ b/server/lib/webhook/recurly-config.js
@@ -0,0 +1,6 @@
+module.exports = {
+ API_KEY: process.env['VVALLS_RECURLY_SECRET'],
+ SUBDOMAIN: 'vvalls',
+ ENVIRONMENT: 'sandbox',
+ DEBUG: true,
+};
diff --git a/server/lib/webhook/webhook.js b/server/lib/webhook/webhook.js
new file mode 100644
index 0000000..896d836
--- /dev/null
+++ b/server/lib/webhook/webhook.js
@@ -0,0 +1,178 @@
+// // where should this live?
+
+/*
+app.use(express.basicAuth(function(user, pass, callback) {
+ var result = (user === 'testUser' && pass === 'testPass');
+ callback(null, result);
+}));
+*/
+
+
+/* jshint node: true */
+
+var User = require('../schemas/User'),
+ Subscription = require('../schemas/Subscription'),
+ config = require('../../../config'),
+ middleware = require('../middleware'),
+ util = require('../util'),
+ _ = require('lodash'),
+ moment = require('moment'),
+ xml2js = require('xml2js'),
+ Recurly = require('node-recurly'),
+ recurly = new Recurly(require('./recurly-config'));
+
+var xml_bodyparser = require('express-xml-bodyparser');
+
+var parser = new xml2js.Parser();
+
+var subscribe = module.exports = {
+ plan_levels: {
+ free: 0,
+ basic: 1,
+ pro: 2,
+ custom: 3,
+ artist: 4,
+ },
+
+ callbacks: {
+/*
+ // accounts
+ new_account_notification: function(data, user){
+ // fires on successful signup
+ },
+ canceled_account_notification: function(data, user){
+ },
+ billing_info_updated_notification: function(data, user){
+ },
+ reactivated_account_notification: function(data, user){
+ },
+
+ // invoices
+ new_invoice_notification: function(data, user){
+ },
+ closed_invoice_notification: function(data, user){
+ },
+ past_due_invoice_notification: function(data, user){
+ },
+*/
+
+ // subscriptions
+ new_subscription_notification: function(data, user){
+ var account = data.account[0].account_code[0]
+ var subscrip = data.subscription[0]
+ var uuid = subscrip.uuid[0]
+ Subscription.findOne({ "uuid": uuid }, function(err, old_subscriber){
+ // if (err) return;
+
+ var plan, plan_type, plan_code
+ var plan_code = subscrip.plan[0].plan_code[0]
+
+ if (plan_code.indexOf("-") !== -1) {
+ plan = plan_code.split("-")
+ plan_type = plan[0]
+ plan_period = plan[1]
+ }
+ else {
+ plan_type = "custom"
+ plan_period = "monthly"
+ }
+
+ user.plan_code = plan_code
+ user.plan_type = plan_type
+ user.plan_level = subscribe.plan_levels[plan_type]
+
+ var subscriber = old_subscriber || new Subscription ()
+ subscriber.uuid = uuid
+ subscriber.user_id = user._id
+ subscriber.plan_code = plan_code
+ subscriber.plan_type = plan_type
+ subscriber.plan_period = plan_period
+ subscriber.plan_level = subscribe.plan_levels[plan_type]
+ subscriber.add_ons = []
+ var add_ons = subscrip.subscription_add_ons[0].subscription_add_on
+ if (add_ons) {
+ add_ons.forEach(function(add_on){
+ switch (add_on.add_on_code[0]) {
+ case 'extra-basic-layout':
+ subscriber.basic_layouts = parseInt(add_on.quantity[0]._, 10) || 0
+ break
+ case 'extra-pro-layout':
+ subscriber.pro_layouts = parseInt(add_on.quantity[0]._, 10) || 0
+ break
+ }
+ })
+ }
+ subscriber.save(function(err, data){
+ if (err) return;
+ user.save(function(err){
+ // saved!
+ })
+ })
+ })
+ },
+
+/*
+ updated_subscription_notification: function(data, user){
+ },
+ canceled_subscription_notification: function(data, user){
+ },
+ expired_subscription_notification: function(data, user){
+ },
+ renewed_subscription_notification: function(data, user){
+ },
+*/
+ // payments
+ successful_payment_notification: function(data, user){
+ var account = data.account[0]
+ user.last_charged = new Date(data.transaction[0].date[0]._)
+ user.save(function(){
+ })
+ },
+/*
+ failed_payment_notification: function(data, user){
+ },
+ successful_refund_notification: function(data, user){
+ },
+ void_payment_notification: function(data, user){
+ },
+*/
+ },
+
+ execute: function(action, data){
+ User.findOne({ _id: data.account[0].account_code[0] }, function(err, user){
+ if (err) { return }
+ subscribe.callbacks[action](data, user)
+ })
+ },
+
+ // then calls to get appropriate info from the recurly api
+ handle: function(req, res){
+ console.log(req.body)
+ // parser.parseString(req.body, function (err, result) {
+ var result = req.body
+ for (var action in result) {
+ if (subscribe.callbacks[action]) {
+ subscribe.execute(action, result[action]);
+ }
+ }
+ return res.status(200).end()
+ },
+
+ list: function(req, res){
+ recurly.subscriptions.listByAccount(req.params.id, function(data){
+ if (data.data != 404) {
+ res.json(data)
+ return
+ }
+ else {
+ res.json(data)
+ return
+ }
+ })
+ },
+
+ route: function(app){
+ app.post('/subscribe/webhook', xml_bodyparser(), subscribe.handle);
+ app.get('/subscribe/list/:id', subscribe.list);
+ },
+}