clear_synced_references.mjs 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. // @ts-check
  2. import minimist from 'minimist'
  3. import logger from '@overleaf/logger'
  4. import { db, ObjectId } from '../../app/src/infrastructure/mongodb.mjs'
  5. import { scriptRunner } from '../lib/ScriptRunner.mjs'
  6. // Only zotero is wired up today; the flag exists so the script generalises to
  7. // other reference providers without a rewrite.
  8. const SUPPORTED_SOURCES = ['zotero']
  9. const argv = minimist(process.argv.slice(2), {
  10. boolean: ['commit', 'keep-sync-state', 'help'],
  11. string: ['user-id', 'source'],
  12. default: { source: 'zotero' },
  13. })
  14. function usage() {
  15. logger.info(
  16. {},
  17. `Usage: node scripts/zotero/clear_synced_references.mjs --user-id <id> [options]
  18. Resets a user's reference-sync state for local debugging. Deletes every
  19. libraryReferences entry linked to the given source (sources.<source> present)
  20. and removes the user's librarySyncStates rows for that provider.
  21. Options:
  22. --user-id <id> Required. The Overleaf user _id to reset.
  23. --source <name> Reference source to clear (default: zotero).
  24. Supported: ${SUPPORTED_SOURCES.join(', ')}.
  25. --commit Apply changes. Without this, runs as a dry run.
  26. --keep-sync-state Only delete the references; leave the
  27. librarySyncStates rows untouched.
  28. `
  29. )
  30. }
  31. if (argv.help || !argv['user-id']) {
  32. usage()
  33. process.exit(argv.help ? 0 : 1)
  34. }
  35. const source = argv.source
  36. if (!SUPPORTED_SOURCES.includes(source)) {
  37. logger.error(
  38. { source },
  39. `unsupported source; expected one of ${SUPPORTED_SOURCES.join(', ')}`
  40. )
  41. process.exit(1)
  42. }
  43. const userId = new ObjectId(argv['user-id'])
  44. /** @param {(message: string) => Promise<void>} trackProgress */
  45. async function main(trackProgress) {
  46. if (!argv.commit) {
  47. await trackProgress('DRY RUN. Pass --commit to apply changes.')
  48. }
  49. const refFilter = { userId, [`sources.${source}`]: { $exists: true } }
  50. const refCount = await db.libraryReferences.countDocuments(refFilter)
  51. if (argv.commit) {
  52. const result = await db.libraryReferences.deleteMany(refFilter)
  53. await trackProgress(
  54. `deleted ${result.deletedCount} ${source}-linked references`
  55. )
  56. } else {
  57. await trackProgress(
  58. `[dry-run] would delete ${refCount} ${source}-linked references`
  59. )
  60. }
  61. if (argv['keep-sync-state']) {
  62. return
  63. }
  64. const stateFilter = { userId, provider: source }
  65. if (argv.commit) {
  66. const result = await db.librarySyncStates.deleteMany(stateFilter)
  67. await trackProgress(
  68. `deleted ${result.deletedCount} librarySyncStates row(s)`
  69. )
  70. } else {
  71. const stateCount = await db.librarySyncStates.countDocuments(stateFilter)
  72. await trackProgress(
  73. `[dry-run] would delete ${stateCount} librarySyncStates row(s)`
  74. )
  75. }
  76. }
  77. try {
  78. await scriptRunner(main, {
  79. userId: argv['user-id'],
  80. source,
  81. commit: Boolean(argv.commit),
  82. keepSyncState: Boolean(argv['keep-sync-state']),
  83. })
  84. process.exit(0)
  85. } catch (err) {
  86. logger.error({ err }, 'clear_synced_references failed')
  87. process.exit(1)
  88. }