mirror of
https://github.com/github-tijlxyz/khatru-pyramid.git
synced 2025-04-19 10:21:18 +00:00
nostr-sdk, eventstore, components, userRow
This commit is contained in:
parent
0c94ef4e34
commit
6565b4dcf5
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,3 +3,4 @@ users.json
|
||||
khatru-invite
|
||||
.env
|
||||
khatru-badgern-db
|
||||
db
|
||||
|
47
components.go
Normal file
47
components.go
Normal file
@ -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 <button />
|
||||
on mouseleave set my innerText to @name then show the next <button />`,
|
||||
),
|
||||
).Class("font-mono py-1"),
|
||||
button,
|
||||
inviteTreeComponent(ctx, profile.PubKey, loggedUser),
|
||||
).Class("ml-6")
|
||||
}
|
27
go.mod
27
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
|
||||
|
35
go.sum
35
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=
|
||||
|
15
handler.go
15
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())
|
||||
}
|
||||
|
31
main.go
31
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,9 +81,13 @@ 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 {
|
||||
if tag := evt.Tags.GetFirst([]string{"domain", ""}); tag != nil && (*tag)[1] == s.Domain {
|
||||
if ok, _ := evt.CheckSignature(); ok {
|
||||
return evt.PubKey
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
53
nostr.go
53
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
|
||||
}
|
||||
|
39
pages.go
39
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...)
|
||||
}
|
||||
|
35
utils.go
35
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
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user