refactor(federation): extract getOurOrigin() into shared federationAuth export

Consolidates 4 inline constructions of the instance origin URL into a
single shared function. Removes the private copy in federationWorker
and two ad-hoc domainOrigin variables in federationOutbox.
This commit is contained in:
Jannis Braun
2026-03-26 18:39:42 +01:00
parent 10d4ca0110
commit 22664a6a14
3 changed files with 18 additions and 17 deletions
@@ -1,4 +1,5 @@
import { createHmac, randomBytes, timingSafeEqual } from 'node:crypto'; import { createHmac, randomBytes, timingSafeEqual } from 'node:crypto';
import { config } from '../config.js';
const DEFAULT_MAX_AGE_MS = 15 * 60 * 1000; // 15 minutes const DEFAULT_MAX_AGE_MS = 15 * 60 * 1000; // 15 minutes
@@ -122,3 +123,14 @@ export function parseFederationHeaders(
return { origin, timestamp: timestampMs, signature }; return { origin, timestamp: timestampMs, signature };
} }
/**
* Return the canonical origin URL for this instance.
* Uses DOMAIN env var for production, falls back to localhost for dev.
*/
export function getOurOrigin(): string {
if (config.domain) {
return `https://${config.domain}`;
}
return `http://localhost:${config.port}`;
}
@@ -4,7 +4,7 @@ import { eq, and } from 'drizzle-orm';
import { generateSnowflake } from './snowflake.js'; import { generateSnowflake } from './snowflake.js';
import crypto from 'node:crypto'; import crypto from 'node:crypto';
import type { FederationRelayEvent, FederationRelayParticipant, FederationRelayAttachment, DmMessageWithUser } from '@backspace/shared'; import type { FederationRelayEvent, FederationRelayParticipant, FederationRelayAttachment, DmMessageWithUser } from '@backspace/shared';
import { config } from '../config.js'; import { getOurOrigin } from './federationAuth.js';
// ─── Settings Cache ────────────────────────────────────────────────────────── // ─── Settings Cache ──────────────────────────────────────────────────────────
@@ -229,7 +229,7 @@ export function getDmParticipants(dmChannelId: string): FederationRelayParticipa
.where(eq(schema.dmMembers.dmChannelId, dmChannelId)) .where(eq(schema.dmMembers.dmChannelId, dmChannelId))
.all(); .all();
const domainOrigin = config.domain ? `https://${config.domain}` : ''; const domainOrigin = getOurOrigin();
return members.map(m => ({ return members.map(m => ({
homeUserId: m.homeUserId || m.id, homeUserId: m.homeUserId || m.id,
@@ -252,7 +252,7 @@ export function queueDmRelay(
dmChannelId: string, dmChannelId: string,
eventType: 'create' | 'update', eventType: 'create' | 'update',
): void { ): void {
const domainOrigin = config.domain ? `https://${config.domain}` : `http://localhost:${config.port}`; const domainOrigin = getOurOrigin();
const attachments: FederationRelayAttachment[] = (message.attachments ?? []).map(a => ({ const attachments: FederationRelayAttachment[] = (message.attachments ?? []).map(a => ({
id: a.id, id: a.id,
@@ -300,7 +300,7 @@ export function buildRelayPayload(
return { return {
userId: user.id, userId: user.id,
homeUserId: user.homeUserId || user.id, homeUserId: user.homeUserId || user.id,
homeInstance: user.homeInstance || config.domain || '', homeInstance: user.homeInstance || getOurOrigin(),
content: message.content, content: message.content,
replyToId: message.replyToId ?? null, replyToId: message.replyToId ?? null,
editedAt: message.editedAt ?? null, editedAt: message.editedAt ?? null,
+2 -13
View File
@@ -3,7 +3,7 @@ import * as schema from '../db/schema.js';
import { eq, and, lte, asc, inArray } from 'drizzle-orm'; import { eq, and, lte, asc, inArray } from 'drizzle-orm';
import { config } from '../config.js'; import { config } from '../config.js';
import { isFederationRelayEnabled } from './federationOutbox.js'; import { isFederationRelayEnabled } from './federationOutbox.js';
import { buildFederationHeaders } from './federationAuth.js'; import { buildFederationHeaders, getOurOrigin } from './federationAuth.js';
import { generateSnowflake } from './snowflake.js'; import { generateSnowflake } from './snowflake.js';
import { getDmMessageWithUser } from '../routes/dm.js'; import { getDmMessageWithUser } from '../routes/dm.js';
import { connectionManager } from '../ws/handler.js'; import { connectionManager } from '../ws/handler.js';
@@ -63,17 +63,6 @@ function getBackoffMs(attempt: number): number {
return BACKOFF_SCHEDULE_MS[Math.max(0, index)] ?? 86_400_000; return BACKOFF_SCHEDULE_MS[Math.max(0, index)] ?? 86_400_000;
} }
/**
* Build the origin URL for this instance.
* Uses DOMAIN env var for production, falls back to localhost for dev.
*/
function getOurOrigin(): string {
if (config.domain) {
return `https://${config.domain}`;
}
return `http://localhost:${config.port}`;
}
/** /**
* Get the effective max upload size from instance settings or config fallback. * Get the effective max upload size from instance settings or config fallback.
*/ */
@@ -627,7 +616,7 @@ async function runInitialSyncForNewPeers(): Promise<void> {
if (unsyncedPeers.length === 0) return; if (unsyncedPeers.length === 0) return;
const ourOrigin = config.domain ? `https://${config.domain}` : `http://localhost:${config.port}`; const ourOrigin = getOurOrigin();
for (const peer of unsyncedPeers) { for (const peer of unsyncedPeers) {
try { try {