Jelajahi Sumber

Included a new route to export (json) an attachment from a board.

GET  /api/boards/:id/attachments/:attachmentId/export
Vagner Nascimento 4 tahun lalu
induk
melakukan
6eb90238b1
2 mengubah file dengan 109 tambahan dan 49 penghapusan
  1. 51 0
      models/export.js
  2. 58 49
      models/exporter.js

+ 51 - 0
models/export.js

@@ -48,6 +48,57 @@ if (Meteor.isServer) {
     }
   });
 
+  // todo XXX once we have a real API in place, move that route there
+  // todo XXX also  share the route definition between the client and the server
+  // so that we could use something like
+  // `ApiRoutes.path('boards/export', boardId)``
+  // on the client instead of copy/pasting the route path manually between the
+  // client and the server.
+  /**
+   * @operation exportJson
+   * @tag Boards
+   *
+   * @summary This route is used to export a attachement to a json file format.
+   *
+   * @description If user is already logged-in, pass loginToken as param
+   * "authToken": '/api/boards/:boardId/attachments/:attachmentId/export?authToken=:token'
+   *
+   *
+   * @param {string} boardId the ID of the board we are exporting
+   * @param {string} attachmentId the ID of the attachment we are exporting
+   * @param {string} authToken the loginToken
+   */
+  JsonRoutes.add(
+    'get',
+    '/api/boards/:boardId/attachments/:attachmentId/export',
+    function(req, res) {
+      const boardId = req.params.boardId;
+      const attachmentId = req.params.attachmentId;
+      let user = null;
+      const loginToken = req.query.authToken;
+      if (loginToken) {
+        const hashToken = Accounts._hashLoginToken(loginToken);
+        user = Meteor.users.findOne({
+          'services.resume.loginTokens.hashedToken': hashToken,
+        });
+      } else if (!Meteor.settings.public.sandstorm) {
+        Authentication.checkUserId(req.userId);
+        user = Users.findOne({ _id: req.userId, isAdmin: true });
+      }
+      const exporter = new Exporter(boardId, attachmentId);
+      if (exporter.canExport(user)) {
+        JsonRoutes.sendResult(res, {
+          code: 200,
+          data: exporter.build(),
+        });
+      } else {
+        // we could send an explicit error message, but on the other hand the only
+        // way to get there is by hacking the UI so let's keep it raw.
+        JsonRoutes.sendResult(res, 403);
+      }
+    },
+  );
+
   /**
    * @operation exportCSV/TSV
    * @tag Boards

+ 58 - 49
models/exporter.js

@@ -2,8 +2,9 @@ const Papa = require('papaparse');
 
 // exporter maybe is broken since Gridfs introduced, add fs and path
 export class Exporter {
-  constructor(boardId) {
+  constructor(boardId, attachmentId) {
     this._boardId = boardId;
+    this._attachmentId = attachmentId;
   }
 
   build() {
@@ -33,6 +34,62 @@ export class Exporter {
         },
       }),
     );
+
+    // [Old] for attachments we only export IDs and absolute url to original doc
+    // [New] Encode attachment to base64
+
+    const getBase64Data = function(doc, callback) {
+      let buffer = Buffer.allocUnsafe(0);
+      buffer.fill(0);
+
+      // callback has the form function (err, res) {}
+      const tmpFile = path.join(
+        os.tmpdir(),
+        `tmpexport${process.pid}${Math.random()}`,
+      );
+      const tmpWriteable = fs.createWriteStream(tmpFile);
+      const readStream = doc.createReadStream();
+      readStream.on('data', function(chunk) {
+        buffer = Buffer.concat([buffer, chunk]);
+      });
+
+      readStream.on('error', function() {
+        callback(null, null);
+      });
+      readStream.on('end', function() {
+        // done
+        fs.unlink(tmpFile, () => {
+          //ignored
+        });
+
+        callback(null, buffer.toString('base64'));
+      });
+      readStream.pipe(tmpWriteable);
+    };
+    const getBase64DataSync = Meteor.wrapAsync(getBase64Data);
+    const byBoardAndAttachment = this._attachmentId
+      ? { boardId: this._boardId, _id: this._attachmentId }
+      : byBoard;
+    result.attachments = Attachments.find(byBoardAndAttachment)
+      .fetch()
+      .map(attachment => {
+        let filebase64 = null;
+        filebase64 = getBase64DataSync(attachment);
+
+        return {
+          _id: attachment._id,
+          cardId: attachment.cardId,
+          //url: FlowRouter.url(attachment.url()),
+          file: filebase64,
+          name: attachment.original.name,
+          type: attachment.original.type,
+        };
+      });
+    //When has a especific valid attachment return the single element
+    if (this._attachmentId) {
+      return result.attachments.length > 0 ? result.attachments[0] : {};
+    }
+
     result.lists = Lists.find(byBoard, noBoardId).fetch();
     result.cards = Cards.find(byBoardNoLinked, noBoardId).fetch();
     result.swimlanes = Swimlanes.find(byBoard, noBoardId).fetch();
@@ -84,54 +141,6 @@ export class Exporter {
       );
     });
 
-    // [Old] for attachments we only export IDs and absolute url to original doc
-    // [New] Encode attachment to base64
-
-    const getBase64Data = function(doc, callback) {
-      let buffer = Buffer.allocUnsafe(0);
-      buffer.fill(0);
-
-      // callback has the form function (err, res) {}
-      const tmpFile = path.join(
-        os.tmpdir(),
-        `tmpexport${process.pid}${Math.random()}`,
-      );
-      const tmpWriteable = fs.createWriteStream(tmpFile);
-      const readStream = doc.createReadStream();
-      readStream.on('data', function(chunk) {
-        buffer = Buffer.concat([buffer, chunk]);
-      });
-
-      readStream.on('error', function() {
-        callback(null, null);
-      });
-      readStream.on('end', function() {
-        // done
-        fs.unlink(tmpFile, () => {
-          //ignored
-        });
-
-        callback(null, buffer.toString('base64'));
-      });
-      readStream.pipe(tmpWriteable);
-    };
-    const getBase64DataSync = Meteor.wrapAsync(getBase64Data);
-    result.attachments = Attachments.find(byBoard)
-      .fetch()
-      .map(attachment => {
-        let filebase64 = null;
-        filebase64 = getBase64DataSync(attachment);
-
-        return {
-          _id: attachment._id,
-          cardId: attachment.cardId,
-          //url: FlowRouter.url(attachment.url()),
-          file: filebase64,
-          name: attachment.original.name,
-          type: attachment.original.type,
-        };
-      });
-
     // we also have to export some user data - as the other elements only
     // include id but we have to be careful:
     // 1- only exports users that are linked somehow to that board