// @ts-check import Settings from '@overleaf/settings' import { waitForDb, db, ObjectId } from '../app/src/infrastructure/mongodb.mjs' import GracefulShutdown from '../app/src/infrastructure/GracefulShutdown.mjs' import UserRegistrationHandler from '../app/src/Features/User/UserRegistrationHandler.mjs' import { Subscription } from '../app/src/models/Subscription.mjs' import minimist from 'minimist' import { createProjectWithOldHistoryId, provisionSplitTests, } from './e2e_test_setup.mjs' import { Project } from '../app/src/models/Project.mjs' import OError from '@overleaf/o-error' import fs from 'node:fs' import Path from 'node:path' import { fileURLToPath } from 'node:url' const MONOREPO = Path.dirname( Path.dirname(Path.dirname(Path.dirname(fileURLToPath(import.meta.url)))) ) const { email: USER_EMAIL, password: PASSWORD } = minimist( process.argv.slice(2), { string: ['email', 'password'] } ) /** * @param {string} email * @param {Object} opts * @param {boolean?} opts.isAdmin * @param {boolean?} opts.forceProfessional * @return {Promise} */ async function createUser( email, opts = { isAdmin: false, forceProfessional: false } ) { const { isAdmin = false, forceProfessional = false } = opts /** @type {import('mongodb-legacy').ObjectId} */ let userId try { const user = await UserRegistrationHandler.promises.registerNewUser({ email, password: PASSWORD, }) userId = user._id } catch (err) { if ( err instanceof OError && err.message.includes('EmailAlreadyRegistered') && err.info && 'userId' in err.info && err.info.userId instanceof ObjectId ) { userId = err.info.userId } else { throw err } } /** @type {string[]} */ let adminRoles = [] if (isAdmin) { adminRoles = ['engineering'] } await db.users.updateOne( { _id: userId }, { $set: { // Set admin flag. isAdmin, adminRoles, // disable AI features, does not work with custom GH Code Spaces domain. 'aiFeatures.enabled': false, // Override features. ...(forceProfessional ? { features: Settings.features.professional, featuresOverrides: [{ features: Settings.features.professional }], } : {}), }, } ) return userId.toString() } async function provisionUsers() { await Promise.all([ createUser(USER_EMAIL, { isAdmin: true, forceProfessional: true }), createUser('admin@overleaf.com', { isAdmin: true, forceProfessional: true, }), createUser('free@overleaf.com'), createUser('premium@overleaf.com').then(async userId => { const subscription = new Subscription({ admin_id: userId, member_ids: [userId], manager_ids: [userId], planCode: 'professional', customAccount: true, }) try { await subscription.save() } catch (err) { if (!isAlreadyExistsErr(err)) throw err // ignore already exists error } }), createUser('group-owner@overleaf.com').then(async userId => { const memberId = await createUser('group-member@overleaf.com') const subscription = new Subscription({ admin_id: userId, member_ids: [memberId], manager_ids: [userId], groupPlan: true, planCode: 'group_professional_10_enterprise', membersLimit: 10, teamName: 'Test Team', customAccount: true, }) try { await subscription.save() } catch (err) { if (!isAlreadyExistsErr(err)) throw err // ignore already exists error } }), createUser('with-old-history@overleaf.com', { isAdmin: true, forceProfessional: true, }).then(async userId => { const projectName = 'old history id (Uses v1 postgres storage)' const ownedProjects = await Project.find( { owner_ref: userId }, { name: true } ).exec() for (const project of ownedProjects) { if (project.name === projectName) return } await createProjectWithOldHistoryId(userId, projectName) }), ]) } /** * @param {unknown} err * @return {boolean} */ function isAlreadyExistsErr(err) { return err instanceof Error && 'code' in err && err.code === 11000 } async function main() { if (process.env.NODE_ENV !== 'development') { throw new Error('only available in dev-env') } const extraSplitTests = JSON.parse( await fs.promises.readFile( Path.join(MONOREPO, '.devcontainer/extra-split-tests.json'), 'utf-8' ) ) await waitForDb() await Promise.all([ provisionUsers(), provisionSplitTests(true, extraSplitTests), ]) } if (import.meta.main) { await main() await GracefulShutdown.gracefulShutdown() }