From 0319bcf7bb090328e67766234723f5986c00bba2 Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Thu, 27 Apr 2017 20:49:24 +0300 Subject: REST API - Meteor 1.4 - first step issue --- .eslintrc.json | 8 ++- .meteor/versions | 24 ++++---- client/components/boards/boardHeader.js | 22 +++---- models/boards.js | 101 ++++++++++++++++++++++++------- models/cards.js | 98 ++++++++++++++++++++++++------ models/export.js | 80 ++++++++++++------------ models/lists.js | 58 +++++++++++++++++- models/users.js | 104 +++++++++++++++++++++++--------- package.json | 3 + 9 files changed, 361 insertions(+), 137 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 2aab7b6f..3fbab2d1 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -52,8 +52,8 @@ "prefer-const": 2, "prefer-spread": 2, "prefer-template": 2, - "no-console":"off", - "no-unused-vars":"warn" + "no-console": 0, + "no-unused-vars" : "warn" }, "globals": { "Meteor": false, @@ -125,6 +125,10 @@ "Checklists": true, "Settings": true, "InvitationCodes": true, +<<<<<<< HEAD "Winston":true +======= + "JsonRoutes" : true +>>>>>>> 3a5150f6eef86816471f7b0134d3d93cf6686413 } } diff --git a/.meteor/versions b/.meteor/versions index 225b3caf..b9259f21 100644 --- a/.meteor/versions +++ b/.meteor/versions @@ -1,6 +1,6 @@ 3stack:presence@1.0.5 -accounts-base@1.2.15 -accounts-password@1.3.4 +accounts-base@1.2.16 +accounts-password@1.3.5 aldeed:collection2@2.10.0 aldeed:collection2-core@1.2.0 aldeed:schema-deny@1.1.0 @@ -11,7 +11,7 @@ allow-deny@1.0.5 arillo:flow-router-helpers@0.5.2 audit-argument-checks@1.0.7 autoupdate@1.3.12 -babel-compiler@6.14.1 +babel-compiler@6.18.1 babel-runtime@1.0.1 base64@1.0.10 binary-heap@1.0.10 @@ -44,16 +44,16 @@ coffeescript@1.12.3_1 cottz:publish-relations@2.0.7 dburles:collection-helpers@1.1.0 ddp@1.2.5 -ddp-client@1.3.3 +ddp-client@1.3.4 ddp-common@1.2.8 ddp-rate-limiter@1.0.7 -ddp-server@1.3.13 +ddp-server@1.3.14 deps@1.0.12 diff-sequence@1.0.7 -ecmascript@0.6.3 +ecmascript@0.7.2 ecmascript-runtime@0.3.15 ejson@1.0.13 -email@1.1.18 +email@1.2.0 es5-shim@4.6.15 fastclick@1.0.13 fortawesome:fontawesome@4.7.0 @@ -92,8 +92,8 @@ minifier-js@1.2.18 minifiers@1.1.8-faster-rebuild.0 minimongo@1.0.21 mobile-status-bar@1.0.14 -modules@0.7.9 -modules-runtime@0.7.9 +modules@0.8.1 +modules-runtime@0.7.10 mongo@1.1.16 mongo-id@1.0.6 mongo-livedata@1.0.12 @@ -123,7 +123,7 @@ raix:eventemitter@0.1.3 raix:handlebar-helpers@0.2.5 rajit:bootstrap3-datepicker@1.6.4 random@1.0.10 -rate-limit@1.0.7 +rate-limit@1.0.8 reactive-dict@1.1.8 reactive-var@1.0.11 reload@1.1.11 @@ -134,7 +134,7 @@ service-configuration@1.0.11 session@1.1.7 sha@1.0.9 shell-server@0.2.3 -simple:json-routes@1.0.4 +simple:json-routes@2.1.0 softwarerero:accounts-t9n@1.3.9 spacebars@1.0.15 spacebars-compiler@1.1.2 @@ -156,6 +156,6 @@ useraccounts:core@1.14.2 useraccounts:flow-routing@1.14.2 useraccounts:unstyled@1.14.2 verron:autosize@3.0.8 -webapp@1.3.14 +webapp@1.3.15 webapp-hashing@1.0.9 zimme:active-route@2.3.2 diff --git a/client/components/boards/boardHeader.js b/client/components/boards/boardHeader.js index 44532c3e..10d9925a 100644 --- a/client/components/boards/boardHeader.js +++ b/client/components/boards/boardHeader.js @@ -15,17 +15,17 @@ Template.boardMenuPopup.events({ }), }); -Template.boardMenuPopup.helpers({ - exportUrl() { - const boardId = Session.get('currentBoard'); - const loginToken = Accounts._storedLoginToken(); - return FlowRouter.url(`api/boards/${boardId}?authToken=${loginToken}`); - }, - exportFilename() { - const boardId = Session.get('currentBoard'); - return `wekan-export-board-${boardId}.json`; - }, -}); +// Template.boardMenuPopup.helpers({ +// exportUrl() { +// const boardId = Session.get('currentBoard'); +// const loginToken = Accounts._storedLoginToken(); +// return FlowRouter.url(`api/boards/${boardId}?authToken=${loginToken}`); +// }, +// exportFilename() { +// const boardId = Session.get('currentBoard'); +// return `wekan-export-board-${boardId}.json`; +// }, +// }); Template.boardChangeTitlePopup.events({ submit(evt, tpl) { diff --git a/models/boards.js b/models/boards.js index f4296a6c..9cbb5b63 100644 --- a/models/boards.js +++ b/models/boards.js @@ -156,7 +156,7 @@ Boards.helpers({ * Is supplied user authorized to view this board? */ isVisibleBy(user) { - if(this.isPublic()) { + if (this.isPublic()) { // public boards are visible to everyone return true; } else { @@ -172,7 +172,7 @@ Boards.helpers({ * @returns {boolean} the member that matches, or undefined/false */ isActiveMember(userId) { - if(userId) { + if (userId) { return this.members.find((member) => (member.userId === userId && member.isActive)); } else { return false; @@ -184,23 +184,23 @@ Boards.helpers({ }, lists() { - return Lists.find({ boardId: this._id, archived: false }, { sort: { sort: 1 }}); + return Lists.find({ boardId: this._id, archived: false }, { sort: { sort: 1 } }); }, activities() { - return Activities.find({ boardId: this._id }, { sort: { createdAt: -1 }}); + return Activities.find({ boardId: this._id }, { sort: { createdAt: -1 } }); }, activeMembers() { - return _.where(this.members, {isActive: true}); + return _.where(this.members, { isActive: true }); }, activeAdmins() { - return _.where(this.members, {isActive: true, isAdmin: true}); + return _.where(this.members, { isActive: true, isAdmin: true }); }, memberUsers() { - return Users.find({ _id: {$in: _.pluck(this.members, 'userId')} }); + return Users.find({ _id: { $in: _.pluck(this.members, 'userId') } }); }, getLabel(name, color) { @@ -216,15 +216,15 @@ Boards.helpers({ }, hasMember(memberId) { - return !!_.findWhere(this.members, {userId: memberId, isActive: true}); + return !!_.findWhere(this.members, { userId: memberId, isActive: true }); }, hasAdmin(memberId) { - return !!_.findWhere(this.members, {userId: memberId, isActive: true, isAdmin: true}); + return !!_.findWhere(this.members, { userId: memberId, isActive: true, isAdmin: true }); }, hasCommentOnly(memberId) { - return !!_.findWhere(this.members, {userId: memberId, isActive: true, isAdmin: false, isCommentOnly: true}); + return !!_.findWhere(this.members, { userId: memberId, isActive: true, isAdmin: false, isCommentOnly: true }); }, absoluteUrl() { @@ -239,34 +239,34 @@ Boards.helpers({ // XXX waiting on https://github.com/mquandalle/meteor-collection-mutations/issues/1 to remove... pushLabel(name, color) { const _id = Random.id(6); - Boards.direct.update(this._id, { $push: {labels: { _id, name, color }}}); + Boards.direct.update(this._id, { $push: { labels: { _id, name, color } } }); return _id; }, }); Boards.mutations({ archive() { - return { $set: { archived: true }}; + return { $set: { archived: true } }; }, restore() { - return { $set: { archived: false }}; + return { $set: { archived: false } }; }, rename(title) { - return { $set: { title }}; + return { $set: { title } }; }, setDescription(description) { - return { $set: {description} }; + return { $set: { description } }; }, setColor(color) { - return { $set: { color }}; + return { $set: { color } }; }, setVisibility(visibility) { - return { $set: { permission: visibility }}; + return { $set: { permission: visibility } }; }, addLabel(name, color) { @@ -276,7 +276,7 @@ Boards.mutations({ // user). if (!this.getLabel(name, color)) { const _id = Random.id(6); - return { $push: {labels: { _id, name, color }}}; + return { $push: { labels: { _id, name, color } } }; } return {}; }, @@ -295,7 +295,7 @@ Boards.mutations({ }, removeLabel(labelId) { - return { $pull: { labels: { _id: labelId }}}; + return { $pull: { labels: { _id: labelId } } }; }, addMember(memberId) { @@ -386,7 +386,7 @@ if (Meteor.isServer) { return false; // If there is more than one admin, it's ok to remove anyone - const nbAdmins = _.where(doc.members, {isActive: true, isAdmin: true}).length; + const nbAdmins = _.where(doc.members, { isActive: true, isAdmin: true }).length; if (nbAdmins > 1) return false; @@ -408,7 +408,7 @@ if (Meteor.isServer) { if (board) { const userId = Meteor.userId(); const index = board.memberIndex(userId); - if (index>=0) { + if (index >= 0) { board.removeMember(userId); return true; } else throw new Meteor.Error('error-board-notAMember'); @@ -424,7 +424,7 @@ if (Meteor.isServer) { _id: 1, 'members.userId': 1, }, { unique: true }); - Boards._collection._ensureIndex({'members.userId': 1}); + Boards._collection._ensureIndex({ 'members.userId': 1 }); }); // Genesis: the first activity of the newly created board @@ -553,3 +553,60 @@ if (Meteor.isServer) { } }); } + +//BOARDS REST API +if (Meteor.isServer) { + JsonRoutes.add('GET', '/api/boards', function (req, res, next) { + JsonRoutes.sendResult(res, { + code: 200, + data: Boards.find({ permission: 'public' }).map(function (doc) { + return { + _id: doc._id, + title: doc.title, + }; + }), + }); + }); + + JsonRoutes.add('GET', '/api/boards/:id', function (req, res, next) { + const id = req.params.id; + JsonRoutes.sendResult(res, { + code: 200, + data: Boards.findOne({ _id: id }), + }); + }); + + JsonRoutes.add('POST', '/api/boards', function (req, res, next) { + const id = Boards.insert({ + title: req.body.title, + members: [ + { + userId: req.body.owner, + isAdmin: true, + isActive: true, + isCommentOnly: false, + }, + ], + permission: 'public', + color: 'belize', + }); + JsonRoutes.sendResult(res, { + code: 200, + data: { + _id: id, + }, + }); + }); + + JsonRoutes.add('DELETE', '/api/boards/:id', function (req, res, next) { + const id = req.params.id; + Boards.remove({ _id: id }); + JsonRoutes.sendResult(res, { + code: 200, + data:{ + _id: id, + }, + }); + }); + +} diff --git a/models/cards.js b/models/cards.js index 922fbecd..2d585825 100644 --- a/models/cards.js +++ b/models/cards.js @@ -123,15 +123,15 @@ Cards.helpers({ }, activities() { - return Activities.find({ cardId: this._id }, { sort: { createdAt: -1 }}); + return Activities.find({ cardId: this._id }, { sort: { createdAt: -1 } }); }, comments() { - return CardComments.find({ cardId: this._id }, { sort: { createdAt: -1 }}); + return CardComments.find({ cardId: this._id }, { sort: { createdAt: -1 } }); }, attachments() { - return Attachments.find({ cardId: this._id }, { sort: { uploadedAt: -1 }}); + return Attachments.find({ cardId: this._id }, { sort: { uploadedAt: -1 } }); }, cover() { @@ -142,7 +142,7 @@ Cards.helpers({ }, checklists() { - return Checklists.find({ cardId: this._id }, { sort: { createdAt: 1 }}); + return Checklists.find({ cardId: this._id }, { sort: { createdAt: 1 } }); }, checklistItemCount() { @@ -183,19 +183,19 @@ Cards.helpers({ Cards.mutations({ archive() { - return { $set: { archived: true }}; + return { $set: { archived: true } }; }, restore() { - return { $set: { archived: false }}; + return { $set: { archived: false } }; }, setTitle(title) { - return { $set: { title }}; + return { $set: { title } }; }, setDescription(description) { - return { $set: { description }}; + return { $set: { description } }; }, move(listId, sortIndex) { @@ -207,11 +207,11 @@ Cards.mutations({ }, addLabel(labelId) { - return { $addToSet: { labelIds: labelId }}; + return { $addToSet: { labelIds: labelId } }; }, removeLabel(labelId) { - return { $pull: { labelIds: labelId }}; + return { $pull: { labelIds: labelId } }; }, toggleLabel(labelId) { @@ -223,11 +223,11 @@ Cards.mutations({ }, assignMember(memberId) { - return { $addToSet: { members: memberId }}; + return { $addToSet: { members: memberId } }; }, unassignMember(memberId) { - return { $pull: { members: memberId }}; + return { $pull: { members: memberId } }; }, toggleMember(memberId) { @@ -239,27 +239,27 @@ Cards.mutations({ }, setCover(coverId) { - return { $set: { coverId }}; + return { $set: { coverId } }; }, unsetCover() { - return { $unset: { coverId: '' }}; + return { $unset: { coverId: '' } }; }, setStart(startAt) { - return { $set: { startAt }}; + return { $set: { startAt } }; }, unsetStart() { - return { $unset: { startAt: '' }}; + return { $unset: { startAt: '' } }; }, setDue(dueAt) { - return { $set: { dueAt }}; + return { $set: { dueAt } }; }, unsetDue() { - return { $unset: { dueAt: '' }}; + return { $unset: { dueAt: '' } }; }, }); @@ -304,7 +304,7 @@ if (Meteor.isServer) { }); // New activity for card moves - Cards.after.update(function(userId, doc, fieldNames) { + Cards.after.update(function (userId, doc, fieldNames) { const oldListId = this.previous.listId; if (_.contains(fieldNames, 'listId') && doc.listId !== oldListId) { Activities.insert({ @@ -370,3 +370,63 @@ if (Meteor.isServer) { }); }); } +//LISTS REST API +if (Meteor.isServer) { + JsonRoutes.add('GET', '/api/boards/:boardId/lists/:listId/cards', function (req, res, next) { + const paramBoardId = req.params.boardId; + const paramListId = req.params.listId; + JsonRoutes.sendResult(res, { + code: 200, + data: Cards.find({ boardId: paramBoardId, listId: paramListId, archived: false }).map(function (doc) { + return { + _id: doc._id, + title: doc.title, + description: doc.description, + }; + }), + }); + }); + + JsonRoutes.add('GET', '/api/boards/:boardId/lists/:listId/cards/:cardId', function (req, res, next) { + const paramBoardId = req.params.boardId; + const paramListId = req.params.listId; + const paramCardId = req.params.cardId; + JsonRoutes.sendResult(res, { + code: 200, + data: Cards.findOne({ _id: paramCardId, listId: paramListId, boardId: paramBoardId, archived: false }), + }); + }); + + JsonRoutes.add('POST', '/api/boards/:boardId/lists/:listId/cards', function (req, res, next) { + const paramBoardId = req.params.boardId; + const paramListId = req.params.listId; + const id = Cards.insert({ + title: req.body.title, + boardId: paramBoardId, + listId: paramListId, + description: req.body.description, + userId : req.body.authorId, + sort: 0, + members:[ req.body.authorId ], + }); + JsonRoutes.sendResult(res, { + code: 200, + data: { + _id: id, + }, + }); + }); + + JsonRoutes.add('DELETE', '/api/boards/:boardId/lists/:listId/cards/:cardId', function (req, res, next) { + const paramBoardId = req.params.boardId; + const paramListId = req.params.listId; + const paramCardId = req.params.cardId; + Cards.remove({ _id: paramCardId, listId: paramListId, boardId: paramBoardId }); + JsonRoutes.sendResult(res, { + code: 200, + data: { + _id: paramCardId, + }, + }); + }); +} diff --git a/models/export.js b/models/export.js index b774cf15..7a363dd3 100644 --- a/models/export.js +++ b/models/export.js @@ -1,5 +1,5 @@ /* global JsonRoutes */ -if(Meteor.isServer) { +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 @@ -14,28 +14,28 @@ if(Meteor.isServer) { * See https://blog.kayla.com.au/server-side-route-authentication-in-meteor/ * for detailed explanations */ - JsonRoutes.add('get', '/api/boards/:boardId', function (req, res) { - const boardId = req.params.boardId; - let user = null; - // todo XXX for real API, first look for token in Authentication: header - // then fallback to parameter - const loginToken = req.query.authToken; - if (loginToken) { - const hashToken = Accounts._hashLoginToken(loginToken); - user = Meteor.users.findOne({ - 'services.resume.loginTokens.hashedToken': hashToken, - }); - } + // JsonRoutes.add('get', '/api/boards/:boardId', function (req, res) { + // const boardId = req.params.boardId; + // let user = null; + // // todo XXX for real API, first look for token in Authentication: header + // // then fallback to parameter + // const loginToken = req.query.authToken; + // if (loginToken) { + // const hashToken = Accounts._hashLoginToken(loginToken); + // user = Meteor.users.findOne({ + // 'services.resume.loginTokens.hashedToken': hashToken, + // }); + // } - const exporter = new Exporter(boardId); - if(exporter.canExport(user)) { - JsonRoutes.sendResult(res, 200, 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); - } - }); + // const exporter = new Exporter(boardId); + // if(exporter.canExport(user)) { + // JsonRoutes.sendResult(res, 200, 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); + // } + // }); } class Exporter { @@ -44,13 +44,13 @@ class Exporter { } build() { - const byBoard = {boardId: this._boardId}; + const byBoard = { boardId: this._boardId }; // we do not want to retrieve boardId in related elements - const noBoardId = {fields: {boardId: 0}}; + const noBoardId = { fields: { boardId: 0 } }; const result = { _format: 'wekan-board-1.0.0', }; - _.extend(result, Boards.findOne(this._boardId, {fields: {stars: 0}})); + _.extend(result, Boards.findOne(this._boardId, { fields: { stars: 0 } })); result.lists = Lists.find(byBoard, noBoardId).fetch(); result.cards = Cards.find(byBoard, noBoardId).fetch(); result.comments = CardComments.find(byBoard, noBoardId).fetch(); @@ -69,29 +69,31 @@ class Exporter { // 1- only exports users that are linked somehow to that board // 2- do not export any sensitive information const users = {}; - result.members.forEach((member) => {users[member.userId] = true;}); - result.lists.forEach((list) => {users[list.userId] = true;}); + result.members.forEach((member) => { users[member.userId] = true; }); + result.lists.forEach((list) => { users[list.userId] = true; }); result.cards.forEach((card) => { users[card.userId] = true; if (card.members) { - card.members.forEach((memberId) => {users[memberId] = true;}); + card.members.forEach((memberId) => { users[memberId] = true; }); } }); - result.comments.forEach((comment) => {users[comment.userId] = true;}); - result.activities.forEach((activity) => {users[activity.userId] = true;}); - const byUserIds = {_id: {$in: Object.getOwnPropertyNames(users)}}; + result.comments.forEach((comment) => { users[comment.userId] = true; }); + result.activities.forEach((activity) => { users[activity.userId] = true; }); + const byUserIds = { _id: { $in: Object.getOwnPropertyNames(users) } }; // we use whitelist to be sure we do not expose inadvertently // some secret fields that gets added to User later. - const userFields = {fields: { - _id: 1, - username: 1, - 'profile.fullname': 1, - 'profile.initials': 1, - 'profile.avatarUrl': 1, - }}; + const userFields = { + fields: { + _id: 1, + username: 1, + 'profile.fullname': 1, + 'profile.initials': 1, + 'profile.avatarUrl': 1, + }, + }; result.users = Users.find(byUserIds, userFields).fetch().map((user) => { // user avatar is stored as a relative url, we export absolute - if(user.profile.avatarUrl) { + if (user.profile.avatarUrl) { user.profile.avatarUrl = FlowRouter.url(user.profile.avatarUrl); } return user; diff --git a/models/lists.js b/models/lists.js index 0ae3ca5f..a10e23b6 100644 --- a/models/lists.js +++ b/models/lists.js @@ -76,15 +76,15 @@ Lists.helpers({ Lists.mutations({ rename(title) { - return { $set: { title }}; + return { $set: { title } }; }, archive() { - return { $set: { archived: true }}; + return { $set: { archived: true } }; }, restore() { - return { $set: { archived: false }}; + return { $set: { archived: false } }; }, }); @@ -128,3 +128,55 @@ if (Meteor.isServer) { } }); } + +//LISTS REST API +if (Meteor.isServer) { + JsonRoutes.add('GET', '/api/boards/:boardId/lists', function (req, res, next) { + const paramBoardId = req.params.boardId; + JsonRoutes.sendResult(res, { + code: 200, + data: Lists.find({ boardId: paramBoardId, archived: false }).map(function (doc) { + return { + _id: doc._id, + title: doc.title, + }; + }), + }); + }); + + JsonRoutes.add('GET', '/api/boards/:boardId/lists/:listId', function (req, res, next) { + const paramBoardId = req.params.boardId; + const paramListId = req.params.listId; + JsonRoutes.sendResult(res, { + code: 200, + data: Lists.findOne({ _id: paramListId, boardId: paramBoardId, archived: false }), + }); + }); + + JsonRoutes.add('POST', '/api/boards/:boardId/lists', function (req, res, next) { + const paramBoardId = req.params.boardId; + const id = Lists.insert({ + title: req.body.title, + boardId: paramBoardId, + }); + JsonRoutes.sendResult(res, { + code: 200, + data: { + _id: id, + }, + }); + }); + + JsonRoutes.add('DELETE', '/api/boards/:boardId/lists/:listId', function (req, res, next) { + const paramBoardId = req.params.boardId; + const paramListId = req.params.listId; + Lists.remove({ _id: paramListId, boardId: paramBoardId }); + JsonRoutes.sendResult(res, { + code: 200, + data: { + _id: paramListId, + }, + }); + }); + +} diff --git a/models/users.js b/models/users.js index 8e2f3bf9..c1ce146a 100644 --- a/models/users.js +++ b/models/users.js @@ -1,7 +1,7 @@ // Sandstorm context is detected using the METEOR_SETTINGS environment variable // in the package definition. const isSandstorm = Meteor.settings && Meteor.settings.public && - Meteor.settings.public.sandstorm; + Meteor.settings.public.sandstorm; Users = Meteor.users; Users.attachSchema(new SimpleSchema({ @@ -148,32 +148,32 @@ Users.helpers({ }, starredBoards() { - const {starredBoards = []} = this.profile; - return Boards.find({archived: false, _id: {$in: starredBoards}}); + const { starredBoards = [] } = this.profile; + return Boards.find({ archived: false, _id: { $in: starredBoards } }); }, hasStarred(boardId) { - const {starredBoards = []} = this.profile; + const { starredBoards = [] } = this.profile; return _.contains(starredBoards, boardId); }, invitedBoards() { - const {invitedBoards = []} = this.profile; - return Boards.find({archived: false, _id: {$in: invitedBoards}}); + const { invitedBoards = [] } = this.profile; + return Boards.find({ archived: false, _id: { $in: invitedBoards } }); }, isInvitedTo(boardId) { - const {invitedBoards = []} = this.profile; + const { invitedBoards = [] } = this.profile; return _.contains(invitedBoards, boardId); }, hasTag(tag) { - const {tags = []} = this.profile; + const { tags = [] } = this.profile; return _.contains(tags, tag); }, hasNotification(activityId) { - const {notifications = []} = this.profile; + const { notifications = [] } = this.profile; return _.contains(notifications, activityId); }, @@ -183,7 +183,7 @@ Users.helpers({ }, getEmailBuffer() { - const {emailBuffer = []} = this.profile; + const { emailBuffer = [] } = this.profile; return emailBuffer; }, @@ -308,7 +308,7 @@ Users.mutations({ }, setAvatarUrl(avatarUrl) { - return { $set: { 'profile.avatarUrl': avatarUrl }}; + return { $set: { 'profile.avatarUrl': avatarUrl } }; }, setShowCardsCountAt(limit) { @@ -323,7 +323,7 @@ Meteor.methods({ if (nUsersWithUsername > 0) { throw new Meteor.Error('username-already-taken'); } else { - Users.update(this.userId, {$set: { username }}); + Users.update(this.userId, { $set: { username } }); } }, toggleSystemMessages() { @@ -346,19 +346,19 @@ if (Meteor.isServer) { const inviter = Meteor.user(); const board = Boards.findOne(boardId); const allowInvite = inviter && - board && - board.members && - _.contains(_.pluck(board.members, 'userId'), inviter._id) && - _.where(board.members, {userId: inviter._id})[0].isActive && - _.where(board.members, {userId: inviter._id})[0].isAdmin; + board && + board.members && + _.contains(_.pluck(board.members, 'userId'), inviter._id) && + _.where(board.members, { userId: inviter._id })[0].isActive && + _.where(board.members, { userId: inviter._id })[0].isAdmin; if (!allowInvite) throw new Meteor.Error('error-board-notAMember'); this.unblock(); const posAt = username.indexOf('@'); let user = null; - if (posAt>=0) { - user = Users.findOne({emails: {$elemMatch: {address: username}}}); + if (posAt >= 0) { + user = Users.findOne({ emails: { $elemMatch: { address: username } } }); } else { user = Users.findOne(username) || Users.findOne({ username }); } @@ -409,7 +409,7 @@ if (Meteor.isServer) { }); Accounts.onCreateUser((options, user) => { const userCount = Users.find().count(); - if (!isSandstorm && userCount === 0 ){ + if (!isSandstorm && userCount === 0) { user.isAdmin = true; return user; } @@ -421,11 +421,11 @@ if (Meteor.isServer) { if (!options || !options.profile) { throw new Meteor.Error('error-invitation-code-blank', 'The invitation code is required'); } - const invitationCode = InvitationCodes.findOne({code: options.profile.invitationcode, email: options.email, valid: true}); + const invitationCode = InvitationCodes.findOne({ code: options.profile.invitationcode, email: options.email, valid: true }); if (!invitationCode) { throw new Meteor.Error('error-invitation-code-not-exist', 'The invitation code doesn\'t exist'); - }else{ - user.profile = {icode: options.profile.invitationcode}; + } else { + user.profile = { icode: options.profile.invitationcode }; } return user; @@ -445,7 +445,7 @@ if (Meteor.isServer) { // counter. // We need to run this code on the server only, otherwise the incrementation // will be done twice. - Users.after.update(function(userId, user, fieldNames) { + Users.after.update(function (userId, user, fieldNames) { // The `starredBoards` list is hosted on the `profile` field. If this // field hasn't been modificated we don't need to run this hook. if (!_.contains(fieldNames, 'profile')) @@ -464,7 +464,7 @@ if (Meteor.isServer) { // direction and then in the other. function incrementBoards(boardsIds, inc) { boardsIds.forEach((boardId) => { - Boards.update(boardId, {$inc: {stars: inc}}); + Boards.update(boardId, { $inc: { stars: inc } }); }); } incrementBoards(_.difference(oldIds, newIds), -1); @@ -505,10 +505,10 @@ if (Meteor.isServer) { //invite user to corresponding boards const disableRegistration = Settings.findOne().disableRegistration; if (disableRegistration) { - const invitationCode = InvitationCodes.findOne({code: doc.profile.icode, valid:true}); + const invitationCode = InvitationCodes.findOne({ code: doc.profile.icode, valid: true }); if (!invitationCode) { throw new Meteor.Error('error-invitation-code-not-exist'); - }else{ + } else { invitationCode.boardsToBeInvited.forEach((boardId) => { const board = Boards.findOne(boardId); board.addMember(doc._id); @@ -517,9 +517,55 @@ if (Meteor.isServer) { doc.profile = {}; } doc.profile.invitedBoards = invitationCode.boardsToBeInvited; - Users.update(doc._id, {$set:{profile: doc.profile}}); - InvitationCodes.update(invitationCode._id, {$set: {valid:false}}); + Users.update(doc._id, { $set: { profile: doc.profile } }); + InvitationCodes.update(invitationCode._id, { $set: { valid: false } }); } } }); } + + +// USERS REST API +if (Meteor.isServer) { + JsonRoutes.add('GET', '/api/users', function (req, res, next) { + JsonRoutes.sendResult(res, { + code: 200, + data: Meteor.users.find({}).map(function (doc) { + return { _id: doc._id, username: doc.username }; + }), + }); + }); + JsonRoutes.add('GET', '/api/users/:id', function (req, res, next) { + const id = req.params.id; + JsonRoutes.sendResult(res, { + code: 200, + data: Meteor.users.findOne({ _id: id }), + }); + }); + JsonRoutes.add('POST', '/api/users/', function (req, res, next) { + const id = Accounts.createUser({ + username: req.body.username, + email: req.body.email, + password: 'default', + }); + + JsonRoutes.sendResult(res, { + code: 200, + data: { + _id: id, + }, + }); + }); + + JsonRoutes.add('DELETE', '/api/users/:id', function (req, res, next) { + const id = req.params.id; + Meteor.users.remove({ _id: id }); + JsonRoutes.sendResult(res, { + code: 200, + data: { + _id: id, + }, + }); + }); +} + diff --git a/package.json b/package.json index 6ceb6e29..d591a213 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,9 @@ "lint": "eslint --ignore-pattern 'packages/*' .", "test": "npm run --silent lint" }, + "eslintConfig": { + "extends": "@meteorjs/eslint-config-meteor" + }, "repository": { "type": "git", "url": "git+https://github.com/wekan/wekan.git" -- cgit v1.2.3-1-g7c22