feat: queueOutboxEvent creates pending peer placeholders, skips rejected/revoked

This commit is contained in:
Jannis Braun
2026-04-09 13:49:04 +02:00
parent 63a7f0c922
commit d5a6d470f9
+55 -11
View File
@@ -1,10 +1,10 @@
import { getDb } from '../db/index.js'; import { getDb } from '../db/index.js';
import * as schema from '../db/schema.js'; import * as schema from '../db/schema.js';
import { eq, and } from 'drizzle-orm'; import { eq, and, inArray } 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, FederationRelayRequest } from '@backspace/shared'; import type { FederationRelayEvent, FederationRelayParticipant, FederationRelayAttachment, DmMessageWithUser, FederationRelayRequest } from '@backspace/shared';
import { getOurOrigin, buildFederationHeaders } from './federationAuth.js'; import { getOurOrigin, buildFederationHeaders, generateHmacSecret } from './federationAuth.js';
import { extractDomain } from '../routes/federation.js'; import { extractDomain } from '../routes/federation.js';
// ─── Settings Cache ────────────────────────────────────────────────────────── // ─── Settings Cache ──────────────────────────────────────────────────────────
@@ -134,25 +134,69 @@ export function queueOutboxEvent(
const db = getDb(); const db = getDb();
const activePeers = db const peers = db
.select() .select()
.from(schema.federationPeers) .from(schema.federationPeers)
.where(eq(schema.federationPeers.status, 'active')) .where(
inArray(schema.federationPeers.status, ['active', 'pending']),
)
.all(); .all();
if (activePeers.length === 0) { if (peers.length === 0 && !targetPeerOrigins) {
return; return;
} }
// If targetPeerOrigins specified, only queue to those peers // If targetPeerOrigins specified, only queue to those peers
const peers = targetPeerOrigins let matchedPeers = targetPeerOrigins
? activePeers.filter(p => targetPeerOrigins.includes(p.origin)) ? peers.filter(p => targetPeerOrigins.includes(p.origin))
: activePeers; : peers;
if (peers.length === 0) { // For targeted origins with no existing peer record, create pending placeholders
if (targetPeerOrigins) { if (targetPeerOrigins) {
console.warn(`[federation] queueOutboxEvent: zero peers matched targets ${JSON.stringify(targetPeerOrigins)}. Active peer origins: ${JSON.stringify(activePeers.map(p => p.origin))}`); const matchedOrigins = new Set(matchedPeers.map(p => p.origin));
for (const origin of targetPeerOrigins) {
if (matchedOrigins.has(origin)) continue;
// Check if there's a rejected/revoked peer we should skip
const existingPeer = db
.select({ status: schema.federationPeers.status })
.from(schema.federationPeers)
.where(eq(schema.federationPeers.origin, origin))
.get();
if (existingPeer && (existingPeer.status === 'rejected' || existingPeer.status === 'revoked')) {
console.warn(`[federation] queueOutboxEvent: skipping ${existingPeer.status} peer ${origin}`);
continue;
} }
// No peer record at all — create a pending placeholder
const peerId = generateSnowflake();
const now = Date.now();
db.insert(schema.federationPeers)
.values({
id: peerId,
origin,
hmacSecret: generateHmacSecret(),
status: 'pending',
createdAt: now,
})
.run();
const newPeer = db
.select()
.from(schema.federationPeers)
.where(eq(schema.federationPeers.id, peerId))
.get();
if (newPeer) {
matchedPeers = [...matchedPeers, newPeer];
console.log(`[federation] queueOutboxEvent: created pending placeholder for ${origin}`);
}
}
}
if (matchedPeers.length === 0) {
return; return;
} }
@@ -160,7 +204,7 @@ export function queueOutboxEvent(
const now = Date.now(); const now = Date.now();
const expiresAt = now + (ttlDays * 86_400_000); const expiresAt = now + (ttlDays * 86_400_000);
for (const peer of peers) { for (const peer of matchedPeers) {
db.transaction((tx) => { db.transaction((tx) => {
const existing = tx const existing = tx
.select() .select()