diff --git a/docker-compose.yml b/docker-compose.yml index 51ac55cd..f7935aa1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -31,14 +31,6 @@ services: # - DEFAULT_ADMIN_NAME=Demo Demo # - DEFAULT_ADMIN_USERNAME=demo - # Email Notifications (https://nodemailer.com/smtp/) - # - SMTP_HOST= - # - SMTP_PORT=587 - # - SMTP_SECURE=true - # - SMTP_USER= - # - SMTP_PASSWORD= - # - SMTP_FROM="Demo Demo" - # - OIDC_ISSUER= # - OIDC_CLIENT_ID= # - OIDC_CLIENT_SECRET= @@ -51,6 +43,17 @@ services: # - OIDC_IGNORE_USERNAME=true # - OIDC_IGNORE_ROLES=true # - OIDC_ENFORCED=true + + # Email Notifications (https://nodemailer.com/smtp/) + # - SMTP_HOST= + # - SMTP_PORT=587 + # - SMTP_SECURE=true + # - SMTP_USER= + # - SMTP_PASSWORD= + # - SMTP_FROM="Demo Demo" + + # - SLACK_BOT_TOKEN= + # - SLACK_CHANNEL_ID= depends_on: postgres: condition: service_healthy diff --git a/server/.env.sample b/server/.env.sample index fb5a8288..15d66ed7 100644 --- a/server/.env.sample +++ b/server/.env.sample @@ -22,14 +22,6 @@ SECRET_KEY=notsecretkey # DEFAULT_ADMIN_NAME=Demo Demo # DEFAULT_ADMIN_USERNAME=demo -# Email Notifications (https://nodemailer.com/smtp/) -# SMTP_HOST= -# SMTP_PORT=587 -# SMTP_SECURE=true -# SMTP_USER= -# SMTP_PASSWORD= -# SMTP_FROM="Demo Demo" - # OIDC_ISSUER= # OIDC_CLIENT_ID= # OIDC_CLIENT_SECRET= @@ -43,6 +35,17 @@ SECRET_KEY=notsecretkey # OIDC_IGNORE_ROLES=true # OIDC_ENFORCED=true +# Email Notifications (https://nodemailer.com/smtp/) +# SMTP_HOST= +# SMTP_PORT=587 +# SMTP_SECURE=true +# SMTP_USER= +# SMTP_PASSWORD= +# SMTP_FROM="Demo Demo" + +# SLACK_BOT_TOKEN= +# SLACK_CHANNEL_ID= + ## Do not edit this TZ=UTC diff --git a/server/api/controllers/cards/delete.js b/server/api/controllers/cards/delete.js index e2b6d00b..dad82de1 100755 --- a/server/api/controllers/cards/delete.js +++ b/server/api/controllers/cards/delete.js @@ -47,6 +47,7 @@ module.exports = { card = await sails.helpers.cards.deleteOne.with({ record: card, + user: currentUser, request: this.req, }); diff --git a/server/api/helpers/actions/create-one.js b/server/api/helpers/actions/create-one.js index 78d5e0c5..b03adfdc 100644 --- a/server/api/helpers/actions/create-one.js +++ b/server/api/helpers/actions/create-one.js @@ -14,6 +14,30 @@ const valuesValidator = (value) => { return true; }; +const buildAndSendSlackMessage = async (user, card, action) => { + 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}*`; + + break; + case Action.Types.MOVE_CARD: + markdown = `${cardLink} was moved by ${user.name} to *${action.data.toList.name}*`; + + break; + case Action.Types.COMMENT_CARD: + markdown = `*${user.name}* commented on ${cardLink}:\n>${action.data.text}`; + + break; + default: + return; + } + + await sails.helpers.utils.sendSlackMessage(markdown); +}; + module.exports = { inputs: { values: { @@ -67,6 +91,10 @@ module.exports = { ), ); + if (sails.config.custom.slackBotToken) { + buildAndSendSlackMessage(values.user, values.card, action); + } + return action; }, }; diff --git a/server/api/helpers/cards/delete-one.js b/server/api/helpers/cards/delete-one.js index fb72787f..a947f738 100644 --- a/server/api/helpers/cards/delete-one.js +++ b/server/api/helpers/cards/delete-one.js @@ -1,9 +1,17 @@ +const buildAndSendSlackMessage = async (user, card) => { + await sails.helpers.utils.sendSlackMessage(`*${card.name}* was deleted by ${user.name}`); +}; + module.exports = { inputs: { record: { type: 'ref', required: true, }, + user: { + type: 'ref', + required: true, + }, request: { type: 'ref', }, @@ -21,6 +29,10 @@ module.exports = { }, inputs.request, ); + + if (sails.config.custom.slackBotToken) { + buildAndSendSlackMessage(inputs.user, card); + } } return card; diff --git a/server/api/helpers/notifications/create-one.js b/server/api/helpers/notifications/create-one.js index 271a9179..f24e4bfa 100644 --- a/server/api/helpers/notifications/create-one.js +++ b/server/api/helpers/notifications/create-one.js @@ -27,6 +27,7 @@ const buildAndSendEmail = async (user, board, card, action, notifiableUser) => { `from ${action.data.fromList.name} to ${action.data.toList.name} ` + `on ${board.name}

`, }; + break; case Action.Types.COMMENT_CARD: emailData = { @@ -37,6 +38,7 @@ const buildAndSendEmail = async (user, board, card, action, notifiableUser) => { `on ${board.name}

` + `

${action.data.text}

`, }; + break; default: return; diff --git a/server/api/helpers/utils/send-email.js b/server/api/helpers/utils/send-email.js index 5d9be724..7b8d08bf 100644 --- a/server/api/helpers/utils/send-email.js +++ b/server/api/helpers/utils/send-email.js @@ -25,7 +25,7 @@ module.exports = { sails.log.info('Email sent: %s', info.messageId); } catch (error) { - sails.log.error(error); + sails.log.error(error); // TODO: provide description text? } }, }; diff --git a/server/api/helpers/utils/send-slack-message.js b/server/api/helpers/utils/send-slack-message.js new file mode 100644 index 00000000..573fcf87 --- /dev/null +++ b/server/api/helpers/utils/send-slack-message.js @@ -0,0 +1,53 @@ +const POST_MESSAGE_API_URL = 'https://slack.com/api/chat.postMessage'; + +module.exports = { + inputs: { + markdown: { + type: 'string', + required: true, + }, + }, + + async fn(inputs) { + const headers = { + Authorization: `Bearer ${sails.config.custom.slackBotToken}`, + 'Content-Type': 'application/json; charset=utf-8', + }; + + const body = { + blocks: [ + { + type: 'section', + text: { + type: 'mrkdwn', + text: inputs.markdown, + }, + }, + ], + channel: sails.config.custom.slackChannelId, + }; + + let response; + try { + response = await fetch(POST_MESSAGE_API_URL, { + headers, + method: 'POST', + body: JSON.stringify(body), + }); + } catch (error) { + sails.log.error(error); // TODO: provide description text? + return; + } + + if (!response.ok) { + sails.log.error('Error sending to Slack: %s', response.error); + return; + } + + const responseJson = await response.json(); + + if (!responseJson.ok) { + sails.log.error('Error sending to Slack: %s', responseJson.error); + } + }, +}; diff --git a/server/config/custom.js b/server/config/custom.js index 76473245..ac344d05 100644 --- a/server/config/custom.js +++ b/server/config/custom.js @@ -34,13 +34,6 @@ module.exports.custom = { defaultAdminEmail: process.env.DEFAULT_ADMIN_EMAIL && process.env.DEFAULT_ADMIN_EMAIL.toLowerCase(), - smtpHost: process.env.SMTP_HOST, - smtpPort: process.env.SMTP_PORT || 587, - smtpSecure: process.env.SMTP_SECURE === 'true', - smtpUser: process.env.SMTP_USER, - smtpPassword: process.env.SMTP_PASSWORD, - smtpFrom: process.env.SMTP_FROM, - oidcIssuer: process.env.OIDC_ISSUER, oidcClientId: process.env.OIDC_CLIENT_ID, oidcClientSecret: process.env.OIDC_CLIENT_SECRET, @@ -58,4 +51,14 @@ module.exports.custom = { oidcRedirectUri: `${ sails.config.environment === 'production' ? process.env.BASE_URL : 'http://localhost:3000' }/oidc-callback`, + + smtpHost: process.env.SMTP_HOST, + smtpPort: process.env.SMTP_PORT || 587, + smtpSecure: process.env.SMTP_SECURE === 'true', + smtpUser: process.env.SMTP_USER, + smtpPassword: process.env.SMTP_PASSWORD, + smtpFrom: process.env.SMTP_FROM, + + slackBotToken: process.env.SLACK_BOT_TOKEN, + slackChannelId: process.env.SLACK_CHANNEL_ID, };