EventsDB: replaceable deletions support

This commit is contained in:
Alex Gleason 2024-06-07 22:11:17 -05:00
parent 6024586b19
commit fca7825bbf
No known key found for this signature in database
GPG Key ID: 7211D1F99744FBB7
3 changed files with 79 additions and 8 deletions

View File

@ -22,7 +22,7 @@
"@db/sqlite": "jsr:@db/sqlite@^0.11.1",
"@isaacs/ttlcache": "npm:@isaacs/ttlcache@^1.4.1",
"@noble/secp256k1": "npm:@noble/secp256k1@^2.0.0",
"@nostrify/nostrify": "jsr:@nostrify/nostrify@^0.22.5",
"@nostrify/nostrify": "jsr:@nostrify/nostrify@^0.23.0",
"@scure/base": "npm:@scure/base@^1.1.6",
"@sentry/deno": "https://deno.land/x/sentry@7.112.2/index.mjs",
"@soapbox/kysely-deno-sqlite": "jsr:@soapbox/kysely-deno-sqlite@^2.1.0",

View File

@ -10,6 +10,7 @@
"jsr:@nostrify/nostrify@^0.22.1": "jsr:@nostrify/nostrify@0.22.5",
"jsr:@nostrify/nostrify@^0.22.4": "jsr:@nostrify/nostrify@0.22.4",
"jsr:@nostrify/nostrify@^0.22.5": "jsr:@nostrify/nostrify@0.22.5",
"jsr:@nostrify/nostrify@^0.23.0": "jsr:@nostrify/nostrify@0.23.0",
"jsr:@soapbox/kysely-deno-sqlite@^2.1.0": "jsr:@soapbox/kysely-deno-sqlite@2.2.0",
"jsr:@soapbox/stickynotes@^0.4.0": "jsr:@soapbox/stickynotes@0.4.0",
"jsr:@std/assert@^0.217.0": "jsr:@std/assert@0.217.0",
@ -17,6 +18,7 @@
"jsr:@std/assert@^0.224.0": "jsr:@std/assert@0.224.0",
"jsr:@std/assert@^0.225.1": "jsr:@std/assert@0.225.3",
"jsr:@std/bytes@^0.224.0": "jsr:@std/bytes@0.224.0",
"jsr:@std/bytes@^1.0.0-rc.3": "jsr:@std/bytes@1.0.0",
"jsr:@std/crypto@^0.224.0": "jsr:@std/crypto@0.224.0",
"jsr:@std/dotenv@^0.224.0": "jsr:@std/dotenv@0.224.0",
"jsr:@std/encoding@^0.221.0": "jsr:@std/encoding@0.221.0",
@ -25,7 +27,7 @@
"jsr:@std/fmt@^0.221.0": "jsr:@std/fmt@0.221.0",
"jsr:@std/fs@^0.221.0": "jsr:@std/fs@0.221.0",
"jsr:@std/internal@^1.0.0": "jsr:@std/internal@1.0.0",
"jsr:@std/io@^0.224": "jsr:@std/io@0.224.0",
"jsr:@std/io@^0.224": "jsr:@std/io@0.224.1",
"jsr:@std/media-types@^0.224.1": "jsr:@std/media-types@0.224.1",
"jsr:@std/path@0.217": "jsr:@std/path@0.217.0",
"jsr:@std/path@^0.221.0": "jsr:@std/path@0.221.0",
@ -119,6 +121,20 @@
"npm:zod@^3.23.8"
]
},
"@nostrify/nostrify@0.23.0": {
"integrity": "8636c0322885707d6a7b342ef55f70debf399a1eb65b83abcce7972d69e30920",
"dependencies": [
"jsr:@std/encoding@^0.224.1",
"npm:@scure/base@^1.1.6",
"npm:@scure/bip32@^1.4.0",
"npm:@scure/bip39@^1.3.0",
"npm:kysely@^0.27.3",
"npm:lru-cache@^10.2.0",
"npm:nostr-tools@^2.7.0",
"npm:websocket-ts@^2.1.5",
"npm:zod@^3.23.8"
]
},
"@soapbox/kysely-deno-sqlite@2.2.0": {
"integrity": "668ec94600bc4b4d7bd618dd7ca65d4ef30ee61c46ffcb379b6f45203c08517a",
"dependencies": [
@ -146,6 +162,9 @@
"@std/bytes@0.224.0": {
"integrity": "a2250e1d0eb7d1c5a426f21267ab9bdeac2447fa87a3d0d1a467d3f7a6058e49"
},
"@std/bytes@1.0.0": {
"integrity": "9392e72af80adccaa1197912fa19990ed091cb98d5c9c4344b0c301b22d7c632"
},
"@std/crypto@0.224.0": {
"integrity": "154ef3ff08ef535562ef1a718718c5b2c5fc3808f0f9100daad69e829bfcdf2d",
"dependencies": [
@ -181,6 +200,12 @@
"jsr:@std/bytes@^0.224.0"
]
},
"@std/io@0.224.1": {
"integrity": "73de242551a5c0965eb33e36b1fc7df4834ffbc836a1a643a410ccd11253d6be",
"dependencies": [
"jsr:@std/bytes@^1.0.0-rc.3"
]
},
"@std/media-types@0.224.1": {
"integrity": "9e69a5daed37c5b5c6d3ce4731dc191f80e67f79bed392b0957d1d03b87f11e1"
},
@ -1318,7 +1343,7 @@
"dependencies": [
"jsr:@bradenmacdonald/s3-lite-client@^0.7.4",
"jsr:@db/sqlite@^0.11.1",
"jsr:@nostrify/nostrify@^0.22.5",
"jsr:@nostrify/nostrify@^0.23.0",
"jsr:@soapbox/kysely-deno-sqlite@^2.1.0",
"jsr:@soapbox/stickynotes@^0.4.0",
"jsr:@std/assert@^0.225.1",

View File

@ -27,6 +27,7 @@ class EventsDB implements NStore {
/** Conditions for when to index certain tags. */
static tagConditions: Record<string, TagCondition> = {
'a': ({ count }) => count < 15,
'd': ({ event, count }) => count === 0 && NKinds.parameterizedReplaceable(event.kind),
'e': ({ event, count, value }) => ((event.kind === 10003) || count < 15) && isNostrId(value),
'L': ({ event, count }) => event.kind === 1985 || count === 0,
@ -77,17 +78,62 @@ class EventsDB implements NStore {
/** Check if an event has been deleted by the admin. */
private async isDeletedAdmin(event: NostrEvent): Promise<boolean> {
const [deletion] = await this.query([
const filters: NostrFilter[] = [
{ kinds: [5], authors: [Conf.pubkey], '#e': [event.id], limit: 1 },
]);
return !!deletion;
];
if (NKinds.replaceable(event.kind) || NKinds.parameterizedReplaceable(event.kind)) {
const d = event.tags.find(([tag]) => tag === 'd')?.[1] ?? '';
filters.push({
kinds: [5],
authors: [Conf.pubkey],
'#a': [`${event.kind}:${event.pubkey}:${d}`],
since: event.created_at,
limit: 1,
});
}
const events = await this.query(filters);
return events.length > 0;
}
/** The DITTO_NSEC can delete any event from the database. NDatabase already handles user deletions. */
private async deleteEventsAdmin(event: NostrEvent): Promise<void> {
if (event.kind === 5 && event.pubkey === Conf.pubkey) {
const ids = getTagSet(event.tags, 'e');
await this.remove([{ ids: [...ids] }]);
const ids = new Set(event.tags.filter(([name]) => name === 'e').map(([_name, value]) => value));
const addrs = new Set(event.tags.filter(([name]) => name === 'a').map(([_name, value]) => value));
const filters: NostrFilter[] = [];
if (ids.size) {
filters.push({ ids: [...ids] });
}
for (const addr of addrs) {
const [k, pubkey, d] = addr.split(':');
const kind = Number(k);
if (!(Number.isInteger(kind) && kind >= 0)) continue;
if (!isNostrId(pubkey)) continue;
if (d === undefined) continue;
const filter: NostrFilter = {
kinds: [kind],
authors: [pubkey],
until: event.created_at,
};
if (d) {
filter['#d'] = [d];
}
filters.push(filter);
}
if (filters.length) {
await this.remove(filters);
}
}
}