This commit is contained in:
github-tijlxyz 2023-09-06 20:49:18 +02:00
commit e47ed79135
29 changed files with 6131 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
whitelist.json
khatru-invite

46
go.mod Normal file
View File

@ -0,0 +1,46 @@
module github.com/github-tijlxyz/khatru-invite
go 1.20
require (
github.com/fiatjaf/khatru v0.0.2
github.com/nbd-wtf/go-nostr v0.20.0
)
require (
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // 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/ristretto v0.1.1 // indirect
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/fasthttp/websocket v1.5.3 // 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/groupcache v0.0.0-20190702054246-869f871628b6 // indirect
github.com/golang/protobuf v1.3.1 // indirect
github.com/golang/snappy v0.0.3 // indirect
github.com/google/flatbuffers v1.12.1 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/klauspost/compress v1.16.5 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/puzpuzpuz/xsync v1.5.2 // indirect
github.com/rs/cors v1.7.0 // indirect
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect
github.com/tidwall/gjson v1.14.4 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.47.0 // indirect
go.opencensus.io v0.22.5 // indirect
golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 // indirect
golang.org/x/net v0.8.0 // indirect
golang.org/x/sys v0.8.0 // indirect
)

149
go.sum Normal file
View File

@ -0,0 +1,149 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U=
github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 h1:KdUfX2zKommPRa+PD0sWZUyXe9w277ABlgELO7H04IM=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y=
github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
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/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/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=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
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/khatru v0.0.2 h1:+OaARQhrColJG1NypLDWgkaERjJhbLAtUk8RIYETeGU=
github.com/fiatjaf/khatru v0.0.2/go.mod h1:XDX+UIURFlTrAAw/ya7M5YRC4ft2Qm2gAAvs3ELoVps=
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.2.0 h1:u0p9s3xLYpZCA1z5JgCkMeB34CKCMMQbM+G8Ii7YD0I=
github.com/gobwas/ws v1.2.0/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
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/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=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw=
github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
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/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI=
github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
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/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/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk=
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g=
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.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
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=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.47.0 h1:y7moDoxYzMooFpT5aHgNgVOQDrS3qlkfiP9mDtGGK9c=
github.com/valyala/fasthttp v1.47.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.opencensus.io v0.22.5 h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 h1:5llv2sWeaMSnA3w2kS57ouQQ4pudlXrR0dCgw51QK9o=
golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

20
handler.go Normal file
View File

@ -0,0 +1,20 @@
package main
import (
"encoding/json"
"net/http"
)
func inviteDataApiHandler(w http.ResponseWriter, re *http.Request) {
jsonBytes, err := json.Marshal(whitelist)
if err != nil {
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Access-Control-Allow-Origin", "*")
w.WriteHeader(http.StatusOK)
if _, err := w.Write(jsonBytes); err != nil {
http.Error(w, "internal server error", http.StatusInternalServerError)
}
}

71
main.go Normal file
View File

@ -0,0 +1,71 @@
package main
import (
"fmt"
"net/http"
"os"
"os/signal"
"sync"
"syscall"
"time"
"github.com/fiatjaf/khatru"
"github.com/fiatjaf/khatru/plugins/storage/badgern"
)
func main() {
// save whitelist on shutdown
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
handleSignals()
}()
// backup whitelist every hour
go func() {
for {
time.Sleep(time.Hour)
saveWhitelist()
}
}()
// init relay
relay := khatru.NewRelay()
// load whitelist storage
if err := loadWhitelist(); err != nil {
panic(err)
}
// load db
db := badgern.BadgerBackend{Path: "/tmp/khatru-badgern-tmp"}
if err := db.Init(); err != nil {
panic(err)
}
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.RejectEvent = append(relay.RejectEvent, whitelistRejecter)
// invitedata api
relay.Router().HandleFunc("/invitedata", inviteDataApiHandler)
// ui
relay.Router().Handle("/", http.StripPrefix("/", http.FileServer(http.Dir("./ui/dist"))))
fmt.Println("running on :3334")
http.ListenAndServe(":3334", relay)
}
// save whitelist on shutdown
func handleSignals() {
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM)
<-sigCh
saveWhitelist()
os.Exit(0)
}

13
ui/.eslintignore Normal file
View File

@ -0,0 +1,13 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock

30
ui/.eslintrc.cjs Normal file
View File

@ -0,0 +1,30 @@
module.exports = {
root: true,
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:svelte/recommended",
"prettier",
],
parser: "@typescript-eslint/parser",
plugins: ["@typescript-eslint"],
parserOptions: {
sourceType: "module",
ecmaVersion: 2020,
extraFileExtensions: [".svelte"],
},
env: {
browser: true,
es2017: true,
node: true,
},
overrides: [
{
files: ["*.svelte"],
parser: "svelte-eslint-parser",
parserOptions: {
parser: "@typescript-eslint/parser",
},
},
],
};

1
ui/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules

13
ui/.prettierignore Normal file
View File

@ -0,0 +1,13 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock

2049
ui/dist/assets/index-0320fb82.js vendored Normal file

File diff suppressed because one or more lines are too long

1
ui/dist/assets/index-815d9fe6.css vendored Normal file

File diff suppressed because one or more lines are too long

18
ui/dist/index.html vendored Normal file
View File

@ -0,0 +1,18 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Invite Relay</title>
<link rel="icon" href="data:," />
<meta name="description" content="Invite Relay UI" />
<script type="module" crossorigin src="./assets/index-0320fb82.js"></script>
<link rel="stylesheet" href="./assets/index-815d9fe6.css">
</head>
<body>
<noscript>Please enable JavaScript to run this app.</noscript>
<div id="app"></div>
</body>
</html>

16
ui/index.html Normal file
View File

@ -0,0 +1,16 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Invite Relay</title>
<link rel="icon" href="data:," />
<meta name="description" content="Invite Relay UI" />
</head>
<body>
<noscript>Please enable JavaScript to run this app.</noscript>
<div id="app"></div>
<script type="module" src="./src/main.js"></script>
</body>
</html>

31
ui/package.json Normal file
View File

@ -0,0 +1,31 @@
{
"name": "inviterelay-ui",
"private": true,
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"lint": "prettier --plugin-search-dir . --check . && eslint .",
"format": "prettier --plugin-search-dir . --write ."
},
"type": "module",
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^2.0.2",
"autoprefixer": "^10.4.14",
"postcss": "^8.4.24",
"sass": "^1.45.0",
"svelte": "^3.44.0",
"svelte-flatpickr": "^3.2.6",
"svelte-spa-router": "^3.2.0",
"tailwindcss": "^3.3.2",
"vite": "^4.0.4",
"@nostr-dev-kit/ndk": "^0.8.23",
"@tailwindcss/forms": "^0.5.3",
"@tailwindcss/typography": "^0.5.9",
"eslint-plugin-svelte": "^2.33.0",
"nostr-tools": "^1.14.2",
"prettier": "^3.0.3",
"prettier-plugin-svelte": "^3.0.3"
},
"dependencies": {}
}

6
ui/postcss.config.js Normal file
View File

@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

3
ui/src/App.css Normal file
View File

@ -0,0 +1,3 @@
@tailwind base;
@tailwind utilities;
@tailwind components;

62
ui/src/App.svelte Normal file
View File

@ -0,0 +1,62 @@
<script lang="ts">
import "./App.css";
import Hieracy from "./components/Hierarchy.svelte";
import Invite from "./components/Invite.svelte";
import { onMount } from "svelte";
import { ndk, userPublickey } from "./lib/nostr";
import { NDKNip07Signer } from "@nostr-dev-kit/ndk";
import { buildHierarchy } from "./lib/utils";
import { nip19 } from "nostr-tools";
async function login() {
const signer = new NDKNip07Signer();
$ndk.signer = signer;
ndk.set($ndk);
$userPublickey = (await $ndk.signer.user()).npub;
userPublickey.set($userPublickey);
}
async function fetchData() {
const response = await fetch("/invitedata");
invitedata = Object.values(await response.json());
hierarchy = buildHierarchy(invitedata, { pk: "", invited_by: "" });
}
let invitedata = [];
let hierarchy = [];
onMount(() => {
addEventListener("load", (e) => {
login();
});
fetchData();
});
</script>
<article class="prose font-sans px-4 py-6 lg:max-w-7xl lg:pt-6 lg:pb-28">
<h1>Invite Relay</h1>
<div>
{#if $userPublickey === undefined}
<button
on:click={login}
type="button"
class="inline-flex mr-2 items-center px-3 py-2 border border-gray-300 shadow-sm text-sm leading-4 font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500"
>Login with NIP07</button
>
{/if}
</div>
{#if invitedata.find((p) => $userPublickey == nip19.npubEncode(p.pk))}
<div>
<h3>Invite Someone</h3>
<div>
<Invite reload={fetchData} />
</div>
</div>
{/if}
<div>
<h3>Current hieracy</h3>
<div>
<Hieracy {hierarchy} reload={fetchData} />
</div>
</div>
</article>

View File

@ -0,0 +1,14 @@
<script lang="js">
import TreeNode from "./TreeNode.svelte";
export let reload;
export let hierarchy;
</script>
<div class="px-4">
<ul class="list-disc ml-4">
{#each hierarchy as person (person.pk)}
<TreeNode {reload} {person} />
{/each}
</ul>
</div>

View File

@ -0,0 +1,52 @@
<script lang="js">
import { NDKEvent, NDKRelay, NDKRelaySet } from "@nostr-dev-kit/ndk";
import { ndk } from "../lib/nostr";
import { nip19 } from "nostr-tools";
import { relayUrl } from "../lib/consts";
let pubKeyToInvite = "";
export let reload;
async function invite() {
if (!pubKeyToInvite) return;
try {
// only publish to the relay in question, dont know why this needs so much code
let specificRelay = [new NDKRelay(relayUrl)];
const relaySet = new NDKRelaySet(specificRelay, $ndk);
relaySet.relays.forEach(async (relay) => {
await relay.connect().catch((err) => {
console.log("RELAY CONNECT ERROR");
console.error(err);
});
relay.on("connect", () => {
console.log(relay.url, "connected");
});
});
const pk = pubKeyToInvite.startsWith("npub")
? nip19.decode(pubKeyToInvite).data
: pubKeyToInvite;
const event = new NDKEvent($ndk);
event.kind = 20201;
event.tags.push(["p", pk.toString()]);
await event.publish(relaySet).then(() => reload());
pubKeyToInvite = "";
} catch (error) {
console.log("error while publishing", error);
}
}
</script>
<div class="relative flex items-stretch flex-grow focus-within:z-10">
<input
bind:value={pubKeyToInvite}
class="focus:ring-indigo-500 focus:border-indigo-500 block w-full rounded-none rounded-l-md sm:text-sm border-gray-300"
placeholder="hex or npub"
/>
<button
on:click={invite}
type="submit"
class="-ml-px relative inline-flex items-center space-x-2 px-3 py-2 border border-gray-300 text-sm font-medium rounded-r-md text-gray-700 bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 text-white"
>Go</button
>
</div>

View File

@ -0,0 +1,77 @@
<script lang="js">
import { onMount } from "svelte";
import { ndk, userPublickey } from "../lib/nostr";
import TreeNode from "./TreeNode.svelte";
import { nip19 } from "nostr-tools";
import { NDKEvent, NDKRelay, NDKRelaySet, NDKUser } from "@nostr-dev-kit/ndk";
import { relayUrl } from "../lib/consts";
export let person;
export let reload;
let username = "...";
onMount(async () => {
try {
let user = $ndk.getUser({
hexpubkey: person.pk,
});
await user.fetchProfile();
username = user.profile.name;
} catch (error) {
console.log(error);
}
});
async function removeThisUser() {
let confirmation = confirm(
`Are you sure you want to remove ${
username ? username : person.pk
}? All people they invited will also be removed.`,
);
if (confirmation) {
try {
// only publish to the relay in question, dont know why this needs so much code
let specificRelay = [new NDKRelay(relayUrl)];
const relaySet = new NDKRelaySet(specificRelay, $ndk);
relaySet.relays.forEach(async (relay) => {
await relay.connect().catch((err) => {
console.log("RELAY CONNECT ERROR");
console.error(err);
});
relay.on("connect", () => {
console.log("connected");
});
});
const event = new NDKEvent($ndk);
event.kind = 20202;
event.tags.push(["p", person.pk]);
await event.publish(relaySet).then(() => reload());
} catch (error) {
console.log("error while publishing", error);
}
}
}
</script>
<li>
<span>
<a
class="inline hover:underline"
href={`nostr:${nip19.npubEncode(person.pk)}`}
>{#if username}{username}{:else}{nip19.npubEncode(person.pk)}{/if}</a
>
{#if $userPublickey == nip19.npubEncode(person.invited_by)}<button
on:click={removeThisUser}
class="inline cusor-pointer font-semibold text-red-500">[-]</button
>{/if}{#if $userPublickey == nip19.npubEncode(person.pk)}
<span> (you)</span>
{/if}
</span>
{#if person.children && person.children.length > 0}
<ul class="list-disc ml-2">
{#each person.children as child (child.pk)}
<TreeNode {reload} person={child} />
{/each}
</ul>
{/if}
</li>

2
ui/src/lib/consts.ts Normal file
View File

@ -0,0 +1,2 @@
export const relayUrl = "ws://localhost:3334";
export const someRelays = ["wss://nos.lol", "wss://relay.snort.social"];

12
ui/src/lib/nostr.ts Normal file
View File

@ -0,0 +1,12 @@
import NDK from "@nostr-dev-kit/ndk";
import { writable, type Writable } from "svelte/store";
import { relayUrl, someRelays } from "./consts";
const relays = [relayUrl, ...someRelays];
const Ndk: NDK = new NDK({ explicitRelayUrls: relays });
Ndk.connect().then(() => console.log("ndk connected"));
export const ndk: Writable<NDK> = writable(Ndk);
export const userPublickey: Writable<string | undefined> = writable(undefined);

18
ui/src/lib/utils.ts Normal file
View File

@ -0,0 +1,18 @@
interface TreeNodeType {
pk: string;
invited_by: string;
children?: TreeNodeType[];
}
export function buildHierarchy(
data: TreeNodeType[],
parent: TreeNodeType | null = null,
): TreeNodeType[] {
const children = data.filter(
(item) => item.invited_by === (parent ? parent.pk : null),
);
return children.map((child) => ({
...child,
children: buildHierarchy(data, child),
}));
}

7
ui/src/main.js Normal file
View File

@ -0,0 +1,7 @@
import App from "./App.svelte";
const app = new App({
target: document.getElementById("app"),
});
export default app;

8
ui/tailwind.config.js Normal file
View File

@ -0,0 +1,8 @@
/** @type {import('tailwindcss').Config} */
export default {
content: ["./src/**/*.{html,js,svelte}"],
theme: {
extend: {},
},
plugins: [require("@tailwindcss/forms"), require("@tailwindcss/typography")],
};

26
ui/vite.config.js Normal file
View File

@ -0,0 +1,26 @@
import { defineConfig } from "vite";
import { svelte, vitePreprocess } from "@sveltejs/vite-plugin-svelte";
import postcss from "postcss";
// see https://vitejs.dev/config
export default defineConfig({
envPrefix: "UI",
base: "./",
build: {
chunkSizeWarningLimit: 1000,
reportCompressedSize: false,
},
plugins: [
svelte({
preprocess: [vitePreprocess()],
}),
],
css: {
postcss,
},
resolve: {
alias: {
"@": __dirname + "/src",
},
},
});

3273
ui/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

29
utils.go Normal file
View File

@ -0,0 +1,29 @@
package main
func isPkInWhitelist(targetPk string) bool {
for i := 0; i < len(whitelist); i++ {
if whitelist[i].Pk == targetPk {
return true
}
}
return false
}
func deleteFromWhitelistRecursively (target string) {
var updatedWhitelist []User
var queue []string
for _, user := range whitelist {
if user.Pk != target {
updatedWhitelist = append(updatedWhitelist, user)
}
if user.InvitedBy == target {
queue = append(queue, user.Pk);
}
}
whitelist = updatedWhitelist
for _, pk := range queue {
deleteFromWhitelistRecursively(pk)
}
}

82
whitelist.go Normal file
View File

@ -0,0 +1,82 @@
package main
import (
"context"
"encoding/json"
"os"
"github.com/nbd-wtf/go-nostr"
)
type User struct {
Pk string `json:"pk"`
InvitedBy string `json:"invited_by"`
}
var whitelist []User
func whitelistRejecter(ctx context.Context, evt *nostr.Event) (reject bool, msg string) {
// check if user in whitelist
if !isPkInWhitelist(evt.PubKey) {
return true, "You are not invited to this relay"
}
// add user(s) if needed
if (evt.Kind == 20201) {
pTags := evt.Tags.GetAll([]string{"p"})
for _, tag := range pTags {
if !isPkInWhitelist(tag.Value()) {
whitelist = append(whitelist, User{Pk: tag.Value(), InvitedBy: evt.PubKey})
}
}
}
// remove user(s) if needed
if (evt.Kind == 20202) {
pTags := evt.Tags.GetAll([]string{"p"})
for _, tag := range pTags {
for _, user := range whitelist {
if user.Pk == tag.Value() && user.InvitedBy == evt.PubKey {
deleteFromWhitelistRecursively(tag.Value())
}
}
}
}
return false, ""
}
func loadWhitelist () error {
if _, err := os.Stat("whitelist.json"); os.IsNotExist(err) {
whitelist = []User{}
return nil
} else if err != nil {
return err
}
fileContent, err := os.ReadFile("whitelist.json")
if err != nil {
return err
}
if err := json.Unmarshal(fileContent, &whitelist); err != nil {
return err
}
return nil
}
func saveWhitelist () error {
jsonBytes, err := json.Marshal(whitelist)
if err != nil {
return err
}
if err := os.WriteFile("whitelist.json", jsonBytes, 0644); err != nil {
return err
}
return nil
}