khatru: update docs again, now it seems to be mostly up to date.
This commit is contained in:
@@ -46,9 +46,9 @@ func startPollingGame(relay *khatru.Relay) {
|
|||||||
relay.BroadcastEvent(evt)
|
relay.BroadcastEvent(evt)
|
||||||
|
|
||||||
// just calling BroadcastEvent won't cause this event to be be stored,
|
// just calling BroadcastEvent won't cause this event to be be stored,
|
||||||
// if for any reason you want to store these events you must call the store functions manually
|
// if for any reason you want to store these events you must call the store function manually
|
||||||
for _, store := range relay.StoreEvent {
|
if relay.StoreEvent != nil {
|
||||||
store(context.TODO(), evt)
|
relay.StoreEvent(context.TODO(), evt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if newStatus.TeamB > current.TeamB {
|
if newStatus.TeamB > current.TeamB {
|
||||||
|
|||||||
@@ -69,12 +69,13 @@ func handleWeatherQuery(ctx context.Context, filter nostr.Filter) iter.Seq[nostr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
evt.Sign(global.RelaySecretKey)
|
evt.Sign(global.RelaySecretKey)
|
||||||
ch <- evt
|
if !yield(evt) {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
return ch, nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ func main () {
|
|||||||
// other stuff here
|
// other stuff here
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleEvent(ctx context.Context, event *nostr.Event) error {
|
func handleEvent(ctx context.Context, event nostr.Event) error {
|
||||||
// store each event as a file on google drive
|
// store each event as a file on google drive
|
||||||
_, err := gdriveService.Files.Create(googledrive.CreateOptions{
|
_, err := gdriveService.Files.Create(googledrive.CreateOptions{
|
||||||
Name: event.ID, // with the name set to their id
|
Name: event.ID, // with the name set to their id
|
||||||
@@ -25,12 +25,9 @@ func handleEvent(ctx context.Context, event *nostr.Event) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleQuery(ctx context.Context, filter nostr.Filter) (ch chan *nostr.Event, err error) {
|
func handleQuery(ctx context.Context, filter nostr.Filter) iter.Seq[nostr.Event] {
|
||||||
// QueryEvents functions are expected to return a channel
|
// QueryEvents functions return an iterator
|
||||||
ch := make(chan *nostr.Event)
|
return func(yield func(nostr.Event) bool) {
|
||||||
|
|
||||||
// and they can do their query asynchronously, emitting events to the channel as they come
|
|
||||||
go func () {
|
|
||||||
if len(filter.IDs) > 0 {
|
if len(filter.IDs) > 0 {
|
||||||
// if the query is for ids we can do a simpler name match
|
// if the query is for ids we can do a simpler name match
|
||||||
for _, id := range filter.IDS {
|
for _, id := range filter.IDS {
|
||||||
@@ -40,7 +37,9 @@ func handleQuery(ctx context.Context, filter nostr.Filter) (ch chan *nostr.Event
|
|||||||
if len(results) > 0 {
|
if len(results) > 0 {
|
||||||
var evt nostr.Event
|
var evt nostr.Event
|
||||||
json.Unmarshal(results[0].Body, &evt)
|
json.Unmarshal(results[0].Body, &evt)
|
||||||
ch <- evt
|
if !yield(evt) {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -53,14 +52,14 @@ func handleQuery(ctx context.Context, filter nostr.Filter) (ch chan *nostr.Event
|
|||||||
var evt nostr.Event
|
var evt nostr.Event
|
||||||
json.Unmarshal(results[0].Body, &evt)
|
json.Unmarshal(results[0].Body, &evt)
|
||||||
if filter.Match(evt) {
|
if filter.Match(evt) {
|
||||||
ch <- evt
|
if !yield(evt) {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}
|
||||||
|
|
||||||
return ch, nil
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -25,9 +25,24 @@ func main () {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
relay.StoreEvent = policies.SeqStore(normal.SaveEvent, search.SaveEvent)
|
relay.StoreEvent = func(ctx context.Context, evt nostr.Event) error {
|
||||||
relay.QueryStored = policies.SeqQuery(normal.QueryEvents, search.QueryEvents)
|
if err := normal.SaveEvent(evt); err != nil {
|
||||||
relay.DeleteEvent = policies.SeqDelete(normal.DeleteEvent, search.DeleteEvent)
|
return err
|
||||||
|
}
|
||||||
|
return search.SaveEvent(evt)
|
||||||
|
}
|
||||||
|
relay.QueryStored = func(ctx context.Context, filter nostr.Filter) iter.Seq[nostr.Event] {
|
||||||
|
if filter.Search != "" {
|
||||||
|
return search.QueryEvents(filter)
|
||||||
|
}
|
||||||
|
return normal.QueryEvents(filter)
|
||||||
|
}
|
||||||
|
relay.DeleteEvent = func(ctx context.Context, id nostr.ID) error {
|
||||||
|
if err := normal.DeleteEvent(id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return search.DeleteEvent(id)
|
||||||
|
}
|
||||||
|
|
||||||
// other stuff here
|
// other stuff here
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ If on `OnRequest` or `OnEvent` you prefix the message with `auth-required: `, th
|
|||||||
relay.OnRequest = func(ctx context.Context, filter nostr.Filter) (bool, string) {
|
relay.OnRequest = func(ctx context.Context, filter nostr.Filter) (bool, string) {
|
||||||
return true, "auth-required: this query requires you to be authenticated"
|
return true, "auth-required: this query requires you to be authenticated"
|
||||||
}
|
}
|
||||||
relay.OnEvent = func(ctx context.Context, event *nostr.Event) (bool, string) {
|
relay.OnEvent = func(ctx context.Context, event nostr.Event) (bool, string) {
|
||||||
return true, "auth-required: publishing this event requires authentication"
|
return true, "auth-required: publishing this event requires authentication"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Every [`khatru.Relay`](https://pkg.go.dev/github.com/fiatjaf/khatru#Relay) instance comes with its own ['http.ServeMux`](https://pkg.go.dev/net/http#ServeMux) inside. It ensures all requests are handled normally, but intercepts the requests that are pertinent to the relay operation, specifically the WebSocket requests, and the [NIP-11](https://nips.nostr.com/11) and the [NIP-86](https://nips.nostr.com/86) HTTP requests.
|
Every [`khatru.Relay`](https://pkg.go.dev/fiatjaf.com/nostr/khatru#Relay) instance comes with its own ['http.ServeMux`](https://pkg.go.dev/net/http#ServeMux) inside. It ensures all requests are handled normally, but intercepts the requests that are pertinent to the relay operation, specifically the WebSocket requests, and the [NIP-11](https://nips.nostr.com/11) and the [NIP-86](https://nips.nostr.com/86) HTTP requests.
|
||||||
|
|
||||||
## Exposing multiple relays at the same path or at the root
|
## Exposing multiple relays at the same path or at the root
|
||||||
|
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ You can do a kind of sharding, for example, by storing some events in one store
|
|||||||
For example, maybe you want kind 1 events in `db1` and kind 30023 events in `db30023`:
|
For example, maybe you want kind 1 events in `db1` and kind 30023 events in `db30023`:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
relay.StoreEvent = func (ctx context.Context, evt *nostr.Event) error {
|
relay.StoreEvent = func (ctx context.Context, evt nostr.Event) error {
|
||||||
switch evt.Kind {
|
switch evt.Kind {
|
||||||
case nostr.Kind(1):
|
case nostr.Kind(1):
|
||||||
return db1.SaveEvent(evt)
|
return db1.SaveEvent(evt)
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ outline: deep
|
|||||||
|
|
||||||
If you have one (or more) set of policies that have to be executed in sequence (for example, first you check for the presence of a tag, then later in the next policies you use that tag without checking) and they only apply to some class of events, but you still want your relay to deal with other classes of events that can lead to cumbersome sets of rules, always having to check if an event meets the requirements and so on. There is where routing can help you.
|
If you have one (or more) set of policies that have to be executed in sequence (for example, first you check for the presence of a tag, then later in the next policies you use that tag without checking) and they only apply to some class of events, but you still want your relay to deal with other classes of events that can lead to cumbersome sets of rules, always having to check if an event meets the requirements and so on. There is where routing can help you.
|
||||||
|
|
||||||
It also can be handy if you get a [`khatru.Relay`](https://pkg.go.dev/github.com/fiatjaf/khatru#Relay) from somewhere else, like a library such as [`relay29`](https://github.com/fiatjaf/relay29), and you want to combine it with other policies without some interfering with the others. As in the example below:
|
|
||||||
|
|
||||||
```go
|
```go
|
||||||
sk := os.Getenv("RELAY_SECRET_KEY")
|
sk := os.Getenv("RELAY_SECRET_KEY")
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ These are lists of functions that will be called in order every time an `EVENT`
|
|||||||
The next step is adding some protection, because maybe we don't want to allow _anyone_ to write to our relay. Maybe we want to only allow people that have a pubkey starting with `"a"`, `"b"` or `"c"`:
|
The next step is adding some protection, because maybe we don't want to allow _anyone_ to write to our relay. Maybe we want to only allow people that have a pubkey starting with `"a"`, `"b"` or `"c"`:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
relay.OnEvent = func (ctx context.Context, event *nostr.Event) (reject bool, msg string) {
|
relay.OnEvent = func (ctx context.Context, event nostr.Event) (reject bool, msg string) {
|
||||||
firstHexChar := event.PubKey.Hex()[0:1]
|
firstHexChar := event.PubKey.Hex()[0:1]
|
||||||
if firstHexChar == "a" || firstHexChar == "b" || firstHexChar == "c" {
|
if firstHexChar == "a" || firstHexChar == "b" || firstHexChar == "c" {
|
||||||
return false, "" // allow
|
return false, "" // allow
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ features:
|
|||||||
details: You just define your custom handlers for each RPC call and they will be exposed appropriately to management clients.
|
details: You just define your custom handlers for each RPC call and they will be exposed appropriately to management clients.
|
||||||
- title: It's written in Go
|
- title: It's written in Go
|
||||||
icon: 🛵
|
icon: 🛵
|
||||||
link: https://pkg.go.dev/github.com/fiatjaf/khatru
|
link: https://pkg.go.dev/fiatjaf.com/nostr/khatru
|
||||||
details: That means it is fast and lightweight, you can learn the language in 5 minutes and it builds your relay into a single binary that's easy to ship and deploy.
|
details: That means it is fast and lightweight, you can learn the language in 5 minutes and it builds your relay into a single binary that's easy to ship and deploy.
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user