mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-18 21:45:31 +09:00
refactor(api): collapse single-impl interfaces and facades (#3382)
This commit is contained in:
1 parent
e7b13fd19f
commit
c16d414d93
426 files changed
+2085
-6445
No files matched your search
@@ -8,7 +8,7 @@ import {resolveLimitSafe} from '@app/api/limits/LimitConfigUtils';
|
||||
import {createLimitMatchContext} from '@app/api/limits/LimitMatchContextBuilder';
|
||||
import type {User} from '@app/api/models/User';
|
||||
import {isPremiumTieringActive} from '@app/api/stripe/BillingConfigCache';
|
||||
import type {IUserRepository} from '@app/api/user/IUserRepository';
|
||||
import type {UserRepository} from '@app/api/user/repositories/UserRepository';
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {NON_SELF_HOSTED_RESERVED_DISCRIMINATORS} from '@fluxer/constants/src/DiscriminatorConstants';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
@@ -59,7 +59,7 @@ export class DiscriminatorService implements IDiscriminatorService {
|
||||
private static readonly DISCRIM_CACHE_TTL_S = seconds('30 seconds');
|
||||
|
||||
constructor(
|
||||
private userRepository: IUserRepository,
|
||||
private userRepository: UserRepository,
|
||||
private cacheService: ICacheService,
|
||||
private limitConfigService: LimitConfigService,
|
||||
) {}
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
createGuildID,
|
||||
createUserID,
|
||||
} from '@app/api/BrandedTypes';
|
||||
import {ChannelRepository} from '@app/api/channel/repositories/ChannelRepository';
|
||||
import {ChannelRepository} from '@app/api/channel/ChannelRepository';
|
||||
import {setCassandraQueryExecutorForTesting, upsertOne} from '@app/api/database/CassandraQueryExecution';
|
||||
import type {ChannelRow} from '@app/api/database/types/ChannelTypes';
|
||||
import {
|
||||
|
||||
@@ -1,43 +1,80 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import crypto from 'node:crypto';
|
||||
import {createReadStream} from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import {Config} from '@app/api/Config';
|
||||
import {Logger} from '@app/api/Logger';
|
||||
import type {ICacheService} from '@pkgs/cache/src/ICacheService';
|
||||
import {VirusHashCache} from '@pkgs/virus_scan/src/cache/VirusHashCache';
|
||||
import type {IVirusScanService} from '@pkgs/virus_scan/src/IVirusScanService';
|
||||
import {ClamAVProvider} from '@pkgs/virus_scan/src/providers/ClamAVProvider';
|
||||
import type {VirusScanProviderResult} from '@pkgs/virus_scan/src/VirusScanProviderResult';
|
||||
import type {VirusScanResult} from '@pkgs/virus_scan/src/VirusScanResult';
|
||||
import {VirusScanService as SharedVirusScanService} from '@pkgs/virus_scan/src/VirusScanService';
|
||||
import {seconds} from 'itty-time';
|
||||
|
||||
function describeError(error: unknown): string {
|
||||
if (typeof error === 'string') {
|
||||
return error;
|
||||
}
|
||||
if (error instanceof Error) {
|
||||
return error.message;
|
||||
}
|
||||
return 'Unknown error';
|
||||
}
|
||||
|
||||
export class VirusScanService implements IVirusScanService {
|
||||
readonly enabled = true;
|
||||
private readonly service: SharedVirusScanService;
|
||||
private readonly provider = new ClamAVProvider({host: Config.clamav.host, port: Config.clamav.port});
|
||||
private readonly failOpen = Config.clamav.failOpen;
|
||||
|
||||
constructor(cacheService: ICacheService) {
|
||||
const provider = new ClamAVProvider({
|
||||
host: Config.clamav.host,
|
||||
port: Config.clamav.port,
|
||||
});
|
||||
const virusHashCache = new VirusHashCache(cacheService);
|
||||
this.service = new SharedVirusScanService({
|
||||
provider,
|
||||
virusHashCache,
|
||||
logger: Logger,
|
||||
config: {
|
||||
failOpen: Config.clamav.failOpen,
|
||||
},
|
||||
});
|
||||
}
|
||||
constructor(private cacheService: ICacheService) {}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
await this.service.initialize();
|
||||
}
|
||||
async initialize(): Promise<void> {}
|
||||
|
||||
async scanFile(filePath: string): Promise<VirusScanResult> {
|
||||
return this.service.scanFile(filePath);
|
||||
const hash = crypto.createHash('sha256');
|
||||
const stream = createReadStream(filePath, {highWaterMark: 1024 * 1024});
|
||||
try {
|
||||
for await (const chunk of stream) {
|
||||
hash.update(chunk as Buffer);
|
||||
}
|
||||
} catch (error) {
|
||||
stream.destroy();
|
||||
throw error;
|
||||
}
|
||||
return this.scan(hash.digest('hex'), path.basename(filePath), () => this.provider.scanFile(filePath));
|
||||
}
|
||||
|
||||
async scanBuffer(buffer: Buffer, filename: string): Promise<VirusScanResult> {
|
||||
return this.service.scanBuffer(buffer, filename);
|
||||
const fileHash = crypto.createHash('sha256').update(buffer).digest('hex');
|
||||
return this.scan(fileHash, filename, () => this.provider.scanBuffer(buffer));
|
||||
}
|
||||
|
||||
private async scan(
|
||||
fileHash: string,
|
||||
filename: string,
|
||||
run: () => Promise<VirusScanProviderResult>,
|
||||
): Promise<VirusScanResult> {
|
||||
const cacheKey = `virus:${fileHash}`;
|
||||
if ((await this.cacheService.get(cacheKey)) != null) {
|
||||
return {isClean: false, threat: 'Cached virus signature', fileHash};
|
||||
}
|
||||
try {
|
||||
const scanResult = await run();
|
||||
if (scanResult.isClean) {
|
||||
return {isClean: true, fileHash};
|
||||
}
|
||||
if (!scanResult.threat) {
|
||||
throw new Error('Virus scan provider returned infected status without threat name');
|
||||
}
|
||||
await this.cacheService.set(cacheKey, 'true', seconds('7 days'));
|
||||
return {isClean: false, threat: scanResult.threat, fileHash};
|
||||
} catch (error) {
|
||||
Logger.error({error: describeError(error), filename, fileHash}, 'Virus scan failed');
|
||||
if (this.failOpen) {
|
||||
return {isClean: true, fileHash};
|
||||
}
|
||||
throw new Error(`Virus scan failed: ${describeError(error)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import type {HonoEnv} from '@app/api/types/HonoEnv';
|
||||
import {lookupGeoip} from '@app/api/utils/IpUtils';
|
||||
import {getRequestClientIp} from '@app/api/utils/RequestClientIp';
|
||||
import {stripApiPrefix} from '@app/api/utils/RequestPathUtils';
|
||||
import {parseAcceptLanguage} from '@pkgs/locale/src/LocaleService';
|
||||
import {parseAcceptLanguage} from '@pkgs/locale/src/resolution/AcceptLanguageNegotiation';
|
||||
import type {Context} from 'hono';
|
||||
import {createMiddleware} from 'hono/factory';
|
||||
|
||||
|
||||
@@ -4,8 +4,7 @@ import type {GuildID, UserID} from '@app/api/BrandedTypes';
|
||||
import {emitActivity} from '@app/api/infrastructure/activity/ActivityEvents';
|
||||
import {anonymousActivityMeta} from '@app/api/infrastructure/activity/ActivityMeta';
|
||||
import type {BanBy, ReportOutcome, ReportTarget, ResolvedBy} from '@app/api/infrastructure/activity/Contract.generated';
|
||||
import type {IARSubmission} from '@app/api/report/IReportRepository';
|
||||
import {ReportType} from '@app/api/report/IReportRepository';
|
||||
import {type IARSubmission, ReportType} from '@app/api/report/ReportModels';
|
||||
|
||||
interface GuildBanFacts {
|
||||
guildId: GuildID;
|
||||
|
||||
@@ -6,7 +6,7 @@ import type {ILiveKitService} from '@app/api/infrastructure/ILiveKitService';
|
||||
import type {IVoiceRoomStore} from '@app/api/infrastructure/IVoiceRoomStore';
|
||||
import {SERVER_MUTE_ATTRIBUTE} from '@app/api/infrastructure/LiveKitService';
|
||||
import {LiveKitWebhookService} from '@app/api/infrastructure/LiveKitWebhookService';
|
||||
import type {IVoiceRepository} from '@app/api/voice/IVoiceRepository';
|
||||
import type {VoiceRepository} from '@app/api/voice/VoiceRepository';
|
||||
import {VoiceTopology} from '@app/api/voice/VoiceTopology';
|
||||
import {TrackSource, type WebhookEvent} from 'livekit-server-sdk';
|
||||
import {describe, expect, it, vi} from 'vitest';
|
||||
@@ -33,7 +33,7 @@ function harness(pinnedServerId: string | null) {
|
||||
voiceRoomStore,
|
||||
gatewayService,
|
||||
liveKitService,
|
||||
new VoiceTopology({} as unknown as IVoiceRepository, null),
|
||||
new VoiceTopology({} as unknown as VoiceRepository, null),
|
||||
);
|
||||
return {service, deleteRoomServer};
|
||||
}
|
||||
@@ -92,7 +92,7 @@ function trackHarness() {
|
||||
{} as unknown as IVoiceRoomStore,
|
||||
{} as unknown as IGatewayService,
|
||||
{muteMicrophoneTrack} as unknown as ILiveKitService,
|
||||
new VoiceTopology({} as unknown as IVoiceRepository, null),
|
||||
new VoiceTopology({} as unknown as VoiceRepository, null),
|
||||
);
|
||||
return {service, muteMicrophoneTrack};
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user