diff --git a/services/web/app/src/Features/Project/ProjectController.mjs b/services/web/app/src/Features/Project/ProjectController.mjs index ea1087bde9..91cebb5509 100644 --- a/services/web/app/src/Features/Project/ProjectController.mjs +++ b/services/web/app/src/Features/Project/ProjectController.mjs @@ -1266,18 +1266,28 @@ const _ProjectController = { if (shouldAutoCreateAccount) { await UserUpdater.promises.updateUser(userId, { $set: { - writefull: { enabled: true, autoCreatedAccount: true }, + writefull: { + enabled: true, + initialized: true, + autoCreatedAccount: true, + }, }, }) user.writefull.enabled = true + user.writefull.initialized = true user.writefull.autoCreatedAccount = true } else if (shouldAutoLoad) { await UserUpdater.promises.updateUser(userId, { $set: { - writefull: { enabled: true, autoCreatedAccount: false }, + writefull: { + enabled: true, + initialized: true, + autoCreatedAccount: false, + }, }, }) user.writefull.enabled = true + user.writefull.initialized = true user.writefull.autoCreatedAccount = false } }, diff --git a/services/web/app/src/models/User.mjs b/services/web/app/src/models/User.mjs index 4ecdee7c9f..bb957b8d57 100644 --- a/services/web/app/src/models/User.mjs +++ b/services/web/app/src/models/User.mjs @@ -194,6 +194,8 @@ export const UserSchema = new Schema( }, writefull: { enabled: { type: Boolean, default: null }, + // whether we have attached an autocreated account or autoloading for the user + initialized: { type: Boolean, default: false }, autoCreatedAccount: { type: Boolean, default: false }, isPremium: { type: Boolean, default: false }, premiumSource: { type: String, default: null }, diff --git a/services/web/scripts/writefull/migrate_to_promotion_set.mjs b/services/web/scripts/writefull/migrate_to_promotion_set.mjs new file mode 100644 index 0000000000..a32f862c15 --- /dev/null +++ b/services/web/scripts/writefull/migrate_to_promotion_set.mjs @@ -0,0 +1,30 @@ +import { db } from '../../app/src/infrastructure/mongodb.mjs' +import { batchedUpdate } from '@overleaf/mongo-utils/batchedUpdate.js' +import { scriptRunner } from '../lib/ScriptRunner.mjs' + +async function main(trackProgress) { + // if writefull.enabled is unset or null then the account has no promotion attached yet + await batchedUpdate(db.users, {}, [ + { + $set: { + 'writefull.initialized': { + $or: [ + { $eq: ['$writefull.enabled', true] }, + { $eq: ['$writefull.enabled', false] }, + ], + }, + }, + }, + ]) + console.log('completed migration to writefull.initialized') +} + +export default main + +try { + await scriptRunner(main) + process.exit(0) +} catch (error) { + console.error({ error }) + process.exit(1) +} diff --git a/tools/migrations/20260213102825_swap_writefull_enabled_for_initialized.mjs b/tools/migrations/20260213102825_swap_writefull_enabled_for_initialized.mjs new file mode 100644 index 0000000000..2f9c01a0e2 --- /dev/null +++ b/tools/migrations/20260213102825_swap_writefull_enabled_for_initialized.mjs @@ -0,0 +1,41 @@ +/* eslint-disable no-unused-vars */ + +import Helpers from './lib/helpers.mjs' + +import { batchedUpdate } from '@overleaf/mongo-utils/batchedUpdate.js' +const tags = ['saas'] + +const migrate = async client => { + const { db } = client + // if writefull.enabled is unset or null then the account has no promotion attached yet + await batchedUpdate(db.users, {}, [ + { + $set: { + 'writefull.initialized': { + $or: [ + { $eq: ['$writefull.enabled', true] }, + { $eq: ['$writefull.enabled', false] }, + ], + }, + }, + }, + ]) + console.log('completed migration to writefull.initialized') +} + +const rollback = async client => { + const { db } = client + // unset the user.writefull.initialized value only if its been set + await batchedUpdate( + db.users, + { 'writefull.initialized': { $exists: true } }, + { $unset: { 'writefull.initialized': 1 } } + ) + console.log('completed rollback of writefull.initialized migration') +} + +export default { + tags, + migrate, + rollback, +} diff --git a/tools/migrations/20260216141836_back_fill_hiding_ai_features.mjs b/tools/migrations/20260216141836_back_fill_hiding_ai_features.mjs new file mode 100644 index 0000000000..0e6eb170ba --- /dev/null +++ b/tools/migrations/20260216141836_back_fill_hiding_ai_features.mjs @@ -0,0 +1,51 @@ +/* eslint-disable no-unused-vars */ + +import { batchedUpdate } from '@overleaf/mongo-utils/batchedUpdate.js' + +const tags = ['saas'] + +const migrate = async client => { + const { db } = client + // Set aiFeatures.enabled to false where writefull.enabled is false + await batchedUpdate( + db.users, + { + 'writefull.enabled': false, + // dont re-set in cases where we already set aiFeatures, ie: by running the script + 'aiFeatures.enabled': { $exists: false }, + }, + { $set: { 'aiFeatures.enabled': false } } + ) + + // Set aiFeatures.enabled to true for all other cases (true, null, or not exists) + await batchedUpdate( + db.users, + { + 'writefull.enabled': { $ne: false }, + // dont re-set in cases where we already set aiFeatures, ie: by running the script + 'aiFeatures.enabled': { $exists: false }, + }, + { $set: { 'aiFeatures.enabled': true } } + ) + + console.log( + 'completed syncing rename of aiErrorAssist.enabled to aiFeatures.enabled' + ) +} + +const rollback = async client => { + const { db } = client + // unset the user.writefull.initialized value only if its been set + await batchedUpdate( + db.users, + { 'aiFeatures.enabled': { $exists: true } }, + { $unset: { 'aiFeatures.enabled': 1 } } + ) + console.log('completed rollback of aiFeatures.enabled migration') +} + +export default { + tags, + migrate, + rollback, +}