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');
16 * Check on avatars once a week. In future, we may bring this
17 * down or come up with an alternative method of looking
20 var AVATAR_VERSION_CHECK_TIMEOUT
= 7 * 86400 * 1000;
22 var KNOWN_AVATAR_SIZES
= [22, 40, 44, 48, 64, 80, 96, 128];
25 KNOWN_AVATAR_SIZES
.sort((a
, b
) => {
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
;
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('/');
72 pathParts
.push(bestSize
);
73 parsed
.pathname
= pathParts
.join('/');
74 return url
.format(parsed
);
75 } else if (isGitlabSecurityDescriptorType(group
.sd
&& group
.sd
.type
)) {
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
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
);
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}`);
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
;
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
);
169 getAvatarUrlForGroupId
: getAvatarUrlForGroupId