Gitter migration: Setup redirects (rollout pt. 3)
[gitter.git] / modules / groups / lib / group-avatars.js
blobe0d5394815804191e79333a21038b199291dcd74
1 'use strict';
3 var env = require('gitter-web-env');
4 var errorReporter = env.errorReporter;
5 const logger = env.logger;
6 var Group = require('gitter-web-persistence').Group;
7 var url = require('url');
8 var mongoReadPrefs = require('gitter-web-persistence-utils/lib/mongo-read-prefs');
9 var updateGroupAvatar = require('./update-group-avatar');
10 var debug = require('debug')('gitter:app:groups:group-avatars');
11 const getGithubUsernameFromGroup = require('./get-github-username-from-group');
12 const isGitterInternalAvatarUrl = require('./is-gitter-internal-group-avatar-url');
13 const isGitlabSecurityDescriptorType = require('gitter-web-shared/is-gitlab-security-descriptor-type');
15 /**
16 * Check on avatars once a week. In future, we may bring this
17 * down or come up with an alternative method of looking
18 * this up.
20 var AVATAR_VERSION_CHECK_TIMEOUT = 7 * 86400 * 1000;
22 var KNOWN_AVATAR_SIZES = [22, 40, 44, 48, 64, 80, 96, 128];
24 // Just in case
25 KNOWN_AVATAR_SIZES.sort((a, b) => {
26 return a - b;
27 });
29 var SELECT_FIELDS = {
30 _id: 1,
31 avatarUrl: 1,
32 avatarVersion: 1,
33 avatarCheckedDate: 1,
34 'sd.type': 1,
35 'sd.linkPath': 1,
36 'sd.externalId': 1
39 /** Return the best size for the requested avatar size */
40 function getBestSizeFor(size) {
41 for (var i = 0; i < KNOWN_AVATAR_SIZES.length; i++) {
42 var currentSize = KNOWN_AVATAR_SIZES[i];
43 if (size <= currentSize) return currentSize;
46 return null;
49 /**
50 * Returns the optimal avatar url to return for the given size
52 function getGroupAvatarUrlForSize(group, size) {
53 const avatarUrl = group.avatarUrl;
55 const parsed = url.parse(avatarUrl, true);
57 // Tack on a version param otherwise the S3 url is always the same and
58 // you always get the cached avatar from nginx's cache.
59 parsed.query = parsed.query || {};
60 if (group.avatarVersion) {
61 parsed.query.v = group.avatarVersion;
64 if (isGitterInternalAvatarUrl(avatarUrl)) {
65 const bestSize = getBestSizeFor(size);
67 // Just use the original
68 if (!bestSize) return avatarUrl;
70 var pathParts = parsed.pathname.split('/');
71 pathParts.pop();
72 pathParts.push(bestSize);
73 parsed.pathname = pathParts.join('/');
74 return url.format(parsed);
75 } else if (isGitlabSecurityDescriptorType(group.sd && group.sd.type)) {
76 if (size) {
77 // This doesn't actually work but these parameters are added in the GitLab UI
78 parsed.query.width = size;
81 return url.format(parsed);
84 // Just use the original
85 return avatarUrl;
88 /**
89 * Rely on the secondary, but if that doesn't find a recently
90 * created group, fallback to querying the primary
92 function findOnSecondaryOrPrimary(groupId) {
93 return Group.findById(groupId, SELECT_FIELDS, { lean: true })
94 .read(mongoReadPrefs.secondaryPreferred)
95 .then(function(group) {
96 if (group) return group;
98 // Chance that it's not on the secondary yet...
99 return Group.findById(groupId, SELECT_FIELDS, { lean: true }).exec();
103 async function checkForAvatarUpdate(group) {
104 const groupId = group._id;
106 // No need to check GitLab/GitHub if we manage the URL ourselves
107 if (group.avatarUrl && isGitterInternalAvatarUrl(group.avatarUrl)) {
108 debug("Skipping avatar update for groupId=%s because it's an internal Gitter avatar", groupId);
109 return;
112 if (
113 !group.avatarVersion ||
114 !group.avatarCheckedDate ||
115 Date.now() - group.avatarCheckedDate > AVATAR_VERSION_CHECK_TIMEOUT
117 debug('Attempting to fetch group avatar for groupId=%s', groupId);
119 return updateGroupAvatar(group).catch(function(err) {
120 logger.error(err, err.response && `${err.response.status} ${err.response.url}`);
121 errorReporter(
122 err,
124 groupId: groupId
126 { module: 'group-avatar' }
132 // Use the custom group avatar URL if we have one
133 function _getAvatarFromGroup(group, size) {
134 if (group.avatarUrl) {
135 return getGroupAvatarUrlForSize(group, size);
139 function _getAvatarFromSecurityDescriptor(group, size) {
140 // Use the Security Descriptor to
141 // generate an avatar
142 const linkPath = group.sd && group.sd.linkPath;
144 if (!linkPath) return null;
146 const githubUsername = getGithubUsernameFromGroup(group);
147 let avatarUrl = 'https://avatars.githubusercontent.com/' + githubUsername + '?s=' + size;
149 if (group.avatarVersion) {
150 avatarUrl = avatarUrl + '&v=' + group.avatarVersion;
152 return avatarUrl;
153 } else {
154 return avatarUrl;
158 async function getAvatarUrlForGroupId(groupId, size) {
159 const group = await findOnSecondaryOrPrimary(groupId);
161 if (!group) return null;
163 checkForAvatarUpdate(group);
165 return _getAvatarFromGroup(group, size) || _getAvatarFromSecurityDescriptor(group, size);
168 module.exports = {
169 getAvatarUrlForGroupId: getAvatarUrlForGroupId