refactor(federation): rename canonicalDmPairId to computeFederatedId
This commit is contained in:
@@ -9,7 +9,7 @@ import { config } from '../config.js';
|
|||||||
import { connectionManager } from '../ws/handler.js';
|
import { connectionManager } from '../ws/handler.js';
|
||||||
import { sanitizeUser } from '../utils/sanitize.js';
|
import { sanitizeUser } from '../utils/sanitize.js';
|
||||||
import { deleteAttachmentFiles } from '../utils/fileCleanup.js';
|
import { deleteAttachmentFiles } from '../utils/fileCleanup.js';
|
||||||
import { canonicalDmPairId, getDmParticipants } from '../utils/federationOutbox.js';
|
import { computeFederatedId, getDmParticipants } from '../utils/federationOutbox.js';
|
||||||
import { getDmMessageWithUser } from './dm.js';
|
import { getDmMessageWithUser } from './dm.js';
|
||||||
import type { FederationRelayRequest, FederationRelayResponse, FederationRelayEvent, FederationRelayAttachment, FederationSyncRequest, FederationSyncResponse, DmMessageWithUser } from '@backspace/shared';
|
import type { FederationRelayRequest, FederationRelayResponse, FederationRelayEvent, FederationRelayAttachment, FederationSyncRequest, FederationSyncResponse, DmMessageWithUser } from '@backspace/shared';
|
||||||
|
|
||||||
@@ -526,11 +526,12 @@ export async function federationRoutes(app: FastifyInstance): Promise<void> {
|
|||||||
limit = Math.max(1, Math.min(500, Math.floor(limit)));
|
limit = Math.max(1, Math.min(500, Math.floor(limit)));
|
||||||
|
|
||||||
// 3. Determine which DM channels to sync.
|
// 3. Determine which DM channels to sync.
|
||||||
// Use canonical_pair_id: any channel with a pair ID is a federated 1-on-1 DM
|
// Use federated_id: any channel with a federated ID is a federated DM
|
||||||
// that should be synced. The peer's relay endpoint will create the channel
|
// that should be synced. The peer's relay endpoint will create the channel
|
||||||
// if it doesn't exist, or match by canonical_pair_id if it does.
|
// if it doesn't exist, or match by federated_id if it does.
|
||||||
const sharedChannelRows = rawDb.prepare(`
|
const sharedChannelRows = rawDb.prepare(`
|
||||||
SELECT id as dm_channel_id FROM dm_channels WHERE canonical_pair_id IS NOT NULL
|
SELECT id as dm_channel_id FROM dm_channels
|
||||||
|
WHERE federated_id IS NOT NULL AND deleted_at IS NULL
|
||||||
`).all() as Array<{ dm_channel_id: string }>;
|
`).all() as Array<{ dm_channel_id: string }>;
|
||||||
|
|
||||||
const sharedChannelIds = sharedChannelRows.map(r => r.dm_channel_id);
|
const sharedChannelIds = sharedChannelRows.map(r => r.dm_channel_id);
|
||||||
@@ -818,25 +819,24 @@ function resolveLocalUser(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find or create a local DM channel for a federated 1-on-1 pair.
|
* Find or create a local DM channel for a federated DM.
|
||||||
* Uses canonical_pair_id for deterministic cross-instance lookup.
|
* Uses federated_id for deterministic cross-instance lookup.
|
||||||
*/
|
*/
|
||||||
function findOrCreateDmChannel(
|
function findOrCreateDmChannel(
|
||||||
canonicalPairId: string,
|
federatedId: string,
|
||||||
localUserIdA: string,
|
localUserIds: string[],
|
||||||
localUserIdB: string,
|
|
||||||
db: ReturnType<typeof getDb>,
|
db: ReturnType<typeof getDb>,
|
||||||
): string {
|
): string {
|
||||||
// Try to find existing channel by canonical pair ID
|
// Try to find existing channel by federated ID
|
||||||
const existing = db
|
const existing = db
|
||||||
.select()
|
.select()
|
||||||
.from(schema.dmChannels)
|
.from(schema.dmChannels)
|
||||||
.where(eq(schema.dmChannels.canonicalPairId, canonicalPairId))
|
.where(eq(schema.dmChannels.federatedId, federatedId))
|
||||||
.get();
|
.get();
|
||||||
|
|
||||||
if (existing) {
|
if (existing) {
|
||||||
// Ensure both users are members (they might have been removed)
|
// Ensure all users are members (they might have been removed)
|
||||||
for (const userId of [localUserIdA, localUserIdB]) {
|
for (const userId of localUserIds) {
|
||||||
const member = db
|
const member = db
|
||||||
.select()
|
.select()
|
||||||
.from(schema.dmMembers)
|
.from(schema.dmMembers)
|
||||||
@@ -861,19 +861,19 @@ function findOrCreateDmChannel(
|
|||||||
return existing.id;
|
return existing.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new DM channel with canonical pair ID
|
// Create new DM channel with federated ID
|
||||||
const channelId = generateSnowflake();
|
const channelId = generateSnowflake();
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
|
||||||
db.insert(schema.dmChannels)
|
db.insert(schema.dmChannels)
|
||||||
.values({
|
.values({
|
||||||
id: channelId,
|
id: channelId,
|
||||||
canonicalPairId,
|
federatedId,
|
||||||
createdAt: now,
|
createdAt: now,
|
||||||
})
|
})
|
||||||
.run();
|
.run();
|
||||||
|
|
||||||
for (const userId of [localUserIdA, localUserIdB]) {
|
for (const userId of localUserIds) {
|
||||||
db.insert(schema.dmMembers)
|
db.insert(schema.dmMembers)
|
||||||
.values({
|
.values({
|
||||||
dmChannelId: channelId,
|
dmChannelId: channelId,
|
||||||
@@ -993,15 +993,14 @@ function processCreateEvent(
|
|||||||
}
|
}
|
||||||
const authorUser = authorEntry.localUser;
|
const authorUser = authorEntry.localUser;
|
||||||
|
|
||||||
// Compute canonical pair ID from participants' home user IDs and find/create channel
|
// Compute federated ID from participants' home user IDs and find/create channel
|
||||||
const pairId = canonicalDmPairId(
|
const federatedId = computeFederatedId(
|
||||||
resolvedParticipants[0]!.homeUserId,
|
resolvedParticipants[0]!.homeUserId,
|
||||||
resolvedParticipants[1]!.homeUserId,
|
resolvedParticipants[1]!.homeUserId,
|
||||||
);
|
);
|
||||||
const localDmChannelId = findOrCreateDmChannel(
|
const localDmChannelId = findOrCreateDmChannel(
|
||||||
pairId,
|
federatedId,
|
||||||
resolvedParticipants[0]!.localUser.id,
|
[resolvedParticipants[0]!.localUser.id, resolvedParticipants[1]!.localUser.id],
|
||||||
resolvedParticipants[1]!.localUser.id,
|
|
||||||
db,
|
db,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -201,13 +201,21 @@ export function queueOutboxEvent(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compute a deterministic canonical DM pair ID from two home user IDs.
|
* Compute a federated ID for a DM channel.
|
||||||
* The pair is sorted lexicographically before hashing to ensure the same
|
*
|
||||||
* result regardless of argument order. Returns first 32 hex chars of SHA-256.
|
* For 1-on-1 DMs: deterministic SHA-256 hash of 2 sorted home user IDs (backward compatible).
|
||||||
|
* For group DMs: call with no arguments to generate a new UUID.
|
||||||
*/
|
*/
|
||||||
export function canonicalDmPairId(homeUserIdA: string, homeUserIdB: string): string {
|
export function computeFederatedId(homeUserIdA: string, homeUserIdB: string): string;
|
||||||
|
export function computeFederatedId(): string;
|
||||||
|
export function computeFederatedId(homeUserIdA?: string, homeUserIdB?: string): string {
|
||||||
|
if (homeUserIdA && homeUserIdB) {
|
||||||
|
// 1-on-1: deterministic pair hash (backward compatible with canonicalDmPairId)
|
||||||
const sorted = [homeUserIdA, homeUserIdB].sort();
|
const sorted = [homeUserIdA, homeUserIdB].sort();
|
||||||
return crypto.createHash('sha256').update(sorted.join(':')).digest('hex').slice(0, 32);
|
return crypto.createHash('sha256').update(sorted.join(':')).digest('hex').slice(0, 32);
|
||||||
|
}
|
||||||
|
// Group: origin-assigned UUID
|
||||||
|
return crypto.randomUUID();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user