Enhance delete event functionality and UI updates

- Improved logging in handle-delete.go for admin and owner checks during delete operations.
- Updated handle-event.go to ensure delete events are saved before processing, with enhanced error handling.
- Added fetchDeleteEventsByTarget function in nostr.js to retrieve delete events targeting specific event IDs.
- Modified App.svelte to include delete event verification and improved event sorting by creation timestamp.
- Enhanced UI to display delete event information and added loading indicators for event refresh actions.
This commit is contained in:
2025-10-10 21:23:36 +01:00
parent 67a74980f9
commit 40f3cb6f6e
5 changed files with 306 additions and 81 deletions

View File

@@ -37,6 +37,12 @@ func CheckExpiration(ev *event.E) (expired bool) {
func (d *D) QueryEvents(c context.Context, f *filter.F) (
evs event.S, err error,
) {
return d.QueryEventsWithOptions(c, f, true)
}
func (d *D) QueryEventsWithOptions(c context.Context, f *filter.F, includeDeleteEvents bool) (
evs event.S, err error,
) {
// if there is Ids in the query, this overrides anything else
var expDeletes types.Uint40s
@@ -195,16 +201,15 @@ func (d *D) QueryEvents(c context.Context, f *filter.F) (
// We don't need to do anything with direct event ID
// references as we will filter those out in the second pass
}
// Check for 'a' tags that reference parameterized replaceable
// events
// Check for 'a' tags that reference replaceable events
aTags := ev.Tags.GetAll([]byte("a"))
for _, aTag := range aTags {
if aTag.Len() < 2 {
continue
}
// Parse the 'a' tag value: kind:pubkey:d-tag
// Parse the 'a' tag value: kind:pubkey:d-tag (for parameterized) or kind:pubkey (for regular)
split := bytes.Split(aTag.Value(), []byte{':'})
if len(split) != 3 {
if len(split) < 2 {
continue
}
// Parse the kind
@@ -214,8 +219,8 @@ func (d *D) QueryEvents(c context.Context, f *filter.F) (
continue
}
kk := kind.New(uint16(kindInt))
// Only process parameterized replaceable events
if !kind.IsParameterizedReplaceable(kk.K) {
// Process both regular and parameterized replaceable events
if !kind.IsReplaceable(kk.K) {
continue
}
// Parse the pubkey
@@ -230,21 +235,30 @@ func (d *D) QueryEvents(c context.Context, f *filter.F) (
// Create the key for the deletion map using hex
// representation of pubkey
key := hex.Enc(pk) + ":" + strconv.Itoa(int(kk.K))
// Initialize the inner map if it doesn't exist
if _, exists := deletionsByKindPubkeyDTag[key]; !exists {
deletionsByKindPubkeyDTag[key] = make(map[string]int64)
if kind.IsParameterizedReplaceable(kk.K) {
// For parameterized replaceable events, use d-tag specific deletion
if len(split) < 3 {
continue
}
// Initialize the inner map if it doesn't exist
if _, exists := deletionsByKindPubkeyDTag[key]; !exists {
deletionsByKindPubkeyDTag[key] = make(map[string]int64)
}
// Record the newest delete timestamp for this d-tag
dValue := string(split[2])
if ts, ok := deletionsByKindPubkeyDTag[key][dValue]; !ok || ev.CreatedAt > ts {
deletionsByKindPubkeyDTag[key][dValue] = ev.CreatedAt
}
} else {
// For regular replaceable events, mark as deleted by kind/pubkey
deletionsByKindPubkey[key] = true
}
// Record the newest delete timestamp for this d-tag
dValue := string(split[2])
if ts, ok := deletionsByKindPubkeyDTag[key][dValue]; !ok || ev.CreatedAt > ts {
deletionsByKindPubkeyDTag[key][dValue] = ev.CreatedAt
}
// Debug logging
}
// For replaceable events, we need to check if there are any
// e-tags that reference events with the same kind and pubkey
for _, eTag := range eTags {
if eTag.Len() != 64 {
if len(eTag.Value()) != 64 {
continue
}
// Get the event ID from the e-tag
@@ -252,15 +266,30 @@ func (d *D) QueryEvents(c context.Context, f *filter.F) (
if _, err = hex.DecBytes(evId, eTag.Value()); err != nil {
continue
}
// Query for the event
var targetEvs event.S
targetEvs, err = d.QueryEvents(
c, &filter.F{Ids: tag.NewFromBytesSlice(evId)},
)
if err != nil || len(targetEvs) == 0 {
continue
// Look for the target event in our current batch instead of querying
var targetEv *event.E
for _, candidateEv := range allEvents {
if utils.FastEqual(candidateEv.ID, evId) {
targetEv = candidateEv
break
}
}
targetEv := targetEvs[0]
// If not found in current batch, try to fetch it directly
if targetEv == nil {
// Get serial for the event ID
ser, serErr := d.GetSerialById(evId)
if serErr != nil || ser == nil {
continue
}
// Fetch the event by serial
targetEv, serErr = d.FetchEventBySerial(ser)
if serErr != nil || targetEv == nil {
continue
}
}
// Only allow users to delete their own events
if !utils.FastEqual(targetEv.Pubkey, ev.Pubkey) {
continue
@@ -378,8 +407,8 @@ func (d *D) QueryEvents(c context.Context, f *filter.F) (
// )
}
// Skip events with kind 5 (Deletion)
if ev.Kind == kind.Deletion.K {
// Skip events with kind 5 (Deletion) unless explicitly requested
if ev.Kind == kind.Deletion.K && !includeDeleteEvents {
continue
}
// Check if this event's ID is in the filter
@@ -408,16 +437,8 @@ func (d *D) QueryEvents(c context.Context, f *filter.F) (
// kind/pubkey and is not in the filter AND there isn't a newer
// event with the same kind/pubkey
if deletionsByKindPubkey[key] && !isIdInFilter {
// Check if there's a newer event with the same kind/pubkey
// that hasn't been specifically deleted
existing, exists := replaceableEvents[key]
if !exists || ev.CreatedAt > existing.CreatedAt {
// This is the newest event so far, keep it
replaceableEvents[key] = ev
} else {
// There's a newer event, skip this one
continue
}
// This replaceable event has been deleted, skip it
continue
} else {
// Normal replaceable event handling
existing, exists := replaceableEvents[key]
@@ -501,3 +522,23 @@ func (d *D) QueryEvents(c context.Context, f *filter.F) (
}
return
}
// QueryDeleteEventsByTargetId queries for delete events that target a specific event ID
func (d *D) QueryDeleteEventsByTargetId(c context.Context, targetEventId []byte) (
evs event.S, err error,
) {
// Create a filter for deletion events with the target event ID in e-tags
f := &filter.F{
Kinds: kind.NewS(kind.Deletion),
Tags: tag.NewS(
tag.NewFromAny("#e", hex.Enc(targetEventId)),
),
}
// Query for the delete events
if evs, err = d.QueryEventsWithOptions(c, f, true); chk.E(err) {
return
}
return
}