| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214 |
- // @ts-check
- import { db, ObjectId } from '../app/src/infrastructure/mongodb.js'
- import fs from 'node:fs/promises'
- import * as csv from 'csv'
- import { promisify } from 'node:util'
- import { scriptRunner } from './lib/ScriptRunner.mjs'
- import { READ_PREFERENCE_SECONDARY } from '@overleaf/mongo-utils/batchedUpdate.js'
- const CSV_FILENAME = '/tmp/unconfirmed_emails.csv'
- /**
- * @type {(csvString: string) => Promise<string[][]>}
- */
- const parseAsync = promisify(csv.parse)
- /**
- * Checks the fallout of services/web/scripts/remove_unconfirmed_emails.mjs
- * which wrongly removed some emails that have been confirmed by users
- */
- async function main(trackProgress) {
- console.time('check_removed_emails')
- const csvContent = await fs.readFile(CSV_FILENAME, 'utf8')
- const rows = await parseAsync(csvContent)
- rows.shift() // Remove header row
- const emailsByUserId = {}
- for (const [userId, email] of rows) {
- if (!emailsByUserId[userId]) {
- emailsByUserId[userId] = []
- }
- emailsByUserId[userId].push(email)
- }
- const userIds = Object.keys(emailsByUserId)
- let processedUsersCount = 0
- const counts = {
- /** @type {string[]} */
- userNotFound: [],
- /** @type {string[]} */
- notDeleted: [],
- deleted: 0,
- /** @type {string[]} */
- wasConfirmed: [],
- /** @type {string[]} */
- wasConfirmedLegacy: [],
- /** @type {string[]} */
- madePrimary: [],
- /** @type {string[]} */
- madeSecondary: [],
- /** @type {string[]} */
- isPrimary: [],
- /** @type {string[]} */
- isAddedAgain: [],
- }
- console.log('Total emails in the CSV:', rows.length)
- console.log('Total users in the CSV:', userIds.length)
- for (const userId of userIds) {
- const userEmails = emailsByUserId[userId]
- const user = await db.users.findOne(
- { _id: new ObjectId(userId) },
- { readPreference: READ_PREFERENCE_SECONDARY }
- )
- if (!user) {
- counts.userNotFound.push(userId)
- continue
- }
- for (const email of userEmails) {
- const deletionLog = await db.userAuditLogEntries.findOne(
- {
- userId: new ObjectId(userId),
- operation: 'remove-email',
- 'info.removedEmail': email,
- 'info.note': 'remove unconfirmed secondary emails',
- },
- { readPreference: READ_PREFERENCE_SECONDARY }
- )
- if (!deletionLog) {
- counts.notDeleted.push(email)
- continue
- }
- counts.deleted++
- if (user.email === email) {
- counts.isPrimary.push(email)
- }
- const confirmationLog = await db.userAuditLogEntries.findOne(
- {
- userId: new ObjectId(userId),
- operation: 'confirm-email-via-code',
- 'info.email': email,
- timestamp: { $gt: new Date('2025-02-25') },
- },
- { readPreference: READ_PREFERENCE_SECONDARY }
- )
- if (confirmationLog) {
- counts.wasConfirmed.push(email)
- }
- const confirmationLegacyLog = await db.userAuditLogEntries.findOne(
- {
- userId: new ObjectId(userId),
- operation: 'confirm-email',
- 'info.email': email,
- timestamp: { $gt: new Date('2025-02-25') },
- },
- { readPreference: READ_PREFERENCE_SECONDARY }
- )
- if (confirmationLegacyLog) {
- counts.wasConfirmedLegacy.push(email)
- }
- const madePrimaryLog = await db.userAuditLogEntries.findOne(
- {
- userId: new ObjectId(userId),
- operation: 'change-primary-email',
- 'info.newPrimaryEmail': email,
- timestamp: { $gt: new Date('2025-02-25') },
- },
- { readPreference: READ_PREFERENCE_SECONDARY }
- )
- if (madePrimaryLog) {
- counts.madePrimary.push(email)
- }
- const madeSecondaryLog = await db.userAuditLogEntries.findOne(
- {
- userId: new ObjectId(userId),
- operation: 'change-primary-email',
- 'info.oldPrimaryEmail': email,
- timestamp: { $gt: new Date('2025-02-25') },
- },
- { readPreference: READ_PREFERENCE_SECONDARY }
- )
- if (madeSecondaryLog) {
- counts.madeSecondary.push(email)
- }
- if (user.emails.some(item => item.email === email)) {
- counts.isAddedAgain.push(email)
- }
- }
- processedUsersCount++
- if (processedUsersCount % 100 === 0) {
- trackProgress(`Processed ${processedUsersCount} users`)
- }
- }
- console.log()
- console.log('Total emails in the CSV:', rows.length)
- console.log('Total users in the CSV:', userIds.length)
- console.log('Total users processed:', processedUsersCount)
- console.log()
- console.log('Users not found:', JSON.stringify(counts.userNotFound))
- console.log()
- console.log('Emails not deleted:', counts.notDeleted.length)
- console.log('Emails deleted:', counts.deleted)
- console.log()
- console.log('Emails that were confirmed:', counts.wasConfirmed.length)
- console.log(
- 'Emails that were confirmed:',
- JSON.stringify(counts.wasConfirmed)
- )
- console.log()
- console.log(
- 'Emails that were confirmed (legacy):',
- counts.wasConfirmedLegacy.length
- )
- console.log(
- 'Emails that were confirmed (legacy):',
- JSON.stringify(counts.wasConfirmedLegacy)
- )
- console.log()
- console.log('Emails that are primary:', counts.isPrimary.length)
- console.log('Emails that are primary:', JSON.stringify(counts.isPrimary))
- console.log()
- console.log('Emails that were made primary:', counts.madePrimary.length)
- console.log(
- 'Emails that were made primary:',
- JSON.stringify(counts.madePrimary)
- )
- console.log()
- console.log('Emails that were made secondary:', counts.madeSecondary.length)
- console.log(
- 'Emails that were made secondary:',
- JSON.stringify(counts.madeSecondary)
- )
- console.log()
- console.log('Emails that were added again:', counts.isAddedAgain.length)
- console.log(
- 'Emails that were added again:',
- JSON.stringify(counts.isAddedAgain)
- )
- console.log()
- console.timeEnd('check_removed_emails')
- console.log()
- }
- try {
- await scriptRunner(main)
- process.exit(0)
- } catch (error) {
- console.error(error)
- process.exit(1)
- }
|