summaryrefslogtreecommitdiff
path: root/server/lib/api
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/api
parent343b0b3dc5bb7dbe762182a486e63a4aff6ef8fc (diff)
parent9e7bacd46c1e5d0e1c24433690d421ab3f3a11f2 (diff)
merge
Diffstat (limited to 'server/lib/api')
-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
7 files changed, 499 insertions, 53 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