UNPKG

@state-less/leap-backend

Version:

A collection of open source fullstack services

391 lines (389 loc) 20.3 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.Post = exports.Platform = exports.ForumPolicies = exports.Forum = exports.Answer = void 0; var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties")); var _reactServer = require("@state-less/react-server"); var _uuid = require("uuid"); var _ServerSideProps = require("../ServerSideProps"); var _Comments = require("../Comments"); var _Votings = require("../Votings"); var _config = require("../../lib/config"); var _ViewCounter = require("../ViewCounter"); var _Reactions = require("../Reactions"); var _jsxRuntime = require("@state-less/react-server/dist/jsxRenderer/jsx-runtime"); var _excluded = ["id", "deleteAnswer", "users"], _excluded2 = ["id", "deletePost", "approvePost", "setPostSticky", "users"]; function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2["default"])(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } var Answer = exports.Answer = function Answer(_ref, _ref2) { var _post$owner, _post$owner2, _user, _post$owner3, _user2, _post$owner4, _user3, _post$owner5, _user4, _user5, _user6; var id = _ref.id, deleteAnswer = _ref.deleteAnswer, users = _ref.users, initialAnswer = (0, _objectWithoutProperties2["default"])(_ref, _excluded); var context = _ref2.context; var user = null; if ((0, _reactServer.isClientContext)(context)) try { user = (0, _reactServer.authenticate)(context.headers, _config.JWT_SECRET); } catch (e) {} var _useState = (0, _reactServer.useState)(initialAnswer, { key: 'answer', scope: id }), _useState2 = (0, _slicedToArray2["default"])(_useState, 2), post = _useState2[0], setPost = _useState2[1]; var _useState3 = (0, _reactServer.useState)(false, { key: 'deleted', scope: id }), _useState4 = (0, _slicedToArray2["default"])(_useState3, 2), deleted = _useState4[0], setDeleted = _useState4[1]; var setBody = function setBody(body) { if (typeof body !== 'string') throw new Error('Body must be a string'); if (deleted) throw new Error('Cannot edit a deleted post'); setPost(_objectSpread(_objectSpread({}, post), {}, { body: body })); }; var del = function del(id) { setDeleted(true); deleteAnswer(); }; return (0, _jsxRuntime.jsxs)(_ServerSideProps.ServerSideProps, _objectSpread(_objectSpread({}, post), {}, { deleted: deleted, setBody: setBody, owner: { id: (_post$owner = post.owner) === null || _post$owner === void 0 ? void 0 : _post$owner.id, name: (_post$owner2 = post.owner) === null || _post$owner2 === void 0 || (_post$owner2 = _post$owner2.strategies) === null || _post$owner2 === void 0 || (_post$owner2 = _post$owner2[(_user = user) === null || _user === void 0 ? void 0 : _user.strategy]) === null || _post$owner2 === void 0 ? void 0 : _post$owner2.decoded.name, picture: (_post$owner3 = post.owner) === null || _post$owner3 === void 0 || (_post$owner3 = _post$owner3.strategies) === null || _post$owner3 === void 0 || (_post$owner3 = _post$owner3[(_user2 = user) === null || _user2 === void 0 ? void 0 : _user2.strategy]) === null || _post$owner3 === void 0 ? void 0 : _post$owner3.decoded.picture, email: (_post$owner4 = post.owner) === null || _post$owner4 === void 0 || (_post$owner4 = _post$owner4.strategies) === null || _post$owner4 === void 0 || (_post$owner4 = _post$owner4[(_user3 = user) === null || _user3 === void 0 ? void 0 : _user3.strategy]) === null || _post$owner4 === void 0 ? void 0 : _post$owner4.email }, del: del, canDelete: (post === null || post === void 0 || (_post$owner5 = post.owner) === null || _post$owner5 === void 0 ? void 0 : _post$owner5.id) === ((_user4 = user) === null || _user4 === void 0 ? void 0 : _user4.id) || users.includes((_user5 = user) === null || _user5 === void 0 || (_user5 = _user5.strategies) === null || _user5 === void 0 || (_user5 = _user5[(_user6 = user) === null || _user6 === void 0 ? void 0 : _user6.strategy]) === null || _user5 === void 0 ? void 0 : _user5.email), children: [(0, _jsxRuntime.jsx)(_Votings.Votings, { policies: [_Votings.VotingPolicies.SingleVote] }, "answer-".concat(id, "-votings")), (0, _jsxRuntime.jsx)(_Comments.Comments, {}, "answer-".concat(id, "-comments")), (0, _jsxRuntime.jsx)(_Reactions.Reactions, { policies: [_Reactions.ReactionPolicies.SingleVote], values: _Reactions.reactionsWhiteList }, "answer-".concat(id, "-reactions"))] }), "answer-".concat(id, "-props")); }; var Post = exports.Post = function Post(_ref3, _ref4) { var _context$headers, _user7, _user8, _post$owner6, _user9, _post$owner7, _post$owner8, _user11, _post$owner9, _user12, _post$owner10, _user13, _user14, _user15; var id = _ref3.id, deletePost = _ref3.deletePost, approvePost = _ref3.approvePost, setPostSticky = _ref3.setPostSticky, users = _ref3.users, initialPost = (0, _objectWithoutProperties2["default"])(_ref3, _excluded2); var context = _ref4.context; var user = null; var clientId = (context === null || context === void 0 || (_context$headers = context.headers) === null || _context$headers === void 0 ? void 0 : _context$headers['x-unique-id']) || 'server'; if ((0, _reactServer.isClientContext)(context)) try { user = (0, _reactServer.authenticate)(context.headers, _config.JWT_SECRET); } catch (e) {} var _useState5 = (0, _reactServer.useState)(initialPost, { key: 'post', scope: id, storeInitialState: true }), _useState6 = (0, _slicedToArray2["default"])(_useState5, 2), post = _useState6[0], setPost = _useState6[1]; var _useState7 = (0, _reactServer.useState)([], { key: 'answers', scope: id }), _useState8 = (0, _slicedToArray2["default"])(_useState7, 2), answers = _useState8[0], setAnswers = _useState8[1]; var createAnswer = function createAnswer(_ref5) { var body = _ref5.body; var answer = { id: (0, _uuid.v4)(), owner: user, body: body }; setAnswers([].concat((0, _toConsumableArray2["default"])(answers), [answer])); return answer; }; var setBody = function setBody(body) { if (typeof body !== 'string') throw new Error('Body must be a string'); if (post.deleted) throw new Error('Cannot edit a deleted post'); setPost(_objectSpread(_objectSpread({}, post), {}, { body: body })); }; var del = function del(id) { setPost(_objectSpread(_objectSpread({}, post), {}, { deleted: true })); deletePost(); }; var isAdmin = users.includes((_user7 = user) === null || _user7 === void 0 || (_user7 = _user7.strategies) === null || _user7 === void 0 || (_user7 = _user7[(_user8 = user) === null || _user8 === void 0 ? void 0 : _user8.strategy]) === null || _user7 === void 0 ? void 0 : _user7.email); var isOwner = (post === null || post === void 0 || (_post$owner6 = post.owner) === null || _post$owner6 === void 0 ? void 0 : _post$owner6.id) === (((_user9 = user) === null || _user9 === void 0 ? void 0 : _user9.id) || clientId); var isApproved = post === null || post === void 0 ? void 0 : post.approved; var passProps = { approved: isApproved }; if (isAdmin || isOwner || isApproved) { Object.assign(passProps, post); } var toggleSticky = function toggleSticky() { if (!isAdmin) { throw new Error('Not authorized.'); } setPost(_objectSpread(_objectSpread({}, post), {}, { sticky: !post.sticky })); setPostSticky(!post.sticky); }; var approve = function approve() { if (!isAdmin) { throw new Error('Not authorized.'); } setPost(_objectSpread(_objectSpread({}, post), {}, { approved: !post.approved })); approvePost(); }; var _deleteAnswer = function deleteAnswer(id) { var _deleted$owner, _user10; if (!answers.find(function (answer) { return answer.id === id; })) { throw new Error('Answer not found'); } var deleted = answers.find(function (answer) { return answer.id === id; }); var canDelete = (deleted === null || deleted === void 0 || (_deleted$owner = deleted.owner) === null || _deleted$owner === void 0 ? void 0 : _deleted$owner.id) === ((_user10 = user) === null || _user10 === void 0 ? void 0 : _user10.id) || isAdmin; if (!canDelete) { throw new Error('Not authorized to delete this answer'); } setAnswers(answers.filter(function (answer) { return answer.id !== id; }).concat([_objectSpread(_objectSpread({}, deleted), {}, { deleted: true })])); }; return (0, _jsxRuntime.jsxs)(_ServerSideProps.ServerSideProps, _objectSpread(_objectSpread({ id: id }, passProps), {}, { owner: { id: (_post$owner7 = post.owner) === null || _post$owner7 === void 0 ? void 0 : _post$owner7.id, name: (_post$owner8 = post.owner) === null || _post$owner8 === void 0 || (_post$owner8 = _post$owner8.strategies) === null || _post$owner8 === void 0 || (_post$owner8 = _post$owner8[(_user11 = user) === null || _user11 === void 0 ? void 0 : _user11.strategy]) === null || _post$owner8 === void 0 ? void 0 : _post$owner8.decoded.name, picture: (_post$owner9 = post.owner) === null || _post$owner9 === void 0 || (_post$owner9 = _post$owner9.strategies) === null || _post$owner9 === void 0 || (_post$owner9 = _post$owner9[(_user12 = user) === null || _user12 === void 0 ? void 0 : _user12.strategy]) === null || _post$owner9 === void 0 ? void 0 : _post$owner9.decoded.picture, email: (_post$owner10 = post.owner) === null || _post$owner10 === void 0 || (_post$owner10 = _post$owner10.strategies) === null || _post$owner10 === void 0 || (_post$owner10 = _post$owner10[(_user13 = user) === null || _user13 === void 0 ? void 0 : _user13.strategy]) === null || _post$owner10 === void 0 ? void 0 : _post$owner10.email }, del: del, deleted: post.deleted, approve: approve, toggleSticky: toggleSticky, createAnswer: createAnswer, canDelete: // post?.owner?.id === user?.id || // Only admins can delete posts... // TODO: add policies to toggle owner deletion users.includes((_user14 = user) === null || _user14 === void 0 || (_user14 = _user14.strategies) === null || _user14 === void 0 || (_user14 = _user14[(_user15 = user) === null || _user15 === void 0 ? void 0 : _user15.strategy]) === null || _user14 === void 0 ? void 0 : _user14.email), setBody: setBody // TODO: Add renderWithoutEffects utility. , viewCounter: (0, _reactServer.render)((0, _jsxRuntime.jsx)(_ViewCounter.ViewCounter, {}, "post-".concat(id, "-view-counter")), { clientProps: {}, initiator: _reactServer.Initiator.RenderServer, context: null }, null), children: [(0, _jsxRuntime.jsx)(_Votings.Votings, { policies: [_Votings.VotingPolicies.SingleVote] }, "post-".concat(id, "-votings")), (0, _jsxRuntime.jsx)(_Reactions.Reactions, { policies: [_Reactions.ReactionPolicies.SingleVote], values: _Reactions.reactionsWhiteList }, "post-".concat(id, "-reactions")), answers.filter(function (answer) { var _answer$owner, _user16; return !answer.deleted || ((_answer$owner = answer.owner) === null || _answer$owner === void 0 ? void 0 : _answer$owner.id) === ((_user16 = user) === null || _user16 === void 0 ? void 0 : _user16.id); }).map(function (answer) { return (0, _jsxRuntime.jsx)(Answer, _objectSpread(_objectSpread({}, answer), {}, { deleteAnswer: function deleteAnswer() { return _deleteAnswer(answer.id); } }), "answer-".concat(answer.id)); })] }), "post-".concat(id, "-props")); }; var ForumPolicies = exports.ForumPolicies = /*#__PURE__*/function (ForumPolicies) { ForumPolicies["PostsNeedApproval"] = "PostsNeedApproval"; return ForumPolicies; }({}); var Forum = exports.Forum = function Forum(_ref6, _ref7) { var _context$headers2, _user26, _user27; var id = _ref6.id, name = _ref6.name, policies = _ref6.policies, users = _ref6.users; var key = _ref7.key, context = _ref7.context, clientProps = _ref7.clientProps, initiator = _ref7.initiator; var user = null; var clientId = (context === null || context === void 0 || (_context$headers2 = context.headers) === null || _context$headers2 === void 0 ? void 0 : _context$headers2['x-unique-id']) || 'server'; if ((0, _reactServer.isClientContext)(context)) try { user = (0, _reactServer.authenticate)(context.headers, _config.JWT_SECRET); } catch (e) {} var _useState9 = (0, _reactServer.useState)([], { key: 'posts', scope: id }), _useState10 = (0, _slicedToArray2["default"])(_useState9, 2), posts = _useState10[0], setPosts = _useState10[1]; var createPost = function createPost(_ref8) { var title = _ref8.title, body = _ref8.body, tags = _ref8.tags; var post = { id: (0, _uuid.v4)(), title: title, body: body, tags: tags, deleted: false, approved: false, owner: user || { id: clientId } }; setPosts([].concat((0, _toConsumableArray2["default"])(posts), [post])); return post; }; var _deletePost = function deletePost(postId) { var _owner$strategies, _user17, _user18, _user19, _user20, _user21; var deleted = posts.find(function (post) { return post.id === postId; }); var _ref9 = deleted || {}, owner = _ref9.owner; if ((owner === null || owner === void 0 || (_owner$strategies = owner.strategies) === null || _owner$strategies === void 0 || (_owner$strategies = _owner$strategies[(_user17 = user) === null || _user17 === void 0 ? void 0 : _user17.strategy]) === null || _owner$strategies === void 0 ? void 0 : _owner$strategies.email) !== ((_user18 = user) === null || _user18 === void 0 || (_user18 = _user18.strategies) === null || _user18 === void 0 || (_user18 = _user18[(_user19 = user) === null || _user19 === void 0 ? void 0 : _user19.strategy]) === null || _user18 === void 0 ? void 0 : _user18.email) && !users.includes((_user20 = user) === null || _user20 === void 0 || (_user20 = _user20.strategies) === null || _user20 === void 0 || (_user20 = _user20[(_user21 = user) === null || _user21 === void 0 ? void 0 : _user21.strategy]) === null || _user20 === void 0 ? void 0 : _user20.email)) { throw new Error('Not an admin'); } setPosts(posts.filter(function (post) { return post.id !== postId; }).concat([_objectSpread(_objectSpread({}, deleted), {}, { deleted: true })])); return deleted; }; var _approvePost = function approvePost(postId) { var _user22, _user23; var post = posts.find(function (p) { return p.id === postId; }); if (!post) { throw new Error('Post not found'); } if (!users.includes((_user22 = user) === null || _user22 === void 0 || (_user22 = _user22.strategies) === null || _user22 === void 0 || (_user22 = _user22[(_user23 = user) === null || _user23 === void 0 ? void 0 : _user23.strategy]) === null || _user22 === void 0 ? void 0 : _user22.email)) { throw new Error('Not an admin'); } var newPosts = [_objectSpread(_objectSpread({}, post), {}, { approved: true })].concat(posts.filter(function (p) { return p.id !== postId; })); setPosts(newPosts); }; var _setPostSticky = function setPostSticky(postId) { var _user24, _user25; var post = posts.find(function (p) { return p.id === postId; }); if (!post) { throw new Error('Post not found'); } if (!users.includes((_user24 = user) === null || _user24 === void 0 || (_user24 = _user24.strategies) === null || _user24 === void 0 || (_user24 = _user24[(_user25 = user) === null || _user25 === void 0 ? void 0 : _user25.strategy]) === null || _user24 === void 0 ? void 0 : _user24.email)) { throw new Error('Not an admin'); } var newPosts = [_objectSpread(_objectSpread({}, post), {}, { sticky: true })].concat(posts.filter(function (p) { return p.id !== postId; })); setPosts(newPosts); }; var isAdmin = users.includes((_user26 = user) === null || _user26 === void 0 || (_user26 = _user26.strategies) === null || _user26 === void 0 || (_user26 = _user26[(_user27 = user) === null || _user27 === void 0 ? void 0 : _user27.strategy]) === null || _user26 === void 0 ? void 0 : _user26.email); /** * When filtering posts for the client, you need to make sure they are still * visible to the server. i.e. the server should be treated as admin. * If you don't do this and a user tries to directly access a post the server won't find it * as it's been filtered out. */ var filtered = posts.filter(function (post) { return (0, _reactServer.isServerContext)(context) || isAdmin || !post.deleted; }).filter(function (post) { if (policies !== null && policies !== void 0 && policies.includes(ForumPolicies.PostsNeedApproval)) { var _post$owner11, _user28; return (0, _reactServer.isServerContext)(context) || post.approved || (post === null || post === void 0 || (_post$owner11 = post.owner) === null || _post$owner11 === void 0 ? void 0 : _post$owner11.id) === (((_user28 = user) === null || _user28 === void 0 ? void 0 : _user28.id) || clientId) || isAdmin; } return true; }); var _ref10 = clientProps || {}, _ref10$page = _ref10.page, page = _ref10$page === void 0 ? 1 : _ref10$page, _ref10$pageSize = _ref10.pageSize, pageSize = _ref10$pageSize === void 0 ? 25 : _ref10$pageSize, _ref10$compound = _ref10.compound, compound = _ref10$compound === void 0 ? false : _ref10$compound; var start = !compound ? (page - 1) * pageSize : 0; var end = page * pageSize; return (0, _jsxRuntime.jsx)(_ServerSideProps.ServerSideProps, { id: id, name: name, createPost: createPost, deletePost: _deletePost, totalCount: filtered.length, page: page, children: filtered.slice(start, end).map(function (post) { return (0, _jsxRuntime.jsx)(Post, _objectSpread(_objectSpread({}, post), {}, { deletePost: function deletePost() { return _deletePost(post.id); }, approvePost: function approvePost() { return _approvePost(post.id); }, setPostSticky: function setPostSticky() { return _setPostSticky(post.id); }, users: users }), "post-".concat(post.id)); }) }, (0, _reactServer.clientKey)("forum-".concat(id, "-props"), context)); }; var Platform = exports.Platform = function Platform() { var _useState11 = (0, _reactServer.useState)([], { key: 'forums', scope: 'platform' }), _useState12 = (0, _slicedToArray2["default"])(_useState11, 2), forums = _useState12[0], setForums = _useState12[1]; var createForum = function createForum(_ref11) { var name = _ref11.name; var forum = { id: (0, _uuid.v4)(), name: name }; setForums([].concat((0, _toConsumableArray2["default"])(forums), [forum])); }; return (0, _jsxRuntime.jsx)(_ServerSideProps.ServerSideProps, { createForum: createForum, children: forums.map(function (forum) { return (0, _jsxRuntime.jsx)(Forum, _objectSpread({}, forum)); }) }); };