initial commit.

This commit is contained in:
mntmn
2017-04-07 01:29:05 +02:00
commit 7ff2926578
258 changed files with 83743 additions and 0 deletions

61
routes/api/memberships.js Normal file
View File

@@ -0,0 +1,61 @@
"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};
router.get('/:membership_id/accept', function(req, res, next) {
if (req.user) {
Membership.findOne({
_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"});
}
} else {
res.status(404).json({"error": "not found"});
}
}
});
} else {
res.sendStatus(403);
}
});
module.exports = router;

82
routes/api/sessions.js Normal file
View File

@@ -0,0 +1,82 @@
"use strict";
var config = require('config');
require('../../models/schema');
var bcrypt = require('bcrypt');
var crypo = require('crypto');
var express = require('express');
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 = [];
user.sessions.push(session);
user.save(function(err, result) {
// FIXME
var secure = process.env.NODE_ENV == "production" || process.env.NODE_ENV == "staging";
var domain = (process.env.NODE_ENV == "production") ? ".example.org" : "localhost";
res.cookie('sdsession', token, { domain: domain, httpOnly: true, secure: secure});
res.status(201).json(session);
});
});
}else{
res.sendStatus(403);
}
} else {
res.sendStatus(404);
}
}
}));
} else {
res.status(400).json({});
}
});
router.delete('/current', function(req, res, next) {
if (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) {
// FIXME
var domain = (process.env.NODE_ENV == "production") ? ".example.org" : "localhost";
res.clearCookie('sdsession', { domain: domain });
res.sendStatus(204);
});
} else {
res.sendStatus(404);
}
});
module.exports = router;

View File

@@ -0,0 +1,201 @@
"use strict";
var config = require('config');
require('../../models/schema');
var payloadConverter = require('../../helpers/artifact_converter');
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');
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
}
// ARTIFACTS
router.get('/', (req, res) => {
Artifact.find({
space_id: req.space._id
}).exec((err, artifacts) => {
async.map(artifacts, (a, cb) => {
a = a.toObject();
if (a.user_id) {
User.findOne({
"_id": a.user_id
}).select({
"_id": 1,
"nickname": 1,
"email": 1
}).exec((err, user) => {
a['user'] = user.toObject();
cb(err, a);
});
} else {
cb(null, a);
}
}, (err, mappedArtifacts) => {
if (err) res.status(400).json(err);
else {
res.status(200).json(mappedArtifacts);
}
});
});
});
router.post('/', function(req, res, next) {
var attrs = req.body;
attrs['space_id'] = req.space._id;
var artifact = new Artifact(attrs);
artifact.created_from_ip = req['real_ip'];
if (req.user) {
artifact.user_id = req.user._id;
artifact.last_update_user_id = req.user._id;
} else {
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);
}
});
} else {
res.status(401).json({
"error": "no access"
});
}
});
router.post('/:artifact_id/payload', function(req, res, next) {
if (req.spaceRole == "editor"  ||  req.spaceRole == "admin") {
var a = req.artifact;
var fileName = (req.query.filename || "upload.bin").replace(/[^a-zA-Z0-9_\-\.]/g, '');
var localFilePath = "/tmp/" + fileName;
var writeStream = fs.createWriteStream(localFilePath);
var stream = req.pipe(writeStream);
var progress_callback = function(progress_msg) {
a.description = progress_msg;
a.save();
redis.sendMessage("update", a, a.toJSON(), req.channelId);
};
stream.on('finish', function() {
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()
}
});
res.distributeUpdate("Artifact", artifact);
}
}, progress_callback);
});
} else {
res.status(401).json({
"error": "no access"
});
}
});
router.put('/:artifact_id', function(req, res, next) {
var a = req.artifact;
var newAttr = req.body;
newAttr.updated_at = new Date();
delete newAttr['_id'];
if (req.user) {
newAttr.last_update_user_id = req.user._id;
} else {
newAttr.last_update_editor_name = req.editor_name;
}
Artifact.findOneAndUpdate({
"_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);
}
});
});
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);
}
});
});
module.exports = router;

159
routes/api/space_digest.js Normal file
View File

@@ -0,0 +1,159 @@
"use strict";
var config = require('config');
require('../../models/schema');
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');
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) {
var showActionForSpaces = function(err, spaceIds) {
var userMapping = {
'_id': 1,
'nickname': 1,
'email': 1
};
var spaceMapping = {
'_id': 1,
'name': 1
};
var d = new Date();
d.setDate(d.getDate() - 1);
Action
.find({
created_at: {
"$gt": d
},
space: {
"$in": spaceIds
}
})
.populate('space', spaceMapping)
.populate('user', userMapping)
.exec(function(err, actions) {
var groupedBySpaceEvents = _.groupBy(actions, function(action) {
return action.space._id;
});
var events = _.map(groupedBySpaceEvents, function(value, key) {
var sortByDate = _.sortBy(groupedBySpaceEvents[key], function(o) {
return o.created_at;
});
var lastDate = sortByDate[sortByDate.length - 1].created_at;
var users = groupedBySpaceEvents[key].map(function(action) {
if (action.user) {
if (action.user._id.equals(req.user._id)) {
return "you";
} else {
return action.user.nickname;
}
}
return action.editor_name;
});
var uniqueUsers = _.unique(users);
return {
space: value[0].space,
users: uniqueUsers,
last_action: lastDate
};
});
var sortedEvents = _.sortBy(events, function(o) {
return -o.last_action;
});
res.status(200).json(sortedEvents);
});
};
if (!req.user) {
res.status(403).json({
error: "auth required"
});
} else {
if (!req.space._id.equals(req.user.home_folder_id)) {
Space.getRecursiveSubspacesForSpace(req.space, function(err, spaces) {
showActionForSpaces(err, spaces.map(function(s) {
return s._id;
}));
});
} else {
async.parallel({
bycreator: function(cb) {
Space.find({
creator: req.user._id
}, function(err, spaces) {
cb(null, spaces.map(function(s) {
return s._id;
}));
});
},
bymembership: function(cb) {
Membership.find({
user: req.user,
space: {
"$exists": 1
}
}).populate("space").exec(function(err, memberships) {
async.map(memberships, function(membership, memcb) {
Space.getRecursiveSubspacesForSpace(membership.space, function(err, spaces) {
cb(null, spaces.map(function(s) {
return s._id;
}));
});
}, function(err, spaceArrays) {
cb(null, spaceArrays.map(function(s) {
return s._id;
}));
});
});
}
}, function(err, results) {
var spaceIds = _.unique(results.bycreator.concat(results.bymembership));
showActionForSpaces(err, spaceIds);
});
}
}
});
module.exports = router;

364
routes/api/space_exports.js Normal file
View File

@@ -0,0 +1,364 @@
"use strict";
var config = require('config');
require('../../models/schema');
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 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');
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('/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) {});
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);
res.status(500).send("Error taking screenshot.");
return;
}
uploader.uploadFile(s3_filename, "image/jpeg", localResizedFilePath, function(err, thumbnailUrl) {
if (err) {
console.error("screenshot s3 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) {
res.redirect(thumbnailUrl);
try {
if (oldUrl) {
var oldPath = url.parse(oldUrl).pathname;
uploader.removeFile(oldPath, function(err, res) {});
}
fs.unlink(local_path);
} catch (e) {
console.error(e);
}
});
try {
fs.unlink(localResizedFilePath);
} catch (e) {
console.error(e);
}
});
});
},
function() {
// on_error
console.error("phantom could not create screenshot for space " + req.space_id);
res.status(404).send("Not found");
});
} else {
res.redirect(req.space.thumbnail_url);
}
});
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");
phantom.takeScreenshot(req.space, "pdf", function(local_path) {
uploader.uploadFile(s3_filename, "application/pdf", local_path, function(err, url) {
res.status(201).json({
url: url
});
fs.unlink(local_path);
});
}, (err) => {
res.status(500).json({
error: "PDF could not created (500)"
});
});
});
router.get('/zip', function(req, res, next) {
Artifact.find({
space_id: req.space._id
}, function(err, artifacts) {
if (!artifacts || !artifacts.length || err) {
res.status(404).json({
"error": "no artifacts"
});
return;
}
var localPath = "/tmp/" + req.space._id;
try {
var files = fs.readdirSync(localPath);
for (var i = 0; i < files.length; i++) {
console.log("[zip export] unlink old file: ", localPath + "/" + files[i]);
if (files[i] != "." && files[i] != "..") {
fs.unlinkSync(localPath + "/" + files[i]);
}
}
fs.rmdirSync(localPath);
} catch (e) {
console.error(e);
}
var used_filenames = {};
fs.mkdir(localPath, function(err, cb) {
async.eachLimit(artifacts, 10, function(artifact, cb) {
try {
if (artifact.payload_uri) {
if (artifact.payload_uri.indexOf("https://") > -1 || artifact.payload_uri.indexOf("http://") > -1) {
var parsed = url.parse(artifact.payload_uri);
var fileName = path.basename(parsed.pathname) || "file.bin";
if (fileName.length > 128) {
fileName = fileName.substr(fileName.length - 128);
}
if (used_filenames[fileName]) {
// if there is a fileName collision, we insert a number before the extension
// to differentiate
var ext = path.extname(fileName);
fileName = path.basename(fileName, ext) + "_" + (parseInt(Math.random() * 10000)) + ext;
}
used_filenames[fileName] = true;
//fix for old artifacts where no .pdf is in the filename
if (artifact.mime == "application/pdf" && fileName.indexOf(".pdf") < 0) {
fileName = fileName + ".pdf";
}
if (artifact.mime == "image/png" && fileName.indexOf(".png") < 0) {
fileName = fileName + ".png";
}
var localFilePath = localPath + '/' + fileName;
request
.get(artifact.payload_uri)
.on('error', function(err) {
console.error(err);
cb(null, artifact.payload_uri);
})
.on('end', function() {
cb(null, artifact.payload_uri);
}).pipe(fs.createWriteStream(localFilePath));
} else {
cb(null, artifact.payload_uri);
}
} else {
cb(null, artifact.payload_uri);
}
} catch (e) {
console.log(e);
cb(null);
}
}, function(err, payloads) {
var outputPath = '/tmp/' + req.space._id + '.zip';
var output = fs.createWriteStream(outputPath);
var archive = archiver('zip');
output.on('close', function() {
var name = make_export_filename(req.space, "zip");
uploader.uploadFile(name, "application/zip", outputPath, function(err, url) {
res.status(201).json({
url: url
});
try {
fs.unlink(outputPath);
} catch (e) {
console.error(e);
}
});
});
archive.on('error', function(err) {
console.error(err);
});
archive.pipe(output);
archive.directory(localPath, false, {
date: new Date()
});
archive.finalize();
});
});
});
});
router.get('/html', function(req, res) {
Artifact.find({
space_id: req.space._id
}, function(err, 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;

View File

@@ -0,0 +1,175 @@
"use strict";
var config = require('config');
require('../../models/schema');
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 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) {
res.status(200).json(memberships);
});
});
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);
var msg = attrs.personal_message;
if (membership.email_invited != req.user.email) {
User.findOne({
"email": membership.email_invited
}, function(err, user) {
if (user) {
membership.user = user;
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);
}
});
});
} else {
res.status(400).json({
"error": "user already in space"
});
}
} else {
res.status(403).json({
"error": "not_permitted"
});
}
});
router.put('/:membership_id', function(req, res, next) {
if (req.user) {
if (req.spaceRole == "admin") {
Membership.findOne({
_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);
}
});
}
}
});
} else {
res.sendStatus(403);
}
} else {
res.sendStatus(403);
}
});
router.delete('/:membership_id', function(req, res, next) {
if (req.user) {
Membership.findOne({
_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);
}
});
}
});
} else {
res.sendStatus(403);
}
});
module.exports = router;

View File

@@ -0,0 +1,141 @@
"use strict";
var config = require('config');
require('../../models/schema');
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 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
}
// 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);
});
});
router.post('/', function(req, res, next) {
var attrs = req.body;
attrs.space = req.space;
if (req.user) {
attrs.user = req.user;
} 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.");
}
});
});
res.distributeCreate("Message", msg);
}
});
});
router.delete('/:message_id', function(req, res, next) {
Message.findOne({
"_id": req.params.message_id
}, function(err, 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);
}
}
});
}
});
});
module.exports = router;

569
routes/api/spaces.js Normal file
View File

@@ -0,0 +1,569 @@
"use strict";
var config = require('config');
require('../../models/schema');
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 payloadConverter = require('../../helpers/artifact_converter');
var slug = require('slug');
var fs = require('fs');
var async = require('async');
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');
const exec = require('child_process');
var express = require('express');
var router = express.Router();
// JSON MAPPINGS
var userMapping = {
_id: 1,
nickname: 1,
email: 1,
avatar_thumb_uri: 1
};
var spaceMapping = {
_id: 1,
name: 1,
thumbnail_url: 1
};
router.get('/', function(req, res, next) {
if (!req.user) {
res.status(403).json({
error: "auth required"
});
} else {
if (req.query.writablefolders) {
Membership.find({
user: req.user._id
}, (err, memberships) => {
var validMemberships = memberships.filter((m) => {
if (!m.space || (m.space == "undefined"))
return false;
else
return mongoose.Types.ObjectId.isValid(m.space.toString());
});
var editorMemberships = validMemberships.filter((m) => {
return (m.role == "editor") || (m.role == "admin")
});
var spaceIds = editorMemberships.map(function(m) {
return new mongoose.Types.ObjectId(m.space);
});
var q = {
"space_type": "folder",
"$or": [{
"creator": req.user._id
}, {
"_id": {
"$in": spaceIds
},
"creator": {
"$ne": req.user._id
}
}]
};
Space
.find(q)
.populate('creator', userMapping)
.exec(function(err, spaces) {
if (err) console.error(err);
var updatedSpaces = spaces.map(function(s) {
var spaceObj = s.toObject();
return spaceObj;
});
async.map(spaces, (space, cb) => {
Space.getRecursiveSubspacesForSpace(space, (err, spaces) => {
var allSpaces = spaces;
cb(err, allSpaces);
})
}, (err, spaces) => {
var allSpaces = _.flatten(spaces);
var onlyFolders = _.filter(allSpaces, (s) => {
return s.space_type == "folder";
})
var uniqueFolders = _.unique(onlyFolders, (s) => {
return s._id.toString();
})
res.status(200).json(uniqueFolders);
});
});
});
} else if (req.query.search) {
Membership.find({
user: req.user._id
}, function(err, memberships) {
var validMemberships = memberships.filter(function(m) {
if (!m.space || (m.space == "undefined"))
return false;
else
return mongoose.Types.ObjectId.isValid(m.space.toString());
});
var spaceIds = validMemberships.map(function(m) {
return new mongoose.Types.ObjectId(m.space);
});
var q = {
"$or": [{"creator": req.user._id},
{"_id": {"$in": spaceIds}},
{"parent_space_id": {"$in": spaceIds}}],
name: new RegExp(req.query.search, "i")
};
Space
.find(q)
.populate('creator', userMapping)
.exec(function(err, spaces) {
if (err) console.error(err);
var updatedSpaces = spaces.map(function(s) {
var spaceObj = s.toObject();
return spaceObj;
});
res.status(200).json(spaces);
});
});
} else if (req.query.parent_space_id && req.query.parent_space_id != req.user.home_folder_id) {
Space
.findOne({
_id: req.query.parent_space_id
})
.populate('creator', userMapping)
.exec(function(err, space) {
if (space) {
Space.roleInSpace(space, req.user, function(err, role) {
if (role == "none") {
if(space.access_mode == "public") {
role = "viewer";
}
}
if (role != "none") {
Space
.find({
parent_space_id: req.query.parent_space_id
})
.populate('creator', userMapping)
.exec(function(err, spaces) {
res.status(200).json(spaces);
});
} else {
res.status(403).json({"error": "no authorized"});
}
});
} else {
res.status(404).json({"error": "space not found"});
}
});
} else {
Membership.find({
user: req.user._id
}, function(err, memberships) {
var validMemberships = memberships.filter(function(m) {
if (!m.space || (m.space == "undefined"))
return false;
else
return mongoose.Types.ObjectId.isValid(m.space.toString());
});
var spaceIds = validMemberships.map(function(m) {
return new mongoose.Types.ObjectId(m.space);
});
var q = {
"$or": [{
"creator": req.user._id,
"parent_space_id": req.user.home_folder_id
}, {
"_id": {
"$in": spaceIds
},
"creator": {
"$ne": req.user._id
}
}]
};
Space
.find(q)
.populate('creator', userMapping)
.exec(function(err, spaces) {
if (err) console.error(err);
var updatedSpaces = spaces.map(function(s) {
var spaceObj = s.toObject();
return spaceObj;
});
res.status(200).json(spaces);
});
});
}
}
});
router.post('/', function(req, res, next) {
if (req.user) {
var attrs = req.body;
var createSpace = () => {
attrs.creator = req.user;
attrs.edit_hash = crypto.randomBytes(64).toString('hex').substring(0, 7);
attrs.edit_slug = slug(attrs.name);
var space = new Space(attrs);
space.save(function(err, createdSpace) {
if (err) res.sendStatus(400);
else {
var membership = new Membership({
user: req.user,
space: createdSpace,
role: "admin"
});
membership.save(function(err, createdTeam) {
if (err) {
res.status(400).json(err);
} else {
res.status(201).json(createdSpace);
}
});
}
});
}
if (attrs.parent_space_id) {
Space.findOne({
"_id": attrs.parent_space_id
}).populate('creator', userMapping).exec((err, parentSpace) => {
if (parentSpace) {
Space.roleInSpace(parentSpace, req.user, (err, role) => {
if ((role == "editor") || (role == "admin")) {
createSpace();
} else {
res.status(403).json({
"error": "not editor in parent Space"
});
}
});
} else {
res.status(404).json({
"error": "parent Space not found"
});
}
});
} else {
createSpace();
}
} else {
res.sendStatus(403);
}
});
router.get('/:id', function(req, res, next) {
res.status(200).json(req.space);
});
router.put('/:id', function(req, res) {
var space = req.space;
var newAttr = req.body;
if (req['spaceRole'] != "editor" && req['spaceRole'] != "admin") {
res.sendStatus(403);
return;
}
newAttr.updated_at = new Date();
newAttr.edit_slug = slug(newAttr['name']);
delete newAttr['_id'];
delete newAttr['editor_name'];
delete newAttr['creator'];
Space.findOneAndUpdate({
"_id": space._id
}, {
"$set": newAttr
}, {
"new": true
}, function(err, space) {
if (err) res.status(400).json(err);
else {
res.distributeUpdate("Space", space);
}
});
});
router.post('/:id/background', function(req, res, next) {
var space = req.space;
var newDate = new Date();
var fileName = (req.query.filename || "upload.bin").replace(/[^a-zA-Z0-9\.]/g, '');
var localFilePath = "/tmp/" + fileName;
var writeStream = fs.createWriteStream(localFilePath);
var stream = req.pipe(writeStream);
req.on('end', function() {
uploader.uploadFile("s" + req.space._id + "/bg_" + newDate.getTime() + "_" + fileName, "image/jpeg", localFilePath, function(err, backgroundUrl) {
if (err) res.status(400).json(err);
else {
var adv = space.advanced;
if (adv.background_uri) {
var oldPath = url.parse(req.space.thumbnail_url).pathname;
uploader.removeFile(oldPath, function(err) {
console.log("removed old bg error:", err);
});
}
adv.background_uri = backgroundUrl;
Space.findOneAndUpdate({
"_id": space._id
}, {
"$set": {
advanced: adv
}
}, {
"new": true
}, function(err, space) {
if (err) {
res.sendStatus(400);
} else {
fs.unlink(localFilePath, function(err) {
if (err) {
console.error(err);
res.status(400).json(err);
} else {
res.status(200).json(space);
}
});
}
});
}
});
});
});
var handleDuplicateSpaceRequest = function(req, res, parentSpace) {
Space.duplicateSpace(req.space, req.user, 0, (err, newSpace) => {
if (err) {
console.error(err);
res.status(400).json(err);
} else {
res.status(201).json(newSpace);
}
}, parentSpace);
}
router.post('/:id/duplicate', (req, res, next) => {
if (req.query.parent_space_id) {
Space.findOne({
_id: req.query.parent_space_id
}).populate('creator', userMapping).exec((err, parentSpace) => {
if (!parentSpace) {
res.status(404).json({
"error": "parent space not found for dupicate"
});
} else {
Space.roleInSpace(parentSpace, req.user, (err, role) => {
if (role == "admin" ||  role == "editor") {
handleDuplicateSpaceRequest(req, res, parentSpace);
} else {
res.status(403).json({
"error": "not authed for parent_space_id"
});
}
});
}
});
} else {
handleDuplicateSpaceRequest(req, res);
}
});
router.delete('/:id', function(req, res, next) {
if (req.user) {
const space = req.space;
if (req.spaceRole == "admin") {
const attrs = req.body;
Space.recursiveDelete(space, function(err) {
if (err) res.status(400).json(err);
else {
res.distributeDelete("Space", space);
}
});
} else {
res.status(403).json({
"error": "requires admin status"
});
}
} else {
res.sendStatus(403);
}
});
router.post('/:id/artifacts-pdf', function(req, res, next) {
if (req.spaceRole == "editor" || req.spaceRole == "admin") {
var withZones = (req.query.zones) ? req.query.zones == "true" : false;
var fileName = (req.query.filename || "upload.bin").replace(/[^a-zA-Z0-9\.]/g, '');
var localFilePath = "/tmp/" + fileName;
var writeStream = fs.createWriteStream(localFilePath);
var stream = req.pipe(writeStream);
req.on('end', function() {
var rawName = fileName.slice(0, fileName.length - 4);
var outputFolder = "/tmp/" + rawName;
var rights = 777;
fs.mkdir(outputFolder, function(db) {
var images = outputFolder + "/" + rawName + "-page-%03d.jpeg";
exec.execFile("gs", ["-sDEVICE=jpeg", "-dDownScaleFactor=4", "-dDOINTERPOLATE", "-dNOPAUSE", "-dJPEGQ=80", "-dBATCH", "-sOutputFile=" + images, "-r250", "-f", localFilePath], {}, function(error, stdout, stderr) {
if (error === null) {
glob(outputFolder + "/*.jpeg", function(er, files) {
var count = files.length;
var delta = 10;
var limitPerRow = Math.ceil(Math.sqrt(count));
var startX = parseInt(req.query.x, delta);
var startY = parseInt(req.query.y, delta);
async.mapLimit(files, 20, function(localfilePath, cb) {
var fileName = path.basename(localfilePath);
var baseName = path.basename(localfilePath, ".jpeg");
var number = parseInt(baseName.slice(baseName.length - 3, baseName.length), 10);
gm(localFilePath).size(function(err, size) {
var w = 350;
var h = w;
var x = startX + (((number - 1) % limitPerRow) * w);
var y = startY + ((parseInt(((number - 1) / limitPerRow), 10) + 1) * w);
var userId;
if (req.user)
userId = req.user._id;
var a = new Artifact({
mime: "image/jpg",
space_id: req.space._id,
user_id: userId,
editor_name: req.guest_name,
board: {
w: w,
h: h,
x: x,
y: y,
z: (number + (count + 100))
}
});
payloadConverter.convert(a, fileName, localfilePath, (error, artifact) => {
if (error) res.status(400).json(error);
else {
if (withZones) {
var zone = new Artifact({
mime: "x-spacedeck/zone",
description: "Zone " + (number),
space_id: req.space._id,
user_id: userId,
editor_name: req.guest_name,
board: {
w: artifact.board.w + 20,
h: artifact.board.h + 40,
x: x - 10,
y: y - 30,
z: number
},
style: {
order: number,
valign: "middle",
align: "center"
}
});
zone.save((err) => {
redis.sendMessage("create", "Artifact", zone.toJSON(), req.channelId);
cb(null, [artifact, zone]);
});
} else {
cb(null, [artifact]);
}
}
});
});
}, function(err, artifacts) {
exec.execFile("rm", ["-r", outputFolder], function(err) {
res.status(201).json(_.flatten(artifacts));
async.eachLimit(artifacts, 10, (artifact_or_artifacts, cb) => {
if (artifact_or_artifacts instanceof Array) {
_.each(artifact_or_artifacts, (a) => {
redis.sendMessage("create", "Artifact", a.toJSON(), req.channelId);
});
} else  {
redis.sendMessage("create", "Artifact", artifact_or_artifacts.toJSON(), req.channelId);
}
cb(null);
});
});
});
});
} else {
console.error("error:", error);
exec.execFile("rm", ["-r", outputFolder], function(err) {
fs.unlink(localFilePath);
res.status(400).json({});
});
}
});
});
});
} else {
res.status(401).json({
"error": "no access"
});
}
});
module.exports = router;

265
routes/api/teams.js Normal file
View File

@@ -0,0 +1,265 @@
"use strict";
var config = require('config');
require('../../models/schema');
var redis = require('../../helpers/redis');
var mailer = require('../../helpers/mailer');
var fs = require('fs');
var _ = require('underscore');
var crypto = require('crypto');
var bcrypt = require('bcrypt');
var express = require('express');
var router = express.Router();
var userMapping = { '_id': 1, 'nickname': 1, 'email': 1};
router.get('/:id', (req, res) => {
res.status(200).json(req.user.team);
});
router.put('/:id', (req, res) => {
var team = req.user.team;
if (!team) {
res.status(400).json({"error": "user in no team"});
} else {
var newAttr = req.body;
newAttr.updated_at = new Date();
delete newAttr['_id'];
if(newAttr['subdomain']) {
newAttr['subdomain'] = newAttr['subdomain'].toLowerCase();
}
const new_subdomain = newAttr['subdomain'];
var forbidden_subdomains = [];
function updateTeam() {
Team.findOneAndUpdate({"_id": team._id}, {"$set": newAttr}, {"new": true}, (err, team) => {
if (err) res.status(400).json(err);
else {
res.status(200).json(team);
}
});
}
var isForbidden = forbidden_subdomains.indexOf(new_subdomain) > -1;
if (isForbidden) {
res.bad_request("subdomain not valid");
} else {
if (new_subdomain) {
Team.findOne({"domain": new_subdomain}).exec((err, team) => {
if(team) {
res.bad_request("subdomain already used");
} else {
updateTeam()
}
});
} else {
updateTeam()
}
}
}
});
router.get('/:id/memberships', (req, res) => {
User
.find({team: req.user.team})
.populate("team")
.exec(function(err, users){
if (err) res.status(400).json(err);
else {
res.status(200).json(users);
}
});
});
router.post('/:id/memberships', (req, res, next) => {
if (req.body.email) {
const email = req.body.email.toLowerCase();
const team = req.user.team;
User.findOne({"email": email}).populate('team').exec((err, user) => {
if (user) {
const code = crypto.randomBytes(64).toString('hex').substring(0,7);
team.invitation_codes.push(code);
team.save((err) => {
if (err){ res.status(400).json(err); }
else {
mailer.sendMail(email, req.i18n.__("team_invite_membership_subject", team.name), req.i18n.__("team_invite_membership_body", team.name), { action: {
link: config.endpoint + "/teams/" + req.user.team._id + "/join?code=" + code,
name: req.i18n.__("team_invite_membership_action"),
teamname: team.name
}});
res.status(201).json(user);
}
});
} else {
// complete new user
const password = crypto.randomBytes(64).toString('hex').substring(0,7);
const confirmation_token = crypto.randomBytes(64).toString('hex').substring(0,7);
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(password, salt, (err, hash) => {
crypto.randomBytes(16, (ex, buf) => {
const token = buf.toString('hex');
var u = new User({
email: email,
account_type: "email",
nickname: email,
team: team._id,
password_hash: hash,
payment_plan_key: team.payment_plan_key,
confirmation_token: confirmation_token,
preferences: {
language: req.i18n.locale
}
});
u.save((err) => {
if(err) res.sendStatus(400);
else {
var homeSpace = new Space({
name: req.i18n.__("home"),
space_type: "folder",
creator: u
});
homeSpace.save((err, homeSpace) => {
if (err) res.sendStatus(400);
else {
u.home_folder_id = homeSpace._id;
u.save((err) => {
User.find({"_id": {"$in": team.admins }}).exec((err, admins) => {
admins.forEach((admin) => {
var i18n = req.i18n;
if(admin.preferences && admin.preferences.language){
i18n.setLocale(admin.preferences.language || "en");
}
mailer.sendMail(admin.email, i18n.__("team_invite_membership_subject", team.name), i18n.__("team_invite_admin_body", email, team.name, password), { teamname: team.name });
});
});
mailer.sendMail(email, req.i18n.__("team_invite_membership_subject", team.name), req.i18n.__("team_invite_user_body", team.name, password), { action: {
link: config.endpoint + "/users/byteam/" + req.user.team._id + "/join?confirmation_token=" + confirmation_token,
name: req.i18n.__("team_invite_membership_action")
}, teamname: team.name });
if (err) res.status(400).json(err);
else{
res.status(201).json(u)
}
});
}
});
}
});
});
});
});
}
});
} else {
res.status(400).json({"error": "email missing"});
}
});
router.put('/:id/memberships/:user_id', (req, res) => {
User.findOne({_id: req.params.user_id}, (err,mem) => {
if (err) res.sendStatus(400);
else {
if(user.team._id == req.user.team._id){
user['team'] = req.user.team._id;
user.save((err) => {
res.sendStatus(204);
});
} else {
res.sendStatus(403);
}
}
});
});
router.get('/:id/memberships/:user_id/promote', (req, res) => {
User.findOne({_id: req.params.user_id}, (err,user) => {
if (err) res.sendStatus(400);
else {
if (user.team.toString() == req.user.team._id.toString()) {
var team = req.user.team;
var adminIndex = team.admins.indexOf(user._id);
if (adminIndex == -1) {
team.admins.push(user._id);
team.save((err, team) => {
res.status(204).json(team);
});
} else {
res.status(400).json({"error": "already admin"});
}
} else {
res.status(403).json({"error": "team id not correct"});
}
}
});
});
router.get('/:id/memberships/:user_id/demote', (req, res, next) => {
User.findOne({_id: req.params.user_id}, (err,user) => {
if (err) res.sendStatus(400);
else {
if (user.team.toString() == req.user.team._id.toString()) {
const team = req.user.team;
const adminIndex = team.admins.indexOf(user._id);
if(adminIndex > -1) {
team.admins.splice(adminIndex,1);
team.save((err, team) => {
res.status(204).json(team);
});
} else {
res.sendStatus(404);
}
} else {
res.sendStatus(403);
}
}
});
});
router.delete('/:id/memberships/:user_id', (req, res) => {
User.findOne({_id: req.params.user_id}).populate('team').exec((err,user) => {
if (err) res.sendStatus(400);
else {
const currentUserId = req.user._id.toString();
const team = req.user.team;
const isAdmin = (req.user.team.admins.filter( mem => {
return mem == currentUserId;
}).length == 1)
if (isAdmin) {
user.team = null;
user.payment_plan_key = "free";
user.save( err => {
const adminIndex = team.admins.indexOf(user._id);
if(adminIndex > -1) {
team.admins.splice(adminIndex,1);
team.save((err, team) => {
console.log("admin removed");
});
}
res.sendStatus(204);
});
} else {
res.status(403).json({"error": "not admin"});
}
}
});
});
module.exports = router;

470
routes/api/users.js Normal file
View File

@@ -0,0 +1,470 @@
"use strict";
var config = require('config');
require('../../models/schema');
var mailer = require('../../helpers/mailer');
var uploader = require('../../helpers/uploader');
var bcrypt = require('bcrypt');
var crypo = require('crypto');
var swig = require('swig');
var async = require('async');
var _ = require('underscore');
var fs = require('fs');
var request = require('request');
var gm = require('gm');
var validator = require('validator');
var express = require('express');
var router = express.Router();
router.get('/current', function(req, res, next) {
if (req.user) {
console.log(req.user.team);
res.status(200).json(req.user);
} else {
res.status(401).json({"error":"user_not_found"});
}
});
router.post('/', function(req, res) {
if (req.body["email"] && req.body["password"]) {
var email = req.body["email"].toLowerCase();
var nickname = req.body["nickname"];
var password = req.body["password"];
var password_confirmation = req.body["password_confirmation"];
if (password_confirmation == password) {
if (validator.isEmail(email)) {
var createUser = function() {
bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash(password, salt, function(err, hash) {
crypo.randomBytes(16, function(ex, buf) {
var token = buf.toString('hex');
var u = new User({
email: email,
account_type: "email",
nickname: nickname,
password_hash: hash,
preferences: {
language: req.i18n.locale
},
confirmation_token: token
});
u.save(function (err) {
if (err) res.sendStatus(400);
else {
var homeSpace = new Space({
name: req.i18n.__("home"),
space_type: "folder",
creator: u
});
homeSpace.save((err, homeSpace) => {
if (err) res.sendStatus(400);
else {
u.home_folder_id = homeSpace._id;
u.save((err) => {
mailer.sendMail(u.email, req.i18n.__("confirm_subject"), req.i18n.__("confirm_body"), {
action: {
link: config.endpoint + "/confirm/" + u.confirmation_token,
name: req.i18n.__("confirm_action")
}
});
if (err) res.status(400).json(err);
else {
res.status(201).json({});
}
});
}
});
}
});
});
});
});
};
User.find({email: email}, (function (err, users) {
if (err) {
res.status(400).json({"error":"password_confirmation"});
} else {
if (users.length === 0) {
var domain = email.slice(email.lastIndexOf('@')+1);
Domain.findOne({domain: domain}, function(err, domain) {
if(domain){
if(domain.edu) {
createUser();
} else {
res.status(400).json({"error":"domain_blocked"});
}
} else {
createUser();
}
});
} else {
res.status(400).json({"error":"user_email_already_used"});
}
}
}));
} else {
res.status(400).json({"error":"email_invalid"});
}
} else {
res.status(400).json({"error":"password_confirmation"});
}
} else {
res.status(400).json({"error":"email or password missing"});
}
});
router.get('/oauth2callback/url', function(req, res) {
var google = require('googleapis');
var OAuth2 = google.auth.OAuth2;
var oauth2Client = new OAuth2(
config.google_access,
config.google_secret,
config.endpoint + "/login"
);
var url = oauth2Client.generateAuthUrl({
access_type: 'online',
scope: "email"
});
res.status(200).json({"url":url});
});
router.get('/loginorsignupviagoogle', function(req, res) {
var google = require('googleapis');
var OAuth2 = google.auth.OAuth2;
var plus = google.plus('v1');
var oauth2Client = new OAuth2(
config.google_access,
config.google_secret,
config.endpoint + "/login"
);
var loginUser = function(user, cb) {
crypo.randomBytes(48, function(ex, buf) {
var token = buf.toString('hex');
var session = {
token: token,
created_at: new Date()
};
if(!user.sessions)
user.sessions = [];
user.sessions.push(session);
user.save(function(err, user) {
cb(session);
});
});
};
var code = req.query.code;
oauth2Client.getToken(code, function(err, tokens) {
if (err) res.status(400).json(err);
else {
var apiUrl = "https://www.googleapis.com/oauth2/v1/userinfo?alt=json&access_token=" + tokens.access_token;
var finalizeLogin = function(session){
var secure = process.env.NODE_ENV == "production" || process.env.NODE_ENV == "staging";
res.cookie('sdsession', session.token, { httpOnly: true, secure: secure});
res.status(201).json(session);
};
request.get(apiUrl, function(error, response, body) {
if (error) res.status(400).json(error);
else {
const data = JSON.parse(body);
const email = data.email;
const name = data.name;
User.findOne({email: email}, function (err, user) {
if (user) {
// login new google user
if (user.account_type == "google") {
// just login
loginUser(user, (session) => {
finalizeLogin(session);
});
} else {
res.status(400).json({"error":"user_email_already_used"});
}
} else {
const u = new User({
email: email,
account_type: "google",
nickname: name,
avatar_thumb_uri: body.picture,
preferences: {
language: req.i18n.locale
},
confirmed_at: new Date()
});
u.save(function (err) {
if (err) res.status(400).json(err);
else {
var homeSpace = new Space({
name: req.i18n.__("home"),
space_type: "folder",
creator: u
});
homeSpace.save(function(err, homeSpace) {
if (err) res.status(400).json(err);
else {
u.home_folder_id = homeSpace._id;
u.save(function(err){
if (err) res.sendStatus(400);
else {
mailer.sendMail(u.email, req.i18n.__("welcome_subject"), req.i18n.__("welcome_body"), {});
loginUser(u, function(session) {
finalizeLogin(session);
});
}
});
}
});
}
});
}
});
}
});
}
});
});
router.get('/ ', function(req, res, next) {
if (req.user) {
console.log(req.user.team);
res.status(200).json(req.user);
} else {
res.status(401).json({"error":"user_not_found"});
}
});
router.put('/:id', function(req, res, next) {
var user = req.user;
console.log(req.params.id, user._id);
if (user._id == req.params.id) {
var newAttr = req.body;
newAttr.updated_at = new Date();
delete newAttr['_id'];
User.findOneAndUpdate({"_id": user._id}, {"$set": newAttr}, function(err, updatedUser) {
if (err) {
res.sendStatus(400);
} else {
res.status(200).json(updatedUser);
}
});
} else {
res.sendStatus(403);
}
});
router.post('/:id/password', function(req, res, next) {
var user = req.user;
var old_password = req.body.old_password;
var pass = req.body.new_password;
if (pass.length >= 6) {
if (user._id == req.params.id) {
if (bcrypt.compareSync(old_password, user.password_hash)) {
bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash(pass, salt, function(err, hash) {
user.password_hash = hash;
user.save(function(err){
if(err){
res.status(400).json(err);
}else{
res.sendStatus(204);
}
});
});
});
} else {
res.status(403).json({"error": "old password wrong"});
}
} else {
res.status(403).json({"error": "wrong user"});
}
} else {
res.status(400).json({"error": "password_to_short"});
}
});
router.delete('/:id', (req, res, next) => {
const user = req.user;
if(user._id == req.params.id) {
if (user.account_type == 'email') {
if (bcrypt.compareSync(req.query.password, user.password_hash)) {
user.remove((err) => {
if(err)res.status(400).json(err);
else res.sendStatus(204);
});
} else {
res.bad_request("password_incorrect");
}
} else {
user.remove((err) => {
if(err)res.status(400).json(err);
else res.sendStatus(204);
});
}
}
else res.status(403).json({error: ""});
});
router.put('/:user_id/confirm', (req, res) => {
const token = req.body.token;
const user = req.user;
if (user.confirmation_token === token) {
user.confirmation_token = null;
user.confirmed_at = new Date();
user.save(function(err, updatedUser) {
if(err) {
res.sendStatus(400);
} else {
res.status(200).json(updatedUser);
}
});
} else {
res.sendStatus(400);
}
});
router.post('/:user_id/avatar', (req, res, next) => {
const user = req.user;
const filename = "u"+req.user._id+"_"+(new Date().getTime())+".jpeg"
const localFilePath = "/tmp/"+filename;
const localResizedFilePath = "/tmp/resized_"+filename;
const writeStream = fs.createWriteStream(localFilePath);
const stream = req.pipe(writeStream);
req.on('end', function() {
gm(localFilePath).resize(200, 200).autoOrient().write(localResizedFilePath, (err) => {
if (err) res.status(400).json(err);
else {
uploader.uploadFile(filename, "image/jpeg", localResizedFilePath, (err, url) => {
if (err) res.status(400).json(err);
else {
user.avatar_thumb_uri = url;
user.save((err, updatedUser) => {
if (err) {
res.sendStatus(400);
} else {
fs.unlink(localResizedFilePath, (err) => {
if (err) {
console.error(err);
res.status(400).json(err);
} else {
res.status(200).json(updatedUser);
}
});
}
});
}
});
}
});
});
});
router.post('/feedback', function(req, res, next) {
var text = req.body.text;
// FIXME
mailer.sendMail("support@example.org", "Support Request by " + req.user.email, text, {reply_to: req.user.email});
res.sendStatus(201);
});
router.post('/password_reset_requests', (req, res, next) => {
const email = req.query.email;
User.findOne({"email": email}).exec((err, user) => {
if (err) {
res.status(400).json(err);
} else {
if (user) {
if(user.account_type == "email") {
crypo.randomBytes(16, (ex, buf) => {
user.password_reset_token = buf.toString('hex');
user.save((err, updatedUser) => {
if (err) res.status(400).json(err);
else {
mailer.sendMail(email, req.i18n.__("password_reset_subject"), req.i18n.__("password_reset_body"), {action: {
link: config.endpoint + "/password-confirm/" + user.password_reset_token,
name: req.i18n.__("password_reset_action")
}});
res.status(201).json({});
}
});
});
} else {
res.status(404).json({"error": "error_unknown_email"});
}
} else {
res.status(404).json({"error": "error_unknown_email"});
}
}
});
});
router.post('/password_reset_requests/:confirm_token/confirm', function(req, res, next) {
var password = req.body.password;
User
.findOne({"password_reset_token": req.params.confirm_token})
.exec((err, user) => {
if (err) {
res.sendStatus(400);
} else {
if(user) {
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(password, salt, function(err, hash) {
user.password_hash = hash;
user.password_token = null;
user.save(function(err, updatedUser){
if (err) {
res.sendStatus(400);
} else {
res.sendStatus(201);
}
});
});
});
} else {
res.sendStatus(404);
}
}
});
});
router.post('/:user_id/confirm', function(req, res, next) {
mailer.sendMail(req.user.email, req.i18n.__("confirm_subject"), req.i18n.__("confirm_body"), { action:{
link: config.endpoint + "/confirm/" + req.user.confirmation_token,
name: req.i18n.__("confirm_action")
}});
res.sendStatus(201);
});
module.exports = router;

74
routes/api/webgrabber.js Normal file
View File

@@ -0,0 +1,74 @@
"use strict";
var config = require('config');
require('../../models/schema');
var fs = require('fs');
var phantom = require('node-phantom-simple');
var md5 = require('md5');
var express = require('express');
var router = express.Router();
function website_to_png(url,on_success,on_error) {
var hash = md5(url);
var export_path = "/tmp/webgrabber-"+hash+".png";
var timeout = 2000;
console.log("[webgrabber] url: "+url);
console.log("[webgrabber] export_path: "+export_path);
var on_success_called = false;
var on_exit = function(exit_code) {
if (exit_code>0) {
console.log("[phantom-webgrabber] abnormal exit for url "+url);
if (!on_success_called && on_error) {
on_error();
}
}
};
fs.stat(export_path, function(err, stat) {
if (!err) {
// file exists
console.log("[webgrabber] serving cached snapshot of url: "+url);
on_success(export_path);
} else {
phantom.create({ path: require('phantomjs-prebuilt').path }, function (err, browser) {
return browser.createPage(function (err, page) {
page.set('settings.resourceTimeout',timeout);
page.set('settings.javascriptEnabled',false);
return page.open(url, function(err, status) {
console.log("[webgrabber] status: "+status);
page.render(export_path, function() {
on_success_called = true;
on_success(export_path);
browser.exit();
});
});
});
}, {
onExit: on_exit
});
}
});
}
router.get('/:id', function (req, res) {
var uri = new Buffer(req.params.id, "base64")+"";
var on_success_called = false;
website_to_png(uri, (image_path) => {
on_success_called = true;
res.sendFile(image_path);
}, () => {
if (!on_success_called) {
res.status(500).send("[webgrabber] Error fetching website.");
}
});
});
module.exports = router;