summaryrefslogtreecommitdiff
path: root/packages/backend/src/core/UserSuspendService.ts
diff options
context:
space:
mode:
authorJulia <julia@insertdomain.name>2025-06-19 21:35:18 +0000
committerJulia <julia@insertdomain.name>2025-06-19 21:35:18 +0000
commita77c32b17da63d3932b219f74152cce023a30f4a (patch)
treed2a05796e942c8f250bbd01369eab0cbe5a14531 /packages/backend/src/core/UserSuspendService.ts
parentmerge: release 2025.4.2 (!1051) (diff)
parentMerge branch 'develop' into release/2025.4.3 (diff)
downloadsharkey-a77c32b17da63d3932b219f74152cce023a30f4a.tar.gz
sharkey-a77c32b17da63d3932b219f74152cce023a30f4a.tar.bz2
sharkey-a77c32b17da63d3932b219f74152cce023a30f4a.zip
merge: prepare release 2025.4.3 (!1125)
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/1125 Approved-by: Marie <github@yuugi.dev> Approved-by: Julia <julia@insertdomain.name>
Diffstat (limited to 'packages/backend/src/core/UserSuspendService.ts')
-rw-r--r--packages/backend/src/core/UserSuspendService.ts94
1 files changed, 68 insertions, 26 deletions
diff --git a/packages/backend/src/core/UserSuspendService.ts b/packages/backend/src/core/UserSuspendService.ts
index 30dcaa6f7d..ddadab7022 100644
--- a/packages/backend/src/core/UserSuspendService.ts
+++ b/packages/backend/src/core/UserSuspendService.ts
@@ -6,7 +6,7 @@
import { Inject, Injectable } from '@nestjs/common';
import { Not, IsNull } from 'typeorm';
import type { FollowingsRepository, FollowRequestsRepository, UsersRepository } from '@/models/_.js';
-import type { MiUser } from '@/models/User.js';
+import { MiUser } from '@/models/User.js';
import { QueueService } from '@/core/QueueService.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import { DI } from '@/di-symbols.js';
@@ -16,9 +16,16 @@ import { bindThis } from '@/decorators.js';
import { RelationshipJobData } from '@/queue/types.js';
import { ModerationLogService } from '@/core/ModerationLogService.js';
import { isSystemAccount } from '@/misc/is-system-account.js';
+import { CacheService } from '@/core/CacheService.js';
+import { LoggerService } from '@/core/LoggerService.js';
+import type Logger from '@/logger.js';
+import { renderInlineError } from '@/misc/render-inline-error.js';
+import { trackPromise } from '@/misc/promise-tracker.js';
@Injectable()
export class UserSuspendService {
+ private readonly logger: Logger;
+
constructor(
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
@@ -34,7 +41,11 @@ export class UserSuspendService {
private globalEventService: GlobalEventService,
private apRendererService: ApRendererService,
private moderationLogService: ModerationLogService,
+ private readonly cacheService: CacheService,
+
+ loggerService: LoggerService,
) {
+ this.logger = loggerService.getLogger('user-suspend');
}
@bindThis
@@ -45,16 +56,16 @@ export class UserSuspendService {
isSuspended: true,
});
- this.moderationLogService.log(moderator, 'suspend', {
+ await this.moderationLogService.log(moderator, 'suspend', {
userId: user.id,
userUsername: user.username,
userHost: user.host,
});
- (async () => {
- await this.postSuspend(user).catch(e => {});
- await this.unFollowAll(user).catch(e => {});
- })();
+ trackPromise((async () => {
+ await this.postSuspend(user);
+ await this.freezeAll(user);
+ })().catch(e => this.logger.error(`Error suspending user ${user.id}: ${renderInlineError(e)}`)));
}
@bindThis
@@ -63,33 +74,36 @@ export class UserSuspendService {
isSuspended: false,
});
- this.moderationLogService.log(moderator, 'unsuspend', {
+ await this.moderationLogService.log(moderator, 'unsuspend', {
userId: user.id,
userUsername: user.username,
userHost: user.host,
});
- (async () => {
- await this.postUnsuspend(user).catch(e => {});
- })();
+ trackPromise((async () => {
+ await this.postUnsuspend(user);
+ await this.unFreezeAll(user);
+ })().catch(e => this.logger.error(`Error un-suspending for user ${user.id}: ${renderInlineError(e)}`)));
}
@bindThis
private async postSuspend(user: { id: MiUser['id']; host: MiUser['host'] }): Promise<void> {
this.globalEventService.publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: true });
+ /*
this.followRequestsRepository.delete({
followeeId: user.id,
});
this.followRequestsRepository.delete({
followerId: user.id,
});
+ */
if (this.userEntityService.isLocalUser(user)) {
// 知り得る全SharedInboxにDelete配信
const content = this.apRendererService.addContext(this.apRendererService.renderDelete(this.userEntityService.genLocalUserUri(user.id), user));
- const queue: string[] = [];
+ const queue = new Map<string, boolean>();
const followings = await this.followingsRepository.find({
where: [
@@ -102,12 +116,12 @@ export class UserSuspendService {
const inboxes = followings.map(x => x.followerSharedInbox ?? x.followeeSharedInbox);
for (const inbox of inboxes) {
- if (inbox != null && !queue.includes(inbox)) queue.push(inbox);
+ if (inbox != null) {
+ queue.set(inbox, true);
+ }
}
- for (const inbox of queue) {
- this.queueService.deliver(user, content, inbox, true);
- }
+ await this.queueService.deliverMany(user, content, queue);
}
}
@@ -119,7 +133,7 @@ export class UserSuspendService {
// 知り得る全SharedInboxにUndo Delete配信
const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderDelete(this.userEntityService.genLocalUserUri(user.id), user), user));
- const queue: string[] = [];
+ const queue = new Map<string, boolean>();
const followings = await this.followingsRepository.find({
where: [
@@ -132,23 +146,19 @@ export class UserSuspendService {
const inboxes = followings.map(x => x.followerSharedInbox ?? x.followeeSharedInbox);
for (const inbox of inboxes) {
- if (inbox != null && !queue.includes(inbox)) queue.push(inbox);
+ if (inbox != null) {
+ queue.set(inbox, true);
+ }
}
- for (const inbox of queue) {
- this.queueService.deliver(user as any, content, inbox, true);
- }
+ await this.queueService.deliverMany(user, content, queue);
}
}
@bindThis
private async unFollowAll(follower: MiUser) {
- const followings = await this.followingsRepository.find({
- where: {
- followerId: follower.id,
- followeeId: Not(IsNull()),
- },
- });
+ const followings = await this.cacheService.userFollowingsCache.fetch(follower.id)
+ .then(fs => Array.from(fs.values()).filter(f => f.followeeHost != null));
const jobs: RelationshipJobData[] = [];
for (const following of followings) {
@@ -162,4 +172,36 @@ export class UserSuspendService {
}
this.queueService.createUnfollowJob(jobs);
}
+
+ @bindThis
+ private async freezeAll(user: MiUser): Promise<void> {
+ // Freeze follow relations with all remote users
+ await this.followingsRepository
+ .createQueryBuilder('following')
+ .orWhere({
+ followeeId: user.id,
+ followerHost: Not(IsNull()),
+ })
+ .update({
+ isFollowerHibernated: true,
+ })
+ .execute();
+ }
+
+ @bindThis
+ private async unFreezeAll(user: MiUser): Promise<void> {
+ // Restore follow relations with all remote users
+ await this.followingsRepository
+ .createQueryBuilder('following')
+ .innerJoin(MiUser, 'follower', 'user.id = following.followerId')
+ .andWhere('follower.isHibernated = false') // Don't unfreeze if the follower is *actually* frozen
+ .andWhere({
+ followeeId: user.id,
+ followerHost: Not(IsNull()),
+ })
+ .update({
+ isFollowerHibernated: false,
+ })
+ .execute();
+ }
}