import type { SettingValue } from '@rocket.chat/core-typings'; import { Users, Settings } from '@rocket.chat/models'; import { eventTypes } from '@rocket.chat/core-typings'; import { cronJobs } from '@rocket.chat/cron'; import { resolveSRV, resolveTXT } from '../../app/federation/server/functions/resolveDNS'; import { settings, settingsRegistry } from '../../app/settings/server'; import { dispatchEvent } from '../../app/federation/server/handler'; import { getFederationDomain } from '../../app/federation/server/lib/getFederationDomain'; async function updateSetting(id: string, value: SettingValue | null): Promise { if (value !== null) { const setting = settings.get(id); if (setting === undefined) { await settingsRegistry.add(id, value); } else { await Settings.updateValueById(id, value); } } else { await Settings.updateValueById(id, null); } } async function runFederation(): Promise { // Get the settings const siteUrl = settings.get('Site_Url') as string; const { protocol } = new URL(siteUrl); const rocketChatProtocol = protocol.slice(0, -1); const federationDomain = settings.get('FEDERATION_Domain') as string; // Load public key info try { const resolvedTXT = await resolveTXT(`rocketchat-public-key.${federationDomain}`); await updateSetting('FEDERATION_ResolvedPublicKeyTXT', resolvedTXT); } catch (err) { await updateSetting('FEDERATION_ResolvedPublicKeyTXT', null); } // Load legacy tcp protocol info try { const resolvedTXT = await resolveTXT(`rocketchat-tcp-protocol.${federationDomain}`); await updateSetting('FEDERATION_ResolvedProtocolTXT', resolvedTXT); } catch (err) { await updateSetting('FEDERATION_ResolvedProtocolTXT', null); } // Load SRV info try { // If there is a protocol entry on DNS, we use it const protocol = (settings.get('FEDERATION_ResolvedProtocolTXT') as string) ? 'tcp' : rocketChatProtocol; const resolvedSRV = await resolveSRV(`_rocketchat._${protocol}.${federationDomain}`); await updateSetting('FEDERATION_ResolvedSRV', JSON.stringify(resolvedSRV)); } catch (err) { await updateSetting('FEDERATION_ResolvedSRV', '{}'); } // Test if federation is healthy try { void dispatchEvent([getFederationDomain()], { type: eventTypes.PING, }); await updateSetting('FEDERATION_Healthy', true); } catch (err) { await updateSetting('FEDERATION_Healthy', false); } // If federation is healthy, check if there are remote users if (settings.get('FEDERATION_Healthy') as boolean) { const user = await Users.findOne({ isRemote: true }); await updateSetting('FEDERATION_Populated', !!user); } } // eslint-disable-next-line @typescript-eslint/naming-convention export async function federationCron(): Promise { const name = 'Federation'; settings.watch('FEDERATION_Enabled', async (value) => { if (!value) { return cronJobs.remove(name); } await cronJobs.add(name, '* * * * *', async () => runFederation()); }); }