Merge branch 'main' into fix-revoke-nip05

Conflicts:
	packages/ditto/storages/DittoRelayStore.test.ts
This commit is contained in:
P. Reis 2025-03-05 11:25:04 -03:00
commit fc4f5fd522
4 changed files with 66 additions and 5 deletions

View file

@ -194,7 +194,7 @@ const pgstore = new DittoPgStore({
});
const pool = new DittoPool({ conf, relay: pgstore });
const relay = new DittoRelayStore({ db, conf, relay: pgstore });
const relay = new DittoRelayStore({ db, conf, pool, relay: pgstore });
await seedZapSplits({ conf, relay });

View file

@ -134,9 +134,29 @@ Deno.test('Admin revokes nip05 grant and nip05 column gets null', async () => {
assertEquals(nullRow?.nip05_hostname, null);
});
Deno.test('fetchRelated', async () => {
await using test = setupTest();
const { pool, store } = test;
const post = genEvent({ kind: 1, content: 'hi' });
const reply = genEvent({ kind: 1, content: 'wussup?', tags: [['e', post.id], ['p', post.pubkey]] });
await pool.event(post);
await pool.event(reply);
await store.event(reply);
await waitFor(async () => {
const { count } = await test.store.count([{ ids: [post.id] }]);
return count > 0;
}, 3000);
});
function setupTest(cb?: (req: Request) => Response | Promise<Response>) {
const conf = new DittoConf(Deno.env);
const db = new DittoPolyPg(conf.databaseUrl);
const pool = new MockRelay();
const relay = new MockRelay();
const mockFetch: typeof fetch = async (input, init) => {
@ -148,12 +168,13 @@ function setupTest(cb?: (req: Request) => Response | Promise<Response>) {
}
};
const store = new DittoRelayStore({ conf, db, relay, fetch: mockFetch });
const store = new DittoRelayStore({ conf, db, pool, relay, fetch: mockFetch });
return {
db,
store,
conf,
pool,
store,
[Symbol.asyncDispose]: async () => {
await store[Symbol.asyncDispose]();
await db[Symbol.asyncDispose]();

View file

@ -46,6 +46,7 @@ import { renderWebPushNotification } from '@/views/mastodon/push.ts';
interface DittoRelayStoreOpts {
db: DittoDB;
conf: DittoConf;
pool: NRelay;
relay: NRelay;
fetch?: typeof fetch;
}
@ -193,7 +194,12 @@ export class DittoRelayStore implements NRelay {
this.prewarmLinkPreview(event, signal),
this.generateSetEvents(event),
])
.then(() => this.webPush(event))
.then(() =>
Promise.allSettled([
this.webPush(event),
this.fetchRelated(event),
])
)
.catch(() => {});
}
}
@ -375,6 +381,39 @@ export class DittoRelayStore implements NRelay {
}
}
private async fetchRelated(event: NostrEvent): Promise<void> {
const ids = new Set<string>();
for (const tag of event.tags) {
const [name, value] = tag;
if ((name === 'e' || name === 'q') && isNostrId(value) && !this.encounters.has(value)) {
ids.add(value);
}
}
const { db, pool } = this.opts;
if (ids.size) {
const query = db.kysely
.selectFrom('nostr_events')
.select('id')
.where('id', 'in', [...ids]);
for (const row of await query.execute().catch(() => [])) {
ids.delete(row.id);
}
}
if (ids.size) {
const signal = AbortSignal.timeout(1000);
for (const event of await pool.query([{ ids: [...ids] }], { signal }).catch(() => [])) {
await this.event(event).catch(() => {});
}
}
}
private async prewarmLinkPreview(event: NostrEvent, signal?: AbortSignal): Promise<void> {
const { firstUrl } = parseNoteContent(stripimeta(event.content, event.tags), [], this.opts);

View file

@ -1,5 +1,6 @@
import { Semaphore } from '@core/asyncutil';
import { NostrEvent } from '@nostrify/nostrify';
import { MockRelay } from '@nostrify/nostrify/test';
import { DittoConf } from '@ditto/conf';
import { DittoPolyPg } from '@ditto/db';
@ -11,7 +12,7 @@ const conf = new DittoConf(Deno.env);
const db = new DittoPolyPg(conf.databaseUrl);
const pgstore = new DittoPgStore({ db, conf });
const relaystore = new DittoRelayStore({ conf, db, relay: pgstore });
const relaystore = new DittoRelayStore({ conf, db, pool: new MockRelay(), relay: pgstore });
const sem = new Semaphore(5);