| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- import { db } from '../app/src/infrastructure/mongodb.mjs'
- import minimist from 'minimist'
- import _ from 'lodash'
- import async from 'async'
- import FeaturesUpdater from '../app/src/Features/Subscription/FeaturesUpdater.mjs'
- import FeaturesHelper from '../app/src/Features/Subscription/FeaturesHelper.mjs'
- import UserFeaturesUpdater from '../app/src/Features/Subscription/UserFeaturesUpdater.mjs'
- import AnalyticsManager from '../app/src/Features/Analytics/AnalyticsManager.mjs'
- import DropboxHandler from '../modules/dropbox/app/src/DropboxHandler.mjs'
- import { OError } from '../app/src/Features/Errors/Errors.js'
- const ScriptLogger = {
- checkedUsersCount: 0,
- mismatchUsersCount: 0,
- allDaysSinceLastLoggedIn: [],
- allMismatchReasons: {},
- recordMismatch: (user, mismatchReasons) => {
- const mismatchReasonsString = JSON.stringify(mismatchReasons)
- if (ScriptLogger.allMismatchReasons[mismatchReasonsString]) {
- ScriptLogger.allMismatchReasons[mismatchReasonsString].push(user._id)
- } else {
- ScriptLogger.allMismatchReasons[mismatchReasonsString] = [user._id]
- }
- ScriptLogger.mismatchUsersCount += 1
- if (user.lastLoggedIn) {
- const daysSinceLastLoggedIn =
- (new Date() - user.lastLoggedIn) / 1000 / 3600 / 24
- ScriptLogger.allDaysSinceLastLoggedIn.push(daysSinceLastLoggedIn)
- }
- },
- printProgress: () => {
- console.warn(
- `Users checked: ${ScriptLogger.checkedUsersCount}. Mismatches: ${ScriptLogger.mismatchUsersCount}`
- )
- },
- printSummary: () => {
- console.log('All Mismatch Reasons:', ScriptLogger.allMismatchReasons)
- console.log('Mismatch Users Count', ScriptLogger.mismatchUsersCount)
- console.log(
- 'Average Last Logged In (Days):',
- _.sum(ScriptLogger.allDaysSinceLastLoggedIn) /
- ScriptLogger.allDaysSinceLastLoggedIn.length
- )
- console.log(
- 'Recent Logged In (Last 7 Days):',
- _.filter(ScriptLogger.allDaysSinceLastLoggedIn, a => a < 7).length
- )
- console.log(
- 'Recent Logged In (Last 30 Days):',
- _.filter(ScriptLogger.allDaysSinceLastLoggedIn, a => a < 30).length
- )
- },
- }
- const checkAndUpdateUser = (user, callback) =>
- FeaturesUpdater.computeFeatures(user._id, (error, freshFeatures) => {
- if (error) {
- return callback(error)
- }
- const mismatchReasons = FeaturesHelper.compareFeatures(
- user.features,
- freshFeatures
- )
- if (Object.keys(mismatchReasons).length === 0) {
- // features are matching; nothing else to do
- return callback()
- }
- ScriptLogger.recordMismatch(user, mismatchReasons)
- if (!COMMIT) {
- // not saving features; nothing else to do
- return callback()
- }
- const matchedFeatureSet = FeaturesHelper.getMatchedFeatureSet(freshFeatures)
- AnalyticsManager.setUserPropertyForUserInBackground(
- user._id,
- 'feature-set',
- matchedFeatureSet
- )
- UserFeaturesUpdater.overrideFeatures(
- user._id,
- freshFeatures,
- (error, featuresChanged) => {
- if (error) {
- return callback(error)
- }
- if (
- mismatchReasons.dropbox !== undefined &&
- freshFeatures.dropbox === false
- ) {
- DropboxHandler.unlinkAccount(
- user._id,
- { sendEmail: false },
- error => {
- if (error) {
- return callback(
- OError.tag(error, 'error unlinking dropbox', {
- userId: user._id,
- })
- )
- }
- console.log({ userId: user._id }, 'Unlinked dropbox')
- callback(null, featuresChanged)
- }
- )
- } else {
- callback(null, featuresChanged)
- }
- }
- )
- })
- const checkAndUpdateUsers = (users, callback) =>
- async.eachLimit(users, ASYNC_LIMIT, checkAndUpdateUser, callback)
- const loopForUsers = (skip, callback) => {
- db.users
- .find({})
- .project({ features: 1, lastLoggedIn: 1 })
- .sort({ _id: 1 })
- .skip(skip)
- .limit(FETCH_LIMIT)
- .toArray((error, users) => {
- if (error) {
- return callback(error)
- }
- if (users.length === 0) {
- console.warn('DONE')
- return callback()
- }
- checkAndUpdateUsers(users, error => {
- if (error) {
- return callback(error)
- }
- ScriptLogger.checkedUsersCount += users.length
- retryCounter = 0
- ScriptLogger.printProgress()
- ScriptLogger.printSummary()
- loopForUsers(MONGO_SKIP + ScriptLogger.checkedUsersCount, callback)
- })
- })
- }
- let retryCounter = 0
- const run = () =>
- loopForUsers(MONGO_SKIP + ScriptLogger.checkedUsersCount, error => {
- if (error) {
- if (retryCounter < 3) {
- console.error(error)
- retryCounter += 1
- console.warn(`RETRYING IN 60 SECONDS. (${retryCounter}/3)`)
- return setTimeout(run, 6000)
- }
- throw error
- }
- process.exit()
- })
- let FETCH_LIMIT, ASYNC_LIMIT, COMMIT, MONGO_SKIP
- const setup = () => {
- const argv = minimist(process.argv.slice(2))
- FETCH_LIMIT = argv.fetch ? argv.fetch : 100
- ASYNC_LIMIT = argv.async ? argv.async : 10
- MONGO_SKIP = argv.skip ? argv.skip : 0
- COMMIT = argv.commit !== undefined
- const FORCE = argv.force !== undefined
- if (!FORCE) {
- console.log(
- 'NOTE: features can be automatically refreshed on login (using `featuresEpoch`)\n' +
- 'Consider incrementing settings.featuresEpoch instead of running this script.\n' +
- 'If you really need to run this script, use refresh_features.mjs --force.'
- )
- process.exit(1)
- }
- if (!COMMIT) {
- console.warn('Doing dry run without --commit')
- }
- if (MONGO_SKIP) {
- console.warn(`Skipping first ${MONGO_SKIP} records`)
- }
- }
- setup()
- run()
|