summaryrefslogtreecommitdiff
path: root/packages/backend/src/core/CacheService.ts
diff options
context:
space:
mode:
authorHazelnoot <acomputerdog@gmail.com>2025-05-09 11:53:29 -0400
committerHazelnoot <acomputerdog@gmail.com>2025-05-10 14:44:17 -0400
commit40a73bfcbe083d5a2aa4be57880e389def4757c4 (patch)
tree6daa302e4dc0b32cf48e4f3e1f63a04b3458b907 /packages/backend/src/core/CacheService.ts
parentmerge: Remove moved setting that was left after merge (!1006) (diff)
downloadsharkey-40a73bfcbe083d5a2aa4be57880e389def4757c4.tar.gz
sharkey-40a73bfcbe083d5a2aa4be57880e389def4757c4.tar.bz2
sharkey-40a73bfcbe083d5a2aa4be57880e389def4757c4.zip
add new role conditions for local/remote followers/followees
Diffstat (limited to 'packages/backend/src/core/CacheService.ts')
-rw-r--r--packages/backend/src/core/CacheService.ts66
1 files changed, 66 insertions, 0 deletions
diff --git a/packages/backend/src/core/CacheService.ts b/packages/backend/src/core/CacheService.ts
index e9900373b4..822bb9d42c 100644
--- a/packages/backend/src/core/CacheService.ts
+++ b/packages/backend/src/core/CacheService.ts
@@ -15,6 +15,13 @@ import { bindThis } from '@/decorators.js';
import type { GlobalEvents } from '@/core/GlobalEventService.js';
import type { OnApplicationShutdown } from '@nestjs/common';
+export interface FollowStats {
+ localFollowing: number;
+ localFollowers: number;
+ remoteFollowing: number;
+ remoteFollowers: number;
+}
+
@Injectable()
export class CacheService implements OnApplicationShutdown {
public userByIdCache: MemoryKVCache<MiUser>;
@@ -27,6 +34,7 @@ export class CacheService implements OnApplicationShutdown {
public userBlockedCache: RedisKVCache<Set<string>>; // NOTE: 「被」Blockキャッシュ
public renoteMutingsCache: RedisKVCache<Set<string>>;
public userFollowingsCache: RedisKVCache<Record<string, Pick<MiFollowing, 'withReplies'> | undefined>>;
+ private readonly userFollowStatsCache = new MemoryKVCache<FollowStats>(1000 * 60 * 10); // 10 minutes
constructor(
@Inject(DI.redis)
@@ -167,6 +175,18 @@ export class CacheService implements OnApplicationShutdown {
const followee = this.userByIdCache.get(body.followeeId);
if (followee) followee.followersCount++;
this.userFollowingsCache.delete(body.followerId);
+ this.userFollowStatsCache.delete(body.followerId);
+ this.userFollowStatsCache.delete(body.followeeId);
+ break;
+ }
+ case 'unfollow': {
+ const follower = this.userByIdCache.get(body.followerId);
+ if (follower) follower.followingCount--;
+ const followee = this.userByIdCache.get(body.followeeId);
+ if (followee) followee.followersCount--;
+ this.userFollowingsCache.delete(body.followerId);
+ this.userFollowStatsCache.delete(body.followerId);
+ this.userFollowStatsCache.delete(body.followeeId);
break;
}
default:
@@ -188,6 +208,52 @@ export class CacheService implements OnApplicationShutdown {
}
@bindThis
+ public async getFollowStats(userId: MiUser['id']): Promise<FollowStats> {
+ return await this.userFollowStatsCache.fetch(userId, async () => {
+ const stats = {
+ localFollowing: 0,
+ localFollowers: 0,
+ remoteFollowing: 0,
+ remoteFollowers: 0,
+ };
+
+ const followings = await this.followingsRepository.findBy([
+ { followerId: userId },
+ { followeeId: userId },
+ ]);
+
+ for (const following of followings) {
+ if (following.followerId === userId) {
+ // increment following; user is a follower of someone else
+ if (following.followeeHost == null) {
+ stats.localFollowing++;
+ } else {
+ stats.remoteFollowing++;
+ }
+ } else if (following.followeeId === userId) {
+ // increment followers; user is followed by someone else
+ if (following.followerHost == null) {
+ stats.localFollowers++;
+ } else {
+ stats.remoteFollowers++;
+ }
+ } else {
+ // Should never happen
+ }
+ }
+
+ // Infer remote-remote followers heuristically, since we don't track that info directly.
+ const user = await this.findUserById(userId);
+ if (user.host !== null) {
+ stats.remoteFollowing = Math.max(0, user.followingCount - stats.localFollowing);
+ stats.remoteFollowers = Math.max(0, user.followersCount - stats.localFollowers);
+ }
+
+ return stats;
+ });
+ }
+
+ @bindThis
public dispose(): void {
this.redisForSub.off('message', this.onMessage);
this.userByIdCache.dispose();