copy block and mute and update lists when detecting an account has moved
This commit is contained in:
parent
8c031e9f42
commit
91fcad0c85
4 changed files with 101 additions and 46 deletions
|
|
@ -5,8 +5,8 @@ import { bindThis } from '@/decorators.js';
|
|||
import { DI } from '@/di-symbols.js';
|
||||
import type { Config } from '@/config.js';
|
||||
import type { LocalUser } from '@/models/entities/User.js';
|
||||
import type { BlockingsRepository, FollowingsRepository, Muting, MutingsRepository, UsersRepository } from '@/models/index.js';
|
||||
import type { RelationshipJobData } from '@/queue/types.js';
|
||||
import type { BlockingsRepository, FollowingsRepository, Muting, MutingsRepository, UserListJoiningsRepository, UsersRepository } from '@/models/index.js';
|
||||
import type { RelationshipJobData, ThinUser } from '@/queue/types.js';
|
||||
|
||||
import { User } from '@/models/entities/User.js';
|
||||
|
||||
|
|
@ -20,6 +20,7 @@ import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
|
|||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { CacheService } from '@/core/CacheService';
|
||||
import { ProxyAccountService } from '@/core/ProxyAccountService.js';
|
||||
|
||||
@Injectable()
|
||||
export class AccountMoveService {
|
||||
|
|
@ -39,6 +40,9 @@ export class AccountMoveService {
|
|||
@Inject(DI.mutingsRepository)
|
||||
private mutingsRepository: MutingsRepository,
|
||||
|
||||
@Inject(DI.userListJoiningsRepository)
|
||||
private userListJoiningsRepository: UserListJoiningsRepository,
|
||||
|
||||
private idService: IdService,
|
||||
private userEntityService: UserEntityService,
|
||||
private apRendererService: ApRendererService,
|
||||
|
|
@ -46,6 +50,7 @@ export class AccountMoveService {
|
|||
private globalEventService: GlobalEventService,
|
||||
private userFollowingService: UserFollowingService,
|
||||
private accountUpdateService: AccountUpdateService,
|
||||
private proxyAccountService: ProxyAccountService,
|
||||
private relayService: RelayService,
|
||||
private cacheService: CacheService,
|
||||
private queueService: QueueService,
|
||||
|
|
@ -114,43 +119,13 @@ export class AccountMoveService {
|
|||
@bindThis
|
||||
public async move(src: User, dst: User): Promise<void> {
|
||||
// Copy blockings:
|
||||
// Followers shouldn't overlap with blockers, but the destination account, different from the blockee (i.e., old account), may have followed the local user before moving.
|
||||
// So block the destination account here.
|
||||
const blockings = await this.blockingsRepository.find({
|
||||
relations: {
|
||||
blocker: true
|
||||
},
|
||||
where: {
|
||||
blockeeId: src.id
|
||||
}
|
||||
})
|
||||
// reblock the destination account
|
||||
const blockJobs: RelationshipJobData[] = [];
|
||||
for (const blocking of blockings) {
|
||||
if (!blocking.blocker) continue;
|
||||
blockJobs.push({ from: blocking.blocker, to: dst });
|
||||
}
|
||||
// no need to unblock the old account because it may be still functional
|
||||
this.queueService.createBlockJob(blockJobs);
|
||||
await this.copyBlocking(src, dst);
|
||||
|
||||
// Copy mutings:
|
||||
// Insert new mutings with the same values except mutee
|
||||
const mutings = await this.mutingsRepository.findBy({ muteeId: src.id });
|
||||
const newMuting: Partial<Muting>[] = [];
|
||||
for (const muting of mutings) {
|
||||
newMuting.push({
|
||||
id: this.idService.genId(),
|
||||
createdAt: new Date(),
|
||||
expiresAt: muting.expiresAt,
|
||||
muterId: muting.muterId,
|
||||
muteeId: dst.id,
|
||||
})
|
||||
}
|
||||
this.mutingsRepository.insert(mutings); // no need to wait
|
||||
for (const mute of mutings) {
|
||||
if (mute.muter) this.cacheService.userMutingsCache.refresh(mute.muter.id);
|
||||
}
|
||||
// no need to unmute the old account because it may be still functional
|
||||
await this.copyMutings(src, dst);
|
||||
|
||||
// Update lists:
|
||||
await this.updateLists(src, dst);
|
||||
|
||||
// follow the new account and unfollow the old one
|
||||
const followings = await this.followingsRepository.find({
|
||||
|
|
@ -166,8 +141,8 @@ export class AccountMoveService {
|
|||
const unfollowJobs: RelationshipJobData[] = [];
|
||||
for (const following of followings) {
|
||||
if (!following.follower) continue;
|
||||
followJobs.push({ from: following.follower, to: dst });
|
||||
unfollowJobs.push({ from: following.follower, to: src });
|
||||
followJobs.push({ from: { id: following.follower.id }, to: { id: dst.id } });
|
||||
unfollowJobs.push({ from: { id: following.follower.id }, to: { id: src.id } });
|
||||
}
|
||||
// Should be queued because this can cause a number of follow/unfollow per one move.
|
||||
// No need to care job orders as there should be no overlaps of follow/unfollow target.
|
||||
|
|
@ -175,6 +150,69 @@ export class AccountMoveService {
|
|||
this.queueService.createUnfollowJob(unfollowJobs);
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async copyBlocking(src: ThinUser, dst: ThinUser): Promise<void> {
|
||||
// Followers shouldn't overlap with blockers, but the destination account, different from the blockee (i.e., old account), may have followed the local user before moving.
|
||||
// So block the destination account here.
|
||||
const blockings = await this.blockingsRepository.find({ // FIXME: might be expensive
|
||||
relations: {
|
||||
blocker: true
|
||||
},
|
||||
where: {
|
||||
blockeeId: src.id
|
||||
}
|
||||
});
|
||||
// reblock the destination account
|
||||
const blockJobs: RelationshipJobData[] = [];
|
||||
for (const blocking of blockings) {
|
||||
if (!blocking.blocker) continue;
|
||||
blockJobs.push({ from: { id: blocking.blocker.id }, to: { id: dst.id } });
|
||||
}
|
||||
// no need to unblock the old account because it may be still functional
|
||||
this.queueService.createBlockJob(blockJobs);
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async copyMutings(src: ThinUser, dst: ThinUser): Promise<void> {
|
||||
// Insert new mutings with the same values except mutee
|
||||
const mutings = await this.mutingsRepository.findBy({ muteeId: src.id });
|
||||
const newMuting: Partial<Muting>[] = [];
|
||||
for (const muting of mutings) {
|
||||
newMuting.push({
|
||||
id: this.idService.genId(),
|
||||
createdAt: new Date(),
|
||||
expiresAt: muting.expiresAt,
|
||||
muterId: muting.muterId,
|
||||
muteeId: dst.id,
|
||||
});
|
||||
}
|
||||
this.mutingsRepository.insert(mutings); // no need to wait
|
||||
for (const mute of mutings) {
|
||||
if (mute.muter) this.cacheService.userMutingsCache.refresh(mute.muter.id);
|
||||
}
|
||||
// no need to unmute the old account because it may be still functional
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async updateLists(src: ThinUser, dst: User): Promise<void> {
|
||||
// Return if there is no list to be updated
|
||||
const numOfLists = await this.userListJoiningsRepository.countBy({ userId: src.id });
|
||||
if (numOfLists === 0) return;
|
||||
|
||||
await this.userListJoiningsRepository.update(
|
||||
{ userId: src.id },
|
||||
{ userId: dst.id, user: dst }
|
||||
);
|
||||
|
||||
// Have the proxy account follow the new account in the same way as UserListService.push
|
||||
if (this.userEntityService.isRemoteUser(dst)) {
|
||||
const proxy = await this.proxyAccountService.fetch();
|
||||
if (proxy) {
|
||||
this.queueService.createFollowJob([{ from: { id: proxy.id }, to: { id: dst.id } }]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public getUserUri(user: User): string {
|
||||
return this.userEntityService.isRemoteUser(user)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue