spaces.js 9.33 KB
Newer Older
mntmn's avatar
mntmn committed
1
2
"use strict";
var config = require('config');
Lukas F. Hartmann's avatar
Lukas F. Hartmann committed
3
const os = require('os');
4
5
6
7
const db = require('../../models/db');
const Sequelize = require('sequelize');
const Op = Sequelize.Op;
const uuidv4 = require('uuid/v4');
mntmn's avatar
mntmn committed
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

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
};

45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
function listSpacesInFolder(req, res, parent_space_id) {
  db.Space
    .findOne({where: {
      _id: parent_space_id
    }})
    .then(function(space) {
      if (space) {
        function spacesForRole(role) {
          if (role == "none") {
            if (space.access_mode == "public") {
              role = "viewer";
            }
          }
          if (role != "none") {
            db.Space
              .findAll({where:{
                parent_space_id: parent_space_id
              }, include:[db.CreatorSafeInclude(db)]})
              .then(function(spaces) {
                res.status(200).json(spaces);
              });
          } else {
            res.status(403).json({"error": "not authorized"});
          }
        }

        if (req["spaceAuth"] && space.edit_hash) {
          // TODO could be editor, too
          spacesForRole("none");
        } else {
          db.getUserRoleInSpace(space, req.user, spacesForRole);
        }
      } else {
        res.status(404).json({"error": "space not found"});
      }
    });
}

mntmn's avatar
mntmn committed
83
router.get('/', function(req, res, next) {
84
85
86
87
88
89
90
  
  if (req.query.parent_space_id && req["spaceAuth"]) {
    // list subspaces of a space authorized anonymously
    listSpacesInFolder(req, res, req.query.parent_space_id);
    return;
  }
  
mntmn's avatar
mntmn committed
91
92
93
94
95
  if (!req.user) {
    res.status(403).json({
      error: "auth required"
    });
  } else {
96
    if (req.query.search) {
97
98
99
      db.Membership.findAll({where:{
        user_id: req.user._id
      }}).then(memberships => {
100
101
        // search for spaces

mntmn's avatar
mntmn committed
102
        var validMemberships = memberships.filter(function(m) {
103
          if (!m.space_id || (m.space_id == "undefined"))
mntmn's avatar
mntmn committed
104
105
            return false;
          else
106
            return true;
mntmn's avatar
mntmn committed
107
108
109
        });

        var spaceIds = validMemberships.map(function(m) {
110
          return m.space_id;
mntmn's avatar
mntmn committed
111
112
        });

113
114
115
116
117
118
        // 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+"%"}
119
        }, include: [db.CreatorSafeInclude(db)]};
120
121
122
123

        db.Space
          .findAll(q)
          .then(function(spaces) {
mntmn's avatar
mntmn committed
124
125
126
127
128
            res.status(200).json(spaces);
          });
      });

    } else if (req.query.parent_space_id && req.query.parent_space_id != req.user.home_folder_id) {
129
130
      // list spaces in a folder
      
131
      listSpacesInFolder(req, res, req.query.parent_space_id);
mntmn's avatar
mntmn committed
132
    } else {
133
134
      // list home folder and spaces/folders that the user is a member of
      
135
136
137
138
      db.Membership.findAll({ where: {
        user_id: req.user._id
      }}).then(memberships => {
        if (!memberships) memberships = [];
139

mntmn's avatar
mntmn committed
140
        var validMemberships = memberships.filter(function(m) {
141
          if (!m.space_id || (m.space_id == "undefined"))
mntmn's avatar
mntmn committed
142
            return false;
143
          return true;
mntmn's avatar
mntmn committed
144
145
146
        });

        var spaceIds = validMemberships.map(function(m) {
147
          return m.space_id;
mntmn's avatar
mntmn committed
148
149
150
        });

        var q = {
151
152
          [Op.or]: [{
            "creator_id": req.user._id,
mntmn's avatar
mntmn committed
153
154
155
            "parent_space_id": req.user.home_folder_id
          }, {
            "_id": {
156
              [Op.in]: spaceIds
mntmn's avatar
mntmn committed
157
            },
158
159
            "creator_id": {
              [Op.ne]: req.user._id
mntmn's avatar
mntmn committed
160
161
162
163
            }
          }]
        };

164
        db.Space
165
          .findAll({where: q, include: [db.CreatorSafeInclude(db)]})
166
          .then(function(spaces) {
mntmn's avatar
mntmn committed
167
            var updatedSpaces = spaces.map(function(s) {
168
              var spaceObj = db.spaceToObject(s);
mntmn's avatar
mntmn committed
169
170
171
172
173
174
175
176
177
              return spaceObj;
            });
            res.status(200).json(spaces);
          });
      });
    }
  }
});

178
// create a space
mntmn's avatar
mntmn committed
179
180
181
182
183
router.post('/', function(req, res, next) {
  if (req.user) {
    var attrs = req.body;

    var createSpace = () => {
184
185
      attrs._id = uuidv4();
      attrs.creator_id = req.user._id;
mntmn's avatar
mntmn committed
186
      attrs.edit_hash = crypto.randomBytes(64).toString('hex').substring(0, 7);
187
      attrs.edit_slug = attrs.edit_slug || slug(attrs.name);
mntmn's avatar
mntmn committed
188
      attrs.access_mode = "private";
mntmn's avatar
mntmn committed
189
      
190
      db.Space.create(attrs).then(createdSpace => {
Wolfgang Knopki's avatar
Wolfgang Knopki committed
191
        //res.status(201).json(createdSpace);
192
193
        
        // create initial admin membership
194
195
196
197
        var membership = {
          _id: uuidv4(),
          user_id: req.user._id,
          space_id: attrs._id,
198
199
          role: "admin",
          state: "active"
200
201
202
203
204
        };
        
        db.Membership.create(membership).then(() => {
          res.status(201).json(createdSpace);
        });
mntmn's avatar
mntmn committed
205
206
207
208
      });
    }

    if (attrs.parent_space_id) {
209
      db.Space.findOne({ where: {
mntmn's avatar
mntmn committed
210
        "_id": attrs.parent_space_id
211
      }}).then(parentSpace => {
mntmn's avatar
mntmn committed
212
        if (parentSpace) {
213
          db.getUserRoleInSpace(parentSpace, req.user, (role) => {
mntmn's avatar
mntmn committed
214
215
216
217
            if ((role == "editor") || (role == "admin")) {
              createSpace();
            } else {
              res.status(403).json({
218
                "error": "not editor in parent Space. role: "+role
mntmn's avatar
mntmn committed
219
220
221
222
223
224
225
226
227
228
              });
            }
          });
        } else {
          res.status(404).json({
            "error": "parent Space not found"
          });
        }
      });
    } else {
229
      attrs.parent_space_id = req.user.home_folder_id;
mntmn's avatar
mntmn committed
230
231
232
233
234
235
236
237
238
239
240
241
      createSpace();
    }

  } else {
    res.sendStatus(403);
  }
});

router.get('/:id', function(req, res, next) {
  res.status(200).json(req.space);
});

242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
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);
});

mntmn's avatar
mntmn committed
266
267
268
269
270
271
272
273
274
275
276
277
278
279
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['creator'];
mntmn's avatar
mntmn committed
280
281
282
283
284
285
286
287
288
289
  delete newAttr['creator_id'];
  delete newAttr['space_type'];

  if (req['spaceRole'] != "admin") {
    delete newAttr['access_mode']
    delete newAttr['password']
    delete newAttr['edit_hash']
    delete newAttr['edit_slug']
    delete newAttr['editors_locking']
  }
mntmn's avatar
mntmn committed
290

291
  db.Space.update(newAttr, {where: {
mntmn's avatar
mntmn committed
292
    "_id": space._id
293
294
  }}).then(space => {
    res.distributeUpdate("Space", space);
mntmn's avatar
mntmn committed
295
296
297
298
299
300
  });
});

router.post('/:id/background', function(req, res, next) {
  var space = req.space;
  var newDate = new Date();
301
  var fileName = (req.query.filename || "upload.jpg").replace(/[^a-zA-Z0-9\.]/g, '');
mntmn's avatar
mntmn committed
302
303
304
305
306
307
308
309
  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 {
310
311
        if (space.background_uri) {
          var oldPath = url.parse(req.space.background_uri).pathname;
mntmn's avatar
mntmn committed
312
          uploader.removeFile(oldPath, function(err) {
313
            console.error("removed old bg error:", err);
mntmn's avatar
mntmn committed
314
315
316
          });
        }

317
318
        db.Space.update({
          background_uri: backgroundUrl
mntmn's avatar
mntmn committed
319
        }, {
320
321
322
323
324
325
326
327
328
329
          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);
            }
          });
mntmn's avatar
mntmn committed
330
331
332
333
334
335
336
337
338
339
340
341
        });
      }
    });
  });
});

router.delete('/:id', function(req, res, next) {
  if (req.user) {
    const space = req.space;

    if (req.spaceRole == "admin") {
      const attrs = req.body;
342
343
      space.destroy().then(function() {
        res.distributeDelete("Space", space);
mntmn's avatar
mntmn committed
344
345
346
      });
    } else {
      res.status(403).json({
347
        "error": "requires admin role"
mntmn's avatar
mntmn committed
348
349
350
351
352
353
354
355
      });
    }
  } else {
    res.sendStatus(403);
  }
});

module.exports = router;