diff --git a/README.md b/README.md index 1610142..594365c 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,9 @@ It also has some optional binary dependencies for advanced media conversion: By default, media files are uploaded to the ```storage``` folder. -To run Spacedeck, you only need Node.JS 9.x. Then, to install all node dependencies, run +To use Spacedeck, you only need Node.JS 9.x. + +Then, to install all node dependencies, run npm install @@ -47,13 +49,16 @@ To rebuild the frontend CSS styles (you need to do this at least once): See [config/default.json](config/default.json) -# Run +# Run (web server) - export NODE_ENV=development - npm start + node spacedeck.js Then open http://localhost:9666 in a web browser. +# Run (desktop app with integrated web server) + + electron . + # License The Spacedeck logo and brand assets are registered trademarks of Spacedeck GmbH. All rights reserved. diff --git a/models/action.js b/models/action.js index 71a0da4..61cf38b 100644 --- a/models/action.js +++ b/models/action.js @@ -1,5 +1,7 @@ 'use strict'; +// FIXME port this last model + var mongoose = require('mongoose'); var Schema = mongoose.Schema; diff --git a/models/artifact.js b/models/artifact.js deleted file mode 100644 index 61d7e4a..0000000 --- a/models/artifact.js +++ /dev/null @@ -1,88 +0,0 @@ -'use strict'; - -var mongoose = require('mongoose'); -var Schema = mongoose.Schema; - -module.exports.artifactSchema = Schema({ - mime: String, - thumbnail_uri: String, - space_id: Schema.Types.ObjectId, - user_id: {type: Schema.Types.ObjectId, ref: 'User' }, - last_update_user_id: {type: Schema.Types.ObjectId, ref: 'User' }, - editor_name: String, - last_update_editor_name: String, - description: String, - state: {type: String, default: "idle"}, - meta: { - linked_to: [String], - title: String, - tags: [String], - search_text: String, - link_uri: String, - play_from: Number, - play_to: Number, - }, - board: { - x: {type: Number, default: 0.0}, - y: {type: Number, default: 0.0}, - z: {type: Number, default: 0.0}, - r: {type: Number, default: 0.0}, - w: {type: Number, default: 100}, - h: {type: Number, default: 100}, - }, - control_points: [{ - dx: Number, dy: Number - }], - group:{type: String, default: ""}, - locked: {type: Boolean, default: false}, - payload_uri: String, - payload_thumbnail_web_uri: String, - payload_thumbnail_medium_uri: String, - payload_thumbnail_big_uri: String, - payload_size: Number, // file size in bytes - style: { - fill_color: {type: String, default: "transparent"}, - stroke_color:{type: String, default: "#000000"}, - text_color: String, - stroke: {type: Number, default: 0.0}, - stroke_style: {type: String, default: "solid"}, - alpha: {type: Number, default: 1.0}, - order: {type: Number, default: 0}, - crop: { - x: Number, - y: Number, - w: Number, - h: Number - }, - shape: String, - shape_svg: String, - padding_left: Number, - padding_right: Number, - padding_top: Number, - padding_bottom: Number, - margin_left: Number, - margin_right: Number, - margin_top: Number, - margin_bottom: Number, - border_radius: Number, - align: {type: String, default: "left"}, - valign: {type: String, default: "top"}, - brightness: Number, - contrast: Number, - saturation: Number, - blur: Number, - hue: Number, - opacity: Number - }, - payload_alternatives: [{ - mime: String, - payload_uri: String, - payload_thumbnail_web_uri: String, - payload_thumbnail_medium_uri: String, - payload_thumbnail_big_uri: String, - payload_size: Number - }], - created_at: {type: Date, default: Date.now}, - created_from_ip: {type: String}, - updated_at: {type: Date, default: Date.now} -}); diff --git a/models/membership.js b/models/membership.js deleted file mode 100644 index 44b9b70..0000000 --- a/models/membership.js +++ /dev/null @@ -1,45 +0,0 @@ -'use strict'; - -var mongoose = require('mongoose'); -var Schema = mongoose.Schema; - -module.exports.membershipSchema = mongoose.Schema({ - user: { - type: Schema.Types.ObjectId, - ref: 'User' - }, - space: { - type: Schema.Types.ObjectId, - ref: 'Space' - }, - team: { - type: Schema.Types.ObjectId, - ref: 'Team' - }, - role: { - type: String, - default: "viewer" - }, - state: { - type: String, - default: "active" - }, - email_invited: String, - code: String, - created_at: { - type: Date, - default: Date.now - }, - updated_at: { - type: Date, - default: Date.now - } -}); - -module.exports.membershipSchema.index({ - user: 1, - space: 1, - team: 1, - code: 1 -}); - diff --git a/models/message.js b/models/message.js deleted file mode 100644 index 5cbfe0e..0000000 --- a/models/message.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict'; - -var mongoose = require('mongoose'); -var Schema = mongoose.Schema; - -module.exports.messageSchema = mongoose.Schema({ - user: { - type: Schema.Types.ObjectId, - ref: 'User' - }, - editor_name: String, - space: { - type: Schema.Types.ObjectId, - ref: 'Space' - }, - message: String, - created_from_ip: {type: String}, - created_at: { - type: Date, - default: Date.now - }, - updated_at: { - type: Date, - default: Date.now - } -}); - -module.exports.messageSchema.index({ - space: 1, - user: 1 -}); diff --git a/models/space.js b/models/space.js deleted file mode 100644 index e8ff3f6..0000000 --- a/models/space.js +++ /dev/null @@ -1,273 +0,0 @@ -'use strict'; - -var mongoose = require('mongoose'); -var Schema = mongoose.Schema; -var async = require('async'); -var _ = require("underscore"); -var crypto = require('crypto'); - -module.exports.spaceSchema = Schema({ - name: {type: String, default: "New Space"}, - space_type: {type: String, default: "space"}, - - creator : { type: Schema.Types.ObjectId, ref: 'User' }, - parent_space_id: Schema.Types.ObjectId, - - access_mode: {type: String, default: "private"}, // "public" || "private" - password: String, - edit_hash: String, - edit_slug: String, - editors_locking: Boolean, - - thumbnail_uri: String, - stats: { - num_children: Number, - total_spaces: Number, - total_folders: Number, - storage_bytes: Number, - }, - - advanced: { - type: { - width: Number, - height: Number, - margin: Number, - background_color: String, - background_uri: String, - background_repeat: Boolean, - grid_size: Number, - grid_divisions: Number, - gutter: Number, - columns: Number, - column_max_width: Number, - columns_responsive: Number, - row_max_height: Number, - padding_horz: Number, - padding_vert: Number - }, - default: { - width: 200, - height: 400, - margin: 0, - background_color: "rgba(255,255,255,1)" - } - }, - blocked_at: {type: Date, default: Date.now}, - created_at: {type: Date, default: Date.now}, - updated_at: {type: Date, default: Date.now}, - thumbnail_updated_at: {type: Date}, - thumbnail_url: String -}); - -module.exports.spaceSchema.index({ creator: 1, parent_space_id: 1, created_at: 1, updated_at: 1, edit_hash: 1}); -module.exports.spaceSchema.statics.allForUser = function (user, callback) { - return this.find({user_id: user_id}, callback); -}; - -module.exports.spaceSchema.statics.getMemberships = function (err, callback) { - callback(null, {}); -}; - -var getRecursiveSubspacesForSpace = (parentSpace, cb) => { - if (parentSpace.space_type == "folder") { - Space.find({ - "parent_space_id": parentSpace._id - }).exec((err, subspaces) => { - async.map(subspaces, (space, innerCb) => { - getRecursiveSubspacesForSpace(space, (err, spaces) => { - innerCb(err, spaces); - }); - }, (err, subspaces) => { - var flattenSubspaces = _.flatten(subspaces); - flattenSubspaces.push(parentSpace); - cb(null, flattenSubspaces); - }); - }); - } else { - cb(null, [parentSpace]); - } -}; - -module.exports.spaceSchema.statics.getRecursiveSubspacesForSpace = getRecursiveSubspacesForSpace; - -var roleMapping = { - "none": 0, - "viewer": 1, - "editor": 2, - "admin": 3 -} - -module.exports.spaceSchema.statics.roleInSpace = (originalSpace, user, cb) => { - if (user.home_folder_id.toString() === originalSpace._id.toString()) { - cb(null, "admin"); - return; - } - - if (originalSpace.creator) { - if (originalSpace.creator._id.toString() === user._id.toString()) { - cb(null, "admin"); - return; - } - } - - var findMembershipsForSpace = function(space, allMemberships, prevRole) { - Membership.find({ - "space": space._id - }, (err, parentMemberships) => { - var currentMemberships = parentMemberships.concat(allMemberships); - - if (space.parent_space_id) { - Space.findOne({ - "_id": space.parent_space_id - }, function(err, parentSpace) { - - var role = prevRole; - if(role == "none"){ - if(originalSpace.access_mode == "public") { - role = "viewer"; - } - } - - findMembershipsForSpace(parentSpace, currentMemberships, role); - }); - } else { - // reached the top - var role = prevRole; - space.memberships = currentMemberships; - currentMemberships.forEach(function(m, i) { - if (m.user && m.user.equals(user._id)) { - if (m.role != null) { - if (roleMapping[m.role] > roleMapping[role]) { - role = m.role; - } - } - } - }); - - cb(err, role); - } - }); - }; - findMembershipsForSpace(originalSpace, [], "none"); -} - -module.exports.spaceSchema.statics.recursiveDelete = (space, cb) => { - space.remove(function(err) { - - Action.remove({ - space: space - }, function(err) { - if (err) - console.error("removed actions for space: ", err); - }); - - Membership.remove({ - space: space - }, function(err) { - if (err) - console.error("removed memberships for space: ", err); - }); - - if (space.space_type === "folder") { - Space - .find({ - parent_space_id: space._id - }) - .exec(function(err, spaces) { - async.eachLimit(spaces, 10, function(subSpace, innerCb) { - module.exports.spaceSchema.statics.recursiveDelete(subSpace, function(err) { - innerCb(err); - }); - }, function(err) { - cb(err); - }); - }); - - } else { - - Artifact.find({ - space_id: space._id - }, function(err, artifacts) { - if (err) cb(err); - else { - async.eachLimit(artifacts, 20, function(a, innerCb) { - a.remove(function(err) { - innerCb(null, a); - }); - }, function(err) { - cb(err); - }); - - } - }); - } - }); -}; - -var duplicateRecursiveSpace = (space, user, depth, cb, newParentSpace) => { - var newSpace = new Space(space); - newSpace._id = mongoose.Types.ObjectId(); - - if (newParentSpace) { - newSpace.parent_space_id = newParentSpace._id; - } else { - newSpace.name = newSpace.name + " (b)"; - } - - newSpace.creator = user; - newSpace.created_at = new Date(); - newSpace.updated_at = new Date(); - - if (newSpace.space_type === "space") { - newSpace.edit_hash = crypto.randomBytes(64).toString('hex').substring(0, 7); - } - - newSpace.save(function(err) { - - if (newSpace.space_type === "folder" && depth < 10) { - - Space - .find({ - parent_space_id: space._id - }) - .exec(function(err, spaces) { - async.eachLimit(spaces, 10, function(subSpace, innerCb) { - - duplicateRecursiveSpace(subSpace, user, ++depth, function(err, newSubSpace) { - innerCb(err, newSubSpace); - }, newSpace); - - }, function(err, allNewSubspaces) { - cb(err, newSpace); - }); - }); - - } else { - - Artifact.find({ - space_id: space._id - }, function(err, artifacts) { - if (err) innerCb(err); - else { - async.eachLimit(artifacts, 20, function(a, innerCb) { - var newArtifact = new Artifact(a); - newArtifact._id = mongoose.Types.ObjectId(); - newArtifact.space_id = newSpace._id; - newArtifact.created_at = new Date(); - newArtifact.updated_at = new Date(); - - newArtifact.save(function(err) { - innerCb(null, newArtifact); - }); - - }, function(err, allNewArtifacts) { - cb(err, newSpace); - }); - } - }); - } - - }); -}; - -module.exports.spaceSchema.statics.duplicateSpace = duplicateRecursiveSpace; diff --git a/models/user.js b/models/user.js deleted file mode 100644 index e1c6887..0000000 --- a/models/user.js +++ /dev/null @@ -1,53 +0,0 @@ -'use strict'; - -var mongoose = require('mongoose'); -var Schema = mongoose.Schema; - -module.exports.userSchema = mongoose.Schema({ - email: String, - password_hash: String, - nickname: String, - account_type: {type: String, default: "email"}, - created_at: {type: Date, default: Date.now}, - updated_at: {type: Date, default: Date.now}, - avatar_original_uri: String, - avatar_thumb_uri: String, - src: String, - confirmation_token: String, - confirmed_at: Date, - password_reset_token: String, - home_folder_id: Schema.Types.ObjectId, - team : { type: Schema.Types.ObjectId, ref: 'Team' }, - preferences: { - language: String, - email_notifications: {type: Boolean, default: true}, - daily_digest_last_send: Date, - daily_digest: {type: Boolean, default: true} - }, - sessions: [ - { - token: String, - expires: Date, - device: String, - ip: String, - created_at: Date - } - ], - payment_info: String, - payment_plan_key: {type: String, default: "free"}, - payment_customer_id: String, - payment_subscription_id: String, - payment_notification_state: Number -}); - -module.exports.userSchema.index({ - email: 1, - "sessions.token": 1, - team: 1, - created_at: 1, - home_folder_id: 1 -}); - -module.exports.userSchema.statics.findBySessionToken = function (token, cb) { - return this.findOne({ "sessions.token": token}, cb); -};