Просмотр исходного кода

Merge pull request #26604 from overleaf/mf-resync-recurly-state-single-subscription

Add script to sync recurlyStatus.state for a single subscriptionId

GitOrigin-RevId: 69af89a44b4043d92853862baee65d8b7f84b88f
Lucie Germain 1 год назад
Родитель
Сommit
f53a13ae1e

+ 137 - 0
services/web/scripts/recurly/resync_recurly_state_single_subscription.mjs

@@ -0,0 +1,137 @@
+import { Subscription } from '../../app/src/models/Subscription.js'
+import RecurlyWrapper from '../../app/src/Features/Subscription/RecurlyWrapper.js'
+import SubscriptionUpdater from '../../app/src/Features/Subscription/SubscriptionUpdater.js'
+import minimist from 'minimist'
+import { setTimeout } from 'node:timers/promises'
+import util from 'node:util'
+
+util.inspect.defaultOptions.maxArrayLength = null
+
+const handleSyncSubscriptionError = async (subscription, error) => {
+  console.warn(`Errors with subscription id=${subscription._id}:`, error)
+
+  if (typeof error === 'string' && error.match(/429$/)) {
+    console.warn('Recurly rate limit hit (429). Waiting for 5 minutes...')
+    await setTimeout(1000 * 60 * 5)
+    return
+  }
+  if (typeof error === 'string' && error.match(/5\d\d$/)) {
+    console.warn('Recurly server error (5xx). Retrying in 1 minute...')
+    await setTimeout(1000 * 60)
+    await syncRecurlyStateInSubscription(subscription)
+    return
+  }
+  await setTimeout(80)
+}
+
+const syncRecurlyStateInSubscription = async subscription => {
+  let recurlySubscription
+
+  try {
+    recurlySubscription = await RecurlyWrapper.promises.getSubscription(
+      subscription.recurlySubscription_id
+    )
+  } catch (error) {
+    await handleSyncSubscriptionError(subscription, error)
+    return
+  }
+
+  if (!subscription.recurlyStatus) {
+    subscription.recurlyStatus = {}
+  }
+
+  if (subscription.recurlyStatus.state !== recurlySubscription.state) {
+    console.log(
+      `Mismatched recurlyStatus.state for subscription ID ${subscription._id}. ` +
+        `Our database: '${subscription.recurlyStatus.state || 'undefined/null'}', recurly: '${recurlySubscription.state}'.`
+    )
+
+    subscription.recurlyStatus.state = recurlySubscription.state
+
+    if (COMMIT) {
+      try {
+        console.log(
+          `Committing update for subscription ID: ${subscription._id}`
+        )
+        await SubscriptionUpdater.promises.updateSubscriptionFromRecurly(
+          recurlySubscription,
+          subscription,
+          {}
+        )
+      } catch (error) {
+        await handleSyncSubscriptionError(subscription, error)
+      }
+
+      console.log(
+        `Successfully updated subscription ID ${subscription._id} with new recurlyStatus.state: ${subscription.recurlyStatus.state}`
+      )
+    }
+  } else {
+    console.log(
+      `Subscription ID ${subscription._id}: recurlyStatus.state is already in sync.`
+    )
+  }
+
+  await setTimeout(80)
+}
+
+let COMMIT, SUBSCRIPTION_ID
+
+const setup = () => {
+  const argv = minimist(process.argv.slice(2))
+
+  SUBSCRIPTION_ID = argv.subscriptionId
+  if (!SUBSCRIPTION_ID) {
+    console.error(
+      'Error: Please provide a subscription ID using --subscriptionId=<id>'
+    )
+    process.exit(1)
+  }
+  console.log(
+    `Attempting to sync subscription.recurlyStatus with ID: ${SUBSCRIPTION_ID}`
+  )
+
+  COMMIT = argv.commit !== undefined
+  if (!COMMIT) {
+    console.warn(
+      'Doing dry run without --commit. No database changes will be made.'
+    )
+  }
+}
+
+const run = async () => {
+  try {
+    const subscription = await Subscription.findById(SUBSCRIPTION_ID).exec()
+
+    if (!subscription) {
+      console.error(
+        `Error: Subscription with ID ${SUBSCRIPTION_ID} not found in the database.`
+      )
+      process.exit(1)
+    }
+
+    if (!subscription.recurlySubscription_id) {
+      console.error(
+        `Error: Subscription ID ${SUBSCRIPTION_ID} does not have a Recurly subscription ID.`
+      )
+      process.exit(1)
+    }
+
+    console.log(
+      `Found subscription: ${subscription._id}, Recurly ID: ${subscription.recurlySubscription_id}`
+    )
+
+    await syncRecurlyStateInSubscription(subscription)
+
+    console.log('DONE')
+  } catch (error) {
+    console.error('An unhandled error occurred during script execution:', error)
+    process.exit(1)
+  }
+}
+
+setup()
+
+await run()
+
+process.exit(0)