V1SubscriptionManager.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. /* eslint-disable
  2. node/handle-callback-err,
  3. max-len,
  4. */
  5. // TODO: This file was created by bulk-decaffeinate.
  6. // Fix any style issues and re-enable lint.
  7. /*
  8. * decaffeinate suggestions:
  9. * DS102: Remove unnecessary code created because of implicit returns
  10. * DS103: Rewrite code to no longer use __guard__
  11. * DS207: Consider shorter variations of null checks
  12. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  13. */
  14. let V1SubscriptionManager
  15. const logger = require('@overleaf/logger')
  16. const UserGetter = require('../User/UserGetter')
  17. const request = require('requestretry')
  18. const settings = require('@overleaf/settings')
  19. const { V1ConnectionError, NotFoundError } = require('../Errors/Errors')
  20. const { promisifyAll } = require('../../util/promises')
  21. module.exports = V1SubscriptionManager = {
  22. // Returned planCode = 'v1_pro' | 'v1_pro_plus' | 'v1_student' | 'v1_free' | null
  23. // For this to work, we need plans in settings with plan-codes:
  24. // - 'v1_pro'
  25. // - 'v1_pro_plus'
  26. // - 'v1_student'
  27. // - 'v1_free'
  28. getPlanCodeFromV1(userId, callback) {
  29. if (callback == null) {
  30. callback = function () {}
  31. }
  32. return V1SubscriptionManager._v1Request(
  33. userId,
  34. {
  35. method: 'GET',
  36. url(v1Id) {
  37. return `/api/v1/sharelatex/users/${v1Id}/plan_code`
  38. },
  39. },
  40. function (error, body, v1Id) {
  41. if (error != null) {
  42. return callback(error)
  43. }
  44. let planName = body != null ? body.plan_name : undefined
  45. if (['pro', 'pro_plus', 'student', 'free'].includes(planName)) {
  46. planName = `v1_${planName}`
  47. } else {
  48. // Throw away 'anonymous', etc as being equivalent to null
  49. planName = null
  50. }
  51. if (planName != null && planName !== 'v1_free') {
  52. logger.warn(
  53. { v1Id, planName },
  54. 'got non expired personal subscription'
  55. )
  56. }
  57. return callback(null, planName, v1Id)
  58. }
  59. )
  60. },
  61. getSubscriptionsFromV1(userId, callback) {
  62. if (callback == null) {
  63. callback = function () {}
  64. }
  65. return V1SubscriptionManager._v1Request(
  66. userId,
  67. {
  68. method: 'GET',
  69. url(v1Id) {
  70. return `/api/v1/sharelatex/users/${v1Id}/subscriptions`
  71. },
  72. },
  73. callback
  74. )
  75. },
  76. getSubscriptionStatusFromV1(userId, callback) {
  77. if (callback == null) {
  78. callback = function () {}
  79. }
  80. return V1SubscriptionManager._v1Request(
  81. userId,
  82. {
  83. method: 'GET',
  84. url(v1Id) {
  85. return `/api/v1/sharelatex/users/${v1Id}/subscription_status`
  86. },
  87. },
  88. callback
  89. )
  90. },
  91. cancelV1Subscription(userId, callback) {
  92. if (callback == null) {
  93. callback = function () {}
  94. }
  95. return V1SubscriptionManager._v1Request(
  96. userId,
  97. {
  98. method: 'DELETE',
  99. url(v1Id) {
  100. return `/api/v1/sharelatex/users/${v1Id}/subscription`
  101. },
  102. },
  103. callback
  104. )
  105. },
  106. v1IdForUser(userId, callback) {
  107. if (callback == null) {
  108. callback = function () {}
  109. }
  110. return UserGetter.getUser(
  111. userId,
  112. { 'overleaf.id': 1 },
  113. function (err, user) {
  114. if (err != null) {
  115. return callback(err)
  116. }
  117. const v1Id = __guard__(
  118. user != null ? user.overleaf : undefined,
  119. x => x.id
  120. )
  121. return callback(null, v1Id)
  122. }
  123. )
  124. },
  125. // v1 accounts created before migration to v2 had github and mendeley for free
  126. // but these are now paid-for features for new accounts (v1id > cutoff)
  127. getGrandfatheredFeaturesForV1User(v1Id) {
  128. const cutoff = settings.v1GrandfatheredFeaturesUidCutoff
  129. if (cutoff == null) {
  130. return {}
  131. }
  132. if (v1Id == null) {
  133. return {}
  134. }
  135. if (v1Id < cutoff) {
  136. return settings.v1GrandfatheredFeatures || {}
  137. } else {
  138. return {}
  139. }
  140. },
  141. _v1Request(userId, options, callback) {
  142. if (callback == null) {
  143. callback = function () {}
  144. }
  145. if (!settings.apis.v1.url) {
  146. return callback(null, null)
  147. }
  148. return V1SubscriptionManager.v1IdForUser(userId, function (err, v1Id) {
  149. if (err != null) {
  150. return callback(err)
  151. }
  152. if (v1Id == null) {
  153. return callback(null, null, null)
  154. }
  155. const url = options.url(v1Id)
  156. const requestOptions = {
  157. baseUrl: settings.apis.v1.url,
  158. url,
  159. method: options.method,
  160. auth: {
  161. user: settings.apis.v1.user,
  162. pass: settings.apis.v1.pass,
  163. sendImmediately: true,
  164. },
  165. json: true,
  166. timeout: settings.apis.v1.timeout,
  167. }
  168. if (options.method === 'GET') {
  169. requestOptions.maxAttempts = 3
  170. requestOptions.retryDelay = 500
  171. } else {
  172. requestOptions.maxAttempts = 0
  173. }
  174. request(requestOptions, function (error, response, body) {
  175. if (error != null) {
  176. return callback(
  177. new V1ConnectionError({
  178. message: 'no v1 connection',
  179. info: { url },
  180. }).withCause(error)
  181. )
  182. }
  183. if (response && response.statusCode >= 500) {
  184. return callback(
  185. new V1ConnectionError({
  186. message: 'error from v1',
  187. info: {
  188. status: response.statusCode,
  189. body: body,
  190. },
  191. })
  192. )
  193. }
  194. if (response.statusCode >= 200 && response.statusCode < 300) {
  195. return callback(null, body, v1Id)
  196. } else {
  197. if (response.statusCode === 404) {
  198. return callback(new NotFoundError(`v1 user not found: ${userId}`))
  199. } else {
  200. return callback(
  201. new Error(
  202. `non-success code from v1: ${response.statusCode} ${
  203. options.method
  204. } ${options.url(v1Id)}`
  205. )
  206. )
  207. }
  208. }
  209. })
  210. })
  211. },
  212. }
  213. function __guard__(value, transform) {
  214. return typeof value !== 'undefined' && value !== null
  215. ? transform(value)
  216. : undefined
  217. }
  218. module.exports.promises = promisifyAll(module.exports, {
  219. without: ['getGrandfatheredFeaturesForV1User'],
  220. multiResult: {
  221. getPlanCodeFromV1: ['planCode', 'v1Id'],
  222. },
  223. })