123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553 |
- "use strict";
- var config = require('config');
- const os = require('os');
- 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 payloadConverter = require('../../helpers/artifact_converter');
- var slug = require('slug');
- var fs = require('fs');
- var async = require('async');
- var _ = require("underscore");
- var request = require('request');
- var url = require("url");
- var path = require("path");
- var crypto = require('crypto');
- 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) {
- db.Membership.find({where: {
- user_id: req.user._id
- }}, (memberships) => {
-
- var validMemberships = memberships.filter((m) => {
- if (!m.space_id || (m.space_id == "undefined"))
- return false;
- return true;
- });
- var editorMemberships = validMemberships.filter((m) => {
- return (m.role == "editor") || (m.role == "admin")
- });
- var spaceIds = editorMemberships.map(function(m) {
- return m.space_id;
- });
- // TODO port
- var q = {
- "space_type": "folder",
- "$or": [{
- "creator": req.user._id
- }, {
- "_id": {
- "$in": spaceIds
- },
- "creator": {
- "$ne": req.user._id
- }
- }]
- };
- db.Space
- .findAll({where: q})
- .then(function(spaces) {
- 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;
- })
- res.status(200).json(uniqueFolders);
- });
- });
- });
- } else if (req.query.search) {
- db.Membership.findAll({where:{
- user_id: req.user._id
- }}).then(memberships => {
-
- var validMemberships = memberships.filter(function(m) {
- if (!m.space_id || (m.space_id == "undefined"))
- return false;
- else
- return true;
- });
- var spaceIds = validMemberships.map(function(m) {
- return m.space_id;
- });
- // TODO FIXME port
- var q = { where: {
- [Op.or]: [{"creator_id": req.user._id},
- {"_id": {[Op.in]: spaceIds}},
- {"parent_space_id": {[Op.in]: spaceIds}}],
- name: {[Op.like]: "%"+req.query.search+"%"}
- }, include: ['creator']};
- db.Space
- .findAll(q)
- .then(function(spaces) {
- res.status(200).json(spaces);
- });
- });
- } else if (req.query.parent_space_id && req.query.parent_space_id != req.user.home_folder_id) {
- db.Space
- .findOne({where: {
- _id: req.query.parent_space_id
- }})
- //.populate('creator', userMapping)
- .then(function(space) {
- if (space) {
- db.getUserRoleInSpace(space, req.user, function(role) {
- if (role == "none") {
- if (space.access_mode == "public") {
- role = "viewer";
- }
- }
- if (role != "none") {
- db.Space
- .findAll({where:{
- parent_space_id: req.query.parent_space_id
- }, include:['creator']})
- .then(function(spaces) {
- res.status(200).json(spaces);
- });
- } else {
- res.status(403).json({"error": "no authorized"});
- }
- });
- } else {
- res.status(404).json({"error": "space not found"});
- }
- });
- } else {
- db.Membership.findAll({ where: {
- user_id: req.user._id
- }}).then(memberships => {
- if (!memberships) memberships = [];
-
- var validMemberships = memberships.filter(function(m) {
- if (!m.space_id || (m.space_id == "undefined"))
- return false;
- });
- var spaceIds = validMemberships.map(function(m) {
- return m.space_id;
- });
- var q = {
- [Op.or]: [{
- "creator_id": req.user._id,
- "parent_space_id": req.user.home_folder_id
- }, {
- "_id": {
- [Op.in]: spaceIds
- },
- "creator_id": {
- [Op.ne]: req.user._id
- }
- }]
- };
- db.Space
- .findAll({where: q, include: ['creator']})
- .then(function(spaces) {
- var updatedSpaces = spaces.map(function(s) {
- var spaceObj = db.spaceToObject(s);
- return spaceObj;
- });
- res.status(200).json(spaces);
- });
- });
- }
- }
- });
- // create a space
- router.post('/', function(req, res, next) {
- if (req.user) {
- var attrs = req.body;
- var createSpace = () => {
- attrs._id = uuidv4();
- attrs.creator_id = req.user._id;
- attrs.edit_hash = crypto.randomBytes(64).toString('hex').substring(0, 7);
- attrs.edit_slug = slug(attrs.name);
-
- db.Space.create(attrs).then(createdSpace => {
- //if (err) res.sendStatus(400);
- var membership = {
- _id: uuidv4(),
- user_id: req.user._id,
- space_id: attrs._id,
- role: "admin"
- };
-
- db.Membership.create(membership).then(() => {
- res.status(201).json(createdSpace);
- });
- });
- }
- if (attrs.parent_space_id) {
- db.Space.findOne({ where: {
- "_id": attrs.parent_space_id
- }}).then(parentSpace => {
- if (parentSpace) {
- db.getUserRoleInSpace(parentSpace, req.user, (role) => {
- if ((role == "editor") || (role == "admin")) {
- createSpace();
- } else {
- res.status(403).json({
- "error": "not editor in parent Space. role: "+role
- });
- }
- });
- } 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.get('/:id/path', (req, res) => {
- // build up a breadcrumb trail (path)
- var path = [];
- var buildPath = (space) => {
- if (space.parent_space_id) {
- db.Space.findOne({ where: {
- "_id": space.parent_space_id
- }}).then(parentSpace => {
- if (space._id == parentSpace._id) {
- console.error("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);
- });
- 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'];
- db.Space.update(newAttr, {where: {
- "_id": space._id
- }}).then(space => {
- 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.jpg").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 {
- if (space.background_uri) {
- var oldPath = url.parse(req.space.background_uri).pathname;
- uploader.removeFile(oldPath, function(err) {
- console.error("removed old bg error:", err);
- });
- }
- db.Space.update({
- background_uri: backgroundUrl
- }, {
- where: { "_id": space._id }
- }, function(rows) {
- 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 duplicate"
- });
- } else {
- db.getUserRoleInSpace(parentSpace, req.user, (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.destroy().then(function() {
- res.distributeDelete("Space", space);
- });
- } else {
- res.status(403).json({
- "error": "requires admin role"
- });
- }
- } 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 = os.tmpdir() + "/" + 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 = os.tmpdir() + "/" + rawName;
-
- fs.mkdir(outputFolder, function(err) {
- var images = outputFolder + "/" + rawName + "-page-%03d.jpeg";
-
- // FIXME not portable
- 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((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 = db.Artifact.create({
- _id: uuidv4(),
- mime: "image/jpg",
- space_id: req.space._id,
- user_id: userId,
- editor_name: req.guest_name,
- w: w,
- h: h,
- x: x,
- y: y,
- z: (number + (count + 100))
- }).then(a => {
- payloadConverter.convert(a, fileName, localfilePath, (error, artifact) => {
- if (error) res.status(400).json(error);
- else {
- if (withZones) {
- var zone = {
- _id: uuidv4(),
- mime: "x-spacedeck/zone",
- description: "Zone " + (number),
- space_id: req.space._id,
- user_id: userId,
- editor_name: req.guest_name,
- w: artifact.w + 20,
- h: artifact.h + 40,
- x: x - 10,
- y: y - 30,
- z: number,
- order: number,
- valign: "middle",
- align: "center"
- };
- db.Artifact.create(zone).then((z) => {
- redis.sendMessage("create", "Artifact", z.toJSON(), req.channelId);
- cb(null, [artifact, zone]);
- });
- } else {
- cb(null, [artifact]);
- }
- }
- });
- });
- });
- }, function(err, artifacts) {
- // FIXME not portable
- 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", JSON.stringify(a), req.channelId);
- });
- } else {
- redis.sendMessage("create", "Artifact", JSON.stringify(artifact_or_artifacts), req.channelId);
- }
- cb(null);
- });
- });
- });
- });
- } else {
- console.error("error:", error);
- // FIXME not portable
- 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;
|