mirror of
https://gitlab.com/soapbox-pub/ditto.git
synced 2025-12-06 11:29:46 +00:00
80 lines
2.2 KiB
TypeScript
80 lines
2.2 KiB
TypeScript
import { nip19 } from 'nostr-tools';
|
|
import { NIP05, NStore } from '@nostrify/nostrify';
|
|
import Debug from '@soapbox/stickynotes/debug';
|
|
import tldts from 'tldts';
|
|
|
|
import { Conf } from '@/config.ts';
|
|
import { cachedNip05sSizeGauge } from '@/metrics.ts';
|
|
import { Storages } from '@/storages.ts';
|
|
import { SimpleLRU } from '@/utils/SimpleLRU.ts';
|
|
import { Nip05, parseNip05 } from '@/utils.ts';
|
|
import { fetchWorker } from '@/workers/fetch.ts';
|
|
|
|
const debug = Debug('ditto:nip05');
|
|
|
|
const nip05Cache = new SimpleLRU<string, nip19.ProfilePointer>(
|
|
async (key, { signal }) => {
|
|
debug(`Lookup ${key}`);
|
|
const tld = tldts.parse(key);
|
|
|
|
if (!tld.isIcann || tld.isIp || tld.isPrivate) {
|
|
throw new Error(`Invalid NIP-05: ${key}`);
|
|
}
|
|
|
|
const [name, domain] = key.split('@');
|
|
|
|
try {
|
|
if (domain === Conf.url.host) {
|
|
const store = await Storages.db();
|
|
const pointer = await localNip05Lookup(store, name);
|
|
if (pointer) {
|
|
debug(`Found: ${key} is ${pointer.pubkey}`);
|
|
return pointer;
|
|
} else {
|
|
throw new Error(`Not found: ${key}`);
|
|
}
|
|
} else {
|
|
const result = await NIP05.lookup(key, { fetch: fetchWorker, signal });
|
|
debug(`Found: ${key} is ${result.pubkey}`);
|
|
return result;
|
|
}
|
|
} catch (e) {
|
|
debug(`Not found: ${key}`);
|
|
throw e;
|
|
}
|
|
},
|
|
{ ...Conf.caches.nip05, gauge: cachedNip05sSizeGauge },
|
|
);
|
|
|
|
async function localNip05Lookup(store: NStore, localpart: string): Promise<nip19.ProfilePointer | undefined> {
|
|
const [grant] = await store.query([{
|
|
kinds: [30360],
|
|
'#d': [`${localpart}@${Conf.url.host}`],
|
|
authors: [Conf.pubkey],
|
|
limit: 1,
|
|
}]);
|
|
|
|
const pubkey = grant?.tags.find(([name]) => name === 'p')?.[1];
|
|
|
|
if (pubkey) {
|
|
return { pubkey, relays: [Conf.relay] };
|
|
}
|
|
}
|
|
|
|
export async function parseAndVerifyNip05(
|
|
nip05: string | undefined,
|
|
pubkey: string,
|
|
signal = AbortSignal.timeout(3000),
|
|
): Promise<Nip05 | undefined> {
|
|
if (!nip05) return;
|
|
try {
|
|
const result = await nip05Cache.fetch(nip05, { signal });
|
|
if (result.pubkey === pubkey) {
|
|
return parseNip05(nip05);
|
|
}
|
|
} catch (_e) {
|
|
// do nothing
|
|
}
|
|
}
|
|
|
|
export { localNip05Lookup, nip05Cache };
|