407 lines
17 KiB
TypeScript
407 lines
17 KiB
TypeScript
/*
|
|
* Copyright (C) 2026 Fluxer Contributors
|
|
*
|
|
* This file is part of Fluxer.
|
|
*
|
|
* Fluxer is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Fluxer is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
import {AdminRepository} from '@fluxer/api/src/admin/AdminRepository';
|
|
import {AdminArchiveRepository} from '@fluxer/api/src/admin/repositories/AdminArchiveRepository';
|
|
import {SystemDmJobRepository} from '@fluxer/api/src/admin/repositories/SystemDmJobRepository';
|
|
import {Config} from '@fluxer/api/src/Config';
|
|
import {ChannelRepository} from '@fluxer/api/src/channel/ChannelRepository';
|
|
import {ChannelService} from '@fluxer/api/src/channel/services/ChannelService';
|
|
import {ConnectionRepository} from '@fluxer/api/src/connection/ConnectionRepository';
|
|
import {ConnectionService} from '@fluxer/api/src/connection/ConnectionService';
|
|
import {CsamEvidenceRetentionService} from '@fluxer/api/src/csam/CsamEvidenceRetentionService';
|
|
import {CsamScanJobService} from '@fluxer/api/src/csam/CsamScanJobService';
|
|
import {createEmailProvider} from '@fluxer/api/src/email/EmailProviderFactory';
|
|
import {FavoriteMemeRepository} from '@fluxer/api/src/favorite_meme/FavoriteMemeRepository';
|
|
import {GuildAuditLogService} from '@fluxer/api/src/guild/GuildAuditLogService';
|
|
import {GuildRepository} from '@fluxer/api/src/guild/repositories/GuildRepository';
|
|
import {ExpressionAssetPurger} from '@fluxer/api/src/guild/services/content/ExpressionAssetPurger';
|
|
import {GuildService} from '@fluxer/api/src/guild/services/GuildService';
|
|
import {AssetDeletionQueue} from '@fluxer/api/src/infrastructure/AssetDeletionQueue';
|
|
import {AvatarService} from '@fluxer/api/src/infrastructure/AvatarService';
|
|
import {CloudflarePurgeQueue, NoopPurgeQueue} from '@fluxer/api/src/infrastructure/CloudflarePurgeQueue';
|
|
import {DisabledLiveKitService} from '@fluxer/api/src/infrastructure/DisabledLiveKitService';
|
|
import {DiscriminatorService} from '@fluxer/api/src/infrastructure/DiscriminatorService';
|
|
import {EmbedService} from '@fluxer/api/src/infrastructure/EmbedService';
|
|
import {EntityAssetService} from '@fluxer/api/src/infrastructure/EntityAssetService';
|
|
import type {IGatewayService} from '@fluxer/api/src/infrastructure/IGatewayService';
|
|
import type {ILiveKitService} from '@fluxer/api/src/infrastructure/ILiveKitService';
|
|
import type {IMediaService} from '@fluxer/api/src/infrastructure/IMediaService';
|
|
import {InMemoryVoiceRoomStore} from '@fluxer/api/src/infrastructure/InMemoryVoiceRoomStore';
|
|
import type {IStorageService} from '@fluxer/api/src/infrastructure/IStorageService';
|
|
import type {IVoiceRoomStore} from '@fluxer/api/src/infrastructure/IVoiceRoomStore';
|
|
import {KVAccountDeletionQueueService} from '@fluxer/api/src/infrastructure/KVAccountDeletionQueueService';
|
|
import {KVActivityTracker} from '@fluxer/api/src/infrastructure/KVActivityTracker';
|
|
import {KVBulkMessageDeletionQueueService} from '@fluxer/api/src/infrastructure/KVBulkMessageDeletionQueueService';
|
|
import {LiveKitService} from '@fluxer/api/src/infrastructure/LiveKitService';
|
|
import type {SnowflakeService} from '@fluxer/api/src/infrastructure/SnowflakeService';
|
|
import {createStorageService} from '@fluxer/api/src/infrastructure/StorageServiceFactory';
|
|
import {UnfurlerService} from '@fluxer/api/src/infrastructure/UnfurlerService';
|
|
import {UserCacheService} from '@fluxer/api/src/infrastructure/UserCacheService';
|
|
import {VirusScanService} from '@fluxer/api/src/infrastructure/VirusScanService';
|
|
import {VoiceRoomStore} from '@fluxer/api/src/infrastructure/VoiceRoomStore';
|
|
import {InstanceConfigRepository} from '@fluxer/api/src/instance/InstanceConfigRepository';
|
|
import {InviteRepository} from '@fluxer/api/src/invite/InviteRepository';
|
|
import {InviteService} from '@fluxer/api/src/invite/InviteService';
|
|
import {Logger} from '@fluxer/api/src/Logger';
|
|
import {LimitConfigService} from '@fluxer/api/src/limits/LimitConfigService';
|
|
import {
|
|
getGatewayService,
|
|
getInjectedS3Service,
|
|
getKVClient,
|
|
getMediaService,
|
|
getWorkerService,
|
|
} from '@fluxer/api/src/middleware/ServiceRegistry';
|
|
import {ApplicationRepository} from '@fluxer/api/src/oauth/repositories/ApplicationRepository';
|
|
import {OAuth2TokenRepository} from '@fluxer/api/src/oauth/repositories/OAuth2TokenRepository';
|
|
import {PackRepository} from '@fluxer/api/src/pack/PackRepository';
|
|
import {PackService} from '@fluxer/api/src/pack/PackService';
|
|
import {ReadStateRepository} from '@fluxer/api/src/read_state/ReadStateRepository';
|
|
import {ReadStateService} from '@fluxer/api/src/read_state/ReadStateService';
|
|
import {ReportRepository} from '@fluxer/api/src/report/ReportRepository';
|
|
import {PaymentRepository} from '@fluxer/api/src/user/repositories/PaymentRepository';
|
|
import {UserContactChangeLogRepository} from '@fluxer/api/src/user/repositories/UserContactChangeLogRepository';
|
|
import {UserRepository} from '@fluxer/api/src/user/repositories/UserRepository';
|
|
import {UserContactChangeLogService} from '@fluxer/api/src/user/services/UserContactChangeLogService';
|
|
import {UserDeletionEligibilityService} from '@fluxer/api/src/user/services/UserDeletionEligibilityService';
|
|
import {UserHarvestRepository} from '@fluxer/api/src/user/UserHarvestRepository';
|
|
import {UserPermissionUtils} from '@fluxer/api/src/utils/UserPermissionUtils';
|
|
import {VoiceRepository} from '@fluxer/api/src/voice/VoiceRepository';
|
|
import {VoiceTopology} from '@fluxer/api/src/voice/VoiceTopology';
|
|
import {WebhookRepository} from '@fluxer/api/src/webhook/WebhookRepository';
|
|
import {KVCacheProvider} from '@fluxer/cache/src/providers/KVCacheProvider';
|
|
import {EmailI18nService} from '@fluxer/email/src/EmailI18nService';
|
|
import type {EmailConfig, UserBouncedEmailChecker} from '@fluxer/email/src/EmailProviderTypes';
|
|
import {EmailService} from '@fluxer/email/src/EmailService';
|
|
import type {IEmailService} from '@fluxer/email/src/IEmailService';
|
|
import {TestEmailService} from '@fluxer/email/src/TestEmailService';
|
|
import type {IKVProvider} from '@fluxer/kv_client/src/IKVProvider';
|
|
import {RateLimitService} from '@fluxer/rate_limit/src/RateLimitService';
|
|
import type {IWorkerService} from '@fluxer/worker/src/contracts/IWorkerService';
|
|
import Stripe from 'stripe';
|
|
|
|
let _workerTestEmailService: TestEmailService | null = null;
|
|
function getWorkerTestEmailService(): TestEmailService {
|
|
if (!_workerTestEmailService) {
|
|
_workerTestEmailService = new TestEmailService();
|
|
}
|
|
return _workerTestEmailService;
|
|
}
|
|
|
|
export interface WorkerDependencies {
|
|
kvClient: IKVProvider;
|
|
snowflakeService: SnowflakeService;
|
|
limitConfigService: LimitConfigService;
|
|
|
|
userRepository: UserRepository;
|
|
channelRepository: ChannelRepository;
|
|
guildRepository: GuildRepository;
|
|
favoriteMemeRepository: FavoriteMemeRepository;
|
|
applicationRepository: ApplicationRepository;
|
|
oauth2TokenRepository: OAuth2TokenRepository;
|
|
readStateRepository: ReadStateRepository;
|
|
adminRepository: AdminRepository;
|
|
reportRepository: ReportRepository;
|
|
paymentRepository: PaymentRepository;
|
|
userHarvestRepository: UserHarvestRepository;
|
|
adminArchiveRepository: AdminArchiveRepository;
|
|
systemDmJobRepository: SystemDmJobRepository;
|
|
voiceRepository: VoiceRepository | null;
|
|
connectionRepository: ConnectionRepository;
|
|
connectionService: ConnectionService;
|
|
|
|
cacheService: KVCacheProvider;
|
|
userCacheService: UserCacheService;
|
|
storageService: IStorageService;
|
|
assetDeletionQueue: AssetDeletionQueue;
|
|
purgeQueue: CloudflarePurgeQueue | NoopPurgeQueue;
|
|
|
|
gatewayService: IGatewayService;
|
|
mediaService: IMediaService;
|
|
discriminatorService: DiscriminatorService;
|
|
avatarService: AvatarService;
|
|
virusScanService: VirusScanService;
|
|
rateLimitService: RateLimitService;
|
|
emailService: IEmailService;
|
|
inviteService: InviteService;
|
|
|
|
workerService: IWorkerService;
|
|
unfurlerService: UnfurlerService;
|
|
embedService: EmbedService;
|
|
readStateService: ReadStateService;
|
|
userPermissionUtils: UserPermissionUtils;
|
|
activityTracker: KVActivityTracker;
|
|
deletionQueueService: KVAccountDeletionQueueService;
|
|
bulkMessageDeletionQueueService: KVBulkMessageDeletionQueueService;
|
|
deletionEligibilityService: UserDeletionEligibilityService;
|
|
|
|
voiceRoomStore: IVoiceRoomStore;
|
|
liveKitService: ILiveKitService;
|
|
voiceTopology: VoiceTopology | null;
|
|
|
|
channelService: ChannelService;
|
|
guildAuditLogService: GuildAuditLogService;
|
|
contactChangeLogService: UserContactChangeLogService;
|
|
csamEvidenceRetentionService: CsamEvidenceRetentionService;
|
|
|
|
stripe: Stripe | null;
|
|
csamScanJobService: CsamScanJobService;
|
|
}
|
|
|
|
export async function initializeWorkerDependencies(snowflakeService: SnowflakeService): Promise<WorkerDependencies> {
|
|
Logger.info('Initializing worker dependencies...');
|
|
|
|
const kvClient = getKVClient();
|
|
|
|
const userRepository = new UserRepository();
|
|
const channelRepository = new ChannelRepository();
|
|
const guildRepository = new GuildRepository();
|
|
const favoriteMemeRepository = new FavoriteMemeRepository();
|
|
const applicationRepository = new ApplicationRepository();
|
|
const oauth2TokenRepository = new OAuth2TokenRepository();
|
|
const readStateRepository = new ReadStateRepository();
|
|
const adminRepository = new AdminRepository();
|
|
const adminArchiveRepository = new AdminArchiveRepository();
|
|
const systemDmJobRepository = new SystemDmJobRepository();
|
|
const reportRepository = new ReportRepository();
|
|
const paymentRepository = new PaymentRepository();
|
|
const userHarvestRepository = new UserHarvestRepository();
|
|
const contactChangeLogRepository = new UserContactChangeLogRepository();
|
|
const contactChangeLogService = new UserContactChangeLogService(contactChangeLogRepository);
|
|
const connectionRepository = new ConnectionRepository();
|
|
|
|
const cacheService = new KVCacheProvider({client: kvClient});
|
|
const instanceConfigRepository = new InstanceConfigRepository();
|
|
const limitConfigSubscriber = getKVClient();
|
|
const limitConfigService = new LimitConfigService(instanceConfigRepository, cacheService, limitConfigSubscriber);
|
|
await limitConfigService.initialize();
|
|
limitConfigService.setAsGlobalInstance();
|
|
const userCacheService = new UserCacheService(cacheService, userRepository);
|
|
const storageService = createStorageService({s3Service: getInjectedS3Service()});
|
|
const csamEvidenceRetentionService = new CsamEvidenceRetentionService(storageService);
|
|
const assetDeletionQueue = new AssetDeletionQueue(kvClient);
|
|
const purgeQueue = Config.cloudflare.purgeEnabled ? new CloudflarePurgeQueue(kvClient) : new NoopPurgeQueue();
|
|
|
|
const gatewayService = getGatewayService();
|
|
const connectionService = new ConnectionService(connectionRepository, gatewayService, null);
|
|
const mediaService = getMediaService();
|
|
const discriminatorService = new DiscriminatorService(userRepository, cacheService, limitConfigService);
|
|
const avatarService = new AvatarService(storageService, mediaService, limitConfigService);
|
|
const entityAssetService = new EntityAssetService(
|
|
storageService,
|
|
mediaService,
|
|
assetDeletionQueue,
|
|
limitConfigService,
|
|
);
|
|
const virusScanService = new VirusScanService(cacheService);
|
|
const rateLimitService = new RateLimitService(cacheService);
|
|
const packRepository = new PackRepository();
|
|
const packAssetPurger = new ExpressionAssetPurger(assetDeletionQueue);
|
|
const packService = new PackService(
|
|
packRepository,
|
|
guildRepository,
|
|
avatarService,
|
|
snowflakeService,
|
|
packAssetPurger,
|
|
userRepository,
|
|
userCacheService,
|
|
limitConfigService,
|
|
);
|
|
const emailConfig: EmailConfig = {
|
|
enabled: Config.email.enabled,
|
|
fromEmail: Config.email.fromEmail,
|
|
fromName: Config.email.fromName,
|
|
appBaseUrl: Config.endpoints.webApp,
|
|
marketingBaseUrl: Config.endpoints.marketing,
|
|
};
|
|
|
|
const bouncedEmailChecker: UserBouncedEmailChecker = {
|
|
isEmailBounced: async (email: string) => {
|
|
const user = await userRepository.findByEmail(email);
|
|
return user?.emailBounced ?? false;
|
|
},
|
|
};
|
|
|
|
const emailI18n = new EmailI18nService();
|
|
const emailProvider = createEmailProvider(Config.email);
|
|
const emailService: IEmailService = Config.dev.testModeEnabled
|
|
? getWorkerTestEmailService()
|
|
: new EmailService(emailConfig, emailI18n, emailProvider, bouncedEmailChecker);
|
|
|
|
const workerService = getWorkerService();
|
|
const guildAuditLogService = new GuildAuditLogService(guildRepository, snowflakeService, workerService);
|
|
const unfurlerService = new UnfurlerService(cacheService, mediaService);
|
|
const embedService = new EmbedService(channelRepository, cacheService, unfurlerService, mediaService, workerService);
|
|
const readStateService = new ReadStateService(readStateRepository, gatewayService);
|
|
const userPermissionUtils = new UserPermissionUtils(userRepository, guildRepository);
|
|
const activityTracker = new KVActivityTracker(kvClient);
|
|
const deletionQueueService = new KVAccountDeletionQueueService(kvClient, userRepository);
|
|
const bulkMessageDeletionQueueService = new KVBulkMessageDeletionQueueService(kvClient);
|
|
const deletionEligibilityService = new UserDeletionEligibilityService(kvClient);
|
|
const csamScanJobService = new CsamScanJobService();
|
|
|
|
let voiceRepository: VoiceRepository | null = null;
|
|
let voiceTopology: VoiceTopology | null = null;
|
|
let voiceRoomStore: IVoiceRoomStore;
|
|
let liveKitService: ILiveKitService;
|
|
|
|
if (Config.voice.enabled) {
|
|
voiceRepository = new VoiceRepository();
|
|
voiceTopology = new VoiceTopology(voiceRepository, null);
|
|
await voiceTopology.initialize();
|
|
voiceRoomStore = new VoiceRoomStore(kvClient);
|
|
liveKitService = new LiveKitService(voiceTopology);
|
|
Logger.info('Voice services initialized');
|
|
} else {
|
|
voiceRoomStore = new InMemoryVoiceRoomStore();
|
|
liveKitService = new DisabledLiveKitService();
|
|
}
|
|
|
|
const inviteRepository = new InviteRepository();
|
|
const webhookRepository = new WebhookRepository();
|
|
|
|
const channelService = new ChannelService(
|
|
channelRepository,
|
|
userRepository,
|
|
guildRepository,
|
|
packService,
|
|
userCacheService,
|
|
embedService,
|
|
readStateService,
|
|
cacheService,
|
|
storageService,
|
|
gatewayService,
|
|
mediaService,
|
|
avatarService,
|
|
workerService,
|
|
virusScanService,
|
|
snowflakeService,
|
|
rateLimitService,
|
|
purgeQueue,
|
|
favoriteMemeRepository,
|
|
guildAuditLogService,
|
|
voiceRoomStore,
|
|
liveKitService,
|
|
inviteRepository,
|
|
webhookRepository,
|
|
limitConfigService,
|
|
undefined,
|
|
);
|
|
const guildService = new GuildService(
|
|
guildRepository,
|
|
channelRepository,
|
|
inviteRepository,
|
|
channelService,
|
|
userCacheService,
|
|
gatewayService,
|
|
entityAssetService,
|
|
avatarService,
|
|
assetDeletionQueue,
|
|
userRepository,
|
|
mediaService,
|
|
cacheService,
|
|
snowflakeService,
|
|
rateLimitService,
|
|
workerService,
|
|
webhookRepository,
|
|
guildAuditLogService,
|
|
limitConfigService,
|
|
);
|
|
const inviteService = new InviteService(
|
|
inviteRepository,
|
|
guildService,
|
|
channelService,
|
|
gatewayService,
|
|
guildAuditLogService,
|
|
userRepository,
|
|
packRepository,
|
|
packService,
|
|
limitConfigService,
|
|
);
|
|
|
|
let stripe: Stripe | null = null;
|
|
if (Config.stripe.enabled && Config.stripe.secretKey) {
|
|
stripe = new Stripe(Config.stripe.secretKey, {
|
|
apiVersion: '2026-01-28.clover',
|
|
httpClient: Config.dev.testModeEnabled ? Stripe.createFetchHttpClient() : undefined,
|
|
});
|
|
Logger.info('Stripe initialized');
|
|
}
|
|
|
|
Logger.info('Worker dependencies initialized successfully');
|
|
|
|
return {
|
|
kvClient,
|
|
snowflakeService,
|
|
limitConfigService,
|
|
userRepository,
|
|
channelRepository,
|
|
guildRepository,
|
|
favoriteMemeRepository,
|
|
applicationRepository,
|
|
oauth2TokenRepository,
|
|
readStateRepository,
|
|
adminRepository,
|
|
reportRepository,
|
|
paymentRepository,
|
|
userHarvestRepository,
|
|
adminArchiveRepository,
|
|
systemDmJobRepository,
|
|
voiceRepository,
|
|
connectionRepository,
|
|
connectionService,
|
|
cacheService,
|
|
userCacheService,
|
|
storageService,
|
|
assetDeletionQueue,
|
|
purgeQueue,
|
|
gatewayService,
|
|
mediaService,
|
|
discriminatorService,
|
|
avatarService,
|
|
virusScanService,
|
|
rateLimitService,
|
|
emailService,
|
|
inviteService,
|
|
workerService,
|
|
unfurlerService,
|
|
embedService,
|
|
readStateService,
|
|
userPermissionUtils,
|
|
activityTracker,
|
|
deletionQueueService,
|
|
bulkMessageDeletionQueueService,
|
|
deletionEligibilityService,
|
|
voiceRoomStore,
|
|
liveKitService,
|
|
voiceTopology,
|
|
channelService,
|
|
guildAuditLogService,
|
|
contactChangeLogService,
|
|
csamEvidenceRetentionService,
|
|
stripe,
|
|
csamScanJobService,
|
|
};
|
|
}
|
|
|
|
export async function shutdownWorkerDependencies(_deps: WorkerDependencies): Promise<void> {
|
|
Logger.info('Shutting down worker dependencies...');
|
|
Logger.info('Worker dependencies shut down successfully');
|
|
}
|