Implement Replaceable and Addressable Events
This commit is contained in:
parent
fc6e1c59a5
commit
4c554c91a6
2 changed files with 108 additions and 15 deletions
97
index.ts
97
index.ts
|
@ -7,9 +7,12 @@ import type {
|
|||
import {
|
||||
getCCNPrivateKey,
|
||||
getCCNPubkey,
|
||||
isAddressableEvent,
|
||||
isArray,
|
||||
isLocalhost,
|
||||
isReplaceableEvent,
|
||||
isValidJSON,
|
||||
parseATagQuery,
|
||||
randomTimeUpTo2DaysInThePast,
|
||||
} from "./utils.ts";
|
||||
import * as nostrTools from "@nostr/tools";
|
||||
|
@ -171,6 +174,36 @@ function addEventToDb(
|
|||
if (existingEvent) throw new EventAlreadyExistsException();
|
||||
try {
|
||||
db.run("BEGIN TRANSACTION");
|
||||
if (isReplaceableEvent(decryptedEvent.kind)) {
|
||||
sql`
|
||||
DELETE FROM events
|
||||
WHERE kind = ${decryptedEvent.kind}
|
||||
AND pubkey = ${decryptedEvent.pubkey}
|
||||
AND created_at < ${decryptedEvent.created_at}
|
||||
`(db);
|
||||
}
|
||||
|
||||
if (isAddressableEvent(decryptedEvent.kind)) {
|
||||
const dTag = decryptedEvent.tags.find((tag) => tag[0] === "d")?.[1];
|
||||
if (dTag) {
|
||||
sql`
|
||||
DELETE FROM events
|
||||
WHERE kind = ${decryptedEvent.kind}
|
||||
AND pubkey = ${decryptedEvent.pubkey}
|
||||
AND created_at < ${decryptedEvent.created_at}
|
||||
AND id IN (
|
||||
SELECT event_id FROM event_tags
|
||||
WHERE tag_name = 'd'
|
||||
AND tag_id IN (
|
||||
SELECT tag_id FROM event_tags_values
|
||||
WHERE value_position = 1
|
||||
AND value = ${dTag}
|
||||
)
|
||||
)
|
||||
`(db);
|
||||
}
|
||||
}
|
||||
|
||||
sql`
|
||||
INSERT INTO events (id, original_id, pubkey, created_at, kind, content, sig, first_seen) VALUES (
|
||||
${decryptedEvent.id},
|
||||
|
@ -431,22 +464,56 @@ function handleRequest(connection: UserConnection, request: NostrClientREQ) {
|
|||
const uniqueValues = [...new Set(value)];
|
||||
query = mixQuery(query, sqlPartial`(`);
|
||||
for (let k = 0; k < uniqueValues.length; k++) {
|
||||
const value = uniqueValues[k] as string;
|
||||
const tagValue = uniqueValues[k] as string;
|
||||
if (tag === "a") {
|
||||
const aTagInfo = parseATagQuery(tagValue);
|
||||
|
||||
if (aTagInfo.dTag && aTagInfo.dTag !== "") {
|
||||
// Addressable event reference
|
||||
query = mixQuery(
|
||||
query,
|
||||
sqlPartial`id IN (
|
||||
SELECT e.id
|
||||
FROM events e
|
||||
JOIN event_tags t ON e.id = t.event_id
|
||||
JOIN event_tags_values v ON t.tag_id = v.tag_id
|
||||
WHERE e.kind = ${aTagInfo.kind}
|
||||
AND e.pubkey = ${aTagInfo.pubkey}
|
||||
AND t.tag_name = 'd'
|
||||
AND v.value_position = 1
|
||||
AND v.value = ${aTagInfo.dTag}
|
||||
)`,
|
||||
);
|
||||
} else {
|
||||
// Replaceable event reference
|
||||
query = mixQuery(
|
||||
query,
|
||||
sqlPartial`id IN (
|
||||
SELECT id
|
||||
FROM events
|
||||
WHERE kind = ${aTagInfo.kind}
|
||||
AND pubkey = ${aTagInfo.pubkey}
|
||||
)`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Regular tag handling (unchanged)
|
||||
query = mixQuery(
|
||||
query,
|
||||
sqlPartial`id IN (
|
||||
SELECT t.event_id
|
||||
FROM event_tags t
|
||||
WHERE t.tag_name = ${tag}
|
||||
AND t.tag_id IN (
|
||||
SELECT v.tag_id
|
||||
FROM event_tags_values v
|
||||
WHERE v.value_position = 1
|
||||
AND v.value = ${tagValue}
|
||||
)
|
||||
)`,
|
||||
);
|
||||
}
|
||||
|
||||
query = mixQuery(
|
||||
query,
|
||||
sqlPartial`id IN (
|
||||
SELECT t.event_id
|
||||
FROM event_tags t
|
||||
WHERE t.tag_name = ${tag}
|
||||
AND t.tag_id IN (
|
||||
SELECT v.tag_id
|
||||
FROM event_tags_values v
|
||||
WHERE v.value_position = 1
|
||||
AND v.value = ${value}
|
||||
)
|
||||
)`,
|
||||
);
|
||||
if (k < uniqueValues.length - 1) {
|
||||
query = mixQuery(query, sqlPartial`OR`);
|
||||
}
|
||||
|
|
26
utils.ts
26
utils.ts
|
@ -67,3 +67,29 @@ export async function getCCNPrivateKey(): Promise<Uint8Array> {
|
|||
);
|
||||
return decryptUint8Array(decodeBase64(encryptedPrivateKey), encryptionKey);
|
||||
}
|
||||
|
||||
export function isReplaceableEvent(kind: number): boolean {
|
||||
return (kind >= 10000 && kind < 20000) || kind === 0 || kind === 3;
|
||||
}
|
||||
|
||||
export function isAddressableEvent(kind: number): boolean {
|
||||
return kind >= 30000 && kind < 40000;
|
||||
}
|
||||
|
||||
export function isRegularEvent(kind: number): boolean {
|
||||
return (kind >= 1000 && kind < 10000) ||
|
||||
(kind >= 4 && kind < 45) ||
|
||||
kind === 1 ||
|
||||
kind === 2;
|
||||
}
|
||||
|
||||
export function parseATagQuery(aTagValue: string): { kind: number, pubkey: string, dTag?: string } {
|
||||
const parts = aTagValue.split(':');
|
||||
if (parts.length < 2) return { kind: 0, pubkey: '' };
|
||||
|
||||
return {
|
||||
kind: parseInt(parts[0], 10),
|
||||
pubkey: parts[1],
|
||||
dTag: parts.length > 2 ? parts[2] : undefined
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue