2025-02-25 06:52:50 +00:00
|
|
|
import { User } from '@entities/user.entity';
|
|
|
|
|
import { GroupPermission } from '@entities/group_permission.entity';
|
2022-06-13 15:19:00 +00:00
|
|
|
import { EntityManager, MigrationInterface, QueryRunner, TableColumn, TableUnique } from 'typeorm';
|
2025-02-25 06:52:50 +00:00
|
|
|
import { UserGroupPermission } from '@entities/user_group_permission.entity';
|
|
|
|
|
import { OrganizationUser } from '@entities/organization_user.entity';
|
|
|
|
|
import { App } from '@entities/app.entity';
|
|
|
|
|
import { Thread } from '@entities/thread.entity';
|
|
|
|
|
import { Comment } from '@entities/comment.entity';
|
|
|
|
|
import { AppUser } from '@entities/app_user.entity';
|
|
|
|
|
import { AuditLog } from '@entities/audit_log.entity';
|
2022-06-13 15:19:00 +00:00
|
|
|
|
|
|
|
|
export class ConvertAllUserEmailsToLowercaseAndDeleteDuplicateUsers1654596810662 implements MigrationInterface {
|
|
|
|
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
|
|
|
const entityManager = queryRunner.manager;
|
|
|
|
|
const usersTable = await queryRunner.getTable('users');
|
|
|
|
|
const columnUniques = usersTable.findColumnUniques(
|
|
|
|
|
new TableColumn({
|
|
|
|
|
name: 'email',
|
|
|
|
|
type: 'string',
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
await queryRunner.dropUniqueConstraint('users', columnUniques[0].name);
|
|
|
|
|
|
|
|
|
|
const usersQuery = entityManager
|
|
|
|
|
.getRepository(User)
|
|
|
|
|
.createQueryBuilder('users')
|
2022-06-30 07:03:26 +00:00
|
|
|
.select(['users.id', 'users.email', 'users.invitationToken'])
|
2022-06-13 15:19:00 +00:00
|
|
|
.leftJoinAndSelect('users.organizationUsers', 'organizationUsers')
|
|
|
|
|
.leftJoinAndSelect('users.groupPermissions', 'groupPermissions')
|
2022-06-14 12:09:40 +00:00
|
|
|
.leftJoinAndSelect('users.userGroupPermissions', 'userGroupPermissions')
|
|
|
|
|
.leftJoinAndSelect('userGroupPermissions.groupPermission', 'groupPermission');
|
2022-06-13 15:19:00 +00:00
|
|
|
|
|
|
|
|
const users = await usersQuery.getMany();
|
|
|
|
|
|
|
|
|
|
//change all email addresses to lowercase
|
2022-07-15 09:47:18 +00:00
|
|
|
await entityManager.query('update users set email = lower(email);');
|
2022-06-13 15:19:00 +00:00
|
|
|
|
|
|
|
|
//merge or delete same users
|
|
|
|
|
const deletedUsers = [];
|
2022-07-14 08:33:21 +00:00
|
|
|
for (const user of users) {
|
|
|
|
|
if (user.email && !deletedUsers.includes(user.id)) {
|
|
|
|
|
const { email } = user;
|
|
|
|
|
const usersWithSameEmail = await usersQuery
|
|
|
|
|
.where('users.email = :email', { email: email.toLowerCase() })
|
|
|
|
|
.getMany();
|
|
|
|
|
|
|
|
|
|
if (usersWithSameEmail.length > 1) {
|
|
|
|
|
const originalUser = user;
|
|
|
|
|
const usersToDelete = usersWithSameEmail.filter((user) => user.id !== originalUser.id);
|
|
|
|
|
await this.migrateUsers(originalUser, usersToDelete, entityManager);
|
|
|
|
|
for (const user of usersToDelete) {
|
|
|
|
|
deletedUsers.push(user.id);
|
2022-07-15 09:47:18 +00:00
|
|
|
}
|
2022-06-13 15:19:00 +00:00
|
|
|
}
|
|
|
|
|
}
|
2022-07-14 08:33:21 +00:00
|
|
|
}
|
2022-06-13 15:19:00 +00:00
|
|
|
|
|
|
|
|
await queryRunner.createUniqueConstraint(
|
|
|
|
|
'users',
|
|
|
|
|
new TableUnique({
|
|
|
|
|
columnNames: ['email'],
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-22 07:24:33 +00:00
|
|
|
private findOrgUsersNotInSameOrg = (deletingOrgUsers: OrganizationUser[], originalOrgUsers: OrganizationUser[]) => {
|
|
|
|
|
return deletingOrgUsers.filter(
|
|
|
|
|
(deletingOrgUser: OrganizationUser) =>
|
|
|
|
|
!originalOrgUsers.some(
|
|
|
|
|
(originalOrgUser: OrganizationUser) => deletingOrgUser.organizationId === originalOrgUser.organizationId
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
2022-06-13 15:19:00 +00:00
|
|
|
private async migrateUsers(originalUser: User, usersToDelete: User[], entityManager: EntityManager) {
|
|
|
|
|
const { organizationUsers } = originalUser;
|
|
|
|
|
|
2022-07-14 08:33:21 +00:00
|
|
|
for (const deletingUser of usersToDelete) {
|
2022-07-15 09:47:18 +00:00
|
|
|
const onlyInDeleteUserOrgs = this.findOrgUsersNotInSameOrg(deletingUser.organizationUsers, organizationUsers);
|
|
|
|
|
|
|
|
|
|
if (onlyInDeleteUserOrgs.length > 0) {
|
|
|
|
|
// map other orgs to original user
|
|
|
|
|
for (const orgnizationUser of onlyInDeleteUserOrgs) {
|
|
|
|
|
await entityManager.update(OrganizationUser, orgnizationUser.id, {
|
|
|
|
|
user: originalUser,
|
|
|
|
|
});
|
2022-07-14 08:33:21 +00:00
|
|
|
}
|
2022-07-15 09:47:18 +00:00
|
|
|
}
|
2022-06-14 12:09:40 +00:00
|
|
|
|
2022-07-15 09:47:18 +00:00
|
|
|
//user group permissions
|
|
|
|
|
await this.migrateUserGroupPermissions(entityManager, deletingUser, originalUser);
|
2022-06-14 12:09:40 +00:00
|
|
|
|
2022-07-15 09:47:18 +00:00
|
|
|
//apps
|
|
|
|
|
const appsOfDeletingUser = await entityManager.find(App, { select: ['id'], where: { userId: deletingUser.id } });
|
|
|
|
|
for (const app of appsOfDeletingUser) {
|
|
|
|
|
await entityManager.update(App, app.id, {
|
|
|
|
|
userId: originalUser.id,
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-06-13 15:19:00 +00:00
|
|
|
|
2022-07-15 09:47:18 +00:00
|
|
|
//threads
|
|
|
|
|
await this.migrateThreads(entityManager, deletingUser.id, originalUser);
|
2022-06-14 18:31:03 +00:00
|
|
|
|
2022-07-15 09:47:18 +00:00
|
|
|
//comments
|
|
|
|
|
await this.migrateComments(entityManager, deletingUser.id, originalUser);
|
2022-06-13 15:19:00 +00:00
|
|
|
|
2025-02-25 06:52:50 +00:00
|
|
|
//audit logs
|
|
|
|
|
await this.migrateAuditLogs(entityManager, deletingUser.id, originalUser);
|
|
|
|
|
|
2022-07-15 09:47:18 +00:00
|
|
|
//delete duplicate user
|
|
|
|
|
await entityManager.delete(AppUser, { userId: deletingUser.id });
|
|
|
|
|
await entityManager.delete(User, deletingUser.id);
|
2022-07-14 08:33:21 +00:00
|
|
|
}
|
2022-06-13 15:19:00 +00:00
|
|
|
}
|
|
|
|
|
|
2022-06-14 12:09:40 +00:00
|
|
|
private async migrateUserGroupPermissions(entityManager: EntityManager, deletingUser: User, originalUser: User) {
|
|
|
|
|
const origin_user_permissions = await originalUser.groupPermissions;
|
2022-07-14 08:33:21 +00:00
|
|
|
for (const userGroupPermission of deletingUser.userGroupPermissions) {
|
|
|
|
|
const deleting_group_permission = userGroupPermission.groupPermission;
|
|
|
|
|
const original_group_permission = await this.checkPermissionIsExisted(
|
|
|
|
|
origin_user_permissions,
|
|
|
|
|
deleting_group_permission.organizationId,
|
|
|
|
|
deleting_group_permission.group
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!original_group_permission) {
|
|
|
|
|
await this.updateUserGroupPermission(entityManager, userGroupPermission.id, originalUser.id);
|
2022-06-14 12:09:40 +00:00
|
|
|
}
|
2022-07-14 08:33:21 +00:00
|
|
|
}
|
2022-06-14 12:09:40 +00:00
|
|
|
}
|
|
|
|
|
|
2022-06-14 18:31:03 +00:00
|
|
|
private async migrateThreads(entityManager: EntityManager, deletingUserId: string, originalUser: User) {
|
2022-06-30 09:11:14 +00:00
|
|
|
const threads = await entityManager
|
|
|
|
|
.getRepository(Thread)
|
|
|
|
|
.createQueryBuilder('threads')
|
|
|
|
|
.select(['threads.id'])
|
|
|
|
|
.where('threads.userId = :userId', { userId: deletingUserId })
|
|
|
|
|
.getMany();
|
|
|
|
|
|
2022-07-14 08:33:21 +00:00
|
|
|
for (const thread of threads) {
|
|
|
|
|
await entityManager.update(Thread, thread.id, {
|
|
|
|
|
user: originalUser,
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-06-14 18:31:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async migrateComments(entityManager: EntityManager, deletingUserId: string, originalUser: User) {
|
2022-06-30 09:11:14 +00:00
|
|
|
const comments = await entityManager
|
|
|
|
|
.getRepository(Comment)
|
|
|
|
|
.createQueryBuilder('comments')
|
|
|
|
|
.select(['comments.id'])
|
|
|
|
|
.where('comments.userId = :userId', { userId: deletingUserId })
|
|
|
|
|
.getMany();
|
|
|
|
|
|
2022-07-14 08:33:21 +00:00
|
|
|
for (const comment of comments) {
|
|
|
|
|
await entityManager.update(Comment, comment.id, {
|
|
|
|
|
user: originalUser,
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-06-13 15:19:00 +00:00
|
|
|
}
|
|
|
|
|
|
2025-02-25 06:52:50 +00:00
|
|
|
private async migrateAuditLogs(entityManager: EntityManager, deletingUserId: string, originalUser: User) {
|
|
|
|
|
const auditLogs = await entityManager
|
|
|
|
|
.getRepository(AuditLog)
|
|
|
|
|
.createQueryBuilder('audit_logs')
|
|
|
|
|
.select(['audit_logs.id'])
|
|
|
|
|
.where('audit_logs.userId = :userId', { userId: deletingUserId })
|
|
|
|
|
.getMany();
|
|
|
|
|
|
|
|
|
|
for (const auditLog of auditLogs) {
|
|
|
|
|
await entityManager.update(AuditLog, auditLog.id, {
|
|
|
|
|
userId: originalUser.id,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-14 12:09:40 +00:00
|
|
|
private async updateUserGroupPermission(entityManager: EntityManager, userGroupPermissionId: string, userId: string) {
|
|
|
|
|
return await entityManager.update(
|
|
|
|
|
UserGroupPermission,
|
|
|
|
|
{
|
|
|
|
|
id: userGroupPermissionId,
|
|
|
|
|
},
|
|
|
|
|
{ userId }
|
|
|
|
|
);
|
2022-06-13 15:19:00 +00:00
|
|
|
}
|
|
|
|
|
|
2022-06-14 12:09:40 +00:00
|
|
|
private async checkPermissionIsExisted(permissions: GroupPermission[], organizationId: string, group: string) {
|
|
|
|
|
let group_permission: GroupPermission = null;
|
2022-07-14 08:33:21 +00:00
|
|
|
for (const permission of permissions) {
|
2022-06-14 12:09:40 +00:00
|
|
|
if (permission.organizationId === organizationId && permission.group === group) {
|
|
|
|
|
group_permission = permission;
|
2022-06-13 15:19:00 +00:00
|
|
|
}
|
2022-07-15 09:47:18 +00:00
|
|
|
}
|
2022-06-14 12:09:40 +00:00
|
|
|
return group_permission;
|
2022-06-13 15:19:00 +00:00
|
|
|
}
|
|
|
|
|
|
2022-06-22 07:24:33 +00:00
|
|
|
private hasUpperCase(str: string) {
|
|
|
|
|
return str.toLowerCase() !== str;
|
2022-06-13 15:19:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async down(queryRunner: QueryRunner): Promise<void> {}
|
|
|
|
|
}
|