EventsDB: replaceable deletions support
This commit is contained in:
parent
6024586b19
commit
fca7825bbf
|
@ -22,7 +22,7 @@
|
||||||
"@db/sqlite": "jsr:@db/sqlite@^0.11.1",
|
"@db/sqlite": "jsr:@db/sqlite@^0.11.1",
|
||||||
"@isaacs/ttlcache": "npm:@isaacs/ttlcache@^1.4.1",
|
"@isaacs/ttlcache": "npm:@isaacs/ttlcache@^1.4.1",
|
||||||
"@noble/secp256k1": "npm:@noble/secp256k1@^2.0.0",
|
"@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",
|
"@scure/base": "npm:@scure/base@^1.1.6",
|
||||||
"@sentry/deno": "https://deno.land/x/sentry@7.112.2/index.mjs",
|
"@sentry/deno": "https://deno.land/x/sentry@7.112.2/index.mjs",
|
||||||
"@soapbox/kysely-deno-sqlite": "jsr:@soapbox/kysely-deno-sqlite@^2.1.0",
|
"@soapbox/kysely-deno-sqlite": "jsr:@soapbox/kysely-deno-sqlite@^2.1.0",
|
||||||
|
|
29
deno.lock
29
deno.lock
|
@ -10,6 +10,7 @@
|
||||||
"jsr:@nostrify/nostrify@^0.22.1": "jsr:@nostrify/nostrify@0.22.5",
|
"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.4": "jsr:@nostrify/nostrify@0.22.4",
|
||||||
"jsr:@nostrify/nostrify@^0.22.5": "jsr:@nostrify/nostrify@0.22.5",
|
"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/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:@soapbox/stickynotes@^0.4.0": "jsr:@soapbox/stickynotes@0.4.0",
|
||||||
"jsr:@std/assert@^0.217.0": "jsr:@std/assert@0.217.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.224.0": "jsr:@std/assert@0.224.0",
|
||||||
"jsr:@std/assert@^0.225.1": "jsr:@std/assert@0.225.3",
|
"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@^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/crypto@^0.224.0": "jsr:@std/crypto@0.224.0",
|
||||||
"jsr:@std/dotenv@^0.224.0": "jsr:@std/dotenv@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",
|
"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/fmt@^0.221.0": "jsr:@std/fmt@0.221.0",
|
||||||
"jsr:@std/fs@^0.221.0": "jsr:@std/fs@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/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/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.217": "jsr:@std/path@0.217.0",
|
||||||
"jsr:@std/path@^0.221.0": "jsr:@std/path@0.221.0",
|
"jsr:@std/path@^0.221.0": "jsr:@std/path@0.221.0",
|
||||||
|
@ -119,6 +121,20 @@
|
||||||
"npm:zod@^3.23.8"
|
"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": {
|
"@soapbox/kysely-deno-sqlite@2.2.0": {
|
||||||
"integrity": "668ec94600bc4b4d7bd618dd7ca65d4ef30ee61c46ffcb379b6f45203c08517a",
|
"integrity": "668ec94600bc4b4d7bd618dd7ca65d4ef30ee61c46ffcb379b6f45203c08517a",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
|
@ -146,6 +162,9 @@
|
||||||
"@std/bytes@0.224.0": {
|
"@std/bytes@0.224.0": {
|
||||||
"integrity": "a2250e1d0eb7d1c5a426f21267ab9bdeac2447fa87a3d0d1a467d3f7a6058e49"
|
"integrity": "a2250e1d0eb7d1c5a426f21267ab9bdeac2447fa87a3d0d1a467d3f7a6058e49"
|
||||||
},
|
},
|
||||||
|
"@std/bytes@1.0.0": {
|
||||||
|
"integrity": "9392e72af80adccaa1197912fa19990ed091cb98d5c9c4344b0c301b22d7c632"
|
||||||
|
},
|
||||||
"@std/crypto@0.224.0": {
|
"@std/crypto@0.224.0": {
|
||||||
"integrity": "154ef3ff08ef535562ef1a718718c5b2c5fc3808f0f9100daad69e829bfcdf2d",
|
"integrity": "154ef3ff08ef535562ef1a718718c5b2c5fc3808f0f9100daad69e829bfcdf2d",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
|
@ -181,6 +200,12 @@
|
||||||
"jsr:@std/bytes@^0.224.0"
|
"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": {
|
"@std/media-types@0.224.1": {
|
||||||
"integrity": "9e69a5daed37c5b5c6d3ce4731dc191f80e67f79bed392b0957d1d03b87f11e1"
|
"integrity": "9e69a5daed37c5b5c6d3ce4731dc191f80e67f79bed392b0957d1d03b87f11e1"
|
||||||
},
|
},
|
||||||
|
@ -1318,7 +1343,7 @@
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"jsr:@bradenmacdonald/s3-lite-client@^0.7.4",
|
"jsr:@bradenmacdonald/s3-lite-client@^0.7.4",
|
||||||
"jsr:@db/sqlite@^0.11.1",
|
"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/kysely-deno-sqlite@^2.1.0",
|
||||||
"jsr:@soapbox/stickynotes@^0.4.0",
|
"jsr:@soapbox/stickynotes@^0.4.0",
|
||||||
"jsr:@std/assert@^0.225.1",
|
"jsr:@std/assert@^0.225.1",
|
||||||
|
|
|
@ -27,6 +27,7 @@ class EventsDB implements NStore {
|
||||||
|
|
||||||
/** Conditions for when to index certain tags. */
|
/** Conditions for when to index certain tags. */
|
||||||
static tagConditions: Record<string, TagCondition> = {
|
static tagConditions: Record<string, TagCondition> = {
|
||||||
|
'a': ({ count }) => count < 15,
|
||||||
'd': ({ event, count }) => count === 0 && NKinds.parameterizedReplaceable(event.kind),
|
'd': ({ event, count }) => count === 0 && NKinds.parameterizedReplaceable(event.kind),
|
||||||
'e': ({ event, count, value }) => ((event.kind === 10003) || count < 15) && isNostrId(value),
|
'e': ({ event, count, value }) => ((event.kind === 10003) || count < 15) && isNostrId(value),
|
||||||
'L': ({ event, count }) => event.kind === 1985 || count === 0,
|
'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. */
|
/** Check if an event has been deleted by the admin. */
|
||||||
private async isDeletedAdmin(event: NostrEvent): Promise<boolean> {
|
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 },
|
{ 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. */
|
/** The DITTO_NSEC can delete any event from the database. NDatabase already handles user deletions. */
|
||||||
private async deleteEventsAdmin(event: NostrEvent): Promise<void> {
|
private async deleteEventsAdmin(event: NostrEvent): Promise<void> {
|
||||||
if (event.kind === 5 && event.pubkey === Conf.pubkey) {
|
if (event.kind === 5 && event.pubkey === Conf.pubkey) {
|
||||||
const ids = getTagSet(event.tags, 'e');
|
const ids = new Set(event.tags.filter(([name]) => name === 'e').map(([_name, value]) => value));
|
||||||
await this.remove([{ ids: [...ids] }]);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue