Unverified Commit ebac854d authored by mntmn's avatar mntmn Committed by GitHub
Browse files

Port Backend to SQLite/Sequelize (removes MongoDB), Support Electron (#14)

* The MongoDB/Mongoose data storage is removed in favor of Sequelize. This abstracts over SQLite or RDBMs like PostgreSQL and MSSQL. The default is SQLite, which significantly simplifies deployments in end-user environments.

* As Spacedeck now has no more mandatory server dependencies, we can wrap it in Electron and ship it as a desktop application.

* Removes docker-compose.yml

* First version of import UI
parent 8e0bc69a
......@@ -21,7 +21,7 @@ function vec2_angle(v) {
}
function render_vector_drawing(a, padding) {
var shape = a.style.shape || "";
var shape = a.shape || "";
var path = [];
var p = a.control_points[0];
......@@ -48,8 +48,8 @@ function render_vector_drawing(a, padding) {
var d = "M" + (cps.dx + padding) + "," + (cps.dy + padding) + " Q" + (scaledMiddlePoint.dx + padding) + "," + (scaledMiddlePoint.dy + padding) + " " + (cpe.dx + padding) + "," + (cpe.dy + padding);
var tip = "<defs><marker id='ae" + markerId + "' refX=\"0.1\" refY=\"3\" markerWidth=\"3\" markerHeight=\"6\" orient=\"auto\">";
tip += "<path d=\"M-3,0 V6 L3,3 Z\" fill=\""+a.style.stroke_color+"\" stroke-width=\"0\"/></marker></defs>";
var svg = tip + "<path d='" + d + "' style='stroke-width:" + a.style.stroke + ";' marker-end='url(#ae" + markerId + ")'/>";
tip += "<path d=\"M-3,0 V6 L3,3 Z\" fill=\""+a.stroke_color+"\" stroke-width=\"0\"/></marker></defs>";
var svg = tip + "<path d='" + d + "' style='stroke-width:" + a.stroke + ";' marker-end='url(#ae" + markerId + ")'/>";
return svg;
}
......@@ -237,11 +237,11 @@ function render_vector_rect(xradius,yradius,offset) {
}
function render_vector_shape(a) {
var stroke = parseInt(a.style.stroke) + 4;
var stroke = parseInt(a.stroke) + 4;
var offset = stroke / 2;
var xr = (a.board.w-stroke) / 2;
var yr = (a.board.h-stroke) / 2;
var xr = (a.w-stroke) / 2;
var yr = (a.h-stroke) / 2;
var shape_renderers = {
ellipse: function() { return render_vector_ellipse(xr, yr, offset); },
......@@ -258,7 +258,7 @@ function render_vector_shape(a) {
cloud: function() { return render_vector_cloud(xr, yr, offset); },
}
var render_func = shape_renderers[a.style.shape];
var render_func = shape_renderers[a.shape];
if (!render_func) return "";
......
"use strict";
var config = require('config');
require('../../models/schema');
var fs = require('fs');
var _ = require("underscore");
var mongoose = require("mongoose");
var async = require('async');
var archiver = require('archiver');
var request = require('request');
var url = require("url");
var path = require("path");
var crypto = require('crypto');
var qr = require('qr-image');
var glob = require('glob');
var gm = require('gm');
var express = require('express');
var router = express.Router();
var userMapping = { '_id': 1, 'nickname': 1, 'email': 1};
var spaceMapping = { '_id': 1, name: 1};
const db = require('../../models/db');
const Sequelize = require('sequelize');
const Op = Sequelize.Op;
const uuidv4 = require('uuid/v4');
router.get('/:membership_id/accept', function(req, res, next) {
if (req.user) {
Membership.findOne({
db.Membership.findOne({where:{
_id: req.params.membership_id,
state: "pending",
code: req.query.code,
user: { "$exists": false }
}).populate('space').exec((err,mem) => {
if (err) res.sendStatus(400);
else {
if (mem) {
if(!mem.user) {
mem.code = null;
mem.state = "active";
mem.user = req.user;
mem.save(function(err){
if (err) res.status(400).json(err);
else {
console.log(mem);
res.status(200).json(mem);
}
});
} else {
res.status(400).json({"error": "already_used"});
}
code: req.query.code
}, include: ['space']}).then((mem) => {
if (mem) {
if (!mem.user) {
mem.state = "active";
mem.user_id = req.user._id;
mem.save().then(function() {
res.status(200).json(mem);
});
} else {
res.status(404).json({"error": "not found"});
res.status(200).json(mem);
}
} else {
res.status(404).json({"error": "not found"});
}
});
} else {
......
"use strict";
var config = require('config');
require('../../models/schema');
const db = require('../../models/db');
var bcrypt = require('bcryptjs');
var crypo = require('crypto');
var crypto = require('crypto');
var URL = require('url').URL;
var express = require('express');
......@@ -12,68 +12,64 @@ var router = express.Router();
router.post('/', function(req, res) {
var data = req.body;
if (data.email && data.password) {
var email = req.body.email.toLowerCase();
var password = req.body["password"];
User.find({email: email, account_type: "email"}, (function (err, users) {
if (err) {
res.status(400).json({"error":"session.users"});
} else {
if (users.length == 1) {
var user = users[0];
if (bcrypt.compareSync(password, user.password_hash)) {
crypo.randomBytes(48, function(ex, buf) {
var token = buf.toString('hex');
var session = {
token: token,
ip: req.ip,
device: "web",
created_at: new Date()
};
if (!user.sessions)
user.sessions = [];
if (!data.email || !data.password) {
res.status(400).json({});
return;
}
var email = req.body.email.toLowerCase();
var password = req.body["password"];
user.sessions.push(session);
db.User.findOne({where: {email: email}})
.error(err => {
res.sendStatus(404);
//res.status(400).json({"error":"session.users"});
})
.then(user => {
console.log("!!! user: ",user.password_hash);
if (bcrypt.compareSync(password, user.password_hash)) {
crypto.randomBytes(48, function(ex, buf) {
var token = buf.toString('hex');
console.log("!!! token: ",token);
user.save(function(err, result) {
if (err) console.error("Error saving user:",err);
var domain = (process.env.NODE_ENV == "production") ? new URL(config.get('endpoint')).hostname : "localhost";
var session = {
user_id: user._id,
token: token,
ip: req.ip,
device: "web",
created_at: new Date()
};
res.cookie('sdsession', token, { domain: domain, httpOnly: true });
res.status(201).json(session);
});
db.Session.create(session)
.error(err => {
console.error("Error creating Session:",err);
res.sendStatus(500);
})
.then(() => {
var domain = (process.env.NODE_ENV == "production") ? new URL(config.get('endpoint')).hostname : "localhost";
res.cookie('sdsession', token, { domain: domain, httpOnly: true });
res.status(201).json(session);
});
}else{
res.sendStatus(403);
}
} else {
res.sendStatus(404);
}
});
} else {
res.sendStatus(403);
}
}));
} else {
res.status(400).json({});
}
});
});
router.delete('/current', function(req, res, next) {
if (req.user) {
var user = req.user;
/*var user = req.user;
var newSessions = user.sessions.filter( function(session){
return session.token != req.token;
});
user.sessions = newSessions;
user.save(function(err, result) {
});*/
//user.sessions = newSessions;
//user.save(function(err, result) {
var domain = new URL(config.get('endpoint')).hostname;
res.clearCookie('sdsession', { domain: domain });
res.sendStatus(204);
});
//});
} else {
res.sendStatus(404);
}
......
"use strict";
var config = require('config');
require('../../models/schema');
const os = require('os');
const db = require('../../models/db');
const Sequelize = require('sequelize');
const Op = Sequelize.Op;
const uuidv4 = require('uuid/v4');
var payloadConverter = require('../../helpers/artifact_converter');
var redis = require('../../helpers/redis');
......@@ -9,13 +14,11 @@ var redis = require('../../helpers/redis');
var async = require('async');
var fs = require('fs');
var _ = require("underscore");
var mongoose = require("mongoose");
var archiver = require('archiver');
var request = require('request');
var url = require("url");
var path = require("path");
var crypto = require('crypto');
var qr = require('qr-image');
var glob = require('glob');
var gm = require('gm');
......@@ -46,15 +49,24 @@ var roleMapping = {
// ARTIFACTS
router.get('/', (req, res) => {
Artifact.find({
db.Artifact.findAll({where: {
space_id: req.space._id
}).exec((err, artifacts) => {
}}).then(artifacts => {
async.map(artifacts, (a, cb) => {
a = a.toObject();
//a = a.toObject(); TODO
if (a.control_points) {
a.control_points = JSON.parse(a.control_points);
}
if (a.payload_alternatives) {
a.payload_alternatives = JSON.parse(a.payload_alternatives);
}
if (a.user_id) {
User.findOne({
// FIXME JOIN
/*User.findOne({where: {
"_id": a.user_id
}).select({
}}).select({
"_id": 1,
"nickname": 1,
"email": 1
......@@ -63,7 +75,8 @@ router.get('/', (req, res) => {
a['user'] = user.toObject();
}
cb(err, a);
});
});*/
cb(null, a);
} else {
cb(null, a);
}
......@@ -81,9 +94,8 @@ router.post('/', function(req, res, next) {
attrs['space_id'] = req.space._id;
var artifact = new Artifact(attrs);
artifact.created_from_ip = req['real_ip'];
var artifact = attrs;
artifact._id = uuidv4();
if (req.user) {
artifact.user_id = req.user._id;
......@@ -92,23 +104,18 @@ router.post('/', function(req, res, next) {
artifact.last_update_editor_name = req.editor_name;
}
if (req.spaceRole == "editor"  ||  req.spaceRole == "admin") {
artifact.save(function(err) {
if (err) res.status(400).json(err);
else {
Space.update({
_id: req.space._id
}, {
"$set": {
updated_at: new Date()
}
});
res.distributeCreate("Artifact", artifact);
}
db.packArtifact(artifact);
if (req.spaceRole == "editor" || req.spaceRole == "admin") {
db.Artifact.create(artifact).then(() => {
//if (err) res.status(400).json(err);
db.unpackArtifact(artifact);
db.Space.update({ updated_at: new Date() }, {where: {_id: req.space._id}});
res.distributeCreate("Artifact", artifact);
});
} else {
res.status(401).json({
"error": "no access"
"error": "Access denied"
});
}
});
......@@ -118,7 +125,8 @@ router.post('/:artifact_id/payload', function(req, res, next) {
var a = req.artifact;
var fileName = (req.query.filename || "upload.bin").replace(/[^a-zA-Z0-9_\-\.]/g, '');
var localFilePath = "/tmp/" + fileName;
var localFilePath = os.tmpdir() + "/" + fileName;
var writeStream = fs.createWriteStream(localFilePath);
var stream = req.pipe(writeStream);
......@@ -132,13 +140,7 @@ router.post('/:artifact_id/payload', function(req, res, next) {
payloadConverter.convert(a, fileName, localFilePath, function(error, artifact) {
if (error) res.status(400).json(error);
else {
Space.update({
_id: req.space._id
}, {
"$set": {
updated_at: new Date()
}
});
db.Space.update({ updated_at: new Date() }, {where: {_id: req.space._id}});
res.distributeUpdate("Artifact", artifact);
}
}, progress_callback);
......@@ -161,42 +163,23 @@ router.put('/:artifact_id', function(req, res, next) {
} else {
newAttr.last_update_editor_name = req.editor_name;
}
db.packArtifact(newAttr);
Artifact.findOneAndUpdate({
db.Artifact.update(newAttr, { where: {
"_id": a._id
}, {
"$set": newAttr
}, {
"new": true
}, function(err, artifact) {
if (err) res.status(400).json(err);
else {
Space.update({
_id: req.space._id
}, {
"$set": {
updated_at: new Date()
}
});
res.distributeUpdate("Artifact", artifact);
}
}}).then(rows => {
db.unpackArtifact(newAttr);
db.Space.update({ updated_at: new Date() }, {where: {_id: req.space._id} });
res.distributeUpdate("Artifact", newAttr);
});
});
router.delete('/:artifact_id', function(req, res, next) {
var artifact = req.artifact;
artifact.remove(function(err) {
if (err) res.status(400).json(err);
else {
Space.update({
_id: req.space._id
}, {
"$set": {
updated_at: new Date()
}
});
res.distributeDelete("Artifact", artifact);
}
db.Artifact.destroy({where: { "_id": artifact._id}}).then(() => {
db.Space.update({ updated_at: new Date() }, {where: {_id: req.space._id} });
res.distributeDelete("Artifact", artifact);
});
});
......
"use strict";
var config = require('config');
require('../../models/schema');
require('../../models/db');
var async = require('async');
var fs = require('fs');
var _ = require("underscore");
var mongoose = require("mongoose");
var request = require('request');
var url = require("url");
var path = require("path");
var crypto = require('crypto');
var qr = require('qr-image');
var glob = require('glob');
var gm = require('gm');
......@@ -40,6 +38,12 @@ var roleMapping = {
};
router.get('/', function(req, res, next) {
res.status(200).json([]);
return;
// FIXME TODO
var showActionForSpaces = function(err, spaceIds) {
var userMapping = {
'_id': 1,
......
"use strict";
var config = require('config');
require('../../models/schema');
const db = require('../../models/db');
var redis = require('../../helpers/redis');
var mailer = require('../../helpers/mailer');
var uploader = require('../../helpers/uploader');
var space_render = require('../../helpers/space-render');
......@@ -12,13 +11,11 @@ var async = require('async');
var moment = require('moment');
var fs = require('fs');
var _ = require("underscore");
var mongoose = require("mongoose");
var archiver = require('archiver');
var request = require('request');
var url = require("url");
var path = require("path");
var crypto = require('crypto');
var qr = require('qr-image');
var glob = require('glob');
var gm = require('gm');
var sanitizeHtml = require('sanitize-html');
......@@ -49,26 +46,18 @@ var roleMapping = {
router.get('/png', function(req, res, next) {
var triggered = new Date();
var s3_filename = "s" + req.space._id + "/" + "thumb_" + triggered.getTime() + ".jpg";
if (!req.space.thumbnail_updated_at || req.space.thumbnail_updated_at < req.space.updated_at || !req.space.thumbnail_url) {
Space.update({
"_id": req.space._id
}, {
"$set": {
thumbnail_updated_at: triggered
}
}, function(a, b, c) {});
db.Space.update({ thumbnail_updated_at: triggered }, {where : {"_id": req.space._id }});
phantom.takeScreenshot(req.space, "png",
function(local_path) {
var localResizedFilePath = local_path + ".thumb.jpg";
gm(local_path).resize(640, 480).quality(70.0).autoOrient().write(localResizedFilePath, function(err) {
if (err) {
console.error("screenshot resize error: ", err);
console.error("[space screenshot] resize error: ", err);
res.status(500).send("Error taking screenshot.");
return;
}
......@@ -76,22 +65,15 @@ router.get('/png', function(req, res, next) {
uploader.uploadFile(s3_filename, "image/jpeg", localResizedFilePath, function(err, thumbnailUrl) {
if (err) {
console.error("screenshot s3 upload error. filename: " + s3_filename + " details: ", err);
console.error("[space screenshot] upload error. filename: " + s3_filename + " details: ", err);
res.status(500).send("Error uploading screenshot.");
return;
}
var oldUrl = req.space.thumbnail_url;
Space.update({
"_id": req.space._id
}, {
"$set": {
thumbnail_url: thumbnailUrl
}
}, function(a, b, c) {
db.Space.update({ thumbnail_url: thumbnailUrl }, {where : {"_id": req.space._id }}).then(() => {
res.redirect(thumbnailUrl);
try {
if (oldUrl) {
var oldPath = url.parse(oldUrl).pathname;
......@@ -125,77 +107,6 @@ function make_export_filename(space, extension) {
return space.name.replace(/[^\w]/g, '') + "-" + space._id + "-" + moment().format("YYYYMMDD-HH-mm-ss") + "." + extension;
}
router.get('/list', function(req, res, next) {
if (req.user) {
if (req.spaceRole == "admin" ||  req.spaceRole == "editor") {
if (req.space.space_type == "space") {
Artifact.find({
space_id: req.space._id
}).exec(function(err, artifacts) {
async.map(artifacts, function(a, cb) {
if (a.user_id) {
User.findOne({
"_id": a.user_id
}).exec(function(err, user) {
a.user = user;
if (a.last_update_user_id) {
User.findOne({
"_id": a.last_update_user_id
}).exec(function(err, updateUser) {
a.update_user = updateUser;
cb(null, a);
});
} else {
cb(null, a);
}
});
} else {
cb(null, a);
}
}, function(err, mappedArtifacts) {
req.space.artifacts = mappedArtifacts.map(function(a) {
a.description = sanitizeHtml(a.description, {
allowedTags: [],
allowedAttributes: []
});
if (a.payload_uri) {
var parsed = url.parse(a.payload_uri);
var fileName = path.basename(parsed.pathname) || "file.bin";
a.filename = fileName;
}
return a;
});
res.render('artifact_list', {
space: req.space
});
});
});
} else {
Space.getRecursiveSubspacesForSpace(req.space, (err, subspaces) => {
res.render('space_list', {
subspaces: subspaces.map((s) => {
s.ae_link = config.endpoint + '/s/' + s.edit_hash + (s.edit_slug ? ('-'+s.edit_slug) : '')
return s;
}),
space: req.space
});
});
}
} else {
res.sendStatus(403);
}
} else {
res.sendStatus(403);
}
});
router.get('/pdf', function(req, res, next) {
var s3_filename = make_export_filename(req.space, "pdf");
......@@ -329,36 +240,14 @@ router.get('/zip', function(req, res, next) {
});
router.get('/html', function(req, res) {
Artifact.find({
console.log("!!!!! hello ");
db.Artifact.findAll({where: {
space_id: req.space._id
}, function(err, artifacts) {
}}).then(function(artifacts) {
var space = req.space;
res.send(space_render.render_space_as_html(space, artifacts));
});
});
router.get('/path', (req, res) => {
// build up a breadcrumb trail (path)
var path = [];
var buildPath = (space) => {
if (space.parent_space_id) {
Space.findOne({
"_id": space.parent_space_id
}, (err, parentSpace) => {
if (space._id == parentSpace._id) {
console.log("error: circular parent reference for space " + space._id);
res.send("error: circular reference");
} else {
path.push(parentSpace);
buildPath(parentSpace);
}
});
} else {
// reached the top
res.json(path.reverse());
}
}
buildPath(req.space);
});
module.exports = router;
"use strict";
var config = require('config');
require('../../models/schema');
const db = require('../../models/db');
const Sequelize = require('sequelize');
const Op = Sequelize.Op;
const uuidv4 = require('uuid/v4');
var redis = require('../../helpers/redis');
var mailer = require('../../helpers/mailer');
var uploader = require('../../helpers/uploader');
var space_render = require('../../helpers/space-render');
var phantom = require('../../helpers/phantom');
var async = require('async');
var fs = require('fs');
var _ = require("underscore");
var mongoose = require("mongoose");
var archiver = require('archiver');
var request = require('request');
var url = require("url");
var path = require("path");
var crypto = require('crypto');
var qr = require('qr-image');
var glob = require('glob');
var gm = require('gm');
var crypto = require('crypto');
var express = require('express');
var router = express.Router({mergeParams: true});
// JSON MAPPINGS
var userMapping = {
_id: 1,
nickname: 1,
email: 1,
avatar_thumb_uri: 1
};
var spaceMapping = {
_id: 1,
name: 1,
thumbnail_url: 1
};
var roleMapping = {
"none": 0,
"viewer": 1,
"editor": 2,
"admin": 3
}
router.get('/', function(req, res, next) {
Membership
.find({
space: req.space._id
})
.populate("user")
.exec(function(err, memberships) {
db.Membership
.findAll({where: {
space_id: req.space._id
}, include: ['user']})
.then(memberships => {
res.status(200).json(memberships);
});
});
......@@ -59,52 +33,51 @@ router.get('/', function(req, res, next) {
router.post('/', function(req, res, next) {
if (req.spaceRole == "admin") {
var attrs = req.body;
attrs['space'] = req.space._id;
attrs['state'] = "pending";
var membership = new Membership(attrs);
attrs.space_id = req.space._id;
attrs.state = "pending";
attrs._id = uuidv4();
var membership = attrs;
var msg = attrs.personal_message;
if (membership.email_invited != req.user.email) {
User.findOne({
db.User.findOne({where:{
"email": membership.email_invited
}, function(err, user) {
}}).then(function(user) {
if (user) {
membership.user = user;
membership.user_id = user._id;
membership.state = "active";
} else {
membership.code = crypto.randomBytes(64).toString('hex').substring(0, 12);
}
membership.save(function(err) {
if (err) res.sendStatus(400);
else {
var accept_link = config.endpoint + "/accept/" + membership._id + "?code=" + membership.code;
if (user) {
accept_link = config.endpoint + "/" + req.space.space_type + "s/" + req.space._id;
}
var openText = req.i18n.__("space_invite_membership_action");
if (user) {
req.i18n.__("open");
}
const name = req.user.nickname || req.user.email
const subject = (req.space.space_type == "space") ? req.i18n.__("space_invite_membership_subject", name, req.space.name) : req.i18n.__("folder_invite_membership_subject", req.user.nickname, req.space.name)
const body = (req.space.space_type == "space") ? req.i18n.__("space_invite_membership_body", name, req.space.name) : req.i18n.__("folder_invite_membership_body", req.user.nickname, req.space.name)
mailer.sendMail(
membership.email_invited, subject, body, {
messsage: msg,
action: {
link: accept_link,
name: openText
}
});
res.status(201).json(membership);
db.Membership.create(membership).then(function() {
var accept_link = config.endpoint + "/accept/" + membership._id + "?code=" + membership.code;
if (user) {
accept_link = config.endpoint + "/" + req.space.space_type + "s/" + req.space._id;
}
var openText = req.i18n.__("space_invite_membership_action");
if (user) {
req.i18n.__("open");
}
const name = req.user.nickname || req.user.email
const subject = (req.space.space_type == "space") ? req.i18n.__("space_invite_membership_subject", name, req.space.name) : req.i18n.__("folder_invite_membership_subject", req.user.nickname, req.space.name)
const body = (req.space.space_type == "space") ? req.i18n.__("space_invite_membership_body", name, req.space.name) : req.i18n.__("folder_invite_membership_body", req.user.nickname, req.space.name)
mailer.sendMail(
membership.email_invited, subject, body, {
messsage: msg,
action: {
link: accept_link,
name: openText
}
});
res.status(201).json(membership);
});
});
......@@ -125,21 +98,15 @@ router.post('/', function(req, res, next) {
router.put('/:membership_id', function(req, res, next) {
if (req.user) {
if (req.spaceRole == "admin") {
Membership.findOne({
db.Membership.findOne({ where: {
_id: req.params.membership_id
}, function(err, mem) {
if (err) res.sendStatus(400);
else {
if (mem) {
var attrs = req.body;
mem.role = attrs.role;
mem.save(function(err) {
if (err) res.sendStatus(400);
else {
res.status(201).json(mem);
}
});
}
}}).then(function(mem) {
if (mem) {
var attrs = req.body;
mem.role = attrs.role;
mem.save(function() {
res.status(201).json(mem);
});
}
});
} else {
......@@ -152,20 +119,12 @@ router.put('/:membership_id', function(req, res, next) {
router.delete('/:membership_id', function(req, res, next) {
if (req.user) {
Membership.findOne({
db.Membership.findOne({ where: {
_id: req.params.membership_id
}, function(err, mem) {
if (err) res.sendStatus(400);
else {
mem.remove(function(err) {
if (err) {
res.status(400).json(err);
} else {
// FIXME might need to delete the user?
res.sendStatus(204);
}
});
}
}}).then(function(mem) {
mem.destroy().then(function() {
res.sendStatus(204);
});
});
} else {
res.sendStatus(403);
......
"use strict";
var config = require('config');
require('../../models/schema');
const db = require('../../models/db');
const Sequelize = require('sequelize');
const Op = Sequelize.Op;
const uuidv4 = require('uuid/v4');
var redis = require('../../helpers/redis');
var mailer = require('../../helpers/mailer');
......@@ -11,15 +14,12 @@ var phantom = require('../../helpers/phantom');
var async = require('async');
var fs = require('fs');
var _ = require("underscore");
var mongoose = require("mongoose");
var archiver = require('archiver');
var request = require('request');
var url = require("url");
var path = require("path");
var crypto = require('crypto');
var qr = require('qr-image');
var glob = require('glob');
var gm = require('gm');
var express = require('express');
var router = express.Router({mergeParams: true});
......@@ -49,90 +49,44 @@ var roleMapping = {
// MESSAGES
router.get('/', function(req, res, next) {
Message.find({
space: req.space._id
}).populate('user', userMapping).exec(function(err, messages) {
res.status(200).json(messages);
});
db.Message.findAll({where:{
space_id: req.space._id
}, include: ['user']})
.then(function(messages) {
res.status(200).json(messages);
});
});
router.post('/', function(req, res, next) {
var attrs = req.body;
attrs.space = req.space;
attrs.space_id = req.space._id;
if (req.user) {
attrs.user = req.user;
attrs.user_id = req.user._id;
} else {
attrs.user = null;
}
var msg = new Message(attrs);
msg.save(function(err) {
if (err) res.status(400).json(erra);
else {
if (msg.message.length <= 1) return;
Membership
.find({
space: req.space,
user: {
"$exists": true
}
})
.populate('user')
.exec(function(err, memberships) {
var users = memberships.map(function(m) {
return m.user;
});
users.forEach((user) => {
if (user.preferences.email_notifications) {
redis.isOnlineInSpace(user, req.space, function(err, online) {
if (!online) {
var nickname = msg.editor_name;
if (req.user) {
nickname = req.user.nickname;
}
mailer.sendMail(
user.email,
req.i18n.__("space_message_subject", req.space.name),
req.i18n.__("space_message_body", nickname, req.space.name), {
message: msg.message,
action: {
link: config.endpoint + "/spaces/" + req.space._id.toString(),
name: req.i18n.__("open")
}
});
} else {
console.log("not sending message to user: is online.");
}
});
} else {
console.log("not sending message to user: is disabled notifications.");
}
});
});
var msg = attrs;
msg._id = uuidv4();
res.distributeCreate("Message", msg);
}
db.Message.create(msg).then(function() {
if (msg.message.length <= 1) return;
// TODO reimplement notifications
res.distributeCreate("Message", msg);
});
});
router.delete('/:message_id', function(req, res, next) {
Message.findOne({
db.Message.findOne({where:{
"_id": req.params.message_id
}, function(err, msg) {
}}).then(function(msg) {
if (!msg) {
res.sendStatus(404);
} else {
msg.remove(function(err) {
if (err) res.status(400).json(err);
else {
if (msg) {
res.distributeDelete("Message", msg);
} else {
res.sendStatus(404);
}
}
msg.destroy().then(function() {
res.distributeDelete("Message", msg);
});
}
});
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
"use strict";
var config = require('config');
require('../../models/schema');
require('../../models/db');
var fs = require('fs');
var phantom = require('node-phantom-simple');
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
<div id="pick-mobile" v-if="active_space" class="dialog-section" v-show="opened_dialog=='mobile'">
<h4 class="dialog-title">Mobile Upload</h4>
<img v-if="active_space.edit_hash" v-bind:src="'/api/helper/qrcode/'+ active_space._id" />
<!--img v-if="active_space.edit_hash" v-bind:src="'/api/helper/qrcode/'+ active_space._id"-->
<p class="text-center">
Install the Spacedeck App on your phone and scan this QR code to upload photos, sound, video or text.
......
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment