/* * SPDX-FileCopyrightText: hazelnoot and other Sharkey contributors * SPDX-License-Identifier: AGPL-3.0-only */ import * as Redis from 'ioredis'; import { Inject } from '@nestjs/common'; import { FakeInternalEventService } from './FakeInternalEventService.js'; import type { BlockingsRepository, FollowingsRepository, MiUser, MutingsRepository, RenoteMutingsRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js'; import type { MiLocalUser } from '@/models/User.js'; import { MemoryKVCache, MemorySingleCache, RedisKVCache, RedisSingleCache } from '@/misc/cache.js'; import { QuantumKVCache, QuantumKVOpts } from '@/misc/QuantumKVCache.js'; import { CacheService, FollowStats } from '@/core/CacheService.js'; import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { InternalEventService } from '@/core/InternalEventService.js'; export function noOpRedis() { return { set: () => Promise.resolve(), get: () => Promise.resolve(null), del: () => Promise.resolve(), on: () => {}, off: () => {}, } as unknown as Redis.Redis; } export class NoOpCacheService extends CacheService { public readonly fakeRedis: { [K in keyof Redis.Redis]: Redis.Redis[K]; }; public readonly fakeInternalEventService: FakeInternalEventService; constructor( @Inject(DI.usersRepository) usersRepository: UsersRepository, @Inject(DI.userProfilesRepository) userProfilesRepository: UserProfilesRepository, @Inject(DI.mutingsRepository) mutingsRepository: MutingsRepository, @Inject(DI.blockingsRepository) blockingsRepository: BlockingsRepository, @Inject(DI.renoteMutingsRepository) renoteMutingsRepository: RenoteMutingsRepository, @Inject(DI.followingsRepository) followingsRepository: FollowingsRepository, @Inject(UserEntityService) userEntityService: UserEntityService, ) { const fakeRedis = noOpRedis(); const fakeInternalEventService = new FakeInternalEventService(); super( fakeRedis, fakeRedis, usersRepository, userProfilesRepository, mutingsRepository, blockingsRepository, renoteMutingsRepository, followingsRepository, userEntityService, fakeInternalEventService, ); this.fakeRedis = fakeRedis; this.fakeInternalEventService = fakeInternalEventService; // Override caches this.userByIdCache = new NoOpMemoryKVCache(); this.localUserByNativeTokenCache = new NoOpMemoryKVCache(); this.localUserByIdCache = new NoOpMemoryKVCache(); this.uriPersonCache = new NoOpMemoryKVCache(); this.userProfileCache = NoOpQuantumKVCache.copy(this.userProfileCache, fakeInternalEventService); this.userMutingsCache = NoOpQuantumKVCache.copy(this.userMutingsCache, fakeInternalEventService); this.userBlockingCache = NoOpQuantumKVCache.copy(this.userBlockingCache, fakeInternalEventService); this.userBlockedCache = NoOpQuantumKVCache.copy(this.userBlockedCache, fakeInternalEventService); this.renoteMutingsCache = NoOpQuantumKVCache.copy(this.renoteMutingsCache, fakeInternalEventService); this.userFollowingsCache = NoOpQuantumKVCache.copy(this.userFollowingsCache, fakeInternalEventService); this.userFollowersCache = NoOpQuantumKVCache.copy(this.userFollowersCache, fakeInternalEventService); this.hibernatedUserCache = NoOpQuantumKVCache.copy(this.hibernatedUserCache, fakeInternalEventService); this.userFollowStatsCache = new NoOpMemoryKVCache(); this.translationsCache = NoOpRedisKVCache.copy(this.translationsCache, fakeRedis); } } export class NoOpMemoryKVCache extends MemoryKVCache { constructor() { super(-1); } } export class NoOpMemorySingleCache extends MemorySingleCache { constructor() { super(-1); } } export class NoOpRedisKVCache extends RedisKVCache { constructor(opts?: { redis?: Redis.Redis; fetcher?: RedisKVCache['fetcher']; toRedisConverter?: RedisKVCache['toRedisConverter']; fromRedisConverter?: RedisKVCache['fromRedisConverter']; }) { super( opts?.redis ?? noOpRedis(), 'no-op', { lifetime: -1, memoryCacheLifetime: -1, fetcher: opts?.fetcher, toRedisConverter: opts?.toRedisConverter, fromRedisConverter: opts?.fromRedisConverter, }, ); } public static copy(cache: RedisKVCache, redis?: Redis.Redis): NoOpRedisKVCache { return new NoOpRedisKVCache({ redis, fetcher: cache.fetcher, toRedisConverter: cache.toRedisConverter, fromRedisConverter: cache.fromRedisConverter, }); } } export class NoOpRedisSingleCache extends RedisSingleCache { constructor(opts?: { redis?: Redis.Redis; fetcher?: RedisSingleCache['fetcher']; toRedisConverter?: RedisSingleCache['toRedisConverter']; fromRedisConverter?: RedisSingleCache['fromRedisConverter']; }) { super( opts?.redis ?? noOpRedis(), 'no-op', { lifetime: -1, memoryCacheLifetime: -1, fetcher: opts?.fetcher, toRedisConverter: opts?.toRedisConverter, fromRedisConverter: opts?.fromRedisConverter, }, ); } public static copy(cache: RedisSingleCache, redis?: Redis.Redis): NoOpRedisSingleCache { return new NoOpRedisSingleCache({ redis, fetcher: cache.fetcher, toRedisConverter: cache.toRedisConverter, fromRedisConverter: cache.fromRedisConverter, }); } } export class NoOpQuantumKVCache extends QuantumKVCache { constructor(opts: Omit, 'lifetime'> & { internalEventService?: InternalEventService, }) { super( opts.internalEventService ?? new FakeInternalEventService(), 'no-op', { ...opts, lifetime: -1, }, ); } public static copy(cache: QuantumKVCache, internalEventService?: InternalEventService): NoOpQuantumKVCache { return new NoOpQuantumKVCache({ internalEventService, fetcher: cache.fetcher, bulkFetcher: cache.bulkFetcher, onChanged: cache.onChanged, }); } }