a a vibe-coded abomination experiment of a fragrance review platform built on the atmosphere.
drydown.social
1/**
2 * Service Detection Utilities
3 *
4 * Detects which AT Protocol service a user is using based on OAuth issuer or PDS URL.
5 * Falls back to Bluesky for unknown/custom PDS instances.
6 */
7
8import { KNOWN_SERVICES, DEFAULT_SERVICE, type ServiceConfig } from '../config/services'
9
10/**
11 * Detect which service a user is using based on OAuth issuer or PDS URL
12 *
13 * @param issuer - OAuth issuer from session.server.serverMetadata.issuer
14 * @param pdsUrl - Personal Data Server URL
15 * @returns ServiceConfig for the detected service (defaults to Bluesky)
16 */
17export function detectService(issuer?: string, pdsUrl?: string): ServiceConfig {
18 // Try issuer first (most reliable indicator)
19 if (issuer) {
20 for (const service of KNOWN_SERVICES) {
21 if (service.issuerPatterns.some(pattern => issuer.includes(pattern))) {
22 return service
23 }
24 }
25 }
26
27 // Fallback to PDS URL if issuer didn't match
28 if (pdsUrl) {
29 for (const service of KNOWN_SERVICES) {
30 if (service.pdsPatterns.some(pattern => pdsUrl.includes(pattern))) {
31 return service
32 }
33 }
34 }
35
36 // Default to Bluesky for unknown/custom services
37 return DEFAULT_SERVICE
38}
39
40/**
41 * Detect service for a specific DID by fetching their DID document
42 *
43 * @param did - AT Protocol DID (e.g., "did:plc:...")
44 * @returns ServiceConfig for the user's service
45 */
46export async function detectServiceForDid(did: string): Promise<ServiceConfig> {
47 try {
48 // Fetch DID document to get PDS URL
49 const didDoc = await fetch(`https://plc.directory/${did}`)
50 const didDocData = await didDoc.json()
51
52 // Look for PDS service endpoint
53 const pdsService = didDocData.service?.find(
54 (s: any) => s.type === 'AtprotoPersonalDataServer'
55 )
56
57 if (pdsService?.serviceEndpoint) {
58 return detectService(undefined, pdsService.serviceEndpoint)
59 }
60 } catch (error) {
61 console.warn('Failed to detect service for DID:', did, error)
62 }
63
64 return DEFAULT_SERVICE
65}
66
67/**
68 * Detect service for a specific handle by resolving their DID document
69 *
70 * @param handle - AT Protocol handle (e.g., "user.bsky.social")
71 * @returns ServiceConfig for the user's service
72 */
73export async function detectServiceForHandle(handle: string): Promise<ServiceConfig> {
74 try {
75 // Try multiple resolvers for better cross-service compatibility
76 const resolvers = [
77 `https://bsky.social/xrpc/com.atproto.identity.resolveHandle?handle=${handle}`,
78 `https://blacksky.app/xrpc/com.atproto.identity.resolveHandle?handle=${handle}`,
79 ]
80
81 let did: string | null = null
82
83 for (const resolver of resolvers) {
84 try {
85 const didResolution = await fetch(resolver)
86 if (didResolution.ok) {
87 const didData = await didResolution.json()
88 if (didData.did) {
89 did = didData.did
90 break
91 }
92 }
93 } catch {
94 // Try next resolver
95 continue
96 }
97 }
98
99 if (!did) {
100 return DEFAULT_SERVICE
101 }
102
103 return await detectServiceForDid(did)
104 } catch (error) {
105 console.warn('Failed to detect service for handle:', handle, error)
106 }
107
108 return DEFAULT_SERVICE
109}