mirror of
https://gitlab.com/soapbox-pub/ditto.git
synced 2025-12-06 11:29: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 blockEventCopy = structuredClone(blockEvent);
|
||||||
const event1authorUserMeCopy = structuredClone(event1authorUserMe);
|
const event1authorUserMeCopy = structuredClone(event1authorUserMe);
|
||||||
|
|
||||||
const db = new MockRelay();
|
const relay = new MockRelay();
|
||||||
|
const store = new UserStore({ relay, userPubkey: userBlackCopy.pubkey });
|
||||||
const store = new UserStore(userBlackCopy.pubkey, db);
|
|
||||||
|
|
||||||
await store.event(blockEventCopy);
|
await store.event(blockEventCopy);
|
||||||
await store.event(userBlackCopy);
|
await store.event(userBlackCopy);
|
||||||
|
|
@ -30,9 +29,8 @@ Deno.test('user never muted anyone', async () => {
|
||||||
const userBlackCopy = structuredClone(userBlack);
|
const userBlackCopy = structuredClone(userBlack);
|
||||||
const userMeCopy = structuredClone(userMe);
|
const userMeCopy = structuredClone(userMe);
|
||||||
|
|
||||||
const db = new MockRelay();
|
const relay = new MockRelay();
|
||||||
|
const store = new UserStore({ relay, userPubkey: userBlackCopy.pubkey });
|
||||||
const store = new UserStore(userBlackCopy.pubkey, db);
|
|
||||||
|
|
||||||
await store.event(userBlackCopy);
|
await store.event(userBlackCopy);
|
||||||
await store.event(userMeCopy);
|
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';
|
interface UserStoreOpts {
|
||||||
import { getTagSet } from '@/utils/tags.ts';
|
relay: NRelay;
|
||||||
|
userPubkey: string;
|
||||||
|
adminPubkey?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export class UserStore implements NStore {
|
export class UserStore implements NRelay {
|
||||||
private promise: Promise<DittoEvent[]> | undefined;
|
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> {
|
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
|
* Query events that `pubkey` did not mute
|
||||||
* https://github.com/nostr-protocol/nips/blob/master/51.md#standard-lists
|
* https://github.com/nostr-protocol/nips/blob/master/51.md#standard-lists
|
||||||
*/
|
*/
|
||||||
async query(filters: NostrFilter[], opts: { signal?: AbortSignal; limit?: number } = {}): Promise<DittoEvent[]> {
|
async query(filters: NostrFilter[], opts: { signal?: AbortSignal; limit?: number } = {}): Promise<NostrEvent[]> {
|
||||||
const events = await this.store.query(filters, opts);
|
const { relay, userPubkey, adminPubkey } = this.opts;
|
||||||
const pubkeys = await this.getMutedPubkeys();
|
|
||||||
|
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 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> {
|
close(): Promise<void> {
|
||||||
if (!this.promise) {
|
return this.opts.relay.close();
|
||||||
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');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,9 +35,15 @@ export function userMiddleware(opts: { privileged: boolean; required?: boolean }
|
||||||
const header = c.req.header('authorization');
|
const header = c.req.header('authorization');
|
||||||
|
|
||||||
if (header) {
|
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 = {
|
const user: User = {
|
||||||
signer: await getSigner(header, c.var),
|
signer,
|
||||||
relay: c.var.relay, // TODO: set user's relay
|
relay: new UserStore({ relay, userPubkey, adminPubkey }),
|
||||||
};
|
};
|
||||||
|
|
||||||
c.set('user', user);
|
c.set('user', user);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue