Merge branch 'hotfix/21.56.9' into master
[gitter.git] / server / api / private / avatars / index.js
blob395010602ebc4e2553998d479b074ef6833ba4ad
1 'use strict';
3 var express = require('express');
4 var identifyRoute = require('gitter-web-env').middlewares.identifyRoute;
5 var router = express.Router({ caseSensitive: true, mergeParams: true });
6 var fixMongoIdQueryParam = require('../../../web/fix-mongo-id-query-param');
7 var Promise = require('bluebird');
8 var request = Promise.promisify(require('request'));
9 var cdn = require('gitter-web-cdn');
10 var avatars = require('gitter-web-avatars');
11 var githubUserByUsernameVersioned = require('./github-user-by-username-versioned');
12 var githubUserByUsername = require('./github-user-by-username');
13 var gravatarByEmail = require('./gravatar-by-email');
14 var gravatarByHash = require('./gravatar-by-hash');
15 var twitterByIds = require('./twitter-by-ids');
16 const groupAvatars = require('gitter-web-groups/lib/group-avatars');
17 var userByUsername = require('./user-by-username');
19 var DEFAULT_SIZE = 128;
20 var MISSING_IMAGE_CONTENT_TYPE = 'image/png';
22 function sendMissing(req, res) {
23   // If the nginx image proxy is sitting in front of the app
24   // use that
25   if (req.headers['x-avatar-server']) {
26     res.set('X-Accel-Redirect', '/missing');
27     res.set('Content-Type', MISSING_IMAGE_CONTENT_TYPE);
28     res.status(200).end();
29   } else {
30     res.redirect(avatars.getDefault());
31     res.status(200).end();
32   }
35 function sendAvatar(callback) {
36   return function(req, res, next) {
37     return Promise.try(function() {
38       var size = (req.query.s && parseInt(req.query.s, 10)) || DEFAULT_SIZE;
39       return callback(req, size);
40     })
41       .then(function(response) {
42         if (!response) {
43           return sendMissing(req, res);
44         }
46         var url = response.url;
47         var longTermCachable = response.longTermCachable;
49         if (!url) {
50           return sendMissing(req, res);
51         }
53         // If the nginx image proxy is sitting in front of the app
54         // use that
55         if (req.headers['x-avatar-server']) {
56           if (longTermCachable) {
57             res.set('X-Accel-Redirect', '/fetch_lt/' + url);
58           } else {
59             res.set('X-Accel-Redirect', '/fetch/' + url);
60           }
61           res.send('OK');
62           return;
63         }
65         // No image proxy, in the development environment
66         if (longTermCachable) {
67           res.set('Cache-Control', 'max-age=2592000'); // TODO: add more here
68         } else {
69           res.set('Cache-Control', 'max-age=3600'); // TODO: add more here
70         }
72         res.redirect(url);
73         return;
74       })
75       .catch(next);
76   };
79 /**
80  * Default for development environment
81  */
82 router.get(
83   '/default',
84   identifyRoute('api-private-avatar-default'),
85   sendAvatar(function(/*req, size*/) {
86     return {
87       url: cdn('images/default-avatar.png'),
88       longTermCachable: true
89     };
90   })
93 /**
94  * Group Avatars, by ID
95  */
96 router.get(
97   '/group/i/:groupId',
98   identifyRoute('api-private-avatar-group-id'),
99   sendAvatar(async (req, size) => {
100     var groupId = fixMongoIdQueryParam(req.params.groupId);
101     if (!groupId) return null;
103     const avatarUrl = await groupAvatars.getAvatarUrlForGroupId(groupId, size);
104     if (!avatarUrl) return null;
106     return {
107       url: avatarUrl,
108       longTermCachable: false
109     };
110   })
114  * Group Avatars, by ID (versioned)
115  */
116 router.get(
117   '/group/iv/:version/:groupId',
118   identifyRoute('api-private-avatar-group-id-versioned'),
119   sendAvatar(async (req, size) => {
120     // Ignore the version it's only used as a cache-buster
121     var groupId = fixMongoIdQueryParam(req.params.groupId);
122     if (!groupId) return null;
124     const avatarUrl = await groupAvatars.getAvatarUrlForGroupId(groupId, size);
125     if (!avatarUrl) return null;
127     return {
128       url: avatarUrl,
129       longTermCachable: true
130     };
131   })
134 /* Case sensitive */
135 router.get(
136   '/g/u/:username',
137   identifyRoute('api-private-avatar-gitter-username'),
138   sendAvatar(function(req, size) {
139     var username = req.params.username;
140     if (!username) return null;
142     return userByUsername(username, size);
143   })
146 router.get(
147   '/gravatar/e/:email',
148   identifyRoute('api-private-avatar-gravatar'),
149   sendAvatar(function(req, size) {
150     var email = req.params.email;
151     if (!email) return null;
153     return gravatarByEmail(email, size);
154   })
157 router.get(
158   '/gravatar/m/:md5',
159   identifyRoute('api-private-avatar-checksum'),
160   sendAvatar(function(req, size) {
161     var md5 = req.params.md5;
162     if (!md5) return null;
164     return gravatarByHash(md5, size);
165   })
168 router.get(
169   '/tw/i/:id/:filename',
170   identifyRoute('api-private-avatar-twitter'),
171   // NOTE: it doesn't support a size param yet
172   sendAvatar(function(req) {
173     var id = req.params.id;
174     var filename = req.params.filename;
176     if (!id || !filename) return null;
178     return twitterByIds(id, filename);
179   })
182 router.get(
183   '/gl/u/:username',
184   identifyRoute('api-private-gitlab-username'),
185   sendAvatar(function(req) {
186     const { username } = req.params;
187     if (!username) return null;
189     // Gravatar or https://gitlab.com/uploads/-/system/user/avatar/:userid/avatar.png
190     return request({
191       method: 'GET',
192       uri: 'https://gitlab.com/api/v4/users?username=' + encodeURIComponent(username),
193       json: true
194     }).then(res => {
195       return {
196         url: res.body.avatar_url,
197         longTermCachable: false
198       };
199     });
200   })
204  * Only used in DEV. Otherwise nginx handles this route
205  */
206 router.get(
207   '/gh/u/:username',
208   identifyRoute('api-private-github-username'),
209   sendAvatar(function(req, size) {
210     var username = req.params.username;
211     if (!username) return null;
213     return githubUserByUsername(username, size);
214   })
218  * Only used in DEV. Otherwise nginx handles this route
219  */
220 router.get(
221   '/gh/uv/:version/:username',
222   identifyRoute('api-private-github-versioned-username'),
223   sendAvatar(function(req, size) {
224     var username = req.params.username;
225     var version = req.params.version;
226     if (!username) return null;
228     if (!version) {
229       return githubUserByUsername(username, size);
230     }
232     return githubUserByUsernameVersioned(username, version, size);
233   })
236 /* Default route for anything else on the avatar server */
237 router.use(
238   identifyRoute('api-private-missing-avatar'),
239   sendAvatar(function() {
240     return null;
241   })
244 module.exports = router;