REST API - Meteor 1.4 - first step issue

This commit is contained in:
Lauri Ojansivu 2017-04-27 20:49:24 +03:00
parent a99218e2c7
commit 0319bcf7bb
9 changed files with 361 additions and 137 deletions

View file

@ -52,7 +52,7 @@
"prefer-const": 2,
"prefer-spread": 2,
"prefer-template": 2,
"no-console":"off",
"no-console": 0,
"no-unused-vars" : "warn"
},
"globals": {
@ -125,6 +125,10 @@
"Checklists": true,
"Settings": true,
"InvitationCodes": true,
<<<<<<< HEAD
"Winston":true
=======
"JsonRoutes" : true
>>>>>>> 3a5150f6eef86816471f7b0134d3d93cf6686413
}
}

View file

@ -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

View file

@ -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) {

View file

@ -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,
},
});
});
}

View file

@ -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,
},
});
});
}

View file

@ -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 {
@ -82,13 +82,15 @@ class Exporter {
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: {
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) {

View file

@ -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,
},
});
});
}

View file

@ -523,3 +523,49 @@ if (Meteor.isServer) {
}
});
}
// 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,
},
});
});
}

View file

@ -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"