mirror of
https://github.com/Cameri/nostream.git
synced 2025-03-17 21:31:48 +01:00
feat: implement nip-09 event deletion
This commit is contained in:
parent
246e472fc4
commit
9efdb8d209
@ -14,7 +14,7 @@ NIPs with a relay-specific implementation are listed here.
|
||||
- [ ] NIP-03: OpenTimestams Attestations for Events
|
||||
- [x] NIP-04: Encrypted Direct Message
|
||||
- [ ] NIP-05: Mapping Nostr keys to DNS identifiers
|
||||
- [ ] NIP-09: Event deletion
|
||||
- [x] NIP-09: Event deletion
|
||||
- [x] NIP-11: Relay information document
|
||||
- [x] NIP-12: Generic tag queries
|
||||
- [ ] NIP-13: Proof of Work
|
||||
|
@ -3,7 +3,9 @@ import { Pubkey, TagName } from './base'
|
||||
|
||||
export type EventId = string
|
||||
|
||||
export interface Tag {
|
||||
export type Tag = TagBase | []
|
||||
|
||||
export interface TagBase {
|
||||
0: TagName
|
||||
[index: number]: string
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { PassThrough } from 'stream'
|
||||
import { DBEvent, Event } from './event'
|
||||
import { Pubkey } from './base'
|
||||
import { DBEvent, Event, EventId } from './event'
|
||||
import { SubscriptionFilter } from './subscription'
|
||||
|
||||
export type ExposedPromiseKeys = 'then' | 'catch' | 'finally'
|
||||
@ -12,4 +13,5 @@ export interface IEventRepository {
|
||||
create(event: Event): Promise<number>
|
||||
upsert(event: Event): Promise<number>
|
||||
findByFilters(filters: SubscriptionFilter[]): IQueryResult<DBEvent[]>
|
||||
deleteByPubkeyAndIds(pubkey: Pubkey, ids: EventId[]): Promise<number>
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ export class WebServerAdapter extends EventEmitter implements IWebServerAdapter
|
||||
description,
|
||||
pubkey,
|
||||
contact,
|
||||
supported_nips: [1, 2, 4, 11, 12, 15, 16],
|
||||
supported_nips: [1, 2, 4, 9, 11, 12, 15, 16],
|
||||
software: packageJson.repository.url,
|
||||
version: packageJson.version,
|
||||
}
|
||||
|
@ -7,3 +7,8 @@ export enum EventKinds {
|
||||
DELETE = 5,
|
||||
REACTION = 7,
|
||||
}
|
||||
|
||||
export enum EventTags {
|
||||
Event = 'e',
|
||||
Pubkey = 'p',
|
||||
}
|
||||
|
@ -1,13 +1,14 @@
|
||||
import { DefaultEventStrategy } from '../handlers/event-strategies/default-event-strategy'
|
||||
import { EphemeralEventStrategy } from '../handlers/event-strategies/ephemeral-event-strategy'
|
||||
import { NullEventStrategy } from '../handlers/event-strategies/null-event-strategy copy'
|
||||
import { NullEventStrategy } from '../handlers/event-strategies/null-event-strategy'
|
||||
import { ReplaceableEventStrategy } from '../handlers/event-strategies/replaceable-event-strategy'
|
||||
import { Factory } from '../@types/base'
|
||||
import { Event } from '../@types/event'
|
||||
import { IEventStrategy } from '../@types/message-handlers'
|
||||
import { IEventRepository } from '../@types/repositories'
|
||||
import { isEphemeralEvent, isNullEvent, isReplaceableEvent } from '../utils/event'
|
||||
import { isDeleteEvent, isEphemeralEvent, isNullEvent, isReplaceableEvent } from '../utils/event'
|
||||
import { IWebSocketAdapter } from '../@types/adapters'
|
||||
import { DeleteEventStrategy } from '../handlers/event-strategies/delete-event-strategy'
|
||||
|
||||
|
||||
export const eventStrategyFactory = (
|
||||
@ -19,6 +20,8 @@ export const eventStrategyFactory = (
|
||||
return new EphemeralEventStrategy(adapter)
|
||||
} else if (isNullEvent(event)) {
|
||||
return new NullEventStrategy()
|
||||
} else if (isDeleteEvent(event)) {
|
||||
return new DeleteEventStrategy(eventRepository)
|
||||
}
|
||||
|
||||
return new DefaultEventStrategy(adapter, eventRepository)
|
||||
|
33
src/handlers/event-strategies/delete-event-strategy.ts
Normal file
33
src/handlers/event-strategies/delete-event-strategy.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { Event } from '../../@types/event'
|
||||
import { IEventStrategy } from '../../@types/message-handlers'
|
||||
import { IEventRepository } from '../../@types/repositories'
|
||||
import { EventTags } from '../../constants/base'
|
||||
|
||||
|
||||
export class DeleteEventStrategy implements IEventStrategy<Event, Promise<boolean>> {
|
||||
public constructor(
|
||||
private readonly eventRepository: IEventRepository,
|
||||
) { }
|
||||
|
||||
public async execute(event: Event): Promise<boolean> {
|
||||
try {
|
||||
const eTags = event.tags.filter((tag) => tag[0] === EventTags.Event)
|
||||
|
||||
if (!eTags.length) {
|
||||
return
|
||||
}
|
||||
|
||||
await this.eventRepository.deleteByPubkeyAndIds(
|
||||
event.pubkey,
|
||||
eTags.map((tag) => tag[1])
|
||||
)
|
||||
|
||||
return
|
||||
} catch (error) {
|
||||
console.error('Unable to handle event. Reason:', error)
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import { Knex } from 'knex'
|
||||
import { __, applySpec, equals, modulo, omit, pipe, prop, cond, always, groupBy, T, evolve, forEach, isEmpty, forEachObjIndexed, isNil, complement, toPairs, filter, nth, ifElse, invoker } from 'ramda'
|
||||
import { __, applySpec, equals, modulo, omit, pipe, prop, cond, always, groupBy, T, evolve, forEach, isEmpty, forEachObjIndexed, isNil, complement, toPairs, filter, nth, ifElse, invoker, identity } from 'ramda'
|
||||
|
||||
import { DBEvent, Event } from '../@types/event'
|
||||
import { DBEvent, Event, EventId } from '../@types/event'
|
||||
import { IEventRepository, IQueryResult } from '../@types/repositories'
|
||||
import { SubscriptionFilter } from '../@types/subscription'
|
||||
import { isGenericTagQuery } from '../utils/filter'
|
||||
@ -156,4 +156,15 @@ export class EventRepository implements IEventRepository {
|
||||
.where('events.event_created_at', '<', row.event_created_at)
|
||||
.then(prop('rowCount') as () => number)
|
||||
}
|
||||
|
||||
public async deleteByPubkeyAndIds(pubkey: string, ids: EventId[]): Promise<number> {
|
||||
const query = this.dbClient('events')
|
||||
.where({
|
||||
event_pubkey: pubkey,
|
||||
})
|
||||
.whereIn('event_id', ids)
|
||||
.delete()
|
||||
|
||||
return query.then(identity)
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import { applySpec, pipe, prop } from 'ramda'
|
||||
|
||||
import { CanonicalEvent, Event } from '../@types/event'
|
||||
import { SubscriptionFilter } from '../@types/subscription'
|
||||
import { EventKinds } from '../constants/base'
|
||||
import { isGenericTagQuery } from './filter'
|
||||
import { fromBuffer } from './transform'
|
||||
|
||||
@ -95,3 +96,7 @@ export const isEphemeralEvent = (event: Event): boolean => {
|
||||
export const isNullEvent = (event: Event): boolean => {
|
||||
return event.kind === Number.MAX_SAFE_INTEGER
|
||||
}
|
||||
|
||||
export const isDeleteEvent = (event: Event): boolean => {
|
||||
return event.kind === EventKinds.DELETE
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ const { expect } = chai
|
||||
|
||||
import { EventRepository } from '../../../src/repositories/event-repository'
|
||||
|
||||
describe.only('EventRepository', () => {
|
||||
describe('EventRepository', () => {
|
||||
let repository: IEventRepository
|
||||
let sandbox: sinon.SinonSandbox
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user