1
0
Fork 0
mirror of https://github.com/plankanban/planka.git synced 2025-07-18 12:49:43 +02:00

feat: Webhooks configuration, all events support, refactoring

This commit is contained in:
Maksim Eltyshev 2024-06-12 00:51:36 +02:00
parent 193daf6cfb
commit 87683fe523
96 changed files with 1280 additions and 509 deletions

View file

@ -45,6 +45,15 @@ services:
# - SMTP_PASSWORD=
# - SMTP_FROM="Demo Demo" <demo@demo.demo>
# Optional fields: accessToken, events, excludedEvents
# - |
# WEBHOOKS=[{
# "url": "http://localhost:3001",
# "accessToken": "notaccesstoken",
# "events": ["cardCreate", "cardUpdate", "cardDelete"],
# "excludedEvents": ["notificationCreate", "notificationUpdate"]
# }]
# - SLACK_BOT_TOKEN=
# - SLACK_CHANNEL_ID=

View file

@ -53,6 +53,15 @@ services:
# - SMTP_PASSWORD=
# - SMTP_FROM="Demo Demo" <demo@demo.demo>
# Optional fields: accessToken, events, excludedEvents
# - |
# WEBHOOKS=[{
# "url": "http://localhost:3001",
# "accessToken": "notaccesstoken",
# "events": ["cardCreate", "cardUpdate", "cardDelete"],
# "excludedEvents": ["notificationCreate", "notificationUpdate"]
# }]
# - SLACK_BOT_TOKEN=
# - SLACK_CHANNEL_ID=
depends_on:

View file

@ -43,12 +43,17 @@ SECRET_KEY=notsecretkey
# SMTP_PASSWORD=
# SMTP_FROM="Demo Demo" <demo@demo.demo>
# Optional fields: accessToken, events, excludedEvents
# WEBHOOKS='[{
# "url": "http://localhost:3001",
# "accessToken": "notaccesstoken",
# "events": ["cardCreate", "cardUpdate", "cardDelete"],
# "excludedEvents": ["notificationCreate", "notificationUpdate"]
# }]'
# SLACK_BOT_TOKEN=
# SLACK_CHANNEL_ID=
# WEBHOOK_URL=
# WEBHOOK_BEARER=
## Do not edit this
TZ=UTC

View file

@ -44,12 +44,12 @@ module.exports = {
async fn(inputs, exits) {
const { currentUser } = this.req;
const { card, board } = await sails.helpers.cards
const { card, list, board, project } = await sails.helpers.cards
.getProjectPath(inputs.cardId)
.intercept('pathNotFound', () => Errors.CARD_NOT_FOUND);
const boardMembership = await BoardMembership.findOne({
boardId: card.boardId,
boardId: board.id,
userId: currentUser.id,
});
@ -83,12 +83,14 @@ module.exports = {
const fileData = await sails.helpers.attachments.processUploadedFile(file);
const attachment = await sails.helpers.attachments.createOne.with({
project,
board,
list,
values: {
...fileData,
card,
creatorUser: currentUser,
},
board,
requestId: inputs.requestId,
request: this.req,
});

View file

@ -33,7 +33,7 @@ module.exports = {
.intercept('pathNotFound', () => Errors.ATTACHMENT_NOT_FOUND);
let { attachment } = path;
const { card, board } = path;
const { card, list, board, project } = path;
const boardMembership = await BoardMembership.findOne({
boardId: board.id,
@ -49,9 +49,12 @@ module.exports = {
}
attachment = await sails.helpers.attachments.deleteOne.with({
project,
board,
list,
card,
record: attachment,
actorUser: currentUser,
request: this.req,
});

View file

@ -37,7 +37,7 @@ module.exports = {
.intercept('pathNotFound', () => Errors.ATTACHMENT_NOT_FOUND);
let { attachment } = path;
const { board } = path;
const { card, list, board, project } = path;
const boardMembership = await BoardMembership.findOne({
boardId: board.id,
@ -56,8 +56,12 @@ module.exports = {
attachment = await sails.helpers.attachments.updateOne.with({
values,
project,
board,
list,
card,
record: attachment,
actorUser: currentUser,
request: this.req,
});

View file

@ -48,14 +48,11 @@ module.exports = {
async fn(inputs) {
const { currentUser } = this.req;
const { board } = await sails.helpers.boards
const { board, project } = await sails.helpers.boards
.getProjectPath(inputs.boardId)
.intercept('pathNotFound', () => Errors.BOARD_NOT_FOUND);
const isProjectManager = await sails.helpers.users.isProjectManager(
currentUser.id,
board.projectId,
);
const isProjectManager = await sails.helpers.users.isProjectManager(currentUser.id, project.id);
if (!isProjectManager) {
throw Errors.BOARD_NOT_FOUND; // Forbidden
@ -71,11 +68,13 @@ module.exports = {
const boardMembership = await sails.helpers.boardMemberships.createOne
.with({
project,
values: {
...values,
board,
user,
},
actorUser: currentUser,
request: this.req,
})
.intercept('userAlreadyBoardMember', () => Errors.USER_ALREADY_BOARD_MEMBER);

View file

@ -27,7 +27,7 @@ module.exports = {
.intercept('pathNotFound', () => Errors.BOARD_MEMBERSHIP_NOT_FOUND);
let { boardMembership } = path;
const { project } = path;
const { board, project } = path;
if (boardMembership.userId !== currentUser.id) {
const isProjectManager = await sails.helpers.users.isProjectManager(
@ -42,7 +42,9 @@ module.exports = {
boardMembership = await sails.helpers.boardMemberships.deleteOne.with({
project,
board,
record: boardMembership,
actorUser: currentUser,
request: this.req,
});

View file

@ -35,7 +35,7 @@ module.exports = {
.intercept('pathNotFound', () => Errors.BOARD_MEMBERSHIP_NOT_FOUND);
let { boardMembership } = path;
const { project } = path;
const { board, project } = path;
const isProjectManager = await sails.helpers.users.isProjectManager(currentUser.id, project.id);
@ -47,7 +47,10 @@ module.exports = {
boardMembership = await sails.helpers.boardMemberships.updateOne.with({
values,
project,
board,
record: boardMembership,
actorUser: currentUser,
request: this.req,
});

View file

@ -103,7 +103,7 @@ module.exports = {
project,
},
import: boardImport,
user: currentUser,
actorUser: currentUser,
requestId: inputs.requestId,
request: this.req,
});

View file

@ -22,25 +22,27 @@ module.exports = {
async fn(inputs) {
const { currentUser } = this.req;
let { board } = await sails.helpers.boards
const path = await sails.helpers.boards
.getProjectPath(inputs.id)
.intercept('pathNotFound', () => Errors.BOARD_NOT_FOUND);
let { board } = path;
const { project } = path;
if (!board) {
throw Errors.BOARD_NOT_FOUND;
}
const isProjectManager = await sails.helpers.users.isProjectManager(
currentUser.id,
board.projectId,
);
const isProjectManager = await sails.helpers.users.isProjectManager(currentUser.id, project.id);
if (!isProjectManager) {
throw Errors.BOARD_NOT_FOUND; // Forbidden
}
board = await sails.helpers.boards.deleteOne.with({
project,
record: board,
actorUser: currentUser,
request: this.req,
});

View file

@ -29,18 +29,18 @@ module.exports = {
async fn(inputs) {
const { currentUser } = this.req;
let { board } = await sails.helpers.boards
const path = await sails.helpers.boards
.getProjectPath(inputs.id)
.intercept('pathNotFound', () => Errors.BOARD_NOT_FOUND);
let { board } = path;
const { project } = path;
if (!board) {
throw Errors.BOARD_NOT_FOUND;
}
const isProjectManager = await sails.helpers.users.isProjectManager(
currentUser.id,
board.projectId,
);
const isProjectManager = await sails.helpers.users.isProjectManager(currentUser.id, project.id);
if (!isProjectManager) {
throw Errors.BOARD_NOT_FOUND; // Forbidden
@ -50,7 +50,9 @@ module.exports = {
board = await sails.helpers.boards.updateOne.with({
values,
project,
record: board,
actorUser: currentUser,
request: this.req,
});

View file

@ -45,12 +45,12 @@ module.exports = {
async fn(inputs) {
const { currentUser } = this.req;
const { card } = await sails.helpers.cards
const { card, list, board, project } = await sails.helpers.cards
.getProjectPath(inputs.cardId)
.intercept('pathNotFound', () => Errors.CARD_NOT_FOUND);
const boardMembership = await BoardMembership.findOne({
boardId: card.boardId,
boardId: board.id,
userId: currentUser.id,
});
@ -64,7 +64,7 @@ module.exports = {
const label = await Label.findOne({
id: inputs.labelId,
boardId: card.boardId,
boardId: board.id,
});
if (!label) {
@ -73,10 +73,14 @@ module.exports = {
const cardLabel = await sails.helpers.cardLabels.createOne
.with({
project,
board,
list,
values: {
card,
label,
},
actorUser: currentUser,
request: this.req,
})
.intercept('labelAlreadyInCard', () => Errors.LABEL_ALREADY_IN_CARD);

View file

@ -39,7 +39,7 @@ module.exports = {
async fn(inputs) {
const { currentUser } = this.req;
const { board } = await sails.helpers.cards
const { card, list, board, project } = await sails.helpers.cards
.getProjectPath(inputs.cardId)
.intercept('pathNotFound', () => Errors.CARD_NOT_FOUND);
@ -66,8 +66,12 @@ module.exports = {
}
cardLabel = await sails.helpers.cardLabels.deleteOne.with({
project,
board,
list,
card,
record: cardLabel,
actorUser: currentUser,
request: this.req,
});

View file

@ -45,12 +45,12 @@ module.exports = {
async fn(inputs) {
const { currentUser } = this.req;
const { card, board } = await sails.helpers.cards
const { card, list, board, project } = await sails.helpers.cards
.getProjectPath(inputs.cardId)
.intercept('pathNotFound', () => Errors.CARD_NOT_FOUND);
const boardMembership = await BoardMembership.findOne({
boardId: card.boardId,
boardId: board.id,
userId: currentUser.id,
});
@ -62,7 +62,7 @@ module.exports = {
throw Errors.NOT_ENOUGH_RIGHTS;
}
const isBoardMember = await sails.helpers.users.isBoardMember(inputs.userId, card.boardId);
const isBoardMember = await sails.helpers.users.isBoardMember(inputs.userId, board.id);
if (!isBoardMember) {
throw Errors.USER_NOT_FOUND;
@ -70,11 +70,14 @@ module.exports = {
const cardMembership = await sails.helpers.cardMemberships.createOne
.with({
project,
board,
list,
values: {
card,
userId: inputs.userId,
},
board,
actorUser: currentUser,
request: this.req,
})
.intercept('userAlreadyCardMember', () => Errors.USER_ALREADY_CARD_MEMBER);

View file

@ -39,7 +39,7 @@ module.exports = {
async fn(inputs) {
const { currentUser } = this.req;
const { board, card } = await sails.helpers.cards
const { card, list, board, project } = await sails.helpers.cards
.getProjectPath(inputs.cardId)
.intercept('pathNotFound', () => Errors.CARD_NOT_FOUND);
@ -66,9 +66,12 @@ module.exports = {
}
cardMembership = await sails.helpers.cardMemberships.deleteOne.with({
project,
board,
list,
card,
record: cardMembership,
actorUser: currentUser,
request: this.req,
});

View file

@ -78,7 +78,7 @@ module.exports = {
async fn(inputs) {
const { currentUser } = this.req;
const { board, list } = await sails.helpers.lists
const { list, board, project } = await sails.helpers.lists
.getProjectPath(inputs.listId)
.intercept('pathNotFound', () => Errors.LIST_NOT_FOUND);
@ -99,6 +99,7 @@ module.exports = {
const card = await sails.helpers.cards.createOne
.with({
project,
board,
values: {
...values,

View file

@ -28,12 +28,15 @@ module.exports = {
async fn(inputs) {
const { currentUser } = this.req;
let { card } = await sails.helpers.cards
const path = await sails.helpers.cards
.getProjectPath(inputs.id)
.intercept('pathNotFound', () => Errors.CARD_NOT_FOUND);
let { card } = path;
const { list, board, project } = path;
const boardMembership = await BoardMembership.findOne({
boardId: card.boardId,
boardId: board.id,
userId: currentUser.id,
});
@ -46,8 +49,11 @@ module.exports = {
}
card = await sails.helpers.cards.deleteOne.with({
project,
board,
list,
record: card,
user: currentUser,
actorUser: currentUser,
request: this.req,
});

View file

@ -35,12 +35,12 @@ module.exports = {
async fn(inputs) {
const { currentUser } = this.req;
const { card, list, board } = await sails.helpers.cards
const { card, list, board, project } = await sails.helpers.cards
.getProjectPath(inputs.id)
.intercept('pathNotFound', () => Errors.CARD_NOT_FOUND);
const boardMembership = await BoardMembership.findOne({
boardId: card.boardId,
boardId: board.id,
userId: currentUser.id,
});
@ -60,6 +60,7 @@ module.exports = {
cardLabels,
tasks,
} = await sails.helpers.cards.duplicateOne.with({
project,
board,
list,
record: card,

View file

@ -118,7 +118,7 @@ module.exports = {
.intercept('pathNotFound', () => Errors.CARD_NOT_FOUND);
let { card } = path;
const { list, board } = path;
const { list, board, project } = path;
let boardMembership = await BoardMembership.findOne({
boardId: board.id,
@ -133,9 +133,11 @@ module.exports = {
throw Errors.NOT_ENOUGH_RIGHTS;
}
let nextProject;
let nextBoard;
if (!_.isUndefined(inputs.boardId)) {
({ board: nextBoard } = await sails.helpers.boards
({ board: nextBoard, project: nextProject } = await sails.helpers.boards
.getProjectPath(inputs.boardId)
.intercept('pathNotFound', () => Errors.BOARD_NOT_FOUND));
@ -177,15 +179,17 @@ module.exports = {
card = await sails.helpers.cards.updateOne
.with({
project,
board,
list,
record: card,
values: {
...values,
project: nextProject,
board: nextBoard,
list: nextList,
},
user: currentUser,
actorUser: currentUser,
request: this.req,
})
.intercept('positionMustBeInValues', () => Errors.POSITION_MUST_BE_PRESENT)

View file

@ -32,7 +32,7 @@ module.exports = {
async fn(inputs) {
const { currentUser } = this.req;
const { board, card } = await sails.helpers.cards
const { card, list, board, project } = await sails.helpers.cards
.getProjectPath(inputs.cardId)
.intercept('pathNotFound', () => Errors.CARD_NOT_FOUND);
@ -55,7 +55,9 @@ module.exports = {
};
const action = await sails.helpers.actions.createOne.with({
project,
board,
list,
values: {
...values,
card,

View file

@ -36,7 +36,7 @@ module.exports = {
.intercept('pathNotFound', () => Errors.COMMENT_ACTION_NOT_FOUND);
let { action } = path;
const { board, project, card } = path;
const { card, list, board, project } = path;
const isProjectManager = await sails.helpers.users.isProjectManager(currentUser.id, project.id);
@ -60,9 +60,12 @@ module.exports = {
}
action = await sails.helpers.actions.deleteOne.with({
project,
board,
list,
card,
record: action,
actorUser: currentUser,
request: this.req,
});

View file

@ -40,7 +40,7 @@ module.exports = {
.intercept('pathNotFound', () => Errors.COMMENT_ACTION_NOT_FOUND);
let { action } = path;
const { board, project, card } = path;
const { card, list, board, project } = path;
const isProjectManager = await sails.helpers.users.isProjectManager(currentUser.id, project.id);
@ -69,9 +69,12 @@ module.exports = {
action = await sails.helpers.actions.updateOne.with({
values,
card,
project,
board,
list,
card,
record: action,
actorUser: currentUser,
request: this.req,
});

View file

@ -42,7 +42,7 @@ module.exports = {
async fn(inputs) {
const { currentUser } = this.req;
const { board } = await sails.helpers.boards
const { board, project } = await sails.helpers.boards
.getProjectPath(inputs.boardId)
.intercept('pathNotFound', () => Errors.BOARD_NOT_FOUND);
@ -62,10 +62,12 @@ module.exports = {
const values = _.pick(inputs, ['position', 'name', 'color']);
const label = await sails.helpers.labels.createOne.with({
project,
values: {
...values,
board,
},
actorUser: currentUser,
request: this.req,
});

View file

@ -28,12 +28,15 @@ module.exports = {
async fn(inputs) {
const { currentUser } = this.req;
let { label } = await sails.helpers.labels
const path = await sails.helpers.labels
.getProjectPath(inputs.id)
.intercept('pathNotFound', () => Errors.LABEL_NOT_FOUND);
let { label } = path;
const { board, project } = path;
const boardMembership = await BoardMembership.findOne({
boardId: label.boardId,
boardId: board.id,
userId: currentUser.id,
});
@ -46,7 +49,10 @@ module.exports = {
}
label = await sails.helpers.labels.deleteOne.with({
project,
board,
record: label,
actorUser: currentUser,
request: this.req,
});

View file

@ -40,12 +40,15 @@ module.exports = {
async fn(inputs) {
const { currentUser } = this.req;
let { label } = await sails.helpers.labels
const path = await sails.helpers.labels
.getProjectPath(inputs.id)
.intercept('pathNotFound', () => Errors.LABEL_NOT_FOUND);
let { label } = path;
const { board, project } = path;
const boardMembership = await BoardMembership.findOne({
boardId: label.boardId,
boardId: board.id,
userId: currentUser.id,
});
@ -61,7 +64,10 @@ module.exports = {
label = await sails.helpers.labels.updateOne.with({
values,
project,
board,
record: label,
actorUser: currentUser,
request: this.req,
});

View file

@ -36,7 +36,7 @@ module.exports = {
async fn(inputs) {
const { currentUser } = this.req;
const { board } = await sails.helpers.boards
const { board, project } = await sails.helpers.boards
.getProjectPath(inputs.boardId)
.intercept('pathNotFound', () => Errors.BOARD_NOT_FOUND);
@ -56,10 +56,12 @@ module.exports = {
const values = _.pick(inputs, ['position', 'name']);
const list = await sails.helpers.lists.createOne.with({
project,
values: {
...values,
board,
},
actorUser: currentUser,
request: this.req,
});

View file

@ -28,13 +28,15 @@ module.exports = {
async fn(inputs) {
const { currentUser } = this.req;
// eslint-disable-next-line prefer-const
let { list, board } = await sails.helpers.lists
const path = await sails.helpers.lists
.getProjectPath(inputs.id)
.intercept('pathNotFound', () => Errors.LIST_NOT_FOUND);
let { list } = path;
const { board, project } = path;
const boardMembership = await BoardMembership.findOne({
boardId: list.boardId,
boardId: board.id,
userId: currentUser.id,
});
@ -47,8 +49,10 @@ module.exports = {
}
list = await sails.helpers.lists.deleteOne.with({
record: list,
project,
board,
record: list,
actorUser: currentUser,
request: this.req,
});

View file

@ -32,12 +32,12 @@ module.exports = {
async fn(inputs) {
const { currentUser } = this.req;
const { list } = await sails.helpers.lists
const { list, board, project } = await sails.helpers.lists
.getProjectPath(inputs.id)
.intercept('pathNotFound', () => Errors.LIST_NOT_FOUND);
const boardMembership = await BoardMembership.findOne({
boardId: list.boardId,
boardId: board.id,
userId: currentUser.id,
});
@ -50,8 +50,11 @@ module.exports = {
}
const cards = await sails.helpers.lists.sortOne.with({
project,
board,
record: list,
type: inputs.type,
actorUser: currentUser,
request: this.req,
});

View file

@ -35,13 +35,15 @@ module.exports = {
async fn(inputs) {
const { currentUser } = this.req;
// eslint-disable-next-line prefer-const
let { list, board } = await sails.helpers.lists
const path = await sails.helpers.lists
.getProjectPath(inputs.id)
.intercept('pathNotFound', () => Errors.LIST_NOT_FOUND);
let { list } = path;
const { board, project } = path;
const boardMembership = await BoardMembership.findOne({
boardId: list.boardId,
boardId: board.id,
userId: currentUser.id,
});
@ -57,8 +59,10 @@ module.exports = {
list = await sails.helpers.lists.updateOne.with({
values,
project,
board,
record: list,
actorUser: currentUser,
request: this.req,
});

View file

@ -18,7 +18,7 @@ module.exports = {
const notifications = await sails.helpers.notifications.updateMany.with({
values,
recordsOrIds: inputs.ids.split(','),
user: currentUser,
actorUser: currentUser,
request: this.req,
});

View file

@ -63,6 +63,7 @@ module.exports = {
project,
user,
},
actorUser: currentUser,
request: this.req,
})
.intercept('userAlreadyProjectManager', () => Errors.USER_ALREADY_PROJECT_MANAGER);

View file

@ -40,6 +40,7 @@ module.exports = {
// TODO: check if the last one
projectManager = await sails.helpers.projectManagers.deleteOne.with({
record: projectManager,
actorUser: currentUser,
request: this.req,
});

View file

@ -13,7 +13,7 @@ module.exports = {
const { project, projectManager } = await sails.helpers.projects.createOne.with({
values,
user: currentUser,
actorUser: currentUser,
request: this.req,
});

View file

@ -36,6 +36,7 @@ module.exports = {
project = await sails.helpers.projects.deleteOne.with({
record: project,
actorUser: currentUser,
request: this.req,
});

View file

@ -90,6 +90,7 @@ module.exports = {
values: {
backgroundImage: fileData,
},
actorUser: currentUser,
request: this.req,
});

View file

@ -81,6 +81,7 @@ module.exports = {
project = await sails.helpers.projects.updateOne.with({
values,
record: project,
actorUser: currentUser,
request: this.req,
});

View file

@ -39,12 +39,12 @@ module.exports = {
async fn(inputs) {
const { currentUser } = this.req;
const { card, board } = await sails.helpers.cards
const { card, list, board, project } = await sails.helpers.cards
.getProjectPath(inputs.cardId)
.intercept('pathNotFound', () => Errors.CARD_NOT_FOUND);
const boardMembership = await BoardMembership.findOne({
boardId: card.boardId,
boardId: board.id,
userId: currentUser.id,
});
@ -59,11 +59,14 @@ module.exports = {
const values = _.pick(inputs, ['position', 'name', 'isCompleted']);
const task = await sails.helpers.tasks.createOne.with({
project,
board,
list,
values: {
...values,
card,
},
board,
actorUser: currentUser,
request: this.req,
});

View file

@ -33,7 +33,7 @@ module.exports = {
.intercept('pathNotFound', () => Errors.TASK_NOT_FOUND);
let { task } = path;
const { board } = path;
const { card, list, board, project } = path;
const boardMembership = await BoardMembership.findOne({
boardId: board.id,
@ -49,8 +49,12 @@ module.exports = {
}
task = await sails.helpers.tasks.deleteOne.with({
project,
board,
list,
card,
record: task,
actorUser: currentUser,
request: this.req,
});

View file

@ -43,7 +43,7 @@ module.exports = {
.intercept('pathNotFound', () => Errors.TASK_NOT_FOUND);
let { task } = path;
const { board } = path;
const { card, list, board, project } = path;
const boardMembership = await BoardMembership.findOne({
boardId: board.id,
@ -62,8 +62,12 @@ module.exports = {
task = await sails.helpers.tasks.updateOne.with({
values,
project,
board,
list,
card,
record: task,
actorUser: currentUser,
request: this.req,
});

View file

@ -71,6 +71,8 @@ module.exports = {
},
async fn(inputs) {
const { currentUser } = this.req;
if (sails.config.custom.oidcEnforced) {
throw Errors.NOT_ENOUGH_RIGHTS;
}
@ -89,6 +91,7 @@ module.exports = {
const user = await sails.helpers.users.createOne
.with({
values,
actorUser: currentUser,
request: this.req,
})
.intercept('emailAlreadyInUse', () => Errors.EMAIL_ALREADY_IN_USE)

View file

@ -26,6 +26,8 @@ module.exports = {
},
async fn(inputs) {
const { currentUser } = this.req;
let user = await sails.helpers.users.getOne(inputs.id);
if (!user) {
@ -38,6 +40,7 @@ module.exports = {
user = await sails.helpers.users.deleteOne.with({
record: user,
actorUser: currentUser,
request: this.req,
});

View file

@ -91,7 +91,7 @@ module.exports = {
values: {
avatar: fileData,
},
user: currentUser,
actorUser: currentUser,
request: this.req,
});

View file

@ -82,7 +82,7 @@ module.exports = {
.with({
values,
record: user,
user: currentUser,
actorUser: currentUser,
request: this.req,
})
.intercept('emailAlreadyInUse', () => Errors.EMAIL_ALREADY_IN_USE);

View file

@ -80,7 +80,7 @@ module.exports = {
user = await sails.helpers.users.updateOne.with({
values,
record: user,
user: currentUser,
actorUser: currentUser,
request: this.req,
});

View file

@ -83,7 +83,7 @@ module.exports = {
.with({
values,
record: user,
user: currentUser,
actorUser: currentUser,
request: this.req,
})
.intercept('usernameAlreadyInUse', () => Errors.USERNAME_ALREADY_IN_USE);

View file

@ -95,7 +95,7 @@ module.exports = {
user = await sails.helpers.users.updateOne.with({
values,
record: user,
user: currentUser,
actorUser: currentUser,
request: this.req,
});

View file

@ -14,21 +14,21 @@ const valuesValidator = (value) => {
return true;
};
const buildAndSendSlackMessage = async (user, card, action) => {
const buildAndSendSlackMessage = async (card, action, actorUser) => {
const cardLink = `<${sails.config.custom.baseUrl}/cards/${card.id}|${card.name}>`;
let markdown;
switch (action.type) {
case Action.Types.CREATE_CARD:
markdown = `${cardLink} was created by ${user.name} in *${action.data.list.name}*`;
markdown = `${cardLink} was created by ${actorUser.name} in *${action.data.list.name}*`;
break;
case Action.Types.MOVE_CARD:
markdown = `${cardLink} was moved by ${user.name} to *${action.data.toList.name}*`;
markdown = `${cardLink} was moved by ${actorUser.name} to *${action.data.toList.name}*`;
break;
case Action.Types.COMMENT_CARD:
markdown = `*${user.name}* commented on ${cardLink}:\n>${action.data.text}`;
markdown = `*${actorUser.name}* commented on ${cardLink}:\n>${action.data.text}`;
break;
default:
@ -45,10 +45,18 @@ module.exports = {
custom: valuesValidator,
required: true,
},
project: {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
},
list: {
type: 'ref',
required: true,
},
request: {
type: 'ref',
},
@ -72,6 +80,24 @@ module.exports = {
inputs.request,
);
sails.helpers.utils.sendWebhooks.with({
event: 'actionCreate',
data: {
item: action,
included: {
projects: [inputs.project],
boards: [inputs.board],
lists: [inputs.list],
cards: [values.card],
},
},
user: values.user,
});
if (sails.config.custom.slackBotToken) {
buildAndSendSlackMessage(values.card, action, values.user);
}
const subscriptionUserIds = await sails.helpers.cards.getSubscriptionUserIds(
action.cardId,
action.userId,
@ -84,26 +110,15 @@ module.exports = {
userId,
action,
},
user: values.user,
project: inputs.project,
board: inputs.board,
list: inputs.list,
card: values.card,
actorUser: values.user,
}),
),
);
if (sails.config.custom.slackBotToken) {
buildAndSendSlackMessage(values.user, values.card, action);
}
await sails.helpers.utils.sendWebhook.with({
event: 'ACTION_CREATE',
data: action,
projectId: inputs.board.projectId,
user: inputs.request.currentUser,
card: values.card,
board: inputs.board,
});
return action;
},
};

View file

@ -4,11 +4,23 @@ module.exports = {
type: 'ref',
required: true,
},
project: {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
},
list: {
type: 'ref',
required: true,
},
card: {
type: 'ref',
required: true,
},
board: {
actorUser: {
type: 'ref',
required: true,
},
@ -30,13 +42,18 @@ module.exports = {
inputs.request,
);
await sails.helpers.utils.sendWebhook.with({
event: 'ACTION_DELETE',
data: action,
projectId: inputs.board.projectId,
user: inputs.request.currentUser,
card: inputs.card,
board: inputs.board,
sails.helpers.utils.sendWebhooks.with({
event: 'actionDelete',
data: {
item: action,
included: {
projects: [inputs.project],
boards: [inputs.board],
lists: [inputs.list],
cards: [inputs.card],
},
},
user: inputs.actorUser,
});
}

View file

@ -8,7 +8,7 @@ module.exports = {
type: 'json',
required: true,
},
card: {
project: {
type: 'ref',
required: true,
},
@ -16,6 +16,18 @@ module.exports = {
type: 'ref',
required: true,
},
list: {
type: 'ref',
required: true,
},
card: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: {
type: 'ref',
},
@ -36,13 +48,18 @@ module.exports = {
inputs.request,
);
await sails.helpers.utils.sendWebhook.with({
event: 'ACTION_UPDATE',
data: action,
projectId: inputs.board.projectId,
user: inputs.request.currentUser,
card: inputs.card,
board: inputs.board,
sails.helpers.utils.sendWebhooks.with({
event: 'actionUpdate',
data: {
item: action,
included: {
projects: [inputs.project],
boards: [inputs.board],
lists: [inputs.list],
cards: [inputs.card],
},
},
user: inputs.actorUser,
});
}

View file

@ -21,10 +21,18 @@ module.exports = {
custom: valuesValidator,
required: true,
},
project: {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
},
list: {
type: 'ref',
required: true,
},
requestId: {
type: 'string',
isNotEmptyString: true,
@ -35,7 +43,7 @@ module.exports = {
},
async fn(inputs) {
const { values, board } = inputs;
const { values } = inputs;
const attachment = await Attachment.create({
...values,
@ -53,26 +61,33 @@ module.exports = {
inputs.request,
);
sails.helpers.utils.sendWebhooks.with({
event: 'attachmentCreate',
data: {
item: attachment,
included: {
projects: [inputs.project],
boards: [inputs.board],
lists: [inputs.list],
cards: [values.card],
},
},
user: values.creatorUser,
});
if (!values.card.coverAttachmentId && attachment.image) {
await sails.helpers.cards.updateOne.with({
record: values.card,
values: {
coverAttachmentId: attachment.id,
},
board,
request: inputs.request,
project: inputs.project,
board: inputs.board,
list: inputs.list,
actorUser: values.creatorUser,
});
}
await sails.helpers.utils.sendWebhook.with({
event: 'ATTACHMENT_CREATE',
data: attachment,
projectId: board.projectId,
user: inputs.request.currentUser,
card: values.card,
board,
});
return attachment;
},
};

View file

@ -7,14 +7,26 @@ module.exports = {
type: 'ref',
required: true,
},
project: {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
},
list: {
type: 'ref',
required: true,
},
card: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: {
type: 'ref',
},
@ -27,6 +39,10 @@ module.exports = {
values: {
coverAttachmentId: null,
},
project: inputs.project,
board: inputs.board,
list: inputs.list,
actorUser: inputs.actorUser,
request: inputs.request,
});
}
@ -49,13 +65,18 @@ module.exports = {
inputs.request,
);
await sails.helpers.utils.sendWebhook.with({
event: 'ATTACHMENT_DELETE',
data: attachment,
projectId: inputs.board.projectId,
user: inputs.request.currentUser,
card: inputs.card,
board: inputs.board,
sails.helpers.utils.sendWebhooks.with({
event: 'attachmentDelete',
data: {
item: attachment,
included: {
projects: [inputs.project],
boards: [inputs.board],
lists: [inputs.list],
cards: [inputs.card],
},
},
user: inputs.actorUser,
});
}

View file

@ -8,10 +8,26 @@ module.exports = {
type: 'json',
required: true,
},
project: {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
},
list: {
type: 'ref',
required: true,
},
card: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: {
type: 'ref',
},
@ -32,12 +48,18 @@ module.exports = {
inputs.request,
);
await sails.helpers.utils.sendWebhook.with({
event: 'ATTACHMENT_UPDATE',
data: attachment,
projectId: inputs.board.projectId,
user: inputs.request.currentUser,
board: inputs.board,
sails.helpers.utils.sendWebhooks.with({
event: 'attachmentUpdate',
data: {
item: attachment,
included: {
projects: [inputs.project],
boards: [inputs.board],
lists: [inputs.list],
cards: [inputs.card],
},
},
user: inputs.actorUser,
});
}

View file

@ -21,6 +21,14 @@ module.exports = {
custom: valuesValidator,
required: true,
},
project: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: {
type: 'ref',
},
@ -67,6 +75,19 @@ module.exports = {
inputs.request,
);
sails.helpers.utils.sendWebhooks.with({
event: 'boardMembershipCreate',
data: {
item: boardMembership,
included: {
users: [values.user],
projects: [inputs.project],
boards: [values.board],
},
},
user: inputs.actorUser,
});
return boardMembership;
},
};

View file

@ -10,6 +10,14 @@ module.exports = {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: {
type: 'ref',
},
@ -73,6 +81,18 @@ module.exports = {
);
});
}
sails.helpers.utils.sendWebhooks.with({
event: 'boardMembershipDelete',
data: {
item: boardMembership,
included: {
projects: [inputs.project],
boards: [inputs.board],
},
},
user: inputs.actorUser,
});
}
return boardMembership;

View file

@ -8,6 +8,18 @@ module.exports = {
type: 'json',
required: true,
},
project: {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: {
type: 'ref',
},
@ -40,6 +52,18 @@ module.exports = {
},
inputs.request,
);
sails.helpers.utils.sendWebhooks.with({
event: 'boardMembershipUpdate',
data: {
item: boardMembership,
included: {
projects: [inputs.project],
boards: [inputs.board],
},
},
user: inputs.actorUser,
});
}
return boardMembership;

View file

@ -37,7 +37,7 @@ module.exports = {
type: 'json',
custom: importValidator,
},
user: {
actorUser: {
type: 'ref',
required: true,
},
@ -80,6 +80,8 @@ module.exports = {
position: nextPosition,
},
});
// TODO: send webhooks
});
});
@ -90,12 +92,12 @@ module.exports = {
}).fetch();
if (inputs.import && inputs.import.type === Board.ImportTypes.TRELLO) {
await sails.helpers.boards.importFromTrello(inputs.user, board, inputs.import.board);
await sails.helpers.boards.importFromTrello(board, inputs.import.board, inputs.actorUser);
}
const boardMembership = await BoardMembership.create({
boardId: board.id,
userId: inputs.user.id,
userId: inputs.actorUser.id,
role: BoardMembership.Roles.EDITOR,
}).fetch();
@ -111,12 +113,15 @@ module.exports = {
);
});
await sails.helpers.utils.sendWebhook.with({
event: 'BOARD_CREATE',
data: board,
projectId: board.projectId,
user: inputs.request.currentUser,
board,
sails.helpers.utils.sendWebhooks.with({
event: 'boardCreate',
data: {
item: board,
included: {
projects: [values.project],
},
},
user: inputs.actorUser,
});
return {

View file

@ -4,6 +4,14 @@ module.exports = {
type: 'ref',
required: true,
},
project: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: {
type: 'ref',
},
@ -33,15 +41,18 @@ module.exports = {
inputs.request,
);
});
}
await sails.helpers.utils.sendWebhook.with({
event: 'BOARD_DELETE',
data: board,
projectId: board.projectId,
user: inputs.request.currentUser,
board,
});
sails.helpers.utils.sendWebhooks.with({
event: 'boardDelete',
data: {
item: board,
included: {
projects: [inputs.project],
},
},
user: inputs.actorUser,
});
}
return board;
},

View file

@ -2,10 +2,6 @@ const POSITION_GAP = 65535; // TODO: move to config
module.exports = {
inputs: {
user: {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
@ -14,6 +10,10 @@ module.exports = {
type: 'json',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
},
async fn(inputs) {
@ -87,7 +87,7 @@ module.exports = {
trelloComments.map(async (trelloComment) => {
return Action.create({
cardId: plankaCard.id,
userId: inputs.user.id,
userId: inputs.actorUser.id,
type: 'commentCard',
data: {
text:
@ -105,7 +105,7 @@ module.exports = {
const plankaCard = await Card.create({
boardId: inputs.board.id,
listId: plankaList.id,
creatorUserId: inputs.user.id,
creatorUserId: inputs.actorUser.id,
position: trelloCard.pos,
name: trelloCard.name,
description: trelloCard.desc || null,

View file

@ -21,6 +21,14 @@ module.exports = {
custom: valuesValidator,
required: true,
},
project: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: {
type: 'ref',
},
@ -64,6 +72,8 @@ module.exports = {
position: nextPosition,
},
});
// TODO: send webhooks
});
});
}
@ -81,15 +91,18 @@ module.exports = {
inputs.request,
);
});
}
await sails.helpers.utils.sendWebhook.with({
event: 'BOARD_UPDATE',
data: board,
projectId: board.projectId,
user: inputs.request.currentUser,
board,
});
sails.helpers.utils.sendWebhooks.with({
event: 'boardUpdate',
data: {
item: board,
included: {
projects: [inputs.project],
},
},
user: inputs.actorUser,
});
}
return board;
},

View file

@ -21,6 +21,22 @@ module.exports = {
custom: valuesValidator,
required: true,
},
project: {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
},
list: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: {
type: 'ref',
},
@ -50,6 +66,21 @@ module.exports = {
inputs.request,
);
sails.helpers.utils.sendWebhooks.with({
event: 'cardLabelCreate',
data: {
item: cardLabel,
included: {
projects: [inputs.project],
boards: [inputs.board],
labels: [values.label],
lists: [inputs.list],
cards: [values.card],
},
},
user: inputs.actorUser,
});
return cardLabel;
},
};

View file

@ -4,10 +4,26 @@ module.exports = {
type: 'ref',
required: true,
},
project: {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
},
list: {
type: 'ref',
required: true,
},
card: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: {
type: 'ref',
},
@ -25,6 +41,20 @@ module.exports = {
},
inputs.request,
);
sails.helpers.utils.sendWebhooks.with({
event: 'cardLabelDelete',
data: {
item: cardLabel,
included: {
projects: [inputs.project],
boards: [inputs.board],
lists: [inputs.list],
cards: [inputs.card],
},
},
user: inputs.actorUser,
});
}
return cardLabel;

View file

@ -21,10 +21,22 @@ module.exports = {
custom: valuesValidator,
required: true,
},
project: {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
},
list: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: {
type: 'ref',
},
@ -57,6 +69,20 @@ module.exports = {
inputs.request,
);
sails.helpers.utils.sendWebhooks.with({
event: 'cardMembershipCreate',
data: {
item: cardMembership,
included: {
projects: [inputs.project],
boards: [inputs.board],
lists: [inputs.list],
cards: [values.card],
},
},
user: inputs.actorUser,
});
const cardSubscription = await CardSubscription.create({
cardId: cardMembership.cardId,
userId: cardMembership.userId,
@ -77,16 +103,9 @@ module.exports = {
},
inputs.request,
);
}
await sails.helpers.utils.sendWebhook.with({
event: 'CARD_MEMBERSHIP_CREATE',
data: cardMembership,
projectId: inputs.board.projectId,
user: inputs.request.currentUser,
card: values.card,
board: inputs.board,
});
// TODO: send webhooks
}
return cardMembership;
},

View file

@ -4,11 +4,23 @@ module.exports = {
type: 'ref',
required: true,
},
project: {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
},
list: {
type: 'ref',
required: true,
},
card: {
type: 'ref',
required: true,
},
board: {
actorUser: {
type: 'ref',
required: true,
},
@ -30,6 +42,20 @@ module.exports = {
inputs.request,
);
sails.helpers.utils.sendWebhooks.with({
event: 'cardMembershipDelete',
data: {
item: cardMembership,
included: {
projects: [inputs.project],
boards: [inputs.board],
lists: [inputs.list],
cards: [inputs.card],
},
},
user: inputs.actorUser,
});
const cardSubscription = await CardSubscription.destroyOne({
cardId: cardMembership.cardId,
userId: cardMembership.userId,
@ -43,16 +69,9 @@ module.exports = {
isSubscribed: false,
},
});
}
await sails.helpers.utils.sendWebhook.with({
event: 'CARD_MEMBERSHIP_DELETE',
data: cardMembership,
projectId: inputs.board.projectId,
user: inputs.request.currentUser,
card: inputs.card,
board: inputs.board,
});
// TODO: send webhook
}
}
return cardMembership;

View file

@ -25,6 +25,10 @@ module.exports = {
custom: valuesValidator,
required: true,
},
project: {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
@ -66,6 +70,8 @@ module.exports = {
position: nextPosition,
},
});
// TODO: send webhooks
});
const card = await Card.create({
@ -85,6 +91,19 @@ module.exports = {
inputs.request,
);
sails.helpers.utils.sendWebhooks.with({
event: 'cardCreate',
data: {
item: card,
included: {
projects: [inputs.project],
boards: [inputs.board],
lists: [values.list],
},
},
user: values.creatorUser,
});
if (values.creatorUser.subscribeToOwnCards) {
await CardSubscription.create({
cardId: card.id,
@ -97,6 +116,8 @@ module.exports = {
isSubscribed: true,
},
});
// TODO: send webhooks
}
await sails.helpers.actions.createOne.with({
@ -108,18 +129,10 @@ module.exports = {
},
user: values.creatorUser,
},
board: inputs.board,
request: inputs.request,
});
await sails.helpers.utils.sendWebhook.with({
event: 'CARD_CREATE',
data: card,
projectId: inputs.board.projectId,
user: inputs.request.currentUser,
card,
project: inputs.project,
board: inputs.board,
list: values.list,
request: inputs.request,
});
return card;

View file

@ -1,5 +1,5 @@
const buildAndSendSlackMessage = async (user, card) => {
await sails.helpers.utils.sendSlackMessage(`*${card.name}* was deleted by ${user.name}`);
const buildAndSendSlackMessage = async (card, actorUser) => {
await sails.helpers.utils.sendSlackMessage(`*${card.name}* was deleted by ${actorUser.name}`);
};
module.exports = {
@ -8,7 +8,19 @@ module.exports = {
type: 'ref',
required: true,
},
user: {
project: {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
},
list: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
@ -21,10 +33,6 @@ module.exports = {
const card = await Card.archiveOne(inputs.record.id);
if (card) {
const { board } = await sails.helpers.lists
.getProjectPath(card.listId)
.intercept('pathNotFound', () => Errors.LIST_NOT_FOUND);
sails.sockets.broadcast(
`board:${card.boardId}`,
'cardDelete',
@ -34,18 +42,22 @@ module.exports = {
inputs.request,
);
if (sails.config.custom.slackBotToken) {
buildAndSendSlackMessage(inputs.user, card);
}
await sails.helpers.utils.sendWebhook.with({
event: 'CARD_DELETE',
data: card,
projectId: board.projectId,
user: inputs.request.currentUser,
card,
board,
sails.helpers.utils.sendWebhooks.with({
event: 'cardDelete',
data: {
item: card,
included: {
projects: [inputs.project],
boards: [inputs.board],
lists: [inputs.list],
},
},
user: inputs.actorUser,
});
if (sails.config.custom.slackBotToken) {
buildAndSendSlackMessage(card, inputs.actorUser);
}
}
return card;

View file

@ -25,6 +25,10 @@ module.exports = {
custom: valuesValidator,
required: true,
},
project: {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
@ -62,6 +66,8 @@ module.exports = {
position: nextPosition,
},
});
// TODO: send webhooks
});
const card = await Card.create({
@ -108,6 +114,19 @@ module.exports = {
inputs.request,
);
sails.helpers.utils.sendWebhooks.with({
event: 'cardCreate',
data: {
item: card,
included: {
projects: [inputs.project],
boards: [inputs.board],
lists: [inputs.list],
},
},
user: values.creatorUser,
});
if (values.creatorUser.subscribeToOwnCards) {
await CardSubscription.create({
cardId: card.id,
@ -120,6 +139,8 @@ module.exports = {
isSubscribed: true,
},
});
// TODO: send webhooks
}
await sails.helpers.actions.createOne.with({
@ -131,18 +152,10 @@ module.exports = {
},
user: values.creatorUser,
},
board: inputs.board,
request: inputs.request,
});
await sails.helpers.utils.sendWebhook.with({
event: 'CARD_CREATE',
data: card,
projectId: inputs.board.projectId,
user: inputs.request.currentUser,
card,
project: inputs.project,
board: inputs.board,
list: inputs.list,
request: inputs.request,
});
return {

View file

@ -7,8 +7,14 @@ const valuesValidator = (value) => {
return false;
}
if (!_.isUndefined(value.board) && !_.isPlainObject(value.board)) {
return false;
if (!_.isUndefined(value.board)) {
if (!_.isPlainObject(value.project)) {
return false;
}
if (!_.isPlainObject(value.board)) {
return false;
}
}
if (!_.isUndefined(value.list) && !_.isPlainObject(value.list)) {
@ -29,14 +35,21 @@ module.exports = {
custom: valuesValidator,
required: true,
},
user: {
project: {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
},
list: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: {
type: 'ref',
@ -45,65 +58,56 @@ module.exports = {
exits: {
positionMustBeInValues: {},
boardInValuesMustBelongToProject: {},
listMustBeInValues: {},
listInValuesMustBelongToBoard: {},
userMustBePresent: {},
boardMustBePresent: {},
listMustBePresent: {},
},
async fn(inputs) {
const { isSubscribed, ...values } = inputs.values;
if (values.board || values.list || !_.isUndefined(values.position)) {
if (!inputs.board) {
throw 'boardMustBePresent';
if (values.project && values.project.id === inputs.project.id) {
delete values.project;
}
const project = values.project || inputs.project;
if (values.board) {
if (values.board.projectId !== project.id) {
throw 'boardInValuesMustBelongToProject';
}
if (values.board) {
if (values.board.id === inputs.board.id) {
delete values.board;
} else {
values.boardId = values.board.id;
}
}
const board = values.board || inputs.board;
if (values.list) {
if (!inputs.list) {
throw 'listMustBePresent';
}
if (values.list.boardId !== board.id) {
throw 'listInValuesMustBelongToBoard';
}
if (values.list.id === inputs.list.id) {
delete values.list;
} else {
values.listId = values.list.id;
}
}
if (values.list) {
if (_.isUndefined(values.position)) {
throw 'positionMustBeInValues';
}
} else if (values.board) {
throw 'listMustBeInValues';
if (values.board.id === inputs.board.id) {
delete values.board;
} else {
values.boardId = values.board.id;
}
}
if ((!_.isUndefined(isSubscribed) || values.board || values.list) && !inputs.user) {
throw 'userMustBePresent';
const board = values.board || inputs.board;
if (values.list) {
if (values.list.boardId !== board.id) {
throw 'listInValuesMustBelongToBoard';
}
if (values.list.id === inputs.list.id) {
delete values.list;
} else {
values.listId = values.list.id;
}
} else if (values.board) {
throw 'listMustBeInValues';
}
const list = values.list || inputs.list;
if (values.list && _.isUndefined(values.position)) {
throw 'positionMustBeInValues';
}
if (!_.isUndefined(values.position)) {
const boardId = values.boardId || inputs.record.boardId;
const listId = values.listId || inputs.record.listId;
const cards = await sails.helpers.lists.getCards(listId, inputs.record.id);
const cards = await sails.helpers.lists.getCards(list.id, inputs.record.id);
const { position, repositions } = sails.helpers.utils.insertToPositionables(
values.position,
@ -115,17 +119,19 @@ module.exports = {
repositions.forEach(async ({ id, position: nextPosition }) => {
await Card.update({
id,
listId,
listId: list.id,
}).set({
position: nextPosition,
});
sails.sockets.broadcast(`board:${boardId}`, 'cardUpdate', {
sails.sockets.broadcast(`board:${board.id}`, 'cardUpdate', {
item: {
id,
position: nextPosition,
},
});
// TODO: send webhooks
});
}
@ -175,10 +181,12 @@ module.exports = {
}
const { id } = await sails.helpers.labels.createOne.with({
project,
values: {
..._.omit(label, ['id', 'boardId']),
board: values.board,
},
actorUser: inputs.actorUser,
});
return id;
@ -209,6 +217,8 @@ module.exports = {
isSubscribed: true,
},
});
// TODO: send webhooks
});
} else {
sails.sockets.broadcast(
@ -221,18 +231,33 @@ module.exports = {
);
}
sails.helpers.utils.sendWebhooks.with({
event: 'cardUpdate',
data: {
item: card,
included: {
projects: [project],
boards: [board],
lists: [list],
},
},
user: inputs.actorUser,
});
if (!values.board && values.list) {
await sails.helpers.actions.createOne.with({
project,
board,
list,
values: {
card,
user: inputs.user,
user: inputs.actorUser,
type: Action.Types.MOVE_CARD,
data: {
fromList: _.pick(inputs.list, ['id', 'name']),
toList: _.pick(values.list, ['id', 'name']),
},
},
board: inputs.board,
request: inputs.request,
});
}
@ -241,23 +266,26 @@ module.exports = {
}
if (!_.isUndefined(isSubscribed)) {
const prevIsSubscribed = await sails.helpers.users.isCardSubscriber(inputs.user.id, card.id);
const prevIsSubscribed = await sails.helpers.users.isCardSubscriber(
inputs.actorUser.id,
card.id,
);
if (isSubscribed !== prevIsSubscribed) {
if (isSubscribed) {
await CardSubscription.create({
cardId: card.id,
userId: inputs.user.id,
userId: inputs.actorUser.id,
}).tolerate('E_UNIQUE');
} else {
await CardSubscription.destroyOne({
cardId: card.id,
userId: inputs.user.id,
userId: inputs.actorUser.id,
});
}
sails.sockets.broadcast(
`user:${inputs.user.id}`,
`user:${inputs.actorUser.id}`,
'cardUpdate',
{
item: {
@ -267,18 +295,11 @@ module.exports = {
},
inputs.request,
);
// TODO: send webhooks
}
}
await sails.helpers.utils.sendWebhook.with({
event: 'CARD_UPDATE',
data: card,
projectId: inputs.board.projectId,
user: inputs.request.currentUser,
card,
board: inputs.board,
});
return card;
},
};

View file

@ -21,6 +21,14 @@ module.exports = {
custom: valuesValidator,
required: true,
},
project: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: {
type: 'ref',
},
@ -50,6 +58,8 @@ module.exports = {
position: nextPosition,
},
});
// TODO: send webhooks
});
const label = await Label.create({
@ -67,6 +77,18 @@ module.exports = {
inputs.request,
);
sails.helpers.utils.sendWebhooks.with({
event: 'labelCreate',
data: {
item: label,
included: {
projects: [inputs.project],
boards: [values.board],
},
},
user: inputs.actorUser,
});
return label;
},
};

View file

@ -4,6 +4,18 @@ module.exports = {
type: 'ref',
required: true,
},
project: {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: {
type: 'ref',
},
@ -25,6 +37,18 @@ module.exports = {
},
inputs.request,
);
sails.helpers.utils.sendWebhooks.with({
event: 'labelDelete',
data: {
item: label,
included: {
projects: [inputs.project],
boards: [inputs.board],
},
},
user: inputs.actorUser,
});
}
return label;

View file

@ -21,6 +21,18 @@ module.exports = {
custom: valuesValidator,
required: true,
},
project: {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: {
type: 'ref',
},
@ -53,6 +65,8 @@ module.exports = {
position: nextPosition,
},
});
// TODO: send webhooks
});
}
@ -67,6 +81,18 @@ module.exports = {
},
inputs.request,
);
sails.helpers.utils.sendWebhooks.with({
event: 'labelUpdate',
data: {
item: label,
included: {
projects: [inputs.project],
boards: [inputs.board],
},
},
user: inputs.actorUser,
});
}
return label;

View file

@ -21,6 +21,14 @@ module.exports = {
custom: valuesValidator,
required: true,
},
project: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: {
type: 'ref',
},
@ -50,6 +58,8 @@ module.exports = {
position: nextPosition,
},
});
// TODO: send webhooks
});
const list = await List.create({
@ -67,12 +77,16 @@ module.exports = {
inputs.request,
);
await sails.helpers.utils.sendWebhook.with({
event: 'LIST_CREATE',
data: list,
projectId: values.board.projectId,
user: inputs.request.currentUser,
board: values.board,
sails.helpers.utils.sendWebhooks.with({
event: 'listCreate',
data: {
item: list,
included: {
projects: [inputs.project],
boards: [values.board],
},
},
user: inputs.actorUser,
});
return list;

View file

@ -4,10 +4,18 @@ module.exports = {
type: 'ref',
required: true,
},
project: {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: {
type: 'ref',
},
@ -26,12 +34,16 @@ module.exports = {
inputs.request,
);
await sails.helpers.utils.sendWebhook.with({
event: 'LIST_DELETE',
data: list,
projectId: inputs.board.projectId,
user: inputs.request.currentUser,
board: inputs.board,
sails.helpers.utils.sendWebhooks.with({
event: 'listDelete',
data: {
item: list,
included: {
projects: [inputs.project],
boards: [inputs.board],
},
},
user: inputs.actorUser,
});
}

View file

@ -11,6 +11,18 @@ module.exports = {
isIn: Object.values(List.SortTypes),
defaultsTo: List.SortTypes.NAME_ASC,
},
project: {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: {
type: 'ref',
},
@ -65,6 +77,19 @@ module.exports = {
inputs.request,
);
sails.helpers.utils.sendWebhooks.with({
event: 'listSort',
data: {
item: inputs.record,
included: {
cards,
projects: [inputs.project],
boards: [inputs.board],
},
},
user: inputs.actorUser,
});
return cards;
},
};

View file

@ -21,10 +21,18 @@ module.exports = {
custom: valuesValidator,
required: true,
},
project: {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: {
type: 'ref',
},
@ -57,6 +65,8 @@ module.exports = {
position: nextPosition,
},
});
// TODO: send webhooks
});
}
@ -72,12 +82,16 @@ module.exports = {
inputs.request,
);
await sails.helpers.utils.sendWebhook.with({
event: 'LIST_UPDATE',
data: list,
projectId: inputs.board.projectId,
user: inputs.request.currentUser,
board: inputs.board,
sails.helpers.utils.sendWebhooks.with({
event: 'listUpdate',
data: {
item: list,
included: {
projects: [inputs.project],
boards: [inputs.board],
},
},
user: inputs.actorUser,
});
}

View file

@ -15,14 +15,14 @@ const valuesValidator = (value) => {
};
// TODO: use templates (views) to build html
const buildAndSendEmail = async (user, board, card, action, notifiableUser) => {
const buildAndSendEmail = async (board, card, action, actorUser, notifiableUser) => {
let emailData;
switch (action.type) {
case Action.Types.MOVE_CARD:
emailData = {
subject: `${user.name} moved ${card.name} from ${action.data.fromList.name} to ${action.data.toList.name} on ${board.name}`,
subject: `${actorUser.name} moved ${card.name} from ${action.data.fromList.name} to ${action.data.toList.name} on ${board.name}`,
html:
`<p>${user.name} moved ` +
`<p>${actorUser.name} moved ` +
`<a href="${process.env.BASE_URL}/cards/${card.id}">${card.name}</a> ` +
`from ${action.data.fromList.name} to ${action.data.toList.name} ` +
`on <a href="${process.env.BASE_URL}/boards/${board.id}">${board.name}</a></p>`,
@ -31,9 +31,9 @@ const buildAndSendEmail = async (user, board, card, action, notifiableUser) => {
break;
case Action.Types.COMMENT_CARD:
emailData = {
subject: `${user.name} left a new comment to ${card.name} on ${board.name}`,
subject: `${actorUser.name} left a new comment to ${card.name} on ${board.name}`,
html:
`<p>${user.name} left a new comment to ` +
`<p>${actorUser.name} left a new comment to ` +
`<a href="${process.env.BASE_URL}/cards/${card.id}">${card.name}</a> ` +
`on <a href="${process.env.BASE_URL}/boards/${board.id}">${board.name}</a></p>` +
`<p>${action.data.text}</p>`,
@ -57,7 +57,7 @@ module.exports = {
custom: valuesValidator,
required: true,
},
user: {
project: {
type: 'ref',
required: true,
},
@ -65,10 +65,18 @@ module.exports = {
type: 'ref',
required: true,
},
list: {
type: 'ref',
required: true,
},
card: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
},
async fn(inputs) {
@ -96,9 +104,24 @@ module.exports = {
notifiableUser = await sails.helpers.users.getOne(notification.userId);
}
buildAndSendEmail(inputs.user, inputs.board, inputs.card, values.action, notifiableUser);
buildAndSendEmail(inputs.board, inputs.card, values.action, inputs.actorUser, notifiableUser);
}
sails.helpers.utils.sendWebhooks.with({
event: 'notificationCreate',
data: {
item: notification,
included: {
projects: [inputs.project],
boards: [inputs.board],
lists: [inputs.list],
cards: [inputs.card],
actions: [values.action],
},
},
user: inputs.actorUser,
});
return notification;
},
};

View file

@ -12,8 +12,9 @@ module.exports = {
type: 'json',
required: true,
},
user: {
actorUser: {
type: 'ref',
required: true,
},
request: {
type: 'ref',
@ -23,7 +24,9 @@ module.exports = {
async fn(inputs) {
const { values } = inputs;
const criteria = {};
const criteria = {
userId: inputs.actorUser.id,
};
if (_.every(inputs.recordsOrIds, _.isPlainObject)) {
criteria.id = sails.helpers.utils.mapRecords(inputs.recordsOrIds);
@ -31,10 +34,6 @@ module.exports = {
criteria.id = inputs.recordsOrIds;
}
if (inputs.user) {
criteria.userId = inputs.user.id;
}
const notifications = await Notification.update(criteria)
.set({ ...values })
.fetch();
@ -48,6 +47,14 @@ module.exports = {
},
inputs.request,
);
sails.helpers.utils.sendWebhooks.with({
event: 'notificationUpdate',
data: {
item: notification,
},
user: inputs.actorUser,
});
});
return notifications;

View file

@ -21,6 +21,10 @@ module.exports = {
custom: valuesValidator,
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: {
type: 'ref',
},
@ -55,6 +59,18 @@ module.exports = {
);
});
sails.helpers.utils.sendWebhooks.with({
event: 'projectManagerCreate',
data: {
item: projectManager,
included: {
users: [values.user],
projects: [values.project],
},
},
user: inputs.actorUser,
});
return projectManager;
},
};

View file

@ -4,6 +4,10 @@ module.exports = {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: {
type: 'ref',
},
@ -27,6 +31,14 @@ module.exports = {
inputs.request,
);
});
sails.helpers.utils.sendWebhooks.with({
event: 'projectManagerDelete',
data: {
item: projectManager,
},
user: inputs.actorUser,
});
}
return projectManager;

View file

@ -4,7 +4,7 @@ module.exports = {
type: 'json',
required: true,
},
user: {
actorUser: {
type: 'ref',
required: true,
},
@ -20,7 +20,7 @@ module.exports = {
const projectManager = await ProjectManager.create({
projectId: project.id,
userId: inputs.user.id,
userId: inputs.actorUser.id,
}).fetch();
sails.sockets.broadcast(
@ -32,11 +32,12 @@ module.exports = {
inputs.request,
);
await sails.helpers.utils.sendWebhook.with({
event: 'PROJECT_CREATE',
data: project,
projectId: project.id,
user: inputs.request.currentUser,
sails.helpers.utils.sendWebhooks.with({
event: 'projectCreate',
data: {
item: project,
},
user: inputs.actorUser,
});
return {

View file

@ -4,6 +4,10 @@ module.exports = {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: {
type: 'ref',
},
@ -38,11 +42,12 @@ module.exports = {
);
});
await sails.helpers.utils.sendWebhook.with({
event: 'PROJECT_DELETE',
data: project,
projectId: project.id,
user: inputs.request.currentUser,
sails.helpers.utils.sendWebhooks.with({
event: 'projectDelete',
data: {
item: project,
},
user: inputs.actorUser,
});
}

View file

@ -28,6 +28,10 @@ module.exports = {
custom: valuesValidator,
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: {
type: 'ref',
},
@ -109,11 +113,12 @@ module.exports = {
);
});
await sails.helpers.utils.sendWebhook.with({
event: 'PROJECT_UPDATE',
data: project,
projectId: project.id,
user: inputs.request.currentUser,
sails.helpers.utils.sendWebhooks.with({
event: 'projectUpdate',
data: {
item: project,
},
user: inputs.actorUser,
});
}

View file

@ -21,10 +21,22 @@ module.exports = {
custom: valuesValidator,
required: true,
},
project: {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
},
list: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: {
type: 'ref',
},
@ -54,6 +66,8 @@ module.exports = {
position: nextPosition,
},
});
// TODO: send webhooks
});
const task = await Task.create({
@ -71,13 +85,18 @@ module.exports = {
inputs.request,
);
await sails.helpers.utils.sendWebhook.with({
event: 'TASK_CREATE',
data: task,
projectId: inputs.board.projectId,
user: inputs.request.currentUser,
card: values.card,
board: inputs.board,
sails.helpers.utils.sendWebhooks.with({
event: 'taskCreate',
data: {
item: task,
included: {
projects: [inputs.project],
boards: [inputs.board],
lists: [inputs.list],
cards: [values.card],
},
},
user: inputs.actorUser,
});
return task;

View file

@ -4,10 +4,26 @@ module.exports = {
type: 'ref',
required: true,
},
project: {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
},
list: {
type: 'ref',
required: true,
},
card: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: {
type: 'ref',
},
@ -26,12 +42,18 @@ module.exports = {
inputs.request,
);
await sails.helpers.utils.sendWebhook.with({
event: 'TASK_DELETE',
data: task,
projectId: inputs.board.projectId,
user: inputs.request.currentUser,
board: inputs.board,
sails.helpers.utils.sendWebhooks.with({
event: 'taskDelete',
data: {
item: task,
included: {
projects: [inputs.project],
boards: [inputs.board],
lists: [inputs.list],
cards: [inputs.card],
},
},
user: inputs.actorUser,
});
}

View file

@ -21,10 +21,26 @@ module.exports = {
custom: valuesValidator,
required: true,
},
project: {
type: 'ref',
required: true,
},
board: {
type: 'ref',
required: true,
},
list: {
type: 'ref',
required: true,
},
card: {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: {
type: 'ref',
},
@ -57,6 +73,8 @@ module.exports = {
position: nextPosition,
},
});
// TODO: send webhooks
});
}
@ -72,13 +90,18 @@ module.exports = {
inputs.request,
);
await sails.helpers.utils.sendWebhook.with({
event: 'TASK_UPDATE',
data: task,
projectId: inputs.board.projectId,
user: inputs.request.currentUser,
card: values.card,
board: inputs.board,
sails.helpers.utils.sendWebhooks.with({
event: 'taskUpdate',
data: {
item: task,
included: {
projects: [inputs.project],
boards: [inputs.board],
lists: [inputs.list],
cards: [inputs.card],
},
},
user: inputs.actorUser,
});
}

View file

@ -27,6 +27,10 @@ module.exports = {
custom: valuesValidator,
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: {
type: 'ref',
},
@ -84,21 +88,12 @@ module.exports = {
);
});
/* The user could be created manually by an user or via OIDC. We hijack the id field, so one can differentiate between the two on the webhook side. */
let initiator;
if (inputs.request && inputs.request.currentUser) {
initiator = inputs.request.currentUser;
} else {
initiator = {
id: 'oidc',
};
}
await sails.helpers.utils.sendWebhook.with({
event: 'USER_CREATE',
data: { ...user, password: undefined },
projectId: '',
user: initiator,
sails.helpers.utils.sendWebhooks.with({
event: 'userCreate',
data: {
item: user,
},
user: inputs.actorUser,
});
return user;

View file

@ -4,6 +4,10 @@ module.exports = {
type: 'ref',
required: true,
},
actorUser: {
type: 'ref',
required: true,
},
request: {
type: 'ref',
},
@ -60,11 +64,12 @@ module.exports = {
);
});
await sails.helpers.utils.sendWebhook.with({
event: 'USER_DELETE',
data: { ...user, password: undefined },
projectId: '',
user: inputs.request.currentUser,
sails.helpers.utils.sendWebhooks.with({
event: 'userDelete',
data: {
item: user,
},
user: inputs.actorUser,
});
}

View file

@ -88,8 +88,11 @@ module.exports = {
// Otherwise, create a new user.
if (!user) {
user = await sails.helpers.users
.createOne(values)
user = await sails.helpers.users.createOne
.with({
values,
actorUser: User.OIDC,
})
.intercept('usernameAlreadyInUse', 'usernameAlreadyInUse');
}
@ -115,8 +118,12 @@ module.exports = {
}
if (Object.keys(updateValues).length > 0) {
user = await sails.helpers.users
.updateOne(user, updateValues, {}) // FIXME: hack for last parameter
user = await sails.helpers.users.updateOne
.with({
record: user,
values: updateValues,
actorUser: User.OIDC,
})
.intercept('emailAlreadyInUse', 'emailAlreadyInUse')
.intercept('usernameAlreadyInUse', 'usernameAlreadyInUse');
}

View file

@ -38,7 +38,7 @@ module.exports = {
custom: valuesValidator,
required: true,
},
user: {
actorUser: {
type: 'ref',
required: true,
},
@ -62,14 +62,14 @@ module.exports = {
let isOnlyPasswordChange = false;
if (!_.isUndefined(values.password)) {
if (Object.keys(values).length === 1) {
isOnlyPasswordChange = true;
}
Object.assign(values, {
password: bcrypt.hashSync(values.password, 10),
passwordChangedAt: new Date().toISOString(),
});
if (Object.keys(values).length === 1) {
isOnlyPasswordChange = true;
}
}
if (values.username) {
@ -118,7 +118,7 @@ module.exports = {
inputs.request,
);
if (user.id === inputs.user.id && inputs.request && inputs.request.isSocket) {
if (user.id === inputs.actorUser.id && inputs.request && inputs.request.isSocket) {
const tempRoom = uuid();
sails.sockets.addRoomMembersToRooms(`@user:${user.id}`, tempRoom, () => {
@ -153,14 +153,15 @@ module.exports = {
inputs.request,
);
});
}
await sails.helpers.utils.sendWebhook.with({
event: 'USER_UPDATE',
data: { ...user, password: undefined },
projectId: '',
user: inputs.request.currentUser,
});
sails.helpers.utils.sendWebhooks.with({
event: 'userUpdate',
data: {
item: user,
},
user: inputs.actorUser,
});
}
}
return user;

View file

@ -0,0 +1,14 @@
module.exports = {
sync: true,
inputs: {
record: {
type: 'ref',
required: true,
},
},
fn(inputs) {
return inputs.record.toJSON ? inputs.record.toJSON() : inputs.record;
},
};

View file

@ -1,4 +1,6 @@
module.exports = {
// TODO: make sync?
inputs: {
to: {
type: 'string',

View file

@ -1,6 +1,8 @@
const POST_MESSAGE_API_URL = 'https://slack.com/api/chat.postMessage';
module.exports = {
// TODO: make sync?
inputs: {
markdown: {
type: 'string',

View file

@ -1,115 +0,0 @@
const EVENT_TYPES = {
ACTION_CREATE: 'action_create',
ACTION_UPDATE: 'action_update',
ACTION_DELETE: 'action_delete',
CARD_CREATE: 'card_create',
CARD_UPDATE: 'card_update',
CARD_DELETE: 'card_delete',
CARD_MEMBERSHIP_CREATE: 'card_membership_create',
CARD_MEMBERSHIP_DELETE: 'card_membership_delete',
LIST_CREATE: 'list_create',
LIST_UPDATE: 'list_update',
LIST_DELETE: 'list_delete',
BOARD_CREATE: 'board_create',
BOARD_UPDATE: 'board_update',
BOARD_DELETE: 'board_delete',
ATTACHMENT_CREATE: 'attachment_create',
ATTACHMENT_UPDATE: 'attachment_update',
ATTACHMENT_DELETE: 'attachment_delete',
PROJECT_CREATE: 'project_create',
PROJECT_UPDATE: 'project_update',
PROJECT_DELETE: 'project_delete',
TASK_CREATE: 'task_create',
TASK_UPDATE: 'task_update',
TASK_DELETE: 'task_delete',
USER_CREATE: 'user_create',
USER_UPDATE: 'user_update',
USER_DELETE: 'user_delete',
};
/**
* Sends a webhook notification to a configured URL.
*
* @param {Object} inputs - Data to include in the webhook payload.
* @param {string} inputs.event - The event type (see {@link EVENT_TYPES}).
* @param {*} inputs.data - The actual data related to the event.
* @param {string} inputs.projectId - The project ID associated with the event.
* @param {ref} [inputs.user] - Optional user object associated with the event.
* @param {ref} [inputs.card] - Optional card object associated with the event.
* @param {ref} [inputs.board] - Optional board object associated with the event.
* @param {ref} [inputs.list] - Optional list object associated with the event.
* @returns {Promise<void>}
*/
async function sendWebhook(inputs) {
const url = sails.config.custom.webhookUrl;
const headers = {
'Content-Type': 'application/json',
};
if (sails.config.custom.webhookBearer) {
headers.Authorization = `Bearer ${sails.config.custom.webhookBearer}`;
}
const body = JSON.stringify({
...inputs,
user: {
...inputs.user,
password: undefined,
},
});
const req = await fetch(url, {
method: 'POST',
headers,
body,
});
if (req.status !== 200) {
sails.log.error(`Webhook failed with status ${req.status} and message: ${await req.text()}`);
}
}
module.exports = {
eventTypes: EVENT_TYPES,
inputs: {
event: {
type: 'string',
isIn: Object.keys(EVENT_TYPES),
required: true,
},
data: {
type: 'ref',
required: true,
},
projectId: {
type: 'string',
default: '',
},
user: {
type: 'ref',
},
card: {
type: 'ref',
},
board: {
type: 'ref',
},
list: {
type: 'ref',
},
},
async fn(inputs) {
if (!sails.config.custom.webhookUrl) return;
try {
await sendWebhook(inputs);
} catch (err) {
sails.log.error(err);
}
},
};

View file

@ -0,0 +1,103 @@
const jsonifyData = (data) => {
const nextData = {};
if (data.item) {
nextData.item = sails.helpers.utils.jsonifyRecord(data.item);
}
if (data.items) {
nextData.items = data.items.map((item) => sails.helpers.utils.jsonifyRecord(item));
}
if (data.included) {
nextData.included = Object.entries(data.included).reduce(
(result, [key, items]) => ({
...result,
[key]: items.map((item) => sails.helpers.utils.jsonifyRecord(item)),
}),
{},
);
}
return nextData;
};
/**
* Sends a webhook notification to a configured URL.
*
* @param {*} webhook - Webhook configuration.
* @param {string} event - The event (see {@link Events}).
* @param {*} data - The actual data related to the event.
* @param {ref} user - User object associated with the event.
* @returns {Promise<void>}
*/
async function sendWebhook(webhook, event, data, user) {
const headers = {
'Content-Type': 'application/json',
};
if (webhook.accessToken) {
headers.Authorization = `Bearer ${webhook.accessToken}`;
}
const body = JSON.stringify({
event,
data: jsonifyData(data),
user: sails.helpers.utils.jsonifyRecord(user),
});
const response = await fetch(webhook.url, {
headers,
body,
method: 'POST',
});
if (!response.ok) {
const message = await response.text();
sails.log.error(
`Webhook ${webhook.url} failed with status ${response.status} and message: ${message}`,
);
}
}
module.exports = {
sync: true,
inputs: {
event: {
type: 'string',
required: true,
},
data: {
type: 'ref',
required: true,
},
user: {
type: 'ref',
required: true,
},
},
fn(inputs) {
if (!sails.config.custom.webhooks) {
return;
}
sails.config.custom.webhooks.forEach((webhook) => {
if (!webhook.url) {
return;
}
if (webhook.excludedEvents && webhook.excludedEvents.includes(inputs.event)) {
return;
}
if (webhook.events && !webhook.events.includes(inputs.event)) {
return;
}
sendWebhook(webhook, inputs.event, inputs.data, inputs.user);
});
},
};

View file

@ -5,7 +5,13 @@
* @docs :: https://sailsjs.com/docs/concepts/models-and-orm/models
*/
const OIDC = {
id: '_oidc',
};
module.exports = {
OIDC,
attributes: {
// ╔═╗╦═╗╦╔╦╗╦╔╦╗╦╦ ╦╔═╗╔═╗
// ╠═╝╠╦╝║║║║║ ║ ║╚╗╔╝║╣ ╚═╗

View file

@ -60,9 +60,8 @@ module.exports.custom = {
smtpPassword: process.env.SMTP_PASSWORD,
smtpFrom: process.env.SMTP_FROM,
webhooks: JSON.parse(process.env.WEBHOOKS), // TODO: validate structure
slackBotToken: process.env.SLACK_BOT_TOKEN,
slackChannelId: process.env.SLACK_CHANNEL_ID,
webhookUrl: process.env.WEBHOOK_URL,
webhookBearer: process.env.WEBHOOK_BEARER,
};