1
0
Fork 0
mirror of https://github.com/plankanban/planka.git synced 2025-07-23 23:29: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 3779bdb053
commit c065566c15
96 changed files with 1280 additions and 509 deletions

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