3 var env
= require('gitter-web-env');
4 var logger
= env
.logger
;
6 var config
= env
.config
;
8 var express
= require('express');
9 var request
= require('request');
10 var StatusError
= require('statuserror');
11 var jwt
= require('jwt-simple');
12 const asyncHandler
= require('express-async-handler');
14 var userSettingsService
= require('gitter-web-user-settings');
15 var cdn
= require('gitter-web-cdn');
16 var services
= require('@gitterhq/services');
17 var debug
= require('debug')('gitter:app:settings-route');
18 var userScopes
= require('gitter-web-identity/lib/user-scopes');
19 var fonts
= require('../web/fonts');
20 var acceptInviteService
= require('../services/accept-invite-service');
21 var loginUtils
= require('../web/login-utils');
22 var resolveRoomUri
= require('../utils/resolve-room-uri');
23 const unsubscribeHashes
= require('gitter-web-email-notifications/lib/unsubscribe-hashes');
25 var identifyRoute
= env
.middlewares
.identifyRoute
;
26 var ensureLoggedIn
= require('../web/middlewares/ensure-logged-in');
27 var uriContextResolverMiddleware
= require('./uri-context/uri-context-resolver-middleware');
28 var preventClickjackingMiddleware
= require('../web/middlewares/prevent-clickjacking');
29 var preventClickjackingOnlyGitterEmbedMiddleware
= require('../web/middlewares/prevent-clickjacking-only-gitter-embed');
31 var supportedServices
= [
32 { id
: 'github', name
: 'GitHub' },
33 { id
: 'bitbucket', name
: 'BitBucket' },
34 { id
: 'trello', name
: 'Trello' }
37 var openServices
= Object
.keys(services
).map(function(id
) {
40 name
: services
[id
].name
44 var serviceIdNameMap
= supportedServices
.concat(openServices
).reduce(function(map
, service
) {
45 map
[service
.id
] = service
.name
;
49 function getIntegrations(req
, res
, next
) {
50 debug('Get integrations for %s', req
.troupe
.url
);
52 var url
= config
.get('webhooks:basepath') + '/troupes/' + req
.troupe
.id
+ '/hooks';
58 function(err
, resp
, hooks
) {
59 if (err
|| resp
.statusCode
!== 200 || !Array
.isArray(hooks
)) {
60 logger
.error('failed to fetch hooks for troupe', {
65 return next(new StatusError(500, 'Unable to perform request. Please try again later.'));
68 hooks
.forEach(function(hook
) {
69 hook
.serviceDisplayName
= serviceIdNameMap
[hook
.service
];
72 res
.render('integrations', {
75 accessToken
: req
.accessToken
,
77 supportedServices
: supportedServices
,
78 openServices
: openServices
,
79 fonts
: fonts
.getFonts(),
80 hasCachedFonts
: fonts
.hasCachedFonts(req
.cookies
)
86 function deleteIntegration(req
, res
, next
) {
87 debug('Delete integration %s for %s', req
.body
.id
, req
.troupe
.url
);
91 url
: config
.get('webhooks:basepath') + '/troupes/' + req
.troupe
.id
+ '/hooks/' + req
.body
.id
,
95 if (err
|| resp
.statusCode
!== 200) {
96 logger
.error('failed to delete hook for troupe', { exception
: err
, resp
: resp
});
97 return next(new StatusError(500, 'Unable to perform request. Please try again later.'));
100 res
.redirect('/settings/integrations/' + req
.troupe
.uri
);
105 function createIntegration(req
, res
, next
) {
106 debug('Create integration for %s', req
.body
.service
, req
.troupe
.url
);
110 url
: config
.get('webhooks:basepath') + '/troupes/' + req
.troupe
.id
+ '/hooks',
112 service
: req
.body
.service
,
117 function(err
, resp
, body
) {
118 if (err
|| resp
.statusCode
!== 200 || !body
) {
119 logger
.error('failed to create hook for troupe', { exception
: err
, resp
: resp
});
120 return next(new StatusError(500, 'Unable to perform request. Please try again later.'));
123 var encryptedUserToken
;
125 // Pass through the token if we have write access
126 // TODO: deal with private repos too
127 if (userScopes
.hasGitHubScope(req
.user
, 'public_repo')) {
128 encryptedUserToken
= jwt
.encode(
129 userScopes
.getGitHubToken(req
.user
, 'public_repo'),
130 config
.get('integrations:secret')
133 encryptedUserToken
= '';
137 body
.configurationURL
+
143 config
.get('web:basepath') +
150 const adminAccessCheck
= asyncHandler(async (req
, res
, next
) => {
151 const uriContext
= req
.uriContext
;
152 const policy
= uriContext
.policy
;
154 const access
= await policy
.canAdmin();
155 if (!access
) throw new StatusError(403);
160 var router
= express
.Router({ caseSensitive
: true, mergeParams
: true });
163 '/integrations/:roomPart1',
164 '/integrations/:roomPart1/:roomPart2',
165 '/integrations/:roomPart1/:roomPart2/:roomPart3'
166 ].forEach(function(uri
) {
167 router
.use(uri
, function(req
, res
, next
) {
168 // Shitty method override because the integrations page
169 // doesn't use javascript and relies on forms aka, the web as of 1996.
170 var _method
= req
.body
&& req
.body
._method
? '' + req
.body
._method
: '';
171 if (req
.method
=== 'POST' && _method
.toLowerCase() === 'delete') {
172 req
.method
= 'DELETE';
180 preventClickjackingOnlyGitterEmbedMiddleware
,
181 identifyRoute('settings-room-get'),
182 uriContextResolverMiddleware
,
190 preventClickjackingOnlyGitterEmbedMiddleware
,
191 identifyRoute('settings-room-delete'),
192 uriContextResolverMiddleware
,
200 preventClickjackingOnlyGitterEmbedMiddleware
,
201 identifyRoute('settings-room-create'),
202 uriContextResolverMiddleware
,
209 '/accept-invite/:secret',
210 identifyRoute('settings-accept-invite'),
212 preventClickjackingMiddleware
,
213 function(req
, res
, next
) {
214 var secret
= req
.params
.secret
;
215 return acceptInviteService
216 .acceptInvite(req
.user
, secret
, { source
: req
.query
.source
})
217 .then(function(room
) {
218 return resolveRoomUri(room
, req
.user
._id
);
220 .then(function(roomUri
) {
221 var encodedUri
= encodeURI(roomUri
);
222 res
.relativeRedirect(encodedUri
);
224 .catch(StatusError
, function(err
) {
225 if (err
.status
>= 500) throw err
;
228 var events
= req
.session
.events
;
231 req
.session
.events
= events
;
233 events
.push('invite_failed');
235 // TODO: tell the user why they could not get invited
237 logger
.error('Unable to use invite', {
238 username
: req
.user
&& req
.user
.username
,
241 return loginUtils
.whereToNext(req
.user
).then(function(next
) {
242 res
.relativeRedirect(next
);
250 '/unsubscribe/:hash',
251 identifyRoute('settings-unsubscribe'),
252 preventClickjackingMiddleware
,
253 function(req
, res
, next
) {
255 let notificationType
;
257 ({ userId
, notificationType
} = unsubscribeHashes
.decipherHash(req
.params
.hash
));
259 return next(new StatusError(400, 'Invalid hash'));
262 debug('User %s opted-out from ', userId
, notificationType
);
263 stats
.event('unsubscribed_unread_notifications', { userId
: userId
});
266 .setUserSettings(userId
, 'unread_notifications_optout', 1)
268 var msg
= "Done. You won't receive notifications like that one in the future.";
270 res
.render('unsubscribe', { layout
: 'generic-layout', title
: 'Unsubscribe', msg
: msg
});
279 preventClickjackingMiddleware
,
280 identifyRoute('settings-badger-optout'),
281 function(req
, res
, next
) {
282 var userId
= req
.user
.id
;
284 logger
.info('User ' + userId
+ ' opted-out from auto badgers');
285 stats
.event('optout_badger', { userId
: userId
});
287 return userSettingsService
288 .setUserSettings(userId
, 'badger_optout', 1)
290 var msg
= "Done. We won't send you automatic pull-requests in future.";
292 res
.render('unsubscribe', {
293 layout
: 'generic-layout',
302 module
.exports
= router
;