LimitationsManager.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. const OError = require('@overleaf/o-error')
  2. const logger = require('logger-sharelatex')
  3. const ProjectGetter = require('../Project/ProjectGetter')
  4. const UserGetter = require('../User/UserGetter')
  5. const SubscriptionLocator = require('./SubscriptionLocator')
  6. const Settings = require('@overleaf/settings')
  7. const CollaboratorsGetter = require('../Collaborators/CollaboratorsGetter')
  8. const CollaboratorsInvitesHandler = require('../Collaborators/CollaboratorsInviteHandler')
  9. const V1SubscriptionManager = require('./V1SubscriptionManager')
  10. const { V1ConnectionError } = require('../Errors/Errors')
  11. const { promisifyAll } = require('../../util/promises')
  12. const LimitationsManager = {
  13. allowedNumberOfCollaboratorsInProject(projectId, callback) {
  14. ProjectGetter.getProject(
  15. projectId,
  16. { owner_ref: true },
  17. (error, project) => {
  18. if (error) {
  19. return callback(error)
  20. }
  21. this.allowedNumberOfCollaboratorsForUser(project.owner_ref, callback)
  22. }
  23. )
  24. },
  25. allowedNumberOfCollaboratorsForUser(userId, callback) {
  26. UserGetter.getUser(userId, { features: 1 }, function (error, user) {
  27. if (error) {
  28. return callback(error)
  29. }
  30. if (user.features && user.features.collaborators) {
  31. callback(null, user.features.collaborators)
  32. } else {
  33. callback(null, Settings.defaultFeatures.collaborators)
  34. }
  35. })
  36. },
  37. canAddXCollaborators(projectId, numberOfNewCollaborators, callback) {
  38. this.allowedNumberOfCollaboratorsInProject(
  39. projectId,
  40. (error, allowedNumber) => {
  41. if (error) {
  42. return callback(error)
  43. }
  44. CollaboratorsGetter.getInvitedCollaboratorCount(
  45. projectId,
  46. (error, currentNumber) => {
  47. if (error) {
  48. return callback(error)
  49. }
  50. CollaboratorsInvitesHandler.getInviteCount(
  51. projectId,
  52. (error, inviteCount) => {
  53. if (error) {
  54. return callback(error)
  55. }
  56. if (
  57. currentNumber + inviteCount + numberOfNewCollaborators <=
  58. allowedNumber ||
  59. allowedNumber < 0
  60. ) {
  61. callback(null, true)
  62. } else {
  63. callback(null, false)
  64. }
  65. }
  66. )
  67. }
  68. )
  69. }
  70. )
  71. },
  72. hasPaidSubscription(user, callback) {
  73. this.userHasV2Subscription(user, (err, hasSubscription, subscription) => {
  74. if (err) {
  75. return callback(err)
  76. }
  77. this.userIsMemberOfGroupSubscription(user, (err, isMember) => {
  78. if (err) {
  79. return callback(err)
  80. }
  81. this.userHasV1Subscription(user, (err, hasV1Subscription) => {
  82. if (err) {
  83. return callback(
  84. new V1ConnectionError(
  85. 'error getting subscription from v1'
  86. ).withCause(err)
  87. )
  88. }
  89. callback(
  90. err,
  91. isMember || hasSubscription || hasV1Subscription,
  92. subscription
  93. )
  94. })
  95. })
  96. })
  97. },
  98. // alias for backward-compatibility with modules. Use `haspaidsubscription` instead
  99. userHasSubscriptionOrIsGroupMember(user, callback) {
  100. this.hasPaidSubscription(user, callback)
  101. },
  102. userHasV2Subscription(user, callback) {
  103. SubscriptionLocator.getUsersSubscription(
  104. user._id,
  105. function (err, subscription) {
  106. if (err) {
  107. return callback(err)
  108. }
  109. let hasValidSubscription = false
  110. if (subscription) {
  111. if (
  112. subscription.recurlySubscription_id ||
  113. subscription.customAccount
  114. ) {
  115. hasValidSubscription = true
  116. }
  117. }
  118. callback(err, hasValidSubscription, subscription)
  119. }
  120. )
  121. },
  122. userHasV1OrV2Subscription(user, callback) {
  123. this.userHasV2Subscription(user, (err, hasV2Subscription) => {
  124. if (err) {
  125. return callback(err)
  126. }
  127. if (hasV2Subscription) {
  128. return callback(null, true)
  129. }
  130. this.userHasV1Subscription(user, (err, hasV1Subscription) => {
  131. if (err) {
  132. return callback(err)
  133. }
  134. if (hasV1Subscription) {
  135. return callback(null, true)
  136. }
  137. callback(null, false)
  138. })
  139. })
  140. },
  141. userIsMemberOfGroupSubscription(user, callback) {
  142. SubscriptionLocator.getMemberSubscriptions(
  143. user._id,
  144. function (err, subscriptions) {
  145. if (!subscriptions) {
  146. subscriptions = []
  147. }
  148. if (err) {
  149. return callback(err)
  150. }
  151. callback(err, subscriptions.length > 0, subscriptions)
  152. }
  153. )
  154. },
  155. userHasV1Subscription(user, callback) {
  156. V1SubscriptionManager.getSubscriptionsFromV1(
  157. user._id,
  158. function (err, v1Subscription) {
  159. callback(
  160. err,
  161. !!(v1Subscription ? v1Subscription.has_subscription : undefined)
  162. )
  163. }
  164. )
  165. },
  166. teamHasReachedMemberLimit(subscription) {
  167. const currentTotal =
  168. (subscription.member_ids || []).length +
  169. (subscription.teamInvites || []).length +
  170. (subscription.invited_emails || []).length
  171. return currentTotal >= subscription.membersLimit
  172. },
  173. hasGroupMembersLimitReached(subscriptionId, callback) {
  174. SubscriptionLocator.getSubscription(
  175. subscriptionId,
  176. function (err, subscription) {
  177. if (err) {
  178. OError.tag(err, 'error getting subscription', {
  179. subscriptionId,
  180. })
  181. return callback(err)
  182. }
  183. if (!subscription) {
  184. logger.warn({ subscriptionId }, 'no subscription found')
  185. return callback(new Error('no subscription found'))
  186. }
  187. const limitReached = LimitationsManager.teamHasReachedMemberLimit(
  188. subscription
  189. )
  190. callback(err, limitReached, subscription)
  191. }
  192. )
  193. },
  194. }
  195. LimitationsManager.promises = promisifyAll(LimitationsManager, {
  196. multiResult: {
  197. userHasV2Subscription: ['hasSubscription', 'subscription'],
  198. userIsMemberOfGroupSubscription: ['isMember', 'subscriptions'],
  199. hasGroupMembersLimitReached: ['limitReached', 'subscription'],
  200. },
  201. })
  202. module.exports = LimitationsManager