Handle new account having a team in account merge Closes #2308
Some checks are pending
Tests and checks on push / run-checks-and-tests (push) Waiting to run
Updates translation progress / update-translation-progress-issue (push) Waiting to run

This commit is contained in:
Kalle
2025-05-19 22:49:15 +03:00
parent 2cecab83f6
commit 31191eeaae
6 changed files with 215 additions and 17 deletions

View File

@@ -1,5 +1,6 @@
import type { Transaction } from "kysely";
import { db, sql } from "~/db/sql";
import type { Tables } from "~/db/tables";
import type { DB, Tables } from "~/db/tables";
import { dateToDatabaseTimestamp } from "~/utils/dates";
import invariant from "~/utils/invariant";
import { syncXPBadges } from "../badges/queries/syncXPBadges.server";
@@ -33,8 +34,21 @@ export const cleanUp = () => {
cleanUpStm.run();
};
/**
* Migrates user-related data. Takes data from the "old user" and remaps it to the Discord ID of the "new user". Used when user switches their Discord accounts.
*
* @param args - An object containing:
* - `newUserId`: The ID of the user whose data will be migrated and then deleted.
* - `oldUserId`: The ID of the user who will receive the migrated data.
* @returns A promise that resolves to `null` if the migration succeeds, or an error message if validation fails.
*/
export function migrate(args: { newUserId: number; oldUserId: number }) {
return db.transaction().execute(async (trx) => {
const error = await validateMigration(trx, args);
if (error) {
return error;
}
// delete some limited data from the target user
// idea is to make the migration a bit more smooth
// since it won't fail if some small thing has been added
@@ -53,6 +67,24 @@ export function migrate(args: { newUserId: number; oldUserId: number }) {
.where("userId", "=", args.newUserId)
.set({ userId: args.oldUserId })
.execute();
await trx
.updateTable("UnvalidatedUserSubmittedImage")
.where("submitterUserId", "=", args.newUserId)
.set({ submitterUserId: args.oldUserId })
.execute();
// delete past team membership data (not user visible)
await trx
.deleteFrom("AllTeamMember")
.where("userId", "=", args.newUserId)
.where("leftAt", "is not", null)
.execute();
// existing team membership will stay
await trx
.updateTable("AllTeamMember")
.where("userId", "=", args.newUserId)
.set({ userId: args.oldUserId })
.execute();
const deletedUser = await trx
.deleteFrom("User")
@@ -65,9 +97,34 @@ export function migrate(args: { newUserId: number; oldUserId: number }) {
.set({ discordId: deletedUser.discordId })
.where("User.id", "=", args.oldUserId)
.execute();
return null;
});
}
async function validateMigration(
trx: Transaction<DB>,
args: { newUserId: number; oldUserId: number },
) {
const oldUserCurrentTeam = await trx
.selectFrom("TeamMember")
.select(["teamId"])
.where("userId", "=", args.oldUserId)
.executeTakeFirst();
const newUserCurrentTeam = await trx
.selectFrom("TeamMember")
.select(["teamId"])
.where("userId", "=", args.newUserId)
.executeTakeFirst();
if (oldUserCurrentTeam && newUserCurrentTeam) {
return "both old and new user are in teams";
}
return null;
}
export function replacePlusTiers(
plusTiers: Array<{ userId: number; plusTier: number }>,
) {