|
@@ -1,14 +1,16 @@
|
|
|
import { ErrorV3 } from '@growi/core';
|
|
import { ErrorV3 } from '@growi/core';
|
|
|
import { body } from 'express-validator';
|
|
import { body } from 'express-validator';
|
|
|
|
|
+import { Types } from 'mongoose';
|
|
|
|
|
|
|
|
|
|
+import { BookmarkFolderItems } from '~/interfaces/bookmark-info';
|
|
|
import { apiV3FormValidator } from '~/server/middlewares/apiv3-form-validator';
|
|
import { apiV3FormValidator } from '~/server/middlewares/apiv3-form-validator';
|
|
|
import { InvalidParentBookmarkFolderError } from '~/server/models/errors';
|
|
import { InvalidParentBookmarkFolderError } from '~/server/models/errors';
|
|
|
|
|
+import { serializeBookmarkSecurely } from '~/server/models/serializers/bookmark-serializer';
|
|
|
import loggerFactory from '~/utils/logger';
|
|
import loggerFactory from '~/utils/logger';
|
|
|
|
|
|
|
|
import BookmarkFolder from '../../models/bookmark-folder';
|
|
import BookmarkFolder from '../../models/bookmark-folder';
|
|
|
|
|
|
|
|
const logger = loggerFactory('growi:routes:apiv3:bookmark-folder');
|
|
const logger = loggerFactory('growi:routes:apiv3:bookmark-folder');
|
|
|
-
|
|
|
|
|
const express = require('express');
|
|
const express = require('express');
|
|
|
|
|
|
|
|
const router = express.Router();
|
|
const router = express.Router();
|
|
@@ -23,6 +25,8 @@ const validator = {
|
|
|
throw new Error('Maximum folder hierarchy of 2 levels');
|
|
throw new Error('Maximum folder hierarchy of 2 levels');
|
|
|
}
|
|
}
|
|
|
}),
|
|
}),
|
|
|
|
|
+ body('children').optional().isArray().withMessage('Children must be an array'),
|
|
|
|
|
+ body('bookmarkFolderId').optional().isMongoId().withMessage('Bookark Folder ID must be a valid mongo ID'),
|
|
|
],
|
|
],
|
|
|
bookmarkPage: [
|
|
bookmarkPage: [
|
|
|
body('pageId').isMongoId().withMessage('Page ID must be a valid mongo ID'),
|
|
body('pageId').isMongoId().withMessage('Page ID must be a valid mongo ID'),
|
|
@@ -52,6 +56,7 @@ module.exports = (crowi) => {
|
|
|
return res.apiv3({ bookmarkFolder });
|
|
return res.apiv3({ bookmarkFolder });
|
|
|
}
|
|
}
|
|
|
catch (err) {
|
|
catch (err) {
|
|
|
|
|
+ logger.error(err);
|
|
|
if (err instanceof InvalidParentBookmarkFolderError) {
|
|
if (err instanceof InvalidParentBookmarkFolderError) {
|
|
|
return res.apiv3Err(new ErrorV3(err.message, 'failed_to_create_bookmark_folder'));
|
|
return res.apiv3Err(new ErrorV3(err.message, 'failed_to_create_bookmark_folder'));
|
|
|
}
|
|
}
|
|
@@ -63,12 +68,57 @@ module.exports = (crowi) => {
|
|
|
router.get('/list/:userId', accessTokenParser, loginRequiredStrictly, async(req, res) => {
|
|
router.get('/list/:userId', accessTokenParser, loginRequiredStrictly, async(req, res) => {
|
|
|
const { userId } = req.params;
|
|
const { userId } = req.params;
|
|
|
|
|
|
|
|
|
|
+ const getBookmarkFolders = async(
|
|
|
|
|
+ userId: Types.ObjectId | string,
|
|
|
|
|
+ parentFolderId?: Types.ObjectId | string,
|
|
|
|
|
+ ) => {
|
|
|
|
|
+ const folders = await BookmarkFolder.find({ owner: userId, parent: parentFolderId })
|
|
|
|
|
+ .populate('children')
|
|
|
|
|
+ .populate({
|
|
|
|
|
+ path: 'bookmarks',
|
|
|
|
|
+ model: 'Bookmark',
|
|
|
|
|
+ populate: {
|
|
|
|
|
+ path: 'page',
|
|
|
|
|
+ model: 'Page',
|
|
|
|
|
+ populate: {
|
|
|
|
|
+ path: 'lastUpdateUser',
|
|
|
|
|
+ model: 'User',
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ }).exec();
|
|
|
|
|
+
|
|
|
|
|
+ const returnValue: BookmarkFolderItems[] = [];
|
|
|
|
|
+
|
|
|
|
|
+ const promises = folders.map(async(folder: BookmarkFolderItems) => {
|
|
|
|
|
+ const children = await getBookmarkFolders(userId, folder._id);
|
|
|
|
|
+
|
|
|
|
|
+ // !! DO NOT THIS SERIALIZING OUTSIDE OF PROMISES !! -- 05.23.2023 ryoji-s
|
|
|
|
|
+ // Serializing outside of promises will cause not populated.
|
|
|
|
|
+ const bookmarks = folder.bookmarks.map(bookmark => serializeBookmarkSecurely(bookmark));
|
|
|
|
|
+
|
|
|
|
|
+ const res = {
|
|
|
|
|
+ _id: folder._id.toString(),
|
|
|
|
|
+ name: folder.name,
|
|
|
|
|
+ owner: folder.owner,
|
|
|
|
|
+ bookmarks,
|
|
|
|
|
+ children,
|
|
|
|
|
+ parent: folder.parent,
|
|
|
|
|
+ };
|
|
|
|
|
+ return res;
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ const results = await Promise.all(promises) as unknown as BookmarkFolderItems[];
|
|
|
|
|
+ returnValue.push(...results);
|
|
|
|
|
+ return returnValue;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
try {
|
|
try {
|
|
|
- const bookmarkFolderItems = await BookmarkFolder.findFolderAndChildren(userId);
|
|
|
|
|
|
|
+ const bookmarkFolderItems = await getBookmarkFolders(userId, undefined);
|
|
|
|
|
|
|
|
return res.apiv3({ bookmarkFolderItems });
|
|
return res.apiv3({ bookmarkFolderItems });
|
|
|
}
|
|
}
|
|
|
catch (err) {
|
|
catch (err) {
|
|
|
|
|
+ logger.error(err);
|
|
|
return res.apiv3Err(err, 500);
|
|
return res.apiv3Err(err, 500);
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
@@ -88,12 +138,15 @@ module.exports = (crowi) => {
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
router.put('/', accessTokenParser, loginRequiredStrictly, validator.bookmarkFolder, async(req, res) => {
|
|
router.put('/', accessTokenParser, loginRequiredStrictly, validator.bookmarkFolder, async(req, res) => {
|
|
|
- const { bookmarkFolderId, name, parent } = req.body;
|
|
|
|
|
|
|
+ const {
|
|
|
|
|
+ bookmarkFolderId, name, parent, children,
|
|
|
|
|
+ } = req.body;
|
|
|
try {
|
|
try {
|
|
|
- const bookmarkFolder = await BookmarkFolder.updateBookmarkFolder(bookmarkFolderId, name, parent);
|
|
|
|
|
|
|
+ const bookmarkFolder = await BookmarkFolder.updateBookmarkFolder(bookmarkFolderId, name, parent, children);
|
|
|
return res.apiv3({ bookmarkFolder });
|
|
return res.apiv3({ bookmarkFolder });
|
|
|
}
|
|
}
|
|
|
catch (err) {
|
|
catch (err) {
|
|
|
|
|
+ logger.error(err);
|
|
|
return res.apiv3Err(err, 500);
|
|
return res.apiv3Err(err, 500);
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
@@ -108,6 +161,7 @@ module.exports = (crowi) => {
|
|
|
return res.apiv3({ bookmarkFolder });
|
|
return res.apiv3({ bookmarkFolder });
|
|
|
}
|
|
}
|
|
|
catch (err) {
|
|
catch (err) {
|
|
|
|
|
+ logger.error(err);
|
|
|
return res.apiv3Err(err, 500);
|
|
return res.apiv3Err(err, 500);
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
@@ -120,6 +174,7 @@ module.exports = (crowi) => {
|
|
|
return res.apiv3({ bookmarkFolder });
|
|
return res.apiv3({ bookmarkFolder });
|
|
|
}
|
|
}
|
|
|
catch (err) {
|
|
catch (err) {
|
|
|
|
|
+ logger.error(err);
|
|
|
return res.apiv3Err(err, 500);
|
|
return res.apiv3Err(err, 500);
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|