diff --git a/packages/ditto/controllers/well-known/nostr.ts b/packages/ditto/controllers/well-known/nostr.ts index 7054d7cc..39657296 100644 --- a/packages/ditto/controllers/well-known/nostr.ts +++ b/packages/ditto/controllers/well-known/nostr.ts @@ -11,14 +11,53 @@ const nameSchema = z.string().min(1).regex(/^[\w.-]+$/); * https://github.com/nostr-protocol/nips/blob/master/05.md */ const nostrController: AppController = async (c) => { - const result = nameSchema.safeParse(c.req.query('name')); + const { conf, relay, signal } = c.var; + const nameParam = c.req.query('name'); + + // If no name parameter is provided, return all users + if (!nameParam) { + const adminPubkey = await conf.signer.getPublicKey(); + + // Query all NIP-05 grants (kind 30360 events) + const grants = await relay.query( + [{ kinds: [30360], authors: [adminPubkey] }], + { signal }, + ); + + const names: Record = {}; + const relays: Record = {}; + + for (const grant of grants) { + // Extract the NIP-05 name from the 'd' tag + const nip05 = grant.tags.find(([name]) => name === 'd')?.[1]; + // Extract the pubkey from the 'p' tag + const pubkey = grant.tags.find(([name]) => name === 'p')?.[1]; + + if (nip05 && pubkey) { + // Extract just the localpart (before @) + const localpart = nip05.split('@')[0]; + if (localpart) { + names[localpart] = pubkey; + relays[pubkey] = [conf.relay]; + } + } + } + + // Cache for 6 hours. + c.header('Cache-Control', 'max-age=21600, public, stale-while-revalidate=3600'); + + return c.json({ names, relays } satisfies NostrJson); + } + + // Original behavior: lookup a specific name + const result = nameSchema.safeParse(nameParam); if (!result.success) { return c.json({ error: 'Invalid name parameter' }, { status: 422 }); } const name = result.data; - const pointer = name ? await localNip05Lookup(name, c.var) : undefined; + const pointer = await localNip05Lookup(name, c.var); if (!pointer) { return c.json({ names: {}, relays: {} } satisfies NostrJson, { status: 404 });