From 8f49b99935c2ce001980c3d8b295c9056be49d94 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 20 Feb 2025 20:03:31 -0600 Subject: [PATCH] Consolidate AdminStore and UserStore --- packages/ditto/storages/AdminStore.ts | 43 ----------- packages/ditto/storages/UserStore.test.ts | 10 +-- packages/ditto/storages/UserStore.ts | 73 ++++++++++++------- .../mastoapi/middleware/userMiddleware.ts | 10 ++- 4 files changed, 60 insertions(+), 76 deletions(-) delete mode 100644 packages/ditto/storages/AdminStore.ts diff --git a/packages/ditto/storages/AdminStore.ts b/packages/ditto/storages/AdminStore.ts deleted file mode 100644 index ae03c59d..00000000 --- a/packages/ditto/storages/AdminStore.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { NostrEvent, NostrFilter, NStore } from '@nostrify/nostrify'; - -import { Conf } from '@/config.ts'; -import { DittoEvent } from '@/interfaces/DittoEvent.ts'; -import { getTagSet } from '@/utils/tags.ts'; - -/** A store that prevents banned users from being displayed. */ -export class AdminStore implements NStore { - constructor(private store: NStore) {} - - async event(event: NostrEvent, opts?: { signal?: AbortSignal }): Promise { - return await this.store.event(event, opts); - } - - async query(filters: NostrFilter[], opts: { signal?: AbortSignal; limit?: number } = {}): Promise { - const events = await this.store.query(filters, opts); - const pubkeys = new Set(events.map((event) => event.pubkey)); - - const users = await this.store.query([{ - kinds: [30382], - authors: [await Conf.signer.getPublicKey()], - '#d': [...pubkeys], - limit: pubkeys.size, - }]); - - const adminPubkey = await Conf.signer.getPublicKey(); - - return events.filter((event) => { - const user = users.find( - ({ kind, pubkey, tags }) => - kind === 30382 && pubkey === adminPubkey && tags.find(([name]) => name === 'd')?.[1] === event.pubkey, - ); - - const n = getTagSet(user?.tags ?? [], 'n'); - - if (n.has('disabled')) { - return false; - } - - return true; - }); - } -} diff --git a/packages/ditto/storages/UserStore.test.ts b/packages/ditto/storages/UserStore.test.ts index d04ece07..56ec1254 100644 --- a/packages/ditto/storages/UserStore.test.ts +++ b/packages/ditto/storages/UserStore.test.ts @@ -14,9 +14,8 @@ Deno.test('query events of users that are not muted', async () => { const blockEventCopy = structuredClone(blockEvent); const event1authorUserMeCopy = structuredClone(event1authorUserMe); - const db = new MockRelay(); - - const store = new UserStore(userBlackCopy.pubkey, db); + const relay = new MockRelay(); + const store = new UserStore({ relay, userPubkey: userBlackCopy.pubkey }); await store.event(blockEventCopy); await store.event(userBlackCopy); @@ -30,9 +29,8 @@ Deno.test('user never muted anyone', async () => { const userBlackCopy = structuredClone(userBlack); const userMeCopy = structuredClone(userMe); - const db = new MockRelay(); - - const store = new UserStore(userBlackCopy.pubkey, db); + const relay = new MockRelay(); + const store = new UserStore({ relay, userPubkey: userBlackCopy.pubkey }); await store.event(userBlackCopy); await store.event(userMeCopy); diff --git a/packages/ditto/storages/UserStore.ts b/packages/ditto/storages/UserStore.ts index 2449d8c1..0533917c 100644 --- a/packages/ditto/storages/UserStore.ts +++ b/packages/ditto/storages/UserStore.ts @@ -1,43 +1,66 @@ -import { NostrEvent, NostrFilter, NStore } from '@nostrify/nostrify'; +import { NostrEvent, NostrFilter, NostrRelayCLOSED, NostrRelayEOSE, NostrRelayEVENT, NRelay } from '@nostrify/nostrify'; -import { DittoEvent } from '@/interfaces/DittoEvent.ts'; -import { getTagSet } from '@/utils/tags.ts'; +interface UserStoreOpts { + relay: NRelay; + userPubkey: string; + adminPubkey?: string; +} -export class UserStore implements NStore { - private promise: Promise | undefined; +export class UserStore implements NRelay { + constructor(private opts: UserStoreOpts) {} - constructor(private pubkey: string, private store: NStore) {} + req( + filters: NostrFilter[], + opts?: { signal?: AbortSignal }, + ): AsyncIterable { + // TODO: support req maybe? It would be inefficient. + return this.opts.relay.req(filters, opts); + } async event(event: NostrEvent, opts?: { signal?: AbortSignal }): Promise { - return await this.store.event(event, opts); + return await this.opts.relay.event(event, opts); } /** * Query events that `pubkey` did not mute * https://github.com/nostr-protocol/nips/blob/master/51.md#standard-lists */ - async query(filters: NostrFilter[], opts: { signal?: AbortSignal; limit?: number } = {}): Promise { - const events = await this.store.query(filters, opts); - const pubkeys = await this.getMutedPubkeys(); + async query(filters: NostrFilter[], opts: { signal?: AbortSignal; limit?: number } = {}): Promise { + const { relay, userPubkey, adminPubkey } = this.opts; + + const mutes = new Set(); + const [muteList] = await this.opts.relay.query([{ authors: [userPubkey], kinds: [10000], limit: 1 }]); + + for (const [name, value] of muteList?.tags ?? []) { + if (name === 'p') { + mutes.add(value); + } + } + + const events = await relay.query(filters, opts); + + const users = adminPubkey + ? await relay.query([{ + kinds: [30382], + authors: [adminPubkey], + '#d': [...events.map(({ pubkey }) => pubkey)], + }]) + : []; return events.filter((event) => { - return event.kind === 0 || !pubkeys.has(event.pubkey); + const user = users.find((user) => user.tags.find(([name]) => name === 'd')?.[1] === event.pubkey); + + for (const [name, value] of user?.tags ?? []) { + if (name === 'n' && value === 'disabled') { + return false; + } + } + + return event.kind === 0 || !mutes.has(event.pubkey); }); } - private async getMuteList(): Promise { - if (!this.promise) { - this.promise = this.store.query([{ authors: [this.pubkey], kinds: [10000], limit: 1 }]); - } - const [muteList] = await this.promise; - return muteList; - } - - private async getMutedPubkeys(): Promise> { - const mutedPubkeysEvent = await this.getMuteList(); - if (!mutedPubkeysEvent) { - return new Set(); - } - return getTagSet(mutedPubkeysEvent.tags, 'p'); + close(): Promise { + return this.opts.relay.close(); } } diff --git a/packages/mastoapi/middleware/userMiddleware.ts b/packages/mastoapi/middleware/userMiddleware.ts index 4a88e325..a86fecc5 100644 --- a/packages/mastoapi/middleware/userMiddleware.ts +++ b/packages/mastoapi/middleware/userMiddleware.ts @@ -35,9 +35,15 @@ export function userMiddleware(opts: { privileged: boolean; required?: boolean } const header = c.req.header('authorization'); if (header) { + const { relay, conf } = c.var; + + const signer = await getSigner(header, c.var); + const userPubkey = await signer.getPublicKey(); + const adminPubkey = await conf.signer.getPublicKey(); + const user: User = { - signer: await getSigner(header, c.var), - relay: c.var.relay, // TODO: set user's relay + signer, + relay: new UserStore({ relay, userPubkey, adminPubkey }), }; c.set('user', user);