mirror of
https://gitlab.com/soapbox-pub/ditto.git
synced 2025-12-06 03:19:46 +00:00
Consolidate AdminStore and UserStore
This commit is contained in:
parent
8a978b088b
commit
8f49b99935
4 changed files with 60 additions and 76 deletions
|
|
@ -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<void> {
|
||||
return await this.store.event(event, opts);
|
||||
}
|
||||
|
||||
async query(filters: NostrFilter[], opts: { signal?: AbortSignal; limit?: number } = {}): Promise<DittoEvent[]> {
|
||||
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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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<DittoEvent[]> | undefined;
|
||||
export class UserStore implements NRelay {
|
||||
constructor(private opts: UserStoreOpts) {}
|
||||
|
||||
constructor(private pubkey: string, private store: NStore) {}
|
||||
req(
|
||||
filters: NostrFilter[],
|
||||
opts?: { signal?: AbortSignal },
|
||||
): AsyncIterable<NostrRelayEVENT | NostrRelayEOSE | NostrRelayCLOSED> {
|
||||
// TODO: support req maybe? It would be inefficient.
|
||||
return this.opts.relay.req(filters, opts);
|
||||
}
|
||||
|
||||
async event(event: NostrEvent, opts?: { signal?: AbortSignal }): Promise<void> {
|
||||
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<DittoEvent[]> {
|
||||
const events = await this.store.query(filters, opts);
|
||||
const pubkeys = await this.getMutedPubkeys();
|
||||
async query(filters: NostrFilter[], opts: { signal?: AbortSignal; limit?: number } = {}): Promise<NostrEvent[]> {
|
||||
const { relay, userPubkey, adminPubkey } = this.opts;
|
||||
|
||||
const mutes = new Set<string>();
|
||||
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<DittoEvent | undefined> {
|
||||
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<Set<string>> {
|
||||
const mutedPubkeysEvent = await this.getMuteList();
|
||||
if (!mutedPubkeysEvent) {
|
||||
return new Set();
|
||||
}
|
||||
return getTagSet(mutedPubkeysEvent.tags, 'p');
|
||||
close(): Promise<void> {
|
||||
return this.opts.relay.close();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue