Compare commits

..

12 Commits

Author SHA1 Message Date
fiatjaf
d97a2f1cf2 initialScan() 2025-04-04 17:55:16 -03:00
fiatjaf
c9a7d60543 remove event from expiration manager if it is deleted. 2025-04-03 23:11:47 -03:00
fiatjaf
2bb6d4d29a simplify WriteMessage, remove the defer since it's not needed. 2025-04-03 23:10:39 -03:00
fiatjaf
2292ce4a30 add missing return in repost protected clause. 2025-04-03 23:10:11 -03:00
fiatjaf
2ae219a34c add khatru.IsInternal() for dealing with internal calls specifically in QueryEvents() 2025-04-03 23:06:57 -03:00
fiatjaf
8c9394993b reject reposts that embed nip70 protected events.
in accordance with new stuff added to nip70 that makes some sense.
2025-03-28 18:08:49 -03:00
fiatjaf
850497956c include checkid length check from @pippellia-btc 2025-03-24 15:57:20 -03:00
andrewheadricke
28ce6cfb7a ensure suspected blossom request hash does not have slashes in it 2025-03-24 15:23:00 -03:00
fiatjaf
f47282c745 get rid of base64x temporarily since it doesn't work on arm64. 2025-03-19 15:02:56 -03:00
fiatjaf
f72dea346f rename menu item on docs to say "blossom". 2025-03-17 13:38:35 -03:00
fiatjaf
51632dcc9f update blossom example to use a different database.
closes https://github.com/fiatjaf/khatru/issues/36
2025-03-17 13:36:41 -03:00
andrewheadricke
6cc2477e89 fix blossom upload < 50bytes 2025-03-15 01:58:07 -03:00
15 changed files with 87 additions and 44 deletions

View File

@@ -1,12 +1,12 @@
package blossom package blossom
import ( import (
"encoding/base64"
"fmt" "fmt"
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"
"github.com/cloudwego/base64x"
"github.com/mailru/easyjson" "github.com/mailru/easyjson"
"github.com/nbd-wtf/go-nostr" "github.com/nbd-wtf/go-nostr"
) )
@@ -17,7 +17,7 @@ func readAuthorization(r *http.Request) (*nostr.Event, error) {
return nil, nil return nil, nil
} }
eventj, err := base64x.StdEncoding.DecodeString(token[6:]) eventj, err := base64.StdEncoding.DecodeString(token[6:])
if err != nil { if err != nil {
return nil, fmt.Errorf("invalid base64 token") return nil, fmt.Errorf("invalid base64 token")
} }
@@ -25,7 +25,7 @@ func readAuthorization(r *http.Request) (*nostr.Event, error) {
if err := easyjson.Unmarshal(eventj, &evt); err != nil { if err := easyjson.Unmarshal(eventj, &evt); err != nil {
return nil, fmt.Errorf("broken event") return nil, fmt.Errorf("broken event")
} }
if evt.Kind != 24242 || len(evt.ID) != 64 || !evt.CheckID() { if evt.Kind != 24242 || !evt.CheckID() {
return nil, fmt.Errorf("invalid event") return nil, fmt.Errorf("invalid event")
} }
if ok, _ := evt.CheckSignature(); !ok { if ok, _ := evt.CheckSignature(); !ok {

View File

@@ -73,7 +73,7 @@ func (bs BlossomServer) handleUpload(w http.ResponseWriter, r *http.Request) {
// read first bytes of upload so we can find out the filetype // read first bytes of upload so we can find out the filetype
b := make([]byte, min(50, size), size) b := make([]byte, min(50, size), size)
if _, err = r.Body.Read(b); err != nil { if n, err := r.Body.Read(b); err != nil && n != size {
blossomError(w, "failed to read initial bytes of upload body: "+err.Error(), 400) blossomError(w, "failed to read initial bytes of upload body: "+err.Error(), 400)
return return
} }

View File

@@ -49,7 +49,7 @@ func New(rl *khatru.Relay, serviceURL string) *BlossomServer {
return return
} }
if len(strings.SplitN(r.URL.Path, ".", 2)[0]) == 65 { if (len(r.URL.Path) == 65 || strings.Index(r.URL.Path, ".") == 65) && strings.Index(r.URL.Path[1:], "/") == -1 {
if r.Method == "HEAD" { if r.Method == "HEAD" {
bs.handleHasBlob(w, r) bs.handleHasBlob(w, r)
return return

View File

@@ -39,6 +39,7 @@ func (rl *Relay) handleDeleteRequest(ctx context.Context, evt *nostr.Event) erro
continue continue
} }
ctx := context.WithValue(ctx, internalCallKey, struct{}{})
for _, query := range rl.QueryEvents { for _, query := range rl.QueryEvents {
ch, err := query(ctx, f) ch, err := query(ctx, f)
if err != nil { if err != nil {
@@ -66,6 +67,9 @@ func (rl *Relay) handleDeleteRequest(ctx context.Context, evt *nostr.Event) erro
return err return err
} }
} }
// if it was tracked to be expired that is not needed anymore
rl.expirationManager.removeEvent(target.ID)
} else { } else {
// fail and stop here // fail and stop here
return fmt.Errorf("blocked: %s", msg) return fmt.Errorf("blocked: %s", msg)

View File

@@ -19,7 +19,7 @@ export default {
{ text: 'HTTP Integration', link: '/core/embed' }, { text: 'HTTP Integration', link: '/core/embed' },
{ text: 'Request Routing', link: '/core/routing' }, { text: 'Request Routing', link: '/core/routing' },
{ text: 'Management API', link: '/core/management' }, { text: 'Management API', link: '/core/management' },
{ text: 'Media Storage', link: '/core/blossom' }, { text: 'Media Storage (Blossom)', link: '/core/blossom' },
] ]
}, },
{ {

View File

@@ -18,7 +18,8 @@ func main() {
bl := blossom.New(relay, "http://localhost:3334") bl := blossom.New(relay, "http://localhost:3334")
// create a database for keeping track of blob metadata // create a database for keeping track of blob metadata
bl.Store = blossom.EventStoreBlobIndexWrapper{Store: db, ServiceURL: bl.ServiceURL} // (do not use the same database used for the relay events)
bl.Store = blossom.EventStoreBlobIndexWrapper{Store: blobdb, ServiceURL: bl.ServiceURL}
// implement the required storage functions // implement the required storage functions
bl.StoreBlob = append(bl.StoreBlob, func(ctx context.Context, sha256 string, body []byte) error { bl.StoreBlob = append(bl.StoreBlob, func(ctx context.Context, sha256 string, body []byte) error {

View File

@@ -15,19 +15,22 @@ import (
func main() { func main() {
relay := khatru.NewRelay() relay := khatru.NewRelay()
db := &badger.BadgerBackend{Path: "/tmp/khatru-badger-blossom-tmp"} db := &badger.BadgerBackend{Path: "/tmp/khatru-badger-tmp"}
if err := db.Init(); err != nil { if err := db.Init(); err != nil {
panic(err) panic(err)
} }
relay.StoreEvent = append(relay.StoreEvent, db.SaveEvent) relay.StoreEvent = append(relay.StoreEvent, db.SaveEvent)
relay.QueryEvents = append(relay.QueryEvents, db.QueryEvents) relay.QueryEvents = append(relay.QueryEvents, db.QueryEvents)
relay.CountEvents = append(relay.CountEvents, db.CountEvents) relay.CountEvents = append(relay.CountEvents, db.CountEvents)
relay.DeleteEvent = append(relay.DeleteEvent, db.DeleteEvent) relay.DeleteEvent = append(relay.DeleteEvent, db.DeleteEvent)
relay.ReplaceEvent = append(relay.ReplaceEvent, db.ReplaceEvent) relay.ReplaceEvent = append(relay.ReplaceEvent, db.ReplaceEvent)
bdb := &badger.BadgerBackend{Path: "/tmp/khatru-badger-blossom-tmp"}
if err := bdb.Init(); err != nil {
panic(err)
}
bl := blossom.New(relay, "http://localhost:3334") bl := blossom.New(relay, "http://localhost:3334")
bl.Store = blossom.EventStoreBlobIndexWrapper{Store: db, ServiceURL: bl.ServiceURL} bl.Store = blossom.EventStoreBlobIndexWrapper{Store: bdb, ServiceURL: bl.ServiceURL}
bl.StoreBlob = append(bl.StoreBlob, func(ctx context.Context, sha256 string, body []byte) error { bl.StoreBlob = append(bl.StoreBlob, func(ctx context.Context, sha256 string, body []byte) error {
fmt.Println("storing", sha256, len(body)) fmt.Println("storing", sha256, len(body))
return nil return nil

View File

@@ -73,6 +73,7 @@ func (em *expirationManager) initialScan(ctx context.Context) {
defer em.mu.Unlock() defer em.mu.Unlock()
// query all events // query all events
ctx = context.WithValue(ctx, internalCallKey, struct{}{})
for _, query := range em.relay.QueryEvents { for _, query := range em.relay.QueryEvents {
ch, err := query(ctx, nostr.Filter{}) ch, err := query(ctx, nostr.Filter{})
if err != nil { if err != nil {
@@ -107,6 +108,7 @@ func (em *expirationManager) checkExpiredEvents(ctx context.Context) {
heap.Pop(&em.events) heap.Pop(&em.events)
ctx := context.WithValue(ctx, internalCallKey, struct{}{})
for _, query := range em.relay.QueryEvents { for _, query := range em.relay.QueryEvents {
ch, err := query(ctx, nostr.Filter{IDs: []string{next.id}}) ch, err := query(ctx, nostr.Filter{IDs: []string{next.id}})
if err != nil { if err != nil {
@@ -133,3 +135,16 @@ func (em *expirationManager) trackEvent(evt *nostr.Event) {
em.mu.Unlock() em.mu.Unlock()
} }
} }
func (em *expirationManager) removeEvent(id string) {
em.mu.Lock()
defer em.mu.Unlock()
// Find and remove the event from the heap
for i := 0; i < len(em.events); i++ {
if em.events[i].id == id {
heap.Remove(&em.events, i)
break
}
}
}

8
go.mod
View File

@@ -4,12 +4,11 @@ go 1.24.1
require ( require (
github.com/bep/debounce v1.2.1 github.com/bep/debounce v1.2.1
github.com/cloudwego/base64x v0.1.5
github.com/fasthttp/websocket v1.5.12 github.com/fasthttp/websocket v1.5.12
github.com/fiatjaf/eventstore v0.16.2 github.com/fiatjaf/eventstore v0.16.2
github.com/liamg/magic v0.0.1 github.com/liamg/magic v0.0.1
github.com/mailru/easyjson v0.9.0 github.com/mailru/easyjson v0.9.0
github.com/nbd-wtf/go-nostr v0.51.4 github.com/nbd-wtf/go-nostr v0.51.8
github.com/puzpuzpuz/xsync/v3 v3.5.1 github.com/puzpuzpuz/xsync/v3 v3.5.1
github.com/rs/cors v1.11.1 github.com/rs/cors v1.11.1
github.com/stretchr/testify v1.10.0 github.com/stretchr/testify v1.10.0
@@ -23,10 +22,11 @@ require (
github.com/aquasecurity/esquery v0.2.0 // indirect github.com/aquasecurity/esquery v0.2.0 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect
github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect
github.com/bytedance/sonic v1.13.1 // indirect github.com/bytedance/sonic v1.13.2 // indirect
github.com/bytedance/sonic/loader v0.2.4 // indirect github.com/bytedance/sonic/loader v0.2.4 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/coder/websocket v1.8.12 // indirect github.com/cloudwego/base64x v0.1.5 // indirect
github.com/coder/websocket v1.8.13 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/crypto/blake256 v1.1.0 // indirect github.com/decred/dcrd/crypto/blake256 v1.1.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect

11
go.sum
View File

@@ -14,12 +14,15 @@ github.com/aquasecurity/esquery v0.2.0 h1:9WWXve95TE8hbm3736WB7nS6Owl8UGDeu+0jiy
github.com/aquasecurity/esquery v0.2.0/go.mod h1:VU+CIFR6C+H142HHZf9RUkp4Eedpo9UrEKeCQHWf9ao= github.com/aquasecurity/esquery v0.2.0/go.mod h1:VU+CIFR6C+H142HHZf9RUkp4Eedpo9UrEKeCQHWf9ao=
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
github.com/btcsuite/btcd v0.24.2 h1:aLmxPguqxza+4ag8R1I2nnJjSu2iFn/kqtHTIImswcY=
github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ= github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ=
github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ=
github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
github.com/bytedance/sonic v1.13.1 h1:Jyd5CIvdFnkOWuKXr+wm4Nyk2h0yAFsr8ucJgEasO3g= github.com/bytedance/sonic v1.13.1 h1:Jyd5CIvdFnkOWuKXr+wm4Nyk2h0yAFsr8ucJgEasO3g=
github.com/bytedance/sonic v1.13.1/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4= github.com/bytedance/sonic v1.13.1/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ=
github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY= github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
@@ -33,6 +36,8 @@ github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQ
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo= github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo=
github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs=
github.com/coder/websocket v1.8.13 h1:f3QZdXy7uGVz+4uCJy2nTZyM0yTBj8yANEHhqlXZ9FE=
github.com/coder/websocket v1.8.13/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -129,8 +134,10 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/nbd-wtf/go-nostr v0.51.4 h1:L4kteLUu/U9P1v1i1CP9wXR+n7D7V4aSCWs4hPFdwp0= github.com/nbd-wtf/go-nostr v0.51.7 h1:dGjtaaFQ1kA3H+vF8wt9a9WYl54K8C0JmVDf4cp+a4A=
github.com/nbd-wtf/go-nostr v0.51.4/go.mod h1:raIUNOilCdhiVIqgwe+9enCtdXu1iuPjbLh1hO7wTqI= github.com/nbd-wtf/go-nostr v0.51.7/go.mod h1:d6+DfvMWYG5pA3dmNMBJd6WCHVDDhkXbHqvfljf0Gzg=
github.com/nbd-wtf/go-nostr v0.51.8 h1:CIoS+YqChcm4e1L1rfMZ3/mIwTz4CwApM2qx7MHNzmE=
github.com/nbd-wtf/go-nostr v0.51.8/go.mod h1:d6+DfvMWYG5pA3dmNMBJd6WCHVDDhkXbHqvfljf0Gzg=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=

View File

@@ -17,6 +17,7 @@ import (
"github.com/nbd-wtf/go-nostr/nip42" "github.com/nbd-wtf/go-nostr/nip42"
"github.com/nbd-wtf/go-nostr/nip45" "github.com/nbd-wtf/go-nostr/nip45"
"github.com/nbd-wtf/go-nostr/nip45/hyperloglog" "github.com/nbd-wtf/go-nostr/nip45/hyperloglog"
"github.com/nbd-wtf/go-nostr/nip70"
"github.com/nbd-wtf/go-nostr/nip77" "github.com/nbd-wtf/go-nostr/nip77"
"github.com/nbd-wtf/go-nostr/nip77/negentropy" "github.com/nbd-wtf/go-nostr/nip77/negentropy"
"github.com/puzpuzpuz/xsync/v3" "github.com/puzpuzpuz/xsync/v3"
@@ -175,28 +176,31 @@ func (rl *Relay) HandleWebsocket(w http.ResponseWriter, r *http.Request) {
} }
// check NIP-70 protected // check NIP-70 protected
for _, v := range env.Event.Tags { if nip70.IsProtected(env.Event) {
if len(v) == 1 && v[0] == "-" { authed := GetAuthed(ctx)
msg := "must be published by event author" if authed == "" {
authed := GetAuthed(ctx) RequestAuth(ctx)
if authed == "" { ws.WriteJSON(nostr.OKEnvelope{
RequestAuth(ctx) EventID: env.Event.ID,
ws.WriteJSON(nostr.OKEnvelope{ OK: false,
EventID: env.Event.ID, Reason: "auth-required: must be published by authenticated event author",
OK: false, })
Reason: "auth-required: " + msg, return
}) } else if authed != env.Event.PubKey {
return ws.WriteJSON(nostr.OKEnvelope{
} EventID: env.Event.ID,
if authed != env.Event.PubKey { OK: false,
ws.WriteJSON(nostr.OKEnvelope{ Reason: "blocked: must be published by event author",
EventID: env.Event.ID, })
OK: false, return
Reason: "blocked: " + msg,
})
return
}
} }
} else if nip70.HasEmbeddedProtected(env.Event) {
ws.WriteJSON(nostr.OKEnvelope{
EventID: env.Event.ID,
OK: false,
Reason: "blocked: can't repost nip70 protected",
})
return
} }
srl := rl srl := rl

View File

@@ -3,6 +3,7 @@ package khatru
import ( import (
"context" "context"
"crypto/sha256" "crypto/sha256"
"encoding/base64"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
@@ -12,7 +13,6 @@ import (
"reflect" "reflect"
"strings" "strings"
"github.com/cloudwego/base64x"
"github.com/nbd-wtf/go-nostr" "github.com/nbd-wtf/go-nostr"
"github.com/nbd-wtf/go-nostr/nip86" "github.com/nbd-wtf/go-nostr/nip86"
) )
@@ -72,7 +72,7 @@ func (rl *Relay) HandleNIP86(w http.ResponseWriter, r *http.Request) {
goto respond goto respond
} }
evtj, err := base64x.StdEncoding.DecodeString(spl[1]) evtj, err := base64.StdEncoding.DecodeString(spl[1])
if err != nil { if err != nil {
resp.Error = "invalid base64 auth" resp.Error = "invalid base64 auth"
goto respond goto respond

View File

@@ -110,7 +110,7 @@ func RejectEventsWithBase64Media(ctx context.Context, evt *nostr.Event) (bool, s
} }
func OnlyAllowNIP70ProtectedEvents(ctx context.Context, event *nostr.Event) (reject bool, msg string) { func OnlyAllowNIP70ProtectedEvents(ctx context.Context, event *nostr.Event) (reject bool, msg string) {
if nip70.IsProtected(event) { if nip70.IsProtected(*event) {
return false, "" return false, ""
} }
return true, "blocked: we only accept events protected with the nip70 \"-\" tag" return true, "blocked: we only accept events protected with the nip70 \"-\" tag"

View File

@@ -10,6 +10,7 @@ const (
wsKey = iota wsKey = iota
subscriptionIdKey subscriptionIdKey
nip86HeaderAuthKey nip86HeaderAuthKey
internalCallKey
) )
func RequestAuth(ctx context.Context) { func RequestAuth(ctx context.Context) {
@@ -40,6 +41,12 @@ func GetAuthed(ctx context.Context) string {
return "" return ""
} }
// IsInternalCall returns true when a call to QueryEvents, for example, is being made because of a deletion
// or expiration request.
func IsInternalCall(ctx context.Context) bool {
return ctx.Value(internalCallKey) != nil
}
func GetIP(ctx context.Context) string { func GetIP(ctx context.Context) string {
conn := GetConnection(ctx) conn := GetConnection(ctx)
if conn == nil { if conn == nil {

View File

@@ -33,12 +33,14 @@ type WebSocket struct {
func (ws *WebSocket) WriteJSON(any any) error { func (ws *WebSocket) WriteJSON(any any) error {
ws.mutex.Lock() ws.mutex.Lock()
defer ws.mutex.Unlock() err := ws.conn.WriteJSON(any)
return ws.conn.WriteJSON(any) ws.mutex.Unlock()
return err
} }
func (ws *WebSocket) WriteMessage(t int, b []byte) error { func (ws *WebSocket) WriteMessage(t int, b []byte) error {
ws.mutex.Lock() ws.mutex.Lock()
defer ws.mutex.Unlock() err := ws.conn.WriteMessage(t, b)
return ws.conn.WriteMessage(t, b) ws.mutex.Unlock()
return err
} }