feat(backend): copyOnMoveAccount

This commit is contained in:
anatawa12 2025-04-02 11:03:54 +09:00
parent 3ec5bf114b
commit 3ac98f2032
No known key found for this signature in database
GPG Key ID: 9CA909848B8E4EA6
4 changed files with 48 additions and 0 deletions

View File

@ -0,0 +1,13 @@
export class RoleCopyOnMoveAccount1743558299182 {
name = 'RoleCopyOnMoveAccount1743558299182'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "role" ADD "copyOnMoveAccount" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`COMMENT ON COLUMN "role"."copyOnMoveAccount" IS 'If true, the role will be copied to moved to the new user on moving a user.'`);
}
async down(queryRunner) {
await queryRunner.query(`COMMENT ON COLUMN "role"."copyOnMoveAccount" IS 'If true, the role will be copied to moved to the new user on moving a user.'`);
await queryRunner.query(`ALTER TABLE "role" DROP COLUMN "copyOnMoveAccount"`);
}
}

View File

@ -24,6 +24,7 @@ import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
import InstanceChart from '@/core/chart/charts/instance.js';
import PerUserFollowingChart from '@/core/chart/charts/per-user-following.js';
import { SystemAccountService } from '@/core/SystemAccountService.js';
import { RoleService } from '@/core/RoleService.js';
@Injectable()
export class AccountMoveService {
@ -61,6 +62,7 @@ export class AccountMoveService {
private relayService: RelayService,
private queueService: QueueService,
private systemAccountService: SystemAccountService,
private roleService: RoleService,
) {
}
@ -119,6 +121,7 @@ export class AccountMoveService {
await Promise.all([
this.copyBlocking(src, dst),
this.copyMutings(src, dst),
this.copyRoles(src, dst),
this.updateLists(src, dst),
]);
} catch {
@ -201,6 +204,31 @@ export class AccountMoveService {
await this.mutingsRepository.insert(arrayToInsert);
}
@bindThis
public async copyRoles(src: ThinUser, dst: ThinUser): Promise<void> {
// Insert new roles with the same values except userId
// role service may have cache for roles so retrieve roles from service
const [oldRoleAssignments, roles] = await Promise.all([
this.roleService.getUserAssigns(src.id),
this.roleService.getRoles(),
]);
if (oldRoleAssignments.length === 0) return;
// No promise all since the only async operation is writing to the database
for (const oldRoleAssignment of oldRoleAssignments) {
const role = roles.find(x => x.id === oldRoleAssignment.roleId);
if (role == null) continue; // Very unlikely however removing role may cause this case
try {
await this.roleService.assign(dst.id, role.id, oldRoleAssignment.expiresAt);
} catch (e) {
if (e instanceof RoleService.AlreadyAssignedError) continue;
throw e;
}
}
}
/**
* Update lists while moving accounts.
* - No removal of the old account from the lists

View File

@ -630,6 +630,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
isModerator: values.isModerator,
isExplorable: values.isExplorable,
asBadge: values.asBadge,
copyOnMoveAccount: values.copyOnMoveAccount,
canEditMembersByModerator: values.canEditMembersByModerator,
displayOrder: values.displayOrder,
policies: values.policies,

View File

@ -248,6 +248,12 @@ export class MiRole {
})
public isExplorable: boolean;
@Column('boolean', {
default: false,
comment: 'If true, the role will be copied to moved to the new user on moving a user.',
})
public copyOnMoveAccount: boolean;
@Column('boolean', {
default: false,
})