Skip to content

Commit 7c8e8e3

Browse files
authored
Replace slack notifications with mattermost (#664)
1 parent 447796e commit 7c8e8e3

File tree

8 files changed

+85
-35
lines changed

8 files changed

+85
-35
lines changed

config/config.example.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@
2727
// If not specified, Slack integration is disabled
2828
// No default
2929
"slack_url": "https://hooks.slack.com/services/A1...7/Z.....K/r....g",
30+
// A valid Mattermost incoming webhook url. Used for sending restart and status messages
31+
// If not specified, Mattermost integration is disabled
32+
// No default
33+
"mattermost_url": "https://your-mattermost-server.com/hooks/xxx-generatedkey-xxx",
3034
// Users with this role are allowed to management access to the API
3135
// Default: "admin"
3236
"management_role": "manager",

config/default.js

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,30 +7,31 @@
77
const defer = require('config/defer').deferConfig;
88

99
const defaults = {
10-
'base_domain': 'localhost',
11-
'protocol': 'https',
12-
'port': 8000,
13-
'api_url': '', // if not set, generated from api_protocol and api_base_domain
14-
'honeybadger_apikey': '',
15-
'slack_url': '',
16-
'management_role': 'admin',
17-
'routes': {
18-
'boxes': '/boxes',
19-
'users': '/users',
20-
'statistics': '/statistics',
21-
'management': '/management'
10+
base_domain: 'localhost',
11+
protocol: 'https',
12+
port: 8000,
13+
api_url: '', // if not set, generated from api_protocol and api_base_domain
14+
honeybadger_apikey: '',
15+
slack_url: '',
16+
mattermost_url: '',
17+
management_role: 'admin',
18+
routes: {
19+
boxes: '/boxes',
20+
users: '/users',
21+
statistics: '/statistics',
22+
management: '/management',
2223
},
23-
'jwt': {
24-
'secret': 'OH GOD THIS IS SO INSECURE PLS CHANGE ME', // should be at least 32 characters
25-
'algorithm': 'HS256',
26-
'validity_ms': 3600000, // 1 hour
27-
'issuer': '' // usually the base url of the api. generated if not set from api_protocol and api_base_domain. for example https://api.opensensemap.org
24+
jwt: {
25+
secret: 'OH GOD THIS IS SO INSECURE PLS CHANGE ME', // should be at least 32 characters
26+
algorithm: 'HS256',
27+
validity_ms: 3600000, // 1 hour
28+
issuer: '', // usually the base url of the api. generated if not set from api_protocol and api_base_domain. for example https://api.opensensemap.org
29+
},
30+
refresh_token: {
31+
secret: 'I ALSO WANT TO BE CHANGED',
32+
algorithm: 'sha256',
33+
validity_ms: 604800000, // 1 week
2834
},
29-
'refresh_token': {
30-
'secret': 'I ALSO WANT TO BE CHANGED',
31-
'algorithm': 'sha256',
32-
'validity_ms': 604800000 // 1 week
33-
}
3435
};
3536

3637
// computed keys

packages/api/app.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const
1515
restify = require('restify'),
1616
{ fullResponse, queryParser, jsonBodyParser, pre: { sanitizePath } } = restify.plugins,
1717
config = require('config'),
18-
{ preRequest, preCors, Honeybadger, getVersion, postToSlack } = require('./lib/helpers/apiUtils'),
18+
{ preRequest, preCors, Honeybadger, getVersion, postToMattermost } = require('./lib/helpers/apiUtils'),
1919
routes = require('./lib/routes'),
2020
bunyan = require('bunyan');
2121

@@ -48,7 +48,7 @@ db.connect()
4848
// start the server
4949
server.listen(Number(config.get('port')), function () {
5050
log.info(`${server.name} listening at ${server.url}`);
51-
postToSlack(`openSenseMap API started. Version: ${getVersion}`);
51+
postToMattermost(`openSenseMap API started. Version: ${getVersion}`);
5252
});
5353
})
5454
.catch(function (err) {

packages/api/lib/controllers/boxesController.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838

3939
const
4040
{ Box, User, Claim } = require('@sensebox/opensensemap-api-models'),
41-
{ addCache, clearCache, checkContentType, redactEmail, postToSlack } = require('../helpers/apiUtils'),
41+
{ addCache, clearCache, checkContentType, redactEmail, postToMattermost } = require('../helpers/apiUtils'),
4242
{ point } = require('@turf/helpers'),
4343
classifyTransformer = require('../transformers/classifyTransformer'),
4444
{
@@ -411,7 +411,7 @@ const postNewBox = async function postNewBox (req, res, next) {
411411
newBox = await Box.populate(newBox, Box.BOX_SUB_PROPS_FOR_POPULATION);
412412
res.send(201, { message: 'Box successfully created', data: newBox });
413413
clearCache(['getBoxes', 'getStats']);
414-
postToSlack(`New Box: ${req.user.name} (${redactEmail(req.user.email)}) just registered "${newBox.name}" (${newBox.model}): <https://opensensemap.org/explore/${newBox._id}|link>`);
414+
postToMattermost(`New Box: ${req.user.name} (${redactEmail(req.user.email)}) just registered "${newBox.name}" (${newBox.model}): <https://opensensemap.org/explore/${newBox._id}|link>`);
415415
} catch (err) {
416416
handleError(err, next);
417417
}
@@ -481,7 +481,7 @@ const deleteBox = async function deleteBox (req, res, next) {
481481
const box = await req.user.removeBox(boxId);
482482
res.send({ code: 'Ok', message: 'box and all associated measurements marked for deletion' });
483483
clearCache(['getBoxes', 'getStats']);
484-
postToSlack(`Box deleted: ${req.user.name} (${redactEmail(req.user.email)}) just deleted "${box.name}" (${boxId})`);
484+
postToMattermost(`Box deleted: ${req.user.name} (${redactEmail(req.user.email)}) just deleted "${box.name}" (${boxId})`);
485485

486486
} catch (err) {
487487
handleError(err, next);

packages/api/lib/controllers/managementController.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use strict';
22

33
const { Box, User } = require('@sensebox/opensensemap-api-models'),
4-
{ clearCache, checkContentType, postToSlack } = require('../helpers/apiUtils'),
4+
{ clearCache, checkContentType, postToMattermost } = require('../helpers/apiUtils'),
55
{ NotFoundError, BadRequestError } = require('restify-errors'),
66
handleError = require('../helpers/errorHandler'),
77
{
@@ -96,7 +96,7 @@ const deleteBoxes = async function deleteBoxes (req, res, next) {
9696
const user = await User.findUserOfBox(boxId);
9797
await user.removeBox(boxId);
9898
clearCache(['getBoxes', 'getStats']);
99-
postToSlack(`Management Action: Box deleted: ${req.user.name} (${req.user.email}) just deleted ${boxIds.join(',')}`);
99+
postToMattermost(`Management Action: Box deleted: ${req.user.name} (${req.user.email}) just deleted ${boxIds.join(',')}`);
100100
}
101101
res.send({ boxIds });
102102
} catch (err) {
@@ -121,7 +121,7 @@ const updateBox = async function updateBox (req, res, next) {
121121
const user = await User.findUserOfBox(boxId);
122122
box.owner = user.toJSON({ includeSecrets: true });
123123

124-
postToSlack(`Management Action: Box updated: ${req.user.name} (${req.user.email}) just updated "${box.name}" (${box.model}): <https://opensensemap.org/explore/${box._id}|link>`);
124+
postToMattermost(`Management Action: Box updated: ${req.user.name} (${req.user.email}) just updated "${box.name}" (${box.model}): <https://opensensemap.org/explore/${box._id}|link>`);
125125
res.send({ code: 'Ok', data: box });
126126
clearCache(['getBoxes']);
127127
} catch (err) {
@@ -146,7 +146,7 @@ const updateUser = async function updateUser (req, res, next) {
146146

147147
await user.save();
148148

149-
postToSlack(`Management Action: User updated: ${req.user.name} (${req.user.email}) just updated "${user.name}" (${user.email})`);
149+
postToMattermost(`Management Action: User updated: ${req.user.name} (${req.user.email}) just updated "${user.name}" (${user.email})`);
150150
res.send({ code: 'Ok', data: user });
151151
} catch (err) {
152152
handleError(err, next);
@@ -164,7 +164,7 @@ const deleteUsers = async function deleteUsers (req, res, next) {
164164
userNames.push(`${user.name} (${user.email})`);
165165
await user.destroyUser({ sendMail: false });
166166
clearCache(['getBoxes', 'getStats']);
167-
postToSlack(`Management Action: User deleted: ${req.user.name} (${req.user.email}) just deleted ${userNames.join(',')}`);
167+
postToMattermost(`Management Action: User deleted: ${req.user.name} (${req.user.email}) just deleted ${userNames.join(',')}`);
168168
}
169169
res.send({ userIds });
170170
} catch (err) {

packages/api/lib/controllers/usersController.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
const { User } = require('@sensebox/opensensemap-api-models'),
44
{ InternalServerError, ForbiddenError } = require('restify-errors'),
5-
{ checkContentType, redactEmail, postToSlack, clearCache } = require('../helpers/apiUtils'),
5+
{ checkContentType, redactEmail, clearCache, postToMattermost } = require('../helpers/apiUtils'),
66
{ retrieveParameters } = require('../helpers/userParamHelpers'),
77
handleError = require('../helpers/errorHandler'),
88
{ createToken, refreshJwt, invalidateToken } = require('../helpers/jwtHelpers');
@@ -49,7 +49,7 @@ const registerUser = async function registerUser (req, res, next) {
4949
try {
5050
const newUser = await new User({ name, email, password, language })
5151
.save();
52-
postToSlack(`New User: ${newUser.name} (${redactEmail(newUser.email)})`);
52+
postToMattermost(`New User: ${newUser.name} (${redactEmail(newUser.email)})`);
5353

5454
try {
5555
const { token, refreshToken } = await createToken(newUser);
@@ -273,7 +273,7 @@ const deleteUser = async function deleteUser (req, res, next) {
273273
await req.user.destroyUser();
274274
res.send(200, { code: 'Ok', message: 'User and all boxes of user marked for deletion. Bye Bye!' });
275275
clearCache(['getBoxes', 'getStats']);
276-
postToSlack(`User deleted: ${req.user.name} (${redactEmail(req.user.email)})`);
276+
postToMattermost(`User deleted: ${req.user.name} (${redactEmail(req.user.email)})`);
277277
} catch (err) {
278278
handleError(err, next);
279279
}

packages/api/lib/helpers/apiUtils.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
'use strict';
22

3+
const escapeMarkdown = require('./escapeMarkdown');
4+
35
const { NotAuthorizedError, UnsupportedMediaTypeError } = require('restify-errors'),
46
config = require('config'),
57
apicache = require('apicache'),
@@ -128,6 +130,19 @@ const postToSlack = function postToSlack (text) {
128130
}
129131
};
130132

133+
const postToMattermost = function postToMattermost (text) {
134+
if (config.get('mattermost_url')) {
135+
text = `[${hostname}]: ${text}`;
136+
got
137+
.post(config.get('mattermost_url'), {
138+
json: { text: escapeMarkdown(text) },
139+
retry: 0,
140+
})
141+
// swallow errors, we don't care
142+
.catch(() => {});
143+
}
144+
};
145+
131146
const redactEmail = function redactEmail (email) {
132147
/* eslint-disable prefer-const */
133148
let [name = '', domain = ''] = email.split('@');
@@ -198,6 +213,7 @@ module.exports = {
198213
preCors,
199214
Honeybadger,
200215
postToSlack,
216+
postToMattermost,
201217
getVersion,
202218
redactEmail,
203219
createDownloadFilename,
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
'use strict';
2+
3+
const replacements = [
4+
[/\*/g, '\\*', 'asterisks'],
5+
[/#/g, '\\#', 'number signs'],
6+
[/\//g, '\\/', 'slashes'],
7+
[/\(/g, '\\(', 'parentheses'],
8+
[/\)/g, '\\)', 'parentheses'],
9+
[/\[/g, '\\[', 'square brackets'],
10+
[/\]/g, '\\]', 'square brackets'],
11+
[/</g, '&lt;', 'angle brackets'],
12+
[/>/g, '&gt;', 'angle brackets'],
13+
[/_/g, '\\_', 'underscores'],
14+
[/`/g, '\\`', 'codeblocks']
15+
];
16+
17+
const escapeMarkdown = function (string, skips) {
18+
skips = skips || [];
19+
20+
return replacements.reduce(function (string, replacement) {
21+
const name = replacement[2];
22+
23+
return name && skips.indexOf(name) !== -1
24+
? string
25+
: string.replace(replacement[0], replacement[1]);
26+
}, string);
27+
};
28+
29+
module.exports = escapeMarkdown;

0 commit comments

Comments
 (0)