[READ ONLY MIRROR] Spark Social AppView Server github.com/sprksocial/server
atproto deno hono lexicon
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 166 lines 4.6 kB view raw
1import { DidDocument, getDid, getHandle, IdResolver } from "@atp/identity"; 2import { Code, DataPlaneError } from "../util.ts"; 3 4// Helper function to format DID document result 5function getResultFromDoc(doc: DidDocument) { 6 const keys: Record<string, { Type: string; PublicKeyMultibase: string }> = {}; 7 doc.verificationMethod?.forEach((method) => { 8 const id = method.id.split("#").at(1); 9 if (!id) return; 10 keys[id] = { 11 Type: method.type, 12 PublicKeyMultibase: method.publicKeyMultibase || "", 13 }; 14 }); 15 16 const services: Record<string, { Type: string; URL: string }> = {}; 17 doc.service?.forEach((service) => { 18 const id = service.id.split("#").at(1); 19 if (!id) return; 20 if (typeof service.serviceEndpoint !== "string") return; 21 services[id] = { 22 Type: service.type, 23 URL: service.serviceEndpoint, 24 }; 25 }); 26 27 return { 28 did: getDid(doc), 29 handle: getHandle(doc), 30 keys: JSON.stringify(keys), 31 services: JSON.stringify(services), 32 updated: new Date().toISOString(), 33 }; 34} 35 36export class Identity { 37 private idResolver?: IdResolver; 38 39 constructor(idResolver?: IdResolver) { 40 this.idResolver = idResolver; 41 } 42 43 async getByDid(did: string) { 44 if (!this.idResolver) { 45 throw new DataPlaneError(Code.InternalError); 46 } 47 48 const doc = await this.idResolver.did.resolve(did); 49 if (!doc) { 50 throw new DataPlaneError(Code.NotFound); 51 } 52 53 const result = getResultFromDoc(doc); 54 return result; 55 } 56 57 async getByHandle(handle: string) { 58 if (!this.idResolver) { 59 throw new DataPlaneError(Code.InternalError); 60 } 61 62 const did = await this.idResolver.handle.resolve(handle); 63 if (!did) { 64 throw new DataPlaneError(Code.NotFound); 65 } 66 67 const doc = await this.idResolver.did.resolve(did); 68 if (!doc || did !== getDid(doc)) { 69 throw new DataPlaneError(Code.NotFound); 70 } 71 72 const result = getResultFromDoc(doc); 73 return result; 74 } 75 76 async resolve(identifier: string, type?: "did" | "handle") { 77 if (!this.idResolver) { 78 throw new DataPlaneError(Code.InternalError); 79 } 80 81 let doc: DidDocument | null = null; 82 let resolvedDid: string | null = null; 83 84 // Auto-detect type if not specified 85 const identifierType = type || 86 (identifier.startsWith("did:") ? "did" : "handle"); 87 88 if (identifierType === "did") { 89 doc = await this.idResolver.did.resolve(identifier); 90 resolvedDid = identifier; 91 } else { 92 resolvedDid = await this.idResolver.handle.resolve(identifier) || null; 93 if (resolvedDid) { 94 doc = await this.idResolver.did.resolve(resolvedDid); 95 } 96 } 97 98 if (!doc || (resolvedDid && resolvedDid !== getDid(doc))) { 99 throw new DataPlaneError(Code.NotFound); 100 } 101 102 const result = getResultFromDoc(doc); 103 return { 104 ...result, 105 resolvedFrom: { 106 identifier, 107 type: identifierType, 108 }, 109 }; 110 } 111 112 async resolveBatch( 113 identifiers: Array<{ value: string; type?: "did" | "handle" }>, 114 ) { 115 if (!this.idResolver) { 116 throw new DataPlaneError(Code.InternalError); 117 } 118 119 const results = await Promise.allSettled( 120 identifiers.map(async ({ value, type }) => { 121 let doc: DidDocument | null = null; 122 let resolvedDid: string | undefined; 123 124 const identifierType = type || 125 (value.startsWith("did:") ? "did" : "handle"); 126 if (identifierType === "did") { 127 doc = await this.idResolver!.did.resolve(value); 128 resolvedDid = value; 129 } else { 130 resolvedDid = await this.idResolver!.handle.resolve(value); 131 if (!resolvedDid) throw new DataPlaneError(Code.NotFound); 132 doc = await this.idResolver!.did.resolve(resolvedDid); 133 } 134 135 if (!doc || (resolvedDid && resolvedDid !== getDid(doc))) { 136 return { 137 identifier: value, 138 type: identifierType, 139 error: "Identity not found", 140 }; 141 } 142 return { 143 identifier: value, 144 type: identifierType, 145 ...getResultFromDoc(doc), 146 }; 147 }), 148 ); 149 150 const resolved = results.map((result, index) => ({ 151 index, 152 success: result.status === "fulfilled", 153 data: result.status === "fulfilled" ? result.value : null, 154 error: result.status === "rejected" ? result.reason?.message : null, 155 })); 156 157 return { 158 results: resolved, 159 summary: { 160 total: identifiers.length, 161 successful: resolved.filter((r) => r.success).length, 162 failed: resolved.filter((r) => !r.success).length, 163 }, 164 }; 165 } 166}