Barazo AppView backend barazo.forum
at main 106 lines 3.0 kB view raw
1import { and, eq, inArray } from 'drizzle-orm' 2import type { Database } from '../db/index.js' 3import { users } from '../db/schema/users.js' 4import { communityProfiles } from '../db/schema/community-profiles.js' 5import { resolveProfile, type SourceProfile, type CommunityOverride } from './resolve-profile.js' 6 7/** 8 * Compact author profile for embedding in topic/reply responses. 9 * Intentionally excludes bannerUrl and bio to keep payloads small. 10 */ 11export interface AuthorProfile { 12 did: string 13 handle: string 14 displayName: string | null 15 avatarUrl: string | null 16} 17 18/** 19 * Batch-resolve author profiles for a list of DIDs. 20 * 21 * When `communityDid` is provided, per-community profile overrides are 22 * applied (display name, avatar) via `resolveProfile()`. 23 * 24 * Returns a Map keyed by DID for O(1) lookup during serialization. 25 */ 26export async function resolveAuthors( 27 dids: string[], 28 communityDid: string | null, 29 db: Database 30): Promise<Map<string, AuthorProfile>> { 31 const uniqueDids = [...new Set(dids)] 32 if (uniqueDids.length === 0) { 33 return new Map() 34 } 35 36 // Batch query 1: source profiles from users table 37 const userRows: SourceProfile[] = await db 38 .select({ 39 did: users.did, 40 handle: users.handle, 41 displayName: users.displayName, 42 avatarUrl: users.avatarUrl, 43 bannerUrl: users.bannerUrl, 44 bio: users.bio, 45 }) 46 .from(users) 47 .where(inArray(users.did, uniqueDids)) 48 49 const sourceMap = new Map<string, SourceProfile>() 50 for (const row of userRows) { 51 sourceMap.set(row.did, row) 52 } 53 54 // Batch query 2: community profile overrides (only when community context exists) 55 const overrideMap = new Map<string, CommunityOverride>() 56 if (communityDid) { 57 const overrideRows = await db 58 .select({ 59 did: communityProfiles.did, 60 displayName: communityProfiles.displayName, 61 avatarUrl: communityProfiles.avatarUrl, 62 bannerUrl: communityProfiles.bannerUrl, 63 bio: communityProfiles.bio, 64 }) 65 .from(communityProfiles) 66 .where( 67 and( 68 inArray(communityProfiles.did, uniqueDids), 69 eq(communityProfiles.communityDid, communityDid) 70 ) 71 ) 72 73 for (const row of overrideRows) { 74 overrideMap.set(row.did, { 75 displayName: row.displayName, 76 avatarUrl: row.avatarUrl, 77 bannerUrl: row.bannerUrl, 78 bio: row.bio, 79 }) 80 } 81 } 82 83 // Merge: resolve each DID using resolveProfile, then project to AuthorProfile 84 const result = new Map<string, AuthorProfile>() 85 for (const did of uniqueDids) { 86 const source = sourceMap.get(did) ?? { 87 did, 88 handle: did, 89 displayName: null, 90 avatarUrl: null, 91 bannerUrl: null, 92 bio: null, 93 } 94 95 const resolved = resolveProfile(source, overrideMap.get(did) ?? null) 96 97 result.set(did, { 98 did: resolved.did, 99 handle: resolved.handle, 100 displayName: resolved.displayName, 101 avatarUrl: resolved.avatarUrl, 102 }) 103 } 104 105 return result 106}