This commit is contained in:
fiatjaf
2025-11-29 22:10:23 -03:00
parent 4ad09f81f1
commit 5b923b8057
4 changed files with 339 additions and 9 deletions
+15 -5
View File
@@ -5,15 +5,19 @@ go 1.25.4
require (
fiatjaf.com/lib v0.3.2
fiatjaf.com/nostr v0.0.0-20251126120447-7261a4b515ed
github.com/itchyny/gojq v0.12.17
github.com/bep/debounce v1.2.1
github.com/btcsuite/btcd/btcec/v2 v2.3.4
github.com/mappu/miqt v0.12.0
github.com/puzpuzpuz/xsync/v3 v3.5.1
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394
)
require (
github.com/FastFilter/xorfilter v0.2.1 // indirect
github.com/ImVexed/fasturl v0.0.0-20230304231329-4e41488060f3 // indirect
github.com/andybalholm/brotli v1.1.1 // indirect
github.com/bluekeyes/go-gitdiff v0.7.1 // indirect
github.com/btcsuite/btcd v0.24.2 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect
github.com/btcsuite/btcd/btcutil v1.1.5 // indirect
github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
@@ -23,22 +27,28 @@ require (
github.com/dgraph-io/ristretto/v2 v2.3.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/elnosh/gonuts v0.4.2 // indirect
github.com/fasthttp/websocket v1.5.12 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/itchyny/timefmt-go v0.1.6 // indirect
github.com/go-git/go-git/v5 v5.16.3 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/liamg/magic v0.0.1 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect
github.com/rs/cors v1.11.1 // indirect
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38 // indirect
github.com/templexxx/cpu v0.0.1 // indirect
github.com/templexxx/xhex v0.0.0-20200614015412-aed53437177b // indirect
github.com/tidwall/gjson v1.18.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.59.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
golang.org/x/crypto v0.39.0 // indirect
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
golang.org/x/net v0.41.0 // indirect
golang.org/x/sync v0.15.0 // indirect
golang.org/x/sys v0.35.0 // indirect
golang.org/x/text v0.26.0 // indirect
+24 -4
View File
@@ -6,11 +6,15 @@ github.com/FastFilter/xorfilter v0.2.1 h1:lbdeLG9BdpquK64ZsleBS8B4xO/QW1IM0gMzF7
github.com/FastFilter/xorfilter v0.2.1/go.mod h1:aumvdkhscz6YBZF9ZA/6O4fIoNod4YR50kIVGGZ7l9I=
github.com/ImVexed/fasturl v0.0.0-20230304231329-4e41488060f3 h1:ClzzXMDDuUbWfNNZqGeYq4PnYOlwlOVIvSyNaIy0ykg=
github.com/ImVexed/fasturl v0.0.0-20230304231329-4e41488060f3/go.mod h1:we0YA5CsBbH5+/NUzC/AlMmxaDtWlXeNsqrwXjTzmzA=
github.com/PowerDNS/lmdb-go v1.9.3 h1:AUMY2pZT8WRpkEv39I9Id3MuoHd+NZbTVpNhruVkPTg=
github.com/PowerDNS/lmdb-go v1.9.3/go.mod h1:TE0l+EZK8Z1B4dx070ZxkWTlp8RG1mjN0/+FkFRQMtU=
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
github.com/bluekeyes/go-gitdiff v0.7.1 h1:graP4ElLRshr8ecu0UtqfNTCHrtSyZd3DABQm/DWesQ=
github.com/bluekeyes/go-gitdiff v0.7.1/go.mod h1:QpfYYO1E0fTVHVZAZKiRjtSGY9823iCdvGXBcEzHGbM=
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M=
github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A=
@@ -69,6 +73,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/go-git/go-git/v5 v5.16.3 h1:Z8BtvxZ09bYm/yYNgPKCzgWtaRqDTgIKRgIRHBfU6Z8=
github.com/go-git/go-git/v5 v5.16.3/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
@@ -80,13 +86,11 @@ github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW
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.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/itchyny/gojq v0.12.17 h1:8av8eGduDb5+rvEdaOO+zQUjA04MS0m3Ps8HiD+fceg=
github.com/itchyny/gojq v0.12.17/go.mod h1:WBrEMkgAfAGO1LUcGOckBl5O726KPp+OlkKug0I/FEY=
github.com/itchyny/timefmt-go v0.1.6 h1:ia3s54iciXDdzWzwaVKXZPbiXzxxnv1SPGFfM/myJ5Q=
github.com/itchyny/timefmt-go v0.1.6/go.mod h1:RRDZYC5s9ErkjQvTvvU7keJjxUYzIISJGxm9/mAERQg=
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/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
@@ -97,6 +101,12 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/liamg/magic v0.0.1 h1:Ru22ElY+sCh6RvRTWjQzKKCxsEco8hE0co8n1qe7TBM=
github.com/liamg/magic v0.0.1/go.mod h1:yQkOmZZI52EA+SQ2xyHpVw8fNvTBruF873Y+Vt6S+fk=
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
github.com/mappu/miqt v0.12.0 h1:bBMBDeACmV8TbdLfoN51la7kF6QT3sNAcG+ZdRDgmxU=
@@ -115,10 +125,14 @@ github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
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/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg=
github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=
github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38 h1:D0vL7YNisV2yqE55+q0lFuGse6U8lxlg7fYTctlT5Gc=
@@ -149,6 +163,8 @@ github.com/valyala/fasthttp v1.59.0 h1:Qu0qYHfXvPk1mSLNqcFtEk6DpxgA26hy6bmydotDp
github.com/valyala/fasthttp v1.59.0/go.mod h1:GTxNb9Bc6r2a9D0TWNSPwDz78UxnTGBViY3xZNEqyYU=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@@ -192,6 +208,8 @@ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miE
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
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=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -200,3 +218,5 @@ 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=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
+7
View File
@@ -27,6 +27,7 @@ var (
event int
req int
paste int
serve int
}
statusLabel *qt.QLabel
@@ -144,6 +145,7 @@ func main() {
eventTab := setupEventTab()
reqTab := setupReqTab()
pasteTab := setupPasteTab()
serveTab := setupServeTab()
tabWidget.AddTab(eventTab, "event")
tabIndexes.event = 0
@@ -154,6 +156,9 @@ func main() {
tabWidget.AddTab(pasteTab, "paste")
tabIndexes.paste = 2
tabWidget.AddTab(serveTab, "serve")
tabIndexes.serve = 3
switch *initialTab {
case "event":
tabWidget.SetCurrentIndex(tabIndexes.event)
@@ -161,6 +166,8 @@ func main() {
tabWidget.SetCurrentIndex(tabIndexes.req)
case "paste":
tabWidget.SetCurrentIndex(tabIndexes.paste)
case "serve":
tabWidget.SetCurrentIndex(tabIndexes.serve)
default:
tabWidget.SetCurrentIndex(0)
}
+293
View File
@@ -0,0 +1,293 @@
package main
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/url"
"os"
"sync/atomic"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/eventstore/slicestore"
"fiatjaf.com/nostr/khatru"
"fiatjaf.com/nostr/khatru/blossom"
"fiatjaf.com/nostr/khatru/grasp"
qt "github.com/mappu/miqt/qt6"
"github.com/mappu/miqt/qt6/mainthread"
"github.com/puzpuzpuz/xsync/v3"
)
var version = "dev"
func isPiped() bool {
stat, err := os.Stdin.Stat()
if err != nil {
return false
}
return (stat.Mode() & os.ModeCharDevice) == 0
}
type serveVars struct {
tab *qt.QWidget
graspCheck *qt.QCheckBox
blossomCheck *qt.QCheckBox
negentropyCheck *qt.QCheckBox
startButton *qt.QPushButton
stopButton *qt.QPushButton
logsList *qt.QListWidget
eventsList *qt.QListWidget
relay *khatru.Relay
db *slicestore.SliceStore
blobStore *xsync.MapOf[string, []byte]
repoDir string
running bool
}
var serve = &serveVars{}
func setupServeTab() *qt.QWidget {
serve.tab = qt.NewQWidget(window.QWidget)
layout := qt.NewQVBoxLayout2()
serve.tab.SetLayout(layout.QLayout)
// checkboxes
optionsHBox := qt.NewQHBoxLayout2()
layout.AddLayout(optionsHBox.QLayout)
serve.negentropyCheck = qt.NewQCheckBox(serve.tab)
serve.negentropyCheck.SetText("negentropy")
optionsHBox.AddWidget(serve.negentropyCheck.QWidget)
serve.graspCheck = qt.NewQCheckBox(serve.tab)
serve.graspCheck.SetText("grasp")
optionsHBox.AddWidget(serve.graspCheck.QWidget)
serve.blossomCheck = qt.NewQCheckBox(serve.tab)
serve.blossomCheck.SetText("blossom")
optionsHBox.AddWidget(serve.blossomCheck.QWidget)
// buttons
buttonsHBox := qt.NewQHBoxLayout2()
layout.AddLayout(buttonsHBox.QLayout)
serve.startButton = qt.NewQPushButton5("start", serve.tab)
buttonsHBox.AddWidget(serve.startButton.QWidget)
serve.stopButton = qt.NewQPushButton5("stop", serve.tab)
serve.stopButton.SetEnabled(false)
buttonsHBox.AddWidget(serve.stopButton.QWidget)
// logs
logsLabel := qt.NewQLabel2()
logsLabel.SetText("logs:")
layout.AddWidget(logsLabel.QWidget)
serve.logsList = qt.NewQListWidget(serve.tab)
serve.logsList.SetMinimumHeight(150)
layout.AddWidget(serve.logsList.QWidget)
// events
eventsLabel := qt.NewQLabel2()
eventsLabel.SetText("events:")
layout.AddWidget(eventsLabel.QWidget)
serve.eventsList = qt.NewQListWidget(serve.tab)
serve.eventsList.SetMinimumHeight(200)
layout.AddWidget(serve.eventsList.QWidget)
// double-click events
serve.eventsList.OnItemDoubleClicked(func(item *qt.QListWidgetItem) {
var event nostr.Event
if err := json.Unmarshal([]byte(item.Text()), &event); err != nil {
return
}
pretty, _ := json.MarshalIndent(event, "", " ")
dialog := qt.NewQDialog(window.QWidget)
dialog.SetWindowTitle("event")
dialog.SetMinimumWidth(400)
dialog.SetMinimumHeight(500)
dlayout := qt.NewQVBoxLayout2()
dialog.SetLayout(dlayout.QLayout)
textEdit := qt.NewQTextEdit(dialog.QWidget)
textEdit.SetReadOnly(true)
textEdit.SetPlainText(string(pretty))
dlayout.AddWidget(textEdit.QWidget)
closeButton := qt.NewQPushButton5("close", dialog.QWidget)
closeButton.OnClicked(func() { dialog.Close() })
dlayout.AddWidget(closeButton.QWidget)
dialog.Exec()
})
serve.startButton.OnClicked(func() {
serve.startRelay()
})
serve.stopButton.OnClicked(func() {
serve.stopRelay()
})
return serve.tab
}
func (serve *serveVars) startRelay() {
if serve.running {
return
}
serve.running = true
serve.startButton.SetEnabled(false)
serve.stopButton.SetEnabled(true)
serve.db = &slicestore.SliceStore{}
serve.blobStore = xsync.NewMapOf[string, []byte]()
rl := khatru.NewRelay()
serve.relay = rl
rl.Info.Name = "nak serve"
rl.Info.Description = "a local relay for testing, debugging and development."
rl.Info.Software = "https://github.com/fiatjaf/nak"
rl.Info.Version = "dev"
rl.UseEventstore(serve.db, 500)
if serve.negentropyCheck.IsChecked() {
rl.Negentropy = true
}
started := make(chan bool)
exited := make(chan error)
hostname := "localhost"
port := 10547
if serve.blossomCheck.IsChecked() {
bs := blossom.New(rl, fmt.Sprintf("http://%s:%d", hostname, port))
bs.Store = blossom.NewMemoryBlobIndex()
bs.StoreBlob = func(ctx context.Context, sha256 string, ext string, body []byte) error {
serve.blobStore.Store(sha256+ext, body)
serve.log("blob stored: %s", sha256+ext)
return nil
}
bs.LoadBlob = func(ctx context.Context, sha256 string, ext string) (io.ReadSeeker, *url.URL, error) {
if body, ok := serve.blobStore.Load(sha256 + ext); ok {
serve.log("blob download: %s", sha256+ext)
return bytes.NewReader(body), nil, nil
}
return nil, nil, nil
}
bs.DeleteBlob = func(ctx context.Context, sha256 string, ext string) error {
serve.blobStore.Delete(sha256 + ext)
serve.log("blob delete: %s", sha256+ext)
return nil
}
}
if serve.graspCheck.IsChecked() {
var err error
serve.repoDir, err = os.MkdirTemp("", "vnak-serve-grasp-repos-")
if err != nil {
serve.log("failed to create grasp repos directory: %w", err)
return
}
g := grasp.New(rl, serve.repoDir)
g.OnRead = func(ctx context.Context, pubkey nostr.PubKey, repo string) (reject bool, reason string) {
serve.log("git read by '%s' at '%s'", pubkey.Hex(), repo)
return false, ""
}
g.OnWrite = func(ctx context.Context, pubkey nostr.PubKey, repo string) (reject bool, reason string) {
serve.log("git write by '%s' at '%s'", pubkey.Hex(), repo)
return false, ""
}
}
go func() {
err := rl.Start(hostname, port, started)
exited <- err
}()
// relay logging
rl.OnRequest = func(ctx context.Context, filter nostr.Filter) (reject bool, msg string) {
negentropy := ""
if khatru.IsNegentropySession(ctx) {
negentropy = "negentropy "
}
serve.log("%srequest: %s", negentropy, filter)
return false, ""
}
rl.OnCount = func(ctx context.Context, filter nostr.Filter) (reject bool, msg string) {
serve.log("count request: %s", filter)
return false, ""
}
rl.OnEvent = func(ctx context.Context, event nostr.Event) (reject bool, msg string) {
serve.log("event: %s", event)
serve.addEvent(event)
return false, ""
}
totalConnections := atomic.Int32{}
rl.OnConnect = func(ctx context.Context) {
totalConnections.Add(1)
go func() {
<-ctx.Done()
totalConnections.Add(-1)
}()
}
<-started
serve.log("relay running at %s", fmt.Sprintf("ws://%s:%d", hostname, port))
if serve.graspCheck.IsChecked() {
serve.log("grasp repos at %s", serve.repoDir)
}
go func() {
err := <-exited
if err != nil {
serve.log("relay exited with error: %s", err)
}
mainthread.Wait(func() {
serve.running = false
serve.startButton.SetEnabled(true)
serve.stopButton.SetEnabled(false)
})
}()
}
func (serve *serveVars) stopRelay() {
if !serve.running {
return
}
if serve.relay != nil {
serve.relay.Shutdown(ctx)
}
serve.running = false
serve.startButton.SetEnabled(true)
serve.stopButton.SetEnabled(false)
statusLabel.SetText("relay stopped")
}
func (serve *serveVars) log(format string, args ...interface{}) {
msg := fmt.Sprintf(format, args...)
mainthread.Start(func() {
item := qt.NewQListWidgetItem2(msg)
serve.logsList.InsertItem(0, item)
serve.logsList.ScrollToTop()
})
}
func (serve *serveVars) addEvent(event nostr.Event) {
jsonBytes, _ := json.Marshal(event)
mainthread.Wait(func() {
item := qt.NewQListWidgetItem2(string(jsonBytes))
serve.eventsList.AddItemWithItem(item)
})
}