this repo has no description
10
fork

Configure Feed

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

feat(registry): index inline on publish/delete (no Jetstream worker required)

The registry record is written to the project owner's PDS via OAuth
DPoP, but until now the only thing reading those records into the local
Turso index was the Jetstream worker — which isn't deployed yet — so
new entries never appeared on /explore.

Index inline directly inside PUT/DELETE /api/registry/profile:
- After putProfileRecord succeeds, immediately upsertProfile() with the
exact record we just wrote. Uses recordCid + commit.rev returned by
the PDS so /explore knows what version it's looking at.
- After deleteProfileRecord succeeds, immediately deleteProfile() so
the entry disappears from /explore on the next request.
- Both PDS write + index write are idempotent (rkey 'self', SQL ON
CONFLICT DO UPDATE), so retrying a failed publish is safe.
- If the PDS write succeeds but the index write fails, we surface that
in the response so the user knows to retry instead of silently
diverging.

The Jetstream worker remains useful for picking up records authored
outside this app (other tooling), but it's no longer on the critical
path for the user-facing publish flow.

Made-with: Cursor

+61 -9
+61 -9
routes/api/registry/profile.ts
··· 15 15 uploadBlob, 16 16 } from "../../../lib/pds.ts"; 17 17 import { type ProfileRecord, validateProfile } from "../../../lib/lexicons.ts"; 18 + import { deleteProfile, upsertProfile } from "../../../lib/registry.ts"; 18 19 19 20 interface ProfileFormPayload { 20 21 name?: string; ··· 110 111 }); 111 112 } 112 113 114 + let result: Awaited<ReturnType<typeof putProfileRecord>>; 113 115 try { 114 - const result = await putProfileRecord( 116 + result = await putProfileRecord( 115 117 user.did, 116 118 session.pdsUrl, 117 119 validation.value, 118 120 ); 119 - return new Response( 120 - JSON.stringify({ ok: true, uri: result.uri, cid: result.cid }), 121 - { status: 200, headers: { "content-type": "application/json" } }, 122 - ); 123 121 } catch (err) { 124 122 const m = err instanceof Error ? err.message : String(err); 125 123 return new Response(`putRecord failed: ${m}`, { status: 502 }); 126 124 } 125 + 126 + /** 127 + * Index inline so the new entry appears in /explore the moment the 128 + * user hits Publish, without depending on the Jetstream worker. The 129 + * worker is still useful for picking up records authored outside 130 + * this app (e.g. by other tooling), but it isn't on the critical 131 + * path for the user-facing flow. 132 + * 133 + * Both the PDS write and this index write are idempotent (rkey is 134 + * fixed at "self"; the SQL is ON CONFLICT DO UPDATE), so retrying a 135 + * failed publish is always safe. 136 + */ 137 + try { 138 + await upsertProfile({ 139 + did: user.did, 140 + handle: user.handle, 141 + name: validation.value.name, 142 + description: validation.value.description, 143 + category: validation.value.category, 144 + subcategories: validation.value.subcategories ?? [], 145 + website: validation.value.website ?? null, 146 + bskyClient: validation.value.bskyClient ?? null, 147 + tags: validation.value.tags ?? [], 148 + avatarCid: validation.value.avatar?.ref.$link ?? null, 149 + avatarMime: validation.value.avatar?.mimeType ?? null, 150 + pdsUrl: session.pdsUrl, 151 + recordCid: result.cid, 152 + recordRev: result.commit?.rev ?? result.cid, 153 + createdAt: Date.parse(validation.value.createdAt) || Date.now(), 154 + }); 155 + } catch (err) { 156 + const m = err instanceof Error ? err.message : String(err); 157 + console.error("[registry] inline index after putRecord failed:", err); 158 + return new Response( 159 + `Profile saved to your PDS, but indexing it for Explore failed: ${m}. ` + 160 + `Press Publish again to retry.`, 161 + { status: 502 }, 162 + ); 163 + } 164 + 165 + return new Response( 166 + JSON.stringify({ ok: true, uri: result.uri, cid: result.cid }), 167 + { status: 200, headers: { "content-type": "application/json" } }, 168 + ); 127 169 }, 128 170 129 171 async DELETE(ctx) { ··· 135 177 136 178 try { 137 179 await deleteProfileRecord(user.did, session.pdsUrl); 138 - return new Response(JSON.stringify({ ok: true }), { 139 - status: 200, 140 - headers: { "content-type": "application/json" }, 141 - }); 142 180 } catch (err) { 143 181 const m = err instanceof Error ? err.message : String(err); 144 182 return new Response(`deleteRecord failed: ${m}`, { status: 502 }); 145 183 } 184 + 185 + /** Mirror the delete in our local index so /explore stops listing it 186 + * immediately. As above, the Jetstream worker would eventually do 187 + * this too, but we don't want to wait. */ 188 + try { 189 + await deleteProfile(user.did); 190 + } catch (err) { 191 + console.error("[registry] inline delete-from-index failed:", err); 192 + } 193 + 194 + return new Response(JSON.stringify({ ok: true }), { 195 + status: 200, 196 + headers: { "content-type": "application/json" }, 197 + }); 146 198 }, 147 199 });