diff --git a/.gitignore b/.gitignore
index 98c1348..58fe072 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,4 @@ users.json
khatru-invite
.env
khatru-badgern-db
+db
diff --git a/components.go b/components.go
new file mode 100644
index 0000000..200ad86
--- /dev/null
+++ b/components.go
@@ -0,0 +1,47 @@
+package main
+
+import (
+ "context"
+
+ sdk "github.com/nbd-wtf/nostr-sdk"
+ . "github.com/theplant/htmlgo"
+)
+
+func inviteTreeComponent(ctx context.Context, inviter string, loggedUser string) HTMLComponent {
+ children := make([]HTMLComponent, 0, len(whitelist)/2)
+ for pubkey, invitedBy := range whitelist {
+ if invitedBy == inviter {
+ profile := fetchAndStoreProfile(ctx, pubkey)
+ children = append(children, userRowComponent(ctx, profile, loggedUser))
+ }
+ }
+ return Ul(children...)
+}
+
+func userRowComponent(ctx context.Context, profile sdk.ProfileMetadata, loggedUser string) HTMLComponent {
+ button := Span("")
+ if isAncestorOf(loggedUser, profile.PubKey) && loggedUser != profile.PubKey {
+ button = Button("remove").
+ Class(buttonClass+" px-2 bg-red-100 hover:bg-red-300").
+ Attr(
+ "hx-post", "/remove-from-whitelist",
+ "hx-trigger", "click",
+ "hx-target", "#tree",
+ "hx-vals", `{"profile.pubkey": "`+profile.PubKey+`"}`,
+ )
+ }
+
+ return Li(
+ A().Href("nostr:"+profile.Npub()).Children(
+ Span(profile.ShortName()).Attr(
+ "npub", profile.Npub(),
+ "name", profile.ShortName(),
+ "_", `
+on mouseenter set my innerText to @npub then hide the next
+on mouseleave set my innerText to @name then show the next `,
+ ),
+ ).Class("font-mono py-1"),
+ button,
+ inviteTreeComponent(ctx, profile.PubKey, loggedUser),
+ ).Class("ml-6")
+}
diff --git a/go.mod b/go.mod
index c0f4e1e..1ebb843 100644
--- a/go.mod
+++ b/go.mod
@@ -1,12 +1,13 @@
module github.com/github-tijlxyz/khatru-invite
-go 1.20
+go 1.21.0
require (
- github.com/fiatjaf/khatru v0.0.0-20231003113207-bbe186494e68
- github.com/joho/godotenv v1.5.1
+ github.com/fiatjaf/eventstore v0.0.2
+ github.com/fiatjaf/khatru v0.1.0
github.com/kelseyhightower/envconfig v1.4.0
- github.com/nbd-wtf/go-nostr v0.20.0
+ github.com/nbd-wtf/go-nostr v0.25.0
+ github.com/nbd-wtf/nostr-sdk v0.0.1
github.com/rs/zerolog v1.31.0
github.com/theplant/htmlgo v1.0.3
)
@@ -16,20 +17,21 @@ require (
github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect
github.com/btcsuite/btcd/btcutil v1.1.3 // indirect
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 // indirect
- github.com/cespare/xxhash/v2 v2.1.2 // indirect
+ github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
- github.com/dgraph-io/badger/v4 v4.1.0 // indirect
+ github.com/dgraph-io/badger/v4 v4.2.0 // indirect
github.com/dgraph-io/ristretto v0.1.1 // indirect
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/fasthttp/websocket v1.5.3 // indirect
+ github.com/fiatjaf/generic-ristretto v0.0.1 // indirect
github.com/gobwas/httphead v0.1.0 // indirect
github.com/gobwas/pool v0.2.1 // indirect
github.com/gobwas/ws v1.2.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
- github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect
+ github.com/golang/glog v1.0.0 // indirect
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 // indirect
- github.com/golang/protobuf v1.4.2 // indirect
+ github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/flatbuffers v1.12.1 // indirect
github.com/josharian/intern v1.0.0 // indirect
@@ -38,7 +40,6 @@ require (
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/pkg/errors v0.9.1 // indirect
- github.com/puzpuzpuz/xsync v1.5.2 // indirect
github.com/puzpuzpuz/xsync/v2 v2.5.1 // indirect
github.com/rs/cors v1.7.0 // indirect
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect
@@ -51,7 +52,13 @@ require (
golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 // indirect
golang.org/x/net v0.8.0 // indirect
golang.org/x/sys v0.12.0 // indirect
- google.golang.org/protobuf v1.23.0 // indirect
+ google.golang.org/protobuf v1.28.1 // indirect
)
+replace github.com/fiatjaf/eventstore => /home/fiatjaf/comp/eventstore
+
replace github.com/fiatjaf/khatru => /home/fiatjaf/comp/khatru
+
+replace github.com/nbd-wtf/go-nostr => /home/fiatjaf/comp/go-nostr
+
+replace github.com/nbd-wtf/nostr-sdk => /home/fiatjaf/comp/nostr-sdk
diff --git a/go.sum b/go.sum
index c83dd42..cd3b772 100644
--- a/go.sum
+++ b/go.sum
@@ -28,8 +28,8 @@ github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
-github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
+github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -43,8 +43,8 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeC
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
-github.com/dgraph-io/badger/v4 v4.1.0 h1:E38jc0f+RATYrycSUf9LMv/t47XAy+3CApyYSq4APOQ=
-github.com/dgraph-io/badger/v4 v4.1.0/go.mod h1:P50u28d39ibBRmIJuQC/NSdBOg46HnHw7al2SW5QRHg=
+github.com/dgraph-io/badger/v4 v4.2.0 h1:kJrlajbXXL9DFTNuhhu9yCx7JJa4qpYWxtE8BzuWsEs=
+github.com/dgraph-io/badger/v4 v4.2.0/go.mod h1:qfCqhPoWDFJRx1gp5QwwyGo8xk1lbHUxvK9nK0OGAak=
github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8=
github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
@@ -53,6 +53,8 @@ github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/fasthttp/websocket v1.5.3 h1:TPpQuLwJYfd4LJPXvHDYPMFWbLjsT91n3GpWtCQtdek=
github.com/fasthttp/websocket v1.5.3/go.mod h1:46gg/UBmTU1kUaTcwQXpUxtRwG2PvIZYeA8oL6vF3Fs=
+github.com/fiatjaf/generic-ristretto v0.0.1 h1:LUJSU87X/QWFsBXTwnH3moFe4N8AjUxT+Rfa0+bo6YM=
+github.com/fiatjaf/generic-ristretto v0.0.1/go.mod h1:cvV6ANHDA/GrfzVrig7N7i6l8CWnkVZvtQ2/wk9DPVE=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
@@ -64,8 +66,9 @@ github.com/gobwas/ws v1.2.0/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/K
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
-github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
+github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
@@ -76,8 +79,10 @@ github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:x
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
-github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
+github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw=
@@ -85,12 +90,12 @@ github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
+github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
-github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
-github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
@@ -108,8 +113,6 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
-github.com/nbd-wtf/go-nostr v0.20.0 h1:97SYhg68jWh5G1bW1g454hA0dTV7btwtPg836n4no0o=
-github.com/nbd-wtf/go-nostr v0.20.0/go.mod h1:iFfiZr8YYSC1vmdUei0VfDB7GH/RjS3cbmiD1I5BKyo=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@@ -123,8 +126,6 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/puzpuzpuz/xsync v1.5.2 h1:yRAP4wqSOZG+/4pxJ08fPTwrfL0IzE/LKQ/cw509qGY=
-github.com/puzpuzpuz/xsync v1.5.2/go.mod h1:K98BYhX3k1dQ2M63t1YNVDanbwUPmBCAhNmVrrxfiGg=
github.com/puzpuzpuz/xsync/v2 v2.5.1 h1:mVGYAvzDSu52+zaGyNjC+24Xw2bQi3kTr4QJ6N9pIIU=
github.com/puzpuzpuz/xsync/v2 v2.5.1/go.mod h1:gD2H2krq/w52MfPLE+Uy64TzJDVY7lP2znR9qmR35kU=
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
@@ -137,11 +138,13 @@ github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJ
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
+github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
github.com/theplant/htmlgo v1.0.3 h1:G7/YSf8OrOIRHVQ13avd78T/GV1kDl/jMwpQURrXB0o=
github.com/theplant/htmlgo v1.0.3/go.mod h1:pCKSFJsoVNkyW+yN2i1Mst+8130NSQzIU7L2IbnuyKg=
github.com/theplant/testingutils v0.0.0-20190603093022-26d8b4d95c61 h1:757/ruZNgTsOf5EkQBo0i3Bx/P2wgF5ljVkODeUX/uA=
+github.com/theplant/testingutils v0.0.0-20190603093022-26d8b4d95c61/go.mod h1:p22Q3Bg5ML+hdI3QSQkB/pZ2+CjfOnGugoQIoyE2Ub8=
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
@@ -231,8 +234,11 @@ google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
-google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
+google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
@@ -242,4 +248,5 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/handler.go b/handler.go
index 3eef717..0455d96 100644
--- a/handler.go
+++ b/handler.go
@@ -8,8 +8,6 @@ import (
"github.com/theplant/htmlgo"
)
-// embed ui files
-
func inviteTreeHandler(w http.ResponseWriter, r *http.Request) {
content := inviteTreePageHTML(r.Context(), InviteTreePageParams{
LoggedUser: getLoggedUser(r),
@@ -17,6 +15,13 @@ func inviteTreeHandler(w http.ResponseWriter, r *http.Request) {
htmlgo.Fprint(w, baseHTML(content), r.Context())
}
+func getUserRowHandler(w http.ResponseWriter, r *http.Request) {
+ pubkey := r.PostFormValue("pubkey")
+ profile := fetchAndStoreProfile(r.Context(), pubkey)
+ content := userRowComponent(r.Context(), profile, getLoggedUser(r))
+ htmlgo.Fprint(w, content, r.Context())
+}
+
func addToWhitelistHandler(w http.ResponseWriter, r *http.Request) {
loggedUser := getLoggedUser(r)
@@ -29,7 +34,7 @@ func addToWhitelistHandler(w http.ResponseWriter, r *http.Request) {
http.Error(w, "failed to add to whitelist: "+err.Error(), 500)
return
}
- content := buildInviteTree(r.Context(), "", loggedUser)
+ content := inviteTreeComponent(r.Context(), "", loggedUser)
htmlgo.Fprint(w, content, r.Context())
}
@@ -40,7 +45,7 @@ func removeFromWhitelistHandler(w http.ResponseWriter, r *http.Request) {
http.Error(w, "failed to remove from whitelist: "+err.Error(), 500)
return
}
- content := buildInviteTree(r.Context(), "", loggedUser)
+ content := inviteTreeComponent(r.Context(), "", loggedUser)
htmlgo.Fprint(w, content, r.Context())
}
@@ -157,7 +162,7 @@ func reportsViewerHandler(w http.ResponseWriter, r *http.Request) {
func homePageHandler(w http.ResponseWriter, r *http.Request) {
content := homePageHTML(r.Context(), HomePageParams{
- RelayOwnerInfo: getUserInfo(context.Background(), s.RelayPubkey),
+ RelayOwnerInfo: fetchAndStoreProfile(context.Background(), s.RelayPubkey),
})
htmlgo.Fprint(w, baseHTML(content), r.Context())
}
diff --git a/main.go b/main.go
index 97c3874..d164e54 100644
--- a/main.go
+++ b/main.go
@@ -6,8 +6,8 @@ import (
"net/url"
"os"
+ "github.com/fiatjaf/eventstore/badgern"
"github.com/fiatjaf/khatru"
- "github.com/fiatjaf/khatru/plugins/storage/badgern"
"github.com/kelseyhightower/envconfig"
"github.com/nbd-wtf/go-nostr"
"github.com/rs/zerolog"
@@ -15,17 +15,20 @@ import (
type Settings struct {
Port string `envconfig:"PORT" default:"3334"`
+ Domain string `envconfig:"DOMAIN" required:"true"`
RelayName string `envconfig:"RELAY_NAME" required:"true"`
RelayPubkey string `envconfig:"RELAY_PUBKEY" required:"true"`
RelayDescription string `envconfig:"RELAY_DESCRIPTION"`
RelayContact string `envconfig:"RELAY_CONTACT"`
+ DatabasePath string `envconfig:"DATABASE_PATH" default:"./db"`
}
var (
- db badgern.BadgerBackend
s Settings
+ db = badgern.BadgerBackend{}
log = zerolog.New(os.Stderr).Output(zerolog.ConsoleWriter{Out: os.Stdout}).With().Timestamp().Logger()
whitelist = make(Whitelist)
+ relay = khatru.NewRelay()
)
func main() {
@@ -35,9 +38,15 @@ func main() {
return
}
- // init relay
- relay := khatru.NewRelay()
+ // load db
+ db.Path = s.DatabasePath
+ if err := db.Init(); err != nil {
+ log.Fatal().Err(err).Msg("failed to initialize database")
+ return
+ }
+ log.Debug().Str("path", db.Path).Msg("initialized database")
+ // init relay
relay.Name = s.RelayName
relay.PubKey = s.RelayPubkey
relay.Description = s.RelayDescription
@@ -49,21 +58,15 @@ func main() {
return
}
- // load db
- db = badgern.BadgerBackend{Path: "./khatru-badgern-db"}
- if err := db.Init(); err != nil {
- log.Fatal().Err(err).Msg("failed to initialize database")
- return
- }
-
relay.StoreEvent = append(relay.StoreEvent, db.SaveEvent)
relay.QueryEvents = append(relay.QueryEvents, db.QueryEvents)
relay.CountEvents = append(relay.CountEvents, db.CountEvents)
relay.DeleteEvent = append(relay.DeleteEvent, db.DeleteEvent)
- relay.Router().HandleFunc("/reports", reportsViewerHandler)
+ relay.Router().HandleFunc("/get-user-row", getUserRowHandler)
relay.Router().HandleFunc("/add-to-whitelist", addToWhitelistHandler)
relay.Router().HandleFunc("/remove-from-whitelist", removeFromWhitelistHandler)
+ relay.Router().HandleFunc("/reports", reportsViewerHandler)
relay.Router().HandleFunc("/users", inviteTreeHandler)
relay.Router().HandleFunc("/", homePageHandler)
@@ -78,7 +81,11 @@ func getLoggedUser(r *http.Request) string {
if evtj, err := url.QueryUnescape(cookie.Value); err == nil {
var evt nostr.Event
if err := json.Unmarshal([]byte(evtj), &evt); err == nil {
- return evt.PubKey
+ if tag := evt.Tags.GetFirst([]string{"domain", ""}); tag != nil && (*tag)[1] == s.Domain {
+ if ok, _ := evt.CheckSignature(); ok {
+ return evt.PubKey
+ }
+ }
}
}
}
diff --git a/nostr.go b/nostr.go
index 8357c27..db504ad 100644
--- a/nostr.go
+++ b/nostr.go
@@ -2,48 +2,29 @@ package main
import (
"context"
- "time"
"github.com/nbd-wtf/go-nostr"
- "github.com/nbd-wtf/go-nostr/nip19"
+ sdk "github.com/nbd-wtf/nostr-sdk"
+ cache_memory "github.com/nbd-wtf/nostr-sdk/cache/memory"
)
-type SimpleUserInfo struct {
- Npub string
- Name string
- Picture string
- Time time.Time
+var sys = sdk.System{
+ Pool: nostr.NewSimplePool(context.Background()),
+ RelaysCache: cache_memory.New32[[]sdk.Relay](1000),
+ MetadataCache: cache_memory.New32[sdk.ProfileMetadata](1000),
+ FollowsCache: cache_memory.New32[[]sdk.Follow](1),
+ RelayListRelays: []string{"wss://purplepag.es", "wss://relay.nostr.band"},
+ FollowListRelays: []string{"wss://public.relaying.io", "wss://relay.nostr.band"},
+ MetadataRelays: []string{"wss://nostr-pub.wellorder.net", "wss://purplepag.es", "wss://relay.nostr.band"},
+ Store: &db,
}
-var (
- userInfoCache = make(map[string]SimpleUserInfo)
-)
-
-func getUserInfo(ctx context.Context, hexpubkey string) SimpleUserInfo {
- // check if in cache
- v, o := userInfoCache[hexpubkey]
- if o {
- if !(time.Since(v.Time) > 2*time.Hour) { // use cache for 2 hours
- return v
+func fetchAndStoreProfile(ctx context.Context, pubkey string) sdk.ProfileMetadata {
+ profile := sys.FetchProfileMetadata(ctx, pubkey)
+ if profile.Event != nil {
+ if _, err := sys.StoreRelay().Publish(ctx, *profile.Event); err != nil {
+ log.Warn().Err(err).Msg("failed to save profile locally")
}
}
-
- npub, _ := nip19.EncodePublicKey(hexpubkey)
- var name string = string(npub)
- var picture string = ""
-
- evts, err := db.QueryEvents(ctx, nostr.Filter{
- Authors: []string{hexpubkey},
- Kinds: []int{0},
- Limit: 1,
- })
- if err != nil {
- return SimpleUserInfo{npub, name, picture, time.Now()}
- }
- for ev := range evts {
- name, picture = getProfileInfoFromJson(ev.Content)
- }
-
- userInfoCache[hexpubkey] = SimpleUserInfo{npub, name, picture, time.Now()}
- return SimpleUserInfo{npub, name, picture, time.Now()}
+ return profile
}
diff --git a/pages.go b/pages.go
index 223d80e..e10ade3 100644
--- a/pages.go
+++ b/pages.go
@@ -3,6 +3,7 @@ package main
import (
"context"
+ sdk "github.com/nbd-wtf/nostr-sdk"
. "github.com/theplant/htmlgo"
)
@@ -29,7 +30,8 @@ func baseHTML(inside HTMLComponent) HTMLComponent {
A().Text("information").Href("/").Class(navItemClass).Attr("hx-boost", "true", "hx-target", "main", "hx-select", "main"),
A().Text("invite tree").Href("/users").Class(navItemClass).Attr("hx-boost", "true", "hx-target", "main", "hx-select", "main"),
A().Text("reports").Href("/reports").Class(navItemClass).Attr("hx-boost", "true", "hx-target", "main", "hx-select", "main"),
- A().Text("login").Href("#").Class(navItemClass).Attr("_", "on click get window.nostr.signEvent({created_at: Math.round(Date.now()/1000), kind: 27235, tags: [['u', location.href]], content: ''}) then get JSON.stringify(it) then set cookies['nip98'] to it"),
+ A().Text("login").Href("#").Class(navItemClass).
+ Attr("_", "on click get window.nostr.signEvent({created_at: Math.round(Date.now()/1000), kind: 27235, tags: [['domain', "+s.Domain+"]], content: ''}) then get JSON.stringify(it) then set cookies['nip98'] to it"),
).Class("flex flex-1 items-center justify-center"),
Main(inside).Class("m-4"),
).Class("mx-4 my-6"),
@@ -37,7 +39,7 @@ func baseHTML(inside HTMLComponent) HTMLComponent {
}
type HomePageParams struct {
- RelayOwnerInfo SimpleUserInfo
+ RelayOwnerInfo sdk.ProfileMetadata
}
func homePageHTML(ctx context.Context, params HomePageParams) HTMLComponent {
@@ -57,7 +59,7 @@ func homePageHTML(ctx context.Context, params HomePageParams) HTMLComponent {
contact,
Div(
Text("relay master: "),
- A().Text(params.RelayOwnerInfo.Name).Href("nostr:"+params.RelayOwnerInfo.Npub),
+ A().Text(params.RelayOwnerInfo.Name).Href("nostr:"+params.RelayOwnerInfo.Npub()),
),
Br(),
Div(
@@ -78,7 +80,7 @@ func inviteTreePageHTML(ctx context.Context, params InviteTreePageParams) HTMLCo
Input("pubkey").Type("text").Placeholder("npub1...").Class("w-96 rounded-md border-0 p-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600"),
Button("invite").Class(buttonClass+" p-2 bg-white hover:bg-gray-50"),
Div(
- buildInviteTree(ctx, "", params.LoggedUser),
+ inviteTreeComponent(ctx, "", params.LoggedUser),
).Id("tree").Class("mt-3"),
).Attr(
"hx-post", "/add-to-whitelist",
@@ -87,32 +89,3 @@ func inviteTreePageHTML(ctx context.Context, params InviteTreePageParams) HTMLCo
"_", "on htmx:afterRequest(elt, successful) if successful and elt is I call I.reset()",
)
}
-
-func buildInviteTree(ctx context.Context, inviter string, loggedUser string) HTMLComponent {
- children := make([]HTMLComponent, 0, len(whitelist))
- for pubkey, invitedBy := range whitelist {
- if invitedBy == inviter {
- user := getUserInfo(ctx, pubkey)
- button := Span("")
- if isAncestorOf(loggedUser, pubkey) && loggedUser != pubkey {
- button = Button("remove").
- Class(buttonClass+" px-2 bg-red-100 hover:bg-red-300").
- Attr(
- "hx-post", "/remove-from-whitelist",
- "hx-trigger", "click",
- "hx-target", "#tree",
- "hx-vals", `{"pubkey": "`+pubkey+`"}`,
- )
- }
-
- children = append(children,
- Li(
- A().Href("nostr:"+user.Npub).Text(user.Name).Class("font-mono py-1"),
- button,
- buildInviteTree(ctx, pubkey, loggedUser),
- ).Class("ml-4"),
- )
- }
- }
- return Ul(children...)
-}
diff --git a/utils.go b/utils.go
index 5f3c88c..06ab7d0 100644
--- a/utils.go
+++ b/utils.go
@@ -1,36 +1 @@
package main
-
-import (
- "encoding/json"
-)
-
-func getProfileInfoFromJson(jsonStr string) (string, string) {
- fieldOrder := []string{"displayName", "display_name", "username", "name"}
-
- var data map[string]interface{}
- err := json.Unmarshal([]byte(jsonStr), &data)
- if err != nil {
- log.Error().Err(err).Msg("failed to read profile from json")
- return "", ""
- }
-
- var displayname string = "..."
- var picture string = ""
-
- for _, fieldName := range fieldOrder {
- if val, ok := data[fieldName]; ok {
- if strVal, ok := val.(string); ok && strVal != "" {
- if fieldName == "picture" {
- picture = strVal
- }
- if fieldName == "name" {
- displayname = strVal
- } else if displayname == "" {
- displayname = strVal
- }
- }
- }
- }
-
- return displayname, picture
-}