SubscriptionUpdater.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. const { db, ObjectId } = require('../../infrastructure/mongodb')
  2. const OError = require('@overleaf/o-error')
  3. const async = require('async')
  4. const { promisifyAll } = require('../../util/promises')
  5. const { Subscription } = require('../../models/Subscription')
  6. const SubscriptionLocator = require('./SubscriptionLocator')
  7. const UserGetter = require('../User/UserGetter')
  8. const PlansLocator = require('./PlansLocator')
  9. const FeaturesUpdater = require('./FeaturesUpdater')
  10. const { DeletedSubscription } = require('../../models/DeletedSubscription')
  11. const SubscriptionUpdater = {
  12. /**
  13. * Change the admin of the given subscription.
  14. *
  15. * If the subscription is a group, add the new admin as manager while keeping
  16. * the old admin. Otherwise, replace the manager.
  17. *
  18. * Validation checks are assumed to have been made:
  19. * * subscription exists
  20. * * user exists
  21. * * user does not have another subscription
  22. * * subscription is not a Recurly subscription
  23. *
  24. * If the subscription is Recurly, we silently do nothing.
  25. */
  26. updateAdmin(subscription, adminId, callback) {
  27. const query = {
  28. _id: ObjectId(subscription._id),
  29. customAccount: true
  30. }
  31. const update = {
  32. $set: { admin_id: ObjectId(adminId) }
  33. }
  34. if (subscription.groupPlan) {
  35. update.$addToSet = { manager_ids: ObjectId(adminId) }
  36. } else {
  37. update.$set.manager_ids = [ObjectId(adminId)]
  38. }
  39. Subscription.update(query, update, callback)
  40. },
  41. syncSubscription(recurlySubscription, adminUserId, requesterData, callback) {
  42. if (!callback) {
  43. callback = requesterData
  44. requesterData = {}
  45. }
  46. SubscriptionLocator.getUsersSubscription(adminUserId, function(
  47. err,
  48. subscription
  49. ) {
  50. if (err != null) {
  51. return callback(err)
  52. }
  53. if (subscription != null) {
  54. SubscriptionUpdater._updateSubscriptionFromRecurly(
  55. recurlySubscription,
  56. subscription,
  57. requesterData,
  58. callback
  59. )
  60. } else {
  61. SubscriptionUpdater._createNewSubscription(adminUserId, function(
  62. err,
  63. subscription
  64. ) {
  65. if (err != null) {
  66. return callback(err)
  67. }
  68. SubscriptionUpdater._updateSubscriptionFromRecurly(
  69. recurlySubscription,
  70. subscription,
  71. requesterData,
  72. callback
  73. )
  74. })
  75. }
  76. })
  77. },
  78. addUserToGroup(subscriptionId, userId, callback) {
  79. SubscriptionUpdater.addUsersToGroup(subscriptionId, [userId], callback)
  80. },
  81. addUsersToGroup(subscriptionId, memberIds, callback) {
  82. SubscriptionUpdater.addUsersToGroupWithoutFeaturesRefresh(
  83. subscriptionId,
  84. memberIds,
  85. function(err) {
  86. if (err != null) {
  87. return callback(err)
  88. }
  89. async.map(memberIds, FeaturesUpdater.refreshFeatures, callback)
  90. }
  91. )
  92. },
  93. addUsersToGroupWithoutFeaturesRefresh(subscriptionId, memberIds, callback) {
  94. const searchOps = { _id: subscriptionId }
  95. const insertOperation = { $addToSet: { member_ids: { $each: memberIds } } }
  96. Subscription.findAndModify(searchOps, insertOperation, callback)
  97. },
  98. removeUserFromGroups(filter, userId, callback) {
  99. const removeOperation = { $pull: { member_ids: userId } }
  100. Subscription.updateMany(filter, removeOperation, function(err) {
  101. if (err != null) {
  102. OError.tag(err, 'error removing user from groups', {
  103. filter,
  104. removeOperation
  105. })
  106. return callback(err)
  107. }
  108. UserGetter.getUser(userId, function(error, user) {
  109. if (error) {
  110. return callback(error)
  111. }
  112. FeaturesUpdater.refreshFeatures(userId, callback)
  113. })
  114. })
  115. },
  116. removeUserFromGroup(subscriptionId, userId, callback) {
  117. SubscriptionUpdater.removeUserFromGroups(
  118. { _id: subscriptionId },
  119. userId,
  120. callback
  121. )
  122. },
  123. removeUserFromAllGroups(userId, callback) {
  124. SubscriptionLocator.getMemberSubscriptions(userId, function(
  125. error,
  126. subscriptions
  127. ) {
  128. if (error) {
  129. return callback(error)
  130. }
  131. if (!subscriptions) {
  132. return callback()
  133. }
  134. const subscriptionIds = subscriptions.map(sub => sub._id)
  135. SubscriptionUpdater.removeUserFromGroups(
  136. { _id: subscriptionIds },
  137. userId,
  138. callback
  139. )
  140. })
  141. },
  142. deleteWithV1Id(v1TeamId, callback) {
  143. Subscription.deleteOne({ 'overleaf.id': v1TeamId }, callback)
  144. },
  145. deleteSubscription(subscription, deleterData, callback) {
  146. if (callback == null) {
  147. callback = function() {}
  148. }
  149. async.series(
  150. [
  151. cb =>
  152. // 1. create deletedSubscription
  153. SubscriptionUpdater._createDeletedSubscription(
  154. subscription,
  155. deleterData,
  156. cb
  157. ),
  158. cb =>
  159. // 2. remove subscription
  160. Subscription.remove({ _id: subscription._id }, cb),
  161. cb =>
  162. // 3. refresh users features
  163. SubscriptionUpdater._refreshUsersFeatures(subscription, cb)
  164. ],
  165. callback
  166. )
  167. },
  168. restoreSubscription(subscriptionId, callback) {
  169. SubscriptionLocator.getDeletedSubscription(subscriptionId, function(
  170. err,
  171. deletedSubscription
  172. ) {
  173. if (err) {
  174. return callback(err)
  175. }
  176. let subscription = deletedSubscription.subscription
  177. async.series(
  178. [
  179. cb =>
  180. // 1. upsert subscription
  181. db.subscriptions.updateOne(
  182. { _id: subscription._id },
  183. subscription,
  184. { upsert: true },
  185. cb
  186. ),
  187. cb =>
  188. // 2. refresh users features. Do this before removing the
  189. // subscription so the restore can be retried if this fails
  190. SubscriptionUpdater._refreshUsersFeatures(subscription, cb),
  191. cb =>
  192. // 3. remove deleted subscription
  193. DeletedSubscription.deleteOne(
  194. { 'subscription._id': subscription._id },
  195. callback
  196. )
  197. ],
  198. callback
  199. )
  200. })
  201. },
  202. _refreshUsersFeatures(subscription, callback) {
  203. const userIds = [subscription.admin_id].concat(
  204. subscription.member_ids || []
  205. )
  206. async.mapSeries(userIds, FeaturesUpdater.refreshFeatures, callback)
  207. },
  208. _createDeletedSubscription(subscription, deleterData, callback) {
  209. subscription.teamInvites = []
  210. subscription.invited_emails = []
  211. const filter = { 'subscription._id': subscription._id }
  212. const data = {
  213. deleterData: {
  214. deleterId: deleterData.id,
  215. deleterIpAddress: deleterData.ip
  216. },
  217. subscription: subscription
  218. }
  219. const options = { upsert: true, new: true, setDefaultsOnInsert: true }
  220. DeletedSubscription.findOneAndUpdate(filter, data, options, callback)
  221. },
  222. _createNewSubscription(adminUserId, callback) {
  223. const subscription = new Subscription({
  224. admin_id: adminUserId,
  225. manager_ids: [adminUserId]
  226. })
  227. subscription.save(err => callback(err, subscription))
  228. },
  229. _updateSubscriptionFromRecurly(
  230. recurlySubscription,
  231. subscription,
  232. requesterData,
  233. callback
  234. ) {
  235. if (recurlySubscription.state === 'expired') {
  236. return SubscriptionUpdater.deleteSubscription(
  237. subscription,
  238. requesterData,
  239. callback
  240. )
  241. }
  242. subscription.recurlySubscription_id = recurlySubscription.uuid
  243. subscription.planCode = recurlySubscription.plan.plan_code
  244. const plan = PlansLocator.findLocalPlanInSettings(subscription.planCode)
  245. if (plan == null) {
  246. return callback(
  247. new Error(`plan code not found: ${subscription.planCode}`)
  248. )
  249. }
  250. if (plan.groupPlan) {
  251. if (!subscription.groupPlan) {
  252. subscription.member_ids = subscription.member_ids || []
  253. subscription.member_ids.push(subscription.admin_id)
  254. }
  255. subscription.groupPlan = true
  256. subscription.membersLimit = plan.membersLimit
  257. }
  258. subscription.save(function(error) {
  259. if (error) {
  260. return callback(error)
  261. }
  262. SubscriptionUpdater._refreshUsersFeatures(subscription, callback)
  263. })
  264. }
  265. }
  266. SubscriptionUpdater.promises = promisifyAll(SubscriptionUpdater)
  267. module.exports = SubscriptionUpdater