mirror of
https://github.com/github-tijlxyz/khatru-pyramid.git
synced 2025-06-04 00:02:05 +00:00
use go templating instead of svelte client
This commit is contained in:
parent
7018b5c3ec
commit
216b2aa70b
10
.env
10
.env
@ -1,8 +1,8 @@
|
||||
# Nostr Standard Relay Info
|
||||
RELAY_NAME=""
|
||||
RELAY_DESCRIPTION=""
|
||||
RELAY_PUBKEY=""
|
||||
RELAY_CONTACT=""
|
||||
RELAY_NAME="Name of the Relay"
|
||||
RELAY_DESCRIPTION="Description of the relay"
|
||||
RELAY_PUBKEY="07adfda9c5adc80881bb2a5220f6e3181e0c043b90fa115c4f183464022968e6"
|
||||
RELAY_CONTACT="email@example.com"
|
||||
|
||||
# Custom invite-relay settings
|
||||
INVITE_RELAY_MASTER="" # Master of the relay (pubkey hex)
|
||||
INVITE_RELAY_MASTER="07adfda9c5adc80881bb2a5220f6e3181e0c043b90fa115c4f183464022968e6" # Master of the relay (pubkey hex)
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,4 +1,4 @@
|
||||
whitelist.json
|
||||
khatru-invite
|
||||
.env
|
||||
khatru-badgern-db
|
||||
khatru-badgern-db
|
||||
|
35
Dockerfile
Normal file
35
Dockerfile
Normal file
@ -0,0 +1,35 @@
|
||||
FROM node:latest AS ui-builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY . .
|
||||
|
||||
WORKDIR /app/ui
|
||||
|
||||
RUN yarn install
|
||||
RUN yarn build
|
||||
|
||||
|
||||
FROM golang:1.20 AS go-builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY . .
|
||||
|
||||
COPY --from=ui-builder /app/ui/dist /app/ui/dist
|
||||
|
||||
RUN go build -o app
|
||||
|
||||
|
||||
FROM golang:1.20
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=go-builder /app/app /app/app
|
||||
|
||||
COPY --from=ui-builder /app/ui/dist /app/ui/dist
|
||||
|
||||
|
||||
EXPOSE 3334
|
||||
|
||||
CMD ["./app"]
|
@ -3,6 +3,5 @@
|
||||
A relay based on [Khatru](https://github.com/fiatjaf/khatru) with a invite hierarchy feature.
|
||||
|
||||
some notes before running:
|
||||
1. change `ws://localhost:3334` in `ui/src/lib/consts.ts` to your relay url endpoint and build the UI
|
||||
2. configure the relay settings in `.env`
|
||||
3. manually add someone to the `whitelist.json` file, like this: `[{"pk":"07adfda9c5adc80881bb2a5220f6e3181e0c043b90fa115c4f183464022968e6","invited_by":""}]`
|
||||
1. configure the relay settings in `.env`
|
||||
2. manually add someone to the `whitelist.json` file, like this: `[{"pk":"07adfda9c5adc80881bb2a5220f6e3181e0c043b90fa115c4f183464022968e6","invited_by":""}]`
|
||||
|
15
docker-compose.yaml
Normal file
15
docker-compose.yaml
Normal file
@ -0,0 +1,15 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
khatru-invite:
|
||||
restart: unless-stopped
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- 3335:3334
|
||||
volumes:
|
||||
- ./whitelist.json:/app/whitelist.json
|
||||
- ./khatru-badgern-db:/app/khatru-badgern-db
|
||||
env_file:
|
||||
- .env
|
14
go.mod
14
go.mod
@ -1,17 +1,17 @@
|
||||
module github.com/github-tijlxyz/khatru-invite
|
||||
|
||||
go 1.21.1
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/fiatjaf/khatru v0.0.0-20230916185141-24d1e3aebd67
|
||||
github.com/fiatjaf/khatru v0.0.0-20231003113207-bbe186494e68
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/nbd-wtf/go-nostr v0.20.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||
github.com/bmatsuo/lmdb-go v1.8.0 // indirect
|
||||
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/decred/dcrd/crypto/blake256 v1.0.1 // indirect
|
||||
@ -26,16 +26,15 @@ require (
|
||||
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/golang/protobuf v1.4.2 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/flatbuffers v1.12.1 // indirect
|
||||
github.com/jmoiron/sqlx v1.3.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/mattn/go-sqlite3 v1.14.6 // 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
|
||||
github.com/tidwall/gjson v1.14.4 // indirect
|
||||
@ -47,4 +46,5 @@ require (
|
||||
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
|
||||
google.golang.org/protobuf v1.23.0 // indirect
|
||||
)
|
||||
|
100
go.sum
100
go.sum
@ -1,24 +1,47 @@
|
||||
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/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
|
||||
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/bmatsuo/lmdb-go v1.8.0 h1:ohf3Q4xjXZBKh4AayUY4bb2CXuhRAI8BYGlJq08EfNA=
|
||||
github.com/bmatsuo/lmdb-go v1.8.0/go.mod h1:wWPZmKdOAZsl4qOqkowQ1aCrFie1HU8gWloHMCeAUdM=
|
||||
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.0/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE=
|
||||
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/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A=
|
||||
github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE=
|
||||
github.com/btcsuite/btcd/btcutil v1.1.3 h1:xfbtw8lwpp0G6NwSHb+UE67ryTFHJAiNuipusjXSohQ=
|
||||
github.com/btcsuite/btcd/btcutil v1.1.3/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0=
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
|
||||
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/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
|
||||
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
|
||||
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
|
||||
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
|
||||
github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I=
|
||||
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
|
||||
github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
|
||||
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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
|
||||
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.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
|
||||
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/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8=
|
||||
@ -29,9 +52,10 @@ 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/khatru v0.0.0-20230916185141-24d1e3aebd67 h1:nsaUBS6rjAZCvPDgGUxG3tzfZnx6DX9iUBqPtDWDT/g=
|
||||
github.com/fiatjaf/khatru v0.0.0-20230916185141-24d1e3aebd67/go.mod h1:XDX+UIURFlTrAAw/ya7M5YRC4ft2Qm2gAAvs3ELoVps=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/fiatjaf/khatru v0.0.0-20231003113207-bbe186494e68 h1:oBx0uzT+KILepoPAPsjAghqAPSo9uV4pR1myr1rQQGA=
|
||||
github.com/fiatjaf/khatru v0.0.0-20231003113207-bbe186494e68/go.mod h1:8shKDuVtrdLfsuHV4FBC3qYTTXnyfOLAgKqUt4u+Okk=
|
||||
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=
|
||||
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
||||
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
|
||||
@ -46,44 +70,65 @@ github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM
|
||||
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/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=
|
||||
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/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=
|
||||
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/jmoiron/sqlx v1.3.1 h1:aLN7YINNZ7cYOPK3QC83dbM6KT0NMqVMw961TqrejlE=
|
||||
github.com/jmoiron/sqlx v1.3.1/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ=
|
||||
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.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
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=
|
||||
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/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
|
||||
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/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
|
||||
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
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=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
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 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=
|
||||
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.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
|
||||
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=
|
||||
@ -98,6 +143,7 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
||||
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-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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
@ -109,13 +155,17 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx
|
||||
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-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
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-20180906233101-161cd47e91fd/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-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
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=
|
||||
@ -127,15 +177,23 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
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-20180909124046-d0be0721c37e/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-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/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.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
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=
|
||||
@ -154,8 +212,20 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA
|
||||
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=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
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=
|
||||
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=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
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=
|
||||
|
217
handler.go
217
handler.go
@ -1,24 +1,199 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/nbd-wtf/go-nostr"
|
||||
)
|
||||
|
||||
// embed ui files
|
||||
|
||||
//go:embed ui/dist/*
|
||||
var uiContent embed.FS
|
||||
var dist embed.FS
|
||||
|
||||
func embeddedUIHandler(w http.ResponseWriter, r *http.Request) {
|
||||
path := "ui/dist" + r.URL.Path
|
||||
func inviteTreeHandler(w http.ResponseWriter, r *http.Request) {
|
||||
formattedInviteData := buildHTMLTree(whitelist, "")
|
||||
|
||||
if r.URL.Path == "/" {
|
||||
path = "ui/dist/index.html"
|
||||
data := map[string]interface{}{
|
||||
"Relayname": relayName,
|
||||
"Relaydescription": relayDescription,
|
||||
"Pagetitle": "Invite Hierarchy",
|
||||
"Pagecontent": `
|
||||
<input type="text" id="inviteuser-input" placeholder="npub1..." /><button class="inviteuser">Invite!</button>
|
||||
` + formattedInviteData,
|
||||
}
|
||||
|
||||
data, err := uiContent.ReadFile(path)
|
||||
tmpl, err := template.ParseFS(dist, "ui/dist/index.html")
|
||||
if err != nil {
|
||||
http.Error(w, "Error parsing template: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
err = tmpl.Execute(w, data)
|
||||
if err != nil {
|
||||
http.Error(w, "Error executing template: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func reportsViewerHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var formattedReportsData template.HTML = ""
|
||||
|
||||
events, _ := db.QueryEvents(context.Background(), nostr.Filter{
|
||||
Kinds: []int{1984},
|
||||
Limit: 52,
|
||||
})
|
||||
|
||||
type Report struct {
|
||||
ID string
|
||||
ByUser string
|
||||
AboutUser string
|
||||
AboutEvent string
|
||||
Type string
|
||||
Content string
|
||||
}
|
||||
|
||||
for ev := range events {
|
||||
pTag := ev.Tags.GetFirst([]string{"p"})
|
||||
|
||||
eTag := ev.Tags.GetFirst([]string{"e"})
|
||||
if pTag != nil {
|
||||
var typeReport = eTag.Relay()[6:]
|
||||
if typeReport == "" {
|
||||
typeReport = pTag.Relay()[6:]
|
||||
}
|
||||
report := Report{
|
||||
ID: ev.ID,
|
||||
ByUser: ev.PubKey,
|
||||
AboutUser: pTag.Value(),
|
||||
AboutEvent: eTag.Value(),
|
||||
Type: typeReport,
|
||||
Content: ev.Content,
|
||||
}
|
||||
// get AboutEvent content, note1 ect
|
||||
formattedReportsData += template.HTML(fmt.Sprintf(`
|
||||
<div>
|
||||
<p><b>Report %v</b></p>
|
||||
<p>By User: <a class="user" href="nostr:%v">%v</a></p>
|
||||
<p>About User: <a class="user" href="nostr:%v">%v</a></p>`,
|
||||
report.ID,
|
||||
getUserInfo(context.Background(), report.ByUser).Npub,
|
||||
getUserInfo(context.Background(), report.ByUser).Name,
|
||||
getUserInfo(context.Background(), report.AboutUser).Npub,
|
||||
getUserInfo(context.Background(), report.AboutUser).Name,
|
||||
))
|
||||
if report.AboutEvent != "" {
|
||||
// fetch event data
|
||||
aboutEvents, _ := db.QueryEvents(context.TODO(), nostr.Filter{
|
||||
IDs: []string{report.AboutEvent},
|
||||
})
|
||||
for aboutEvent := range aboutEvents {
|
||||
formattedReportsData += template.HTML(fmt.Sprintf(`
|
||||
<p>
|
||||
About Event: <ul>
|
||||
<p>Kind: %v</p>
|
||||
<p>Tags: %v</p>
|
||||
<p>Content: %v</p>
|
||||
</ul>
|
||||
</p>`,
|
||||
template.HTMLEscaper(aboutEvent.Kind),
|
||||
template.HTMLEscaper(aboutEvent.Tags),
|
||||
template.HTMLEscaper(aboutEvent.Content),
|
||||
))
|
||||
}
|
||||
}
|
||||
formattedReportsData += template.HTML(fmt.Sprintf(`
|
||||
<p>Type: %v</p>`,
|
||||
report.Type,
|
||||
))
|
||||
if report.Content != "" {
|
||||
formattedReportsData += template.HTML(fmt.Sprintf(`
|
||||
<p>Content: %v</p>
|
||||
<div>
|
||||
<button data-actionarg='[["e", "%v"],["p", "%v"]]' class="removefromrelay">Ban Reported User and Remove Report</button>
|
||||
<button data-actionarg='[["e", "%v"]]' class="removefromrelay">Remove This Report</button>
|
||||
<button data-actionarg='[["p", "%v"]]' class="removefromrelay">Ban User who wrote report</button>
|
||||
</div>
|
||||
</div>
|
||||
<hr />`,
|
||||
template.HTMLEscaper(report.Content),
|
||||
template.HTMLEscaper(report.ID),
|
||||
template.HTMLEscaper(report.AboutUser),
|
||||
template.HTMLEscaper(report.ID),
|
||||
template.HTMLEscaper(report.ByUser),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data := map[string]interface{}{
|
||||
"Relayname": relayName,
|
||||
"Relaydescription": relayDescription,
|
||||
"Pagetitle": "Reports Viewer",
|
||||
"Pagecontent": formattedReportsData,
|
||||
}
|
||||
|
||||
tmpl, err := template.ParseFS(dist, "ui/dist/index.html")
|
||||
if err != nil {
|
||||
http.Error(w, "Error parsing template: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Execute the template with the provided data and write it to the response
|
||||
err = tmpl.Execute(w, data)
|
||||
if err != nil {
|
||||
http.Error(w, "Error executing template: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func homePageHandler(w http.ResponseWriter, r *http.Request) {
|
||||
relayOwnerInfo := getUserInfo(context.Background(), relayPubkey)
|
||||
|
||||
data := map[string]interface{}{
|
||||
"Relayname": relayName,
|
||||
"Relaydescription": relayDescription,
|
||||
"Pagetitle": "Info",
|
||||
"Pagecontent": template.HTML(fmt.Sprintf(`
|
||||
<div>Relay Name: %v</div>
|
||||
<div>Relay Description: %v</div>
|
||||
<div>Relay Owner: <a class="user" href="nostr:%v">%v</a></div>
|
||||
<div>Relay Alternative Contact: %v</div>
|
||||
<br />
|
||||
<div><sub>This relay uses <a target="_blank" rel="noopener noreferrer" href="https://github.com/github-tijlxyz/khatru-invite">Khatru Invite</a>, which is build with <a target="_blank" rel="noopener noreferrer" href="https://github.com/fiatjaf/khatru">Khatru</a></sub></div>
|
||||
`, relayName, relayDescription, relayOwnerInfo.Npub, relayOwnerInfo.Name, relayContact)),
|
||||
}
|
||||
|
||||
tmpl, err := template.ParseFS(dist, "ui/dist/index.html")
|
||||
if err != nil {
|
||||
http.Error(w, "Error parsing template: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
err = tmpl.Execute(w, data)
|
||||
if err != nil {
|
||||
http.Error(w, "Error executing template: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func redirectHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if strings.HasPrefix(r.URL.Path, "/assets") {
|
||||
staticHandler("ui/dist", w, r)
|
||||
} else if r.URL.Path == "/" {
|
||||
homePageHandler(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
func staticHandler(prefix string, w http.ResponseWriter, r *http.Request) {
|
||||
path := prefix + r.URL.Path
|
||||
|
||||
data, err := dist.ReadFile(path)
|
||||
if err != nil {
|
||||
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
|
||||
return
|
||||
@ -38,31 +213,3 @@ func embeddedUIHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func inviteDataApiHandler(w http.ResponseWriter, re *http.Request) {
|
||||
jsonBytes, err := json.Marshal(whitelist)
|
||||
if err != nil {
|
||||
http.Error(w, "internal server error 00", 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 01", http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func relayMasterApiHandler(w http.ResponseWriter, re *http.Request) {
|
||||
jsonBytes, err := json.Marshal(relayMaster)
|
||||
if err != nil {
|
||||
http.Error(w, "internal server error 10", 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 11", http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
42
main.go
42
main.go
@ -14,8 +14,14 @@ import (
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
var relayMaster string
|
||||
var db badgern.BadgerBackend
|
||||
var (
|
||||
relayMaster string
|
||||
db badgern.BadgerBackend
|
||||
relayName string = ""
|
||||
relayPubkey string = ""
|
||||
relayDescription string = "none"
|
||||
relayContact string = "none"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// save whitelist on shutdown
|
||||
@ -41,11 +47,17 @@ func main() {
|
||||
relay := khatru.NewRelay()
|
||||
|
||||
relayMaster = os.Getenv("INVITE_RELAY_MASTER")
|
||||
|
||||
|
||||
// add information here!
|
||||
relay.Name = "a invite relay"
|
||||
relay.PubKey = ""
|
||||
relay.Contact = ""
|
||||
relayName = os.Getenv("RELAY_NAME")
|
||||
relayPubkey = os.Getenv("RELAY_PUBKEY")
|
||||
relayDescription = os.Getenv("RELAY_DESCRIPTION")
|
||||
relayContact = os.Getenv("RELAY_CONTACT")
|
||||
|
||||
relay.Name = relayName
|
||||
relay.PubKey = relayPubkey
|
||||
relay.Description = relayDescription
|
||||
relay.Contact = relayContact
|
||||
|
||||
// load whitelist storage
|
||||
if err := loadWhitelist(); err != nil {
|
||||
@ -65,12 +77,10 @@ func main() {
|
||||
|
||||
relay.RejectEvent = append(relay.RejectEvent, whitelistRejecter)
|
||||
|
||||
// invitedata api
|
||||
relay.Router().HandleFunc("/invitedata", inviteDataApiHandler)
|
||||
relay.Router().HandleFunc("/relaymaster", relayMasterApiHandler)
|
||||
|
||||
// ui
|
||||
relay.Router().HandleFunc("/", embeddedUIHandler)
|
||||
relay.Router().HandleFunc("/reports", reportsViewerHandler)
|
||||
relay.Router().HandleFunc("/users", inviteTreeHandler)
|
||||
relay.Router().HandleFunc("/", redirectHandler)
|
||||
|
||||
fmt.Println("running on :3334")
|
||||
http.ListenAndServe(":3334", relay)
|
||||
@ -78,9 +88,9 @@ func main() {
|
||||
|
||||
// save whitelist on shutdown
|
||||
func handleSignals() {
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM)
|
||||
<-sigCh
|
||||
saveWhitelist()
|
||||
os.Exit(0)
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM)
|
||||
<-sigCh
|
||||
saveWhitelist()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
49
nostr.go
Normal file
49
nostr.go
Normal file
@ -0,0 +1,49 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/nbd-wtf/go-nostr"
|
||||
"github.com/nbd-wtf/go-nostr/nip19"
|
||||
)
|
||||
|
||||
type SimpleUserInfo struct {
|
||||
Npub string
|
||||
Name string
|
||||
Picture string
|
||||
Time time.Time
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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()}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
.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
|
@ -1,30 +0,0 @@
|
||||
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",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
24
ui/.gitignore
vendored
24
ui/.gitignore
vendored
@ -1,2 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
@ -1,13 +0,0 @@
|
||||
.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
|
@ -1,16 +1,32 @@
|
||||
<!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>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<body>
|
||||
<noscript>Please enable JavaScript to run this app.</noscript>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="./src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="{{.Relaydescription}}">
|
||||
<script src="./src/lib/main.ts" type="module"></script>
|
||||
<title>{{.Relayname}} | {{.Pagetitle}}</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div>
|
||||
<h1>{{.Relayname}}</h1>
|
||||
<p>{{.Relaydescription}}</p>
|
||||
</div>
|
||||
<hr />
|
||||
<div>
|
||||
<span>
|
||||
<a href="/">information</a> -
|
||||
<a href="/users">invite tree</a> -
|
||||
<a href="/reports">admin reports viewer</a>
|
||||
</span>
|
||||
</div>
|
||||
<hr />
|
||||
<br />
|
||||
<div>
|
||||
{{.Pagecontent}}
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -1,31 +1,20 @@
|
||||
{
|
||||
"name": "inviterelay-ui",
|
||||
"name": "ui",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"lint": "prettier --plugin-search-dir . --check . && eslint .",
|
||||
"format": "prettier --plugin-search-dir . --write ."
|
||||
"build": "tsc && vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"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"
|
||||
"@nostr-dev-kit/ndk": "^1.3.1",
|
||||
"nostr-tools": "^1.16.0",
|
||||
"typescript": "^5.0.2",
|
||||
"vite": "^4.4.5"
|
||||
},
|
||||
"dependencies": {}
|
||||
"dependencies": {
|
||||
"@nostr-dev-kit/ndk-cache-dexie": "^1.3.2"
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +0,0 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
@ -1,3 +0,0 @@
|
||||
@tailwind base;
|
||||
@tailwind utilities;
|
||||
@tailwind components;
|
@ -1,97 +0,0 @@
|
||||
<script lang="ts">
|
||||
import "./App.css";
|
||||
import Hierarchy from "./components/Hierarchy.svelte";
|
||||
import Invite from "./components/Invite.svelte";
|
||||
import { onMount } from "svelte";
|
||||
import { ndk, relayMaster, userPublickey } from "./lib/nostr";
|
||||
import { NDKNip07Signer } from "@nostr-dev-kit/ndk";
|
||||
import { buildHierarchy } from "./lib/utils";
|
||||
import { nip19 } from "nostr-tools";
|
||||
import AdminView from "./components/AdminView.svelte";
|
||||
|
||||
let adminView = false;
|
||||
|
||||
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() {
|
||||
// Fetch Invite Data
|
||||
const response = await fetch("/invitedata");
|
||||
invitedata = Object.values(await response.json());
|
||||
hierarchy = buildHierarchy(invitedata, { pk: "", invited_by: "" });
|
||||
|
||||
// Fetch Relay Master Pubkey
|
||||
const response0 = await fetch("/relaymaster");
|
||||
$relayMaster = await response0.json();
|
||||
relayMaster.set($relayMaster);
|
||||
}
|
||||
|
||||
let invitedata = [];
|
||||
let hierarchy = [];
|
||||
|
||||
onMount(() => {
|
||||
addEventListener("load", (e) => {
|
||||
setTimeout(() => {
|
||||
login();
|
||||
}, 1);
|
||||
});
|
||||
fetchData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<article class="font-sans px-4 py-6 lg:max-w-7xl lg:pt-6 lg:pb-28">
|
||||
<h1 class="text-xl">Invite Relay</h1>
|
||||
{#if adminView == true}
|
||||
<button
|
||||
on:click={() => (adminView = false)}
|
||||
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"
|
||||
>Leave Reports Viewer</button
|
||||
>
|
||||
<p>
|
||||
You are{$userPublickey == nip19.npubEncode($relayMaster) ? "" : " not"} logged
|
||||
in as a relay master
|
||||
</p>
|
||||
<AdminView />
|
||||
{:else if adminView == false}
|
||||
<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 $userPublickey !== undefined}
|
||||
<div>
|
||||
<button
|
||||
on:click={() => (adminView = true)}
|
||||
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"
|
||||
>Open Reports Viewer</button
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
{#if invitedata.find((p) => $userPublickey == nip19.npubEncode(p.pk))}
|
||||
<div>
|
||||
<h3>Invite Someone</h3>
|
||||
<div>
|
||||
<Invite reload={fetchData} />
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<div>
|
||||
<h3>Current Hierarchy</h3>
|
||||
<div>
|
||||
<Hierarchy {hierarchy} reload={fetchData} />
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</article>
|
@ -1,41 +0,0 @@
|
||||
<script>
|
||||
import { NDKRelay, NDKRelaySet } from "@nostr-dev-kit/ndk";
|
||||
import { ndk } from "../lib/nostr";
|
||||
import { onMount } from "svelte";
|
||||
import ReportEvent from "./ReportEvent.svelte";
|
||||
import { relayUrl } from "../lib/consts";
|
||||
|
||||
let events = [];
|
||||
|
||||
onMount(async () => {
|
||||
try {
|
||||
// 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("error while connecting to relay", err);
|
||||
});
|
||||
relay.on("connect", () => {
|
||||
console.log("connected");
|
||||
});
|
||||
});
|
||||
|
||||
let filter = { kinds: [1984], limit: 250 };
|
||||
let options = { closeOnEose: true };
|
||||
let es = await $ndk.fetchEvents(filter, options, relaySet);
|
||||
events = Array.from(es);
|
||||
} catch (error) {
|
||||
console.log("error while getting feed", error);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div>
|
||||
{#each events as event}
|
||||
<ReportEvent {event} />
|
||||
{/each}
|
||||
{#if events.length == 0}
|
||||
<span>Didn't found any events</span>
|
||||
{/if}
|
||||
</div>
|
@ -1,14 +0,0 @@
|
||||
<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>
|
@ -1,52 +0,0 @@
|
||||
<script>
|
||||
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>
|
@ -1,181 +0,0 @@
|
||||
<script>
|
||||
import { NDKEvent, NDKRelay, NDKRelaySet } from "@nostr-dev-kit/ndk";
|
||||
import { relayUrl } from "../lib/consts";
|
||||
import { ndk } from "../lib/nostr";
|
||||
export let event;
|
||||
|
||||
let show = true;
|
||||
|
||||
async function dismissReportEvent() {
|
||||
let confirmation = confirm(
|
||||
"Are you sure you want to delete this event? (You can only do this if you are a relay master)",
|
||||
);
|
||||
if (confirmation) {
|
||||
try {
|
||||
// remove report event
|
||||
// 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 newevent = new NDKEvent($ndk);
|
||||
newevent.kind = 20203;
|
||||
newevent.tags.push(["e", event.id]);
|
||||
await newevent.publish(relaySet);
|
||||
show = false;
|
||||
} catch (error) {
|
||||
console.log("error publishing event", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function removeUser(username, pk) {
|
||||
let confirmation = confirm(
|
||||
`Are you sure you want to remove ${
|
||||
username ? username : pk
|
||||
}? All people they invited will also be removed. (you can only do this if you invited this user or are the relay admin)`,
|
||||
);
|
||||
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("error while connecting to relay", err);
|
||||
});
|
||||
relay.on("connect", () => {
|
||||
console.log("connected");
|
||||
});
|
||||
});
|
||||
|
||||
const newevent = new NDKEvent($ndk);
|
||||
newevent.kind = 20202;
|
||||
newevent.tags.push(["p", pk]);
|
||||
await newevent.publish(relaySet);
|
||||
} catch (error) {
|
||||
console.log("error while publishing", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if show}
|
||||
<div class="rounded-lg border border-slate-500 bg-slate-50 w-full p-4 mt-8">
|
||||
<div class="columns-2 p-0 m-0">
|
||||
<div class="bg-white rounded-lg px-4 py-2">
|
||||
from
|
||||
{#await event.author?.fetchProfile()}
|
||||
<a href={`nostr:${event.author.npub}`}>...</a>
|
||||
{:then profile}
|
||||
<img
|
||||
class="h-7 w-7 m-0 p-0 rounded-full inline"
|
||||
src={profile &&
|
||||
JSON.parse(Array.from(profile)[0]?.content)?.picture}
|
||||
alt=""
|
||||
/>
|
||||
<a class="hover:underline" href={`nostr:${event.author.npub}`}
|
||||
>{profile && JSON.parse(Array.from(profile)[0]?.content)?.name}</a
|
||||
>
|
||||
{/await}
|
||||
</div>
|
||||
<div class="bg-white rounded-lg px-4 py-2">
|
||||
{#if event?.tags.find((e) => e[0] == "p")?.[0] && event?.tags.find((e) => e[0] == "p")?.[1]}
|
||||
to
|
||||
{#await $ndk
|
||||
.getUser({ hexpubkey: event.tags.find((e) => e[0] == "p")?.[1] })
|
||||
.fetchProfile()}
|
||||
<img class="h-7 w-7 m-0 p-0 rounded-full inline" src="" alt="" />
|
||||
<a href={`nostr:${event.tags.find((e) => e[0] == "p")?.[1]}`}>...</a
|
||||
>
|
||||
{:then profile}
|
||||
<img
|
||||
class="h-7 w-7 m-0 p-0 rounded-full inline"
|
||||
src={profile &&
|
||||
JSON.parse(Array.from(profile)[0]?.content)?.picture}
|
||||
alt=""
|
||||
/>
|
||||
<a
|
||||
class="hover:underline"
|
||||
href={`nostr:${event.tags.find((e) => e[0] == "p")?.[1]}`}
|
||||
>{profile &&
|
||||
JSON.parse(Array.from(profile)[0]?.content)?.name.length <= 16
|
||||
? JSON.parse(Array.from(profile)[0]?.content)?.name
|
||||
: JSON.parse(Array.from(profile)[0]?.content)?.name.substring(
|
||||
0,
|
||||
13,
|
||||
) + "..."}</a
|
||||
>
|
||||
{/await}
|
||||
{:else}
|
||||
huh, nothing here
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-white max-h-64 scroll-auto rounded-lg m-0 p-4 mt-2">
|
||||
{#if event?.tags.find((e) => e[0] == "e")?.[0] && event?.tags.find((e) => e[0] == "e")?.[1] && event?.tags.find((e) => e[0] == "e")?.[2]}
|
||||
{event?.tags.find((e) => e[0] == "e")?.[2]}
|
||||
{:else if event?.tags.find((e) => e[0] == "p")?.[0] && event?.tags.find((e) => e[0] == "p")?.[1] && event?.tags.find((e) => e[0] == "p")?.[2]}
|
||||
{event?.tags.find((e) => e[0] == "p")?.[2]}
|
||||
{/if}
|
||||
{#if event?.content}
|
||||
<p class="text-gray-500 m-0 p-0">{event.content}</p>
|
||||
{/if}
|
||||
</div>
|
||||
{#if event?.tags.find((e) => e[0] == "e")?.[0] && event?.tags.find((e) => e[0] == "e")?.[1]}
|
||||
<div
|
||||
class="bg-white max-h-64 max-w-full overflow-y-scroll rounded-lg m-0 p-4 mt-2"
|
||||
>
|
||||
{#await $ndk.fetchEvent( { ids: [event?.tags.find((e) => e[0] == "e")?.[1]] }, )}
|
||||
<p class="text-gray-500 p-0 m-0">... (can we find this event?)</p>
|
||||
{:then theevent}
|
||||
<p class="m-0 p-0">{theevent.content}</p>
|
||||
<p class="text-gray-500 m-0 p-0">
|
||||
kind: <span class="text-black inline">{theevent.kind}</span>
|
||||
</p>
|
||||
<p class="text-gray-500 m-0 p-0">
|
||||
tags: {#each theevent.tags as tag}<p>
|
||||
{tag[0]}: <span class="text-black">{tag[1]}</span>
|
||||
</p>{/each}
|
||||
</p>
|
||||
{/await}
|
||||
</div>
|
||||
{/if}
|
||||
<div class="p-0 mb-0 mx-0 mt-2">
|
||||
<div class="columns-4 inline">
|
||||
<a
|
||||
href={`nostr:${
|
||||
event?.tags.find((e) => e[0] == "e")?.[0] &&
|
||||
event?.tags.find((e) => e[0] == "e")?.[1]
|
||||
? event?.tags.find((e) => e[0] == "e")?.[1]
|
||||
: event?.tags.find((e) => e[0] == "p")?.[1]
|
||||
}`}
|
||||
class="rounded-lg inline bg-slate-100 p-2 cursor-pointer hover:bg-white"
|
||||
>
|
||||
Open in client
|
||||
</a>
|
||||
<button
|
||||
on:click={dismissReportEvent}
|
||||
class="rounded-lg inline bg-green-500 p-2 cursor-pointer hover:bg-green-400"
|
||||
>
|
||||
Remove This Report Event
|
||||
</button>
|
||||
<button
|
||||
on:click={() =>
|
||||
removeUser("user", event.tags.find((e) => e[0] == "p")?.[1])}
|
||||
class="rounded-lg inline bg-red-500 p-2 cursor-pointer hover:bg-red-400"
|
||||
>
|
||||
Exlude reported user
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
@ -1,76 +0,0 @@
|
||||
<script lang="js">
|
||||
import { onMount } from "svelte";
|
||||
import { ndk, relayMaster, 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. (you can only do this if you invited this user or are the relay admin)`,
|
||||
);
|
||||
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("error while connecting to relay", 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) || $userPublickey == $relayMaster}<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>
|
85
ui/src/lib/actions.ts
Normal file
85
ui/src/lib/actions.ts
Normal file
@ -0,0 +1,85 @@
|
||||
import { NDKEvent, NDKRelay, NDKRelaySet } from '@nostr-dev-kit/ndk';
|
||||
import { nip19 } from 'nostr-tools';
|
||||
import { ndk, relayUrl } from './main';
|
||||
|
||||
// Handle Buttons
|
||||
export function loadActions() {
|
||||
document.querySelectorAll(".removefromrelay").forEach((btn) => {
|
||||
btn.addEventListener('click', () => {
|
||||
const arg = btn.getAttribute('data-actionarg');
|
||||
if (arg) {
|
||||
const jsonArg = JSON.parse(arg);
|
||||
if (jsonArg) {
|
||||
removeFromRelay(jsonArg)
|
||||
}
|
||||
};
|
||||
})
|
||||
})
|
||||
document.querySelectorAll(".inviteuser").forEach((btn) => {
|
||||
btn.addEventListener('click', () => {
|
||||
inviteUser();
|
||||
})
|
||||
})
|
||||
|
||||
async function inviteUser() {
|
||||
// only publish to the relay in question, dont know why this needs so much code
|
||||
let specificRelay = new Set<NDKRelay>
|
||||
specificRelay.add(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");
|
||||
});
|
||||
});
|
||||
|
||||
var input = (<HTMLInputElement>document.getElementById('inviteuser-input')).value;
|
||||
let p = input;
|
||||
if (input.startsWith("npub")) {
|
||||
let h = nip19.decode(input).data;
|
||||
if (typeof h == 'string') {
|
||||
p = h;
|
||||
}
|
||||
}
|
||||
const event = new NDKEvent(ndk)
|
||||
event.kind = 20201;
|
||||
event.tags = [['p', p]];
|
||||
event.content = "";
|
||||
const relays = await event.publish(relaySet);
|
||||
relays.forEach(() => {
|
||||
setTimeout(() => {
|
||||
window.location.href = ""
|
||||
}, 128)
|
||||
});
|
||||
}
|
||||
|
||||
async function removeFromRelay(tags: string[][]) {
|
||||
// only publish to the relay in question, dont know why this needs so much code
|
||||
let specificRelay = new Set<NDKRelay>
|
||||
specificRelay.add(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 event = new NDKEvent(ndk)
|
||||
event.kind = 20202;
|
||||
event.tags = tags;
|
||||
event.content = "";
|
||||
const relays = await event.publish();
|
||||
relays.forEach(() => {
|
||||
setTimeout(() => {
|
||||
window.location.href = ""
|
||||
}, 128)
|
||||
});
|
||||
}
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
export const relayUrl = "ws://localhost:3334"; // Important: Change this and rebuild ui if needed
|
||||
export const someRelays = ["wss://nos.lol", "wss://relay.snort.social"];
|
41
ui/src/lib/main.ts
Normal file
41
ui/src/lib/main.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import NDK, { NDKNip07Signer } from '@nostr-dev-kit/ndk';
|
||||
import '../style.css';
|
||||
import { loadUsernames } from './user.js';
|
||||
import { loadActions } from './actions.js';
|
||||
// import NDKCacheAdapterDexie from "@nostr-dev-kit/ndk-cache-dexie";
|
||||
|
||||
export let ndk: NDK;
|
||||
export let relayUrl: string;
|
||||
|
||||
async function load() {
|
||||
// get relay websocket url
|
||||
var loc = window.location;
|
||||
if (loc.protocol === "https:") {
|
||||
relayUrl = "wss:";
|
||||
} else {
|
||||
relayUrl = "ws:";
|
||||
}
|
||||
relayUrl += "//" + loc.host;
|
||||
|
||||
// init NDK
|
||||
// const dexieAdapter = new NDKCacheAdapterDexie({ dbName: 'khatru-invite-ndk-cache' })
|
||||
const nip07signer = new NDKNip07Signer()
|
||||
ndk = new NDK({
|
||||
explicitRelayUrls: [
|
||||
relayUrl,
|
||||
"wss://nos.lol",
|
||||
"wss://nostr-pub.wellorder.net",
|
||||
], signer: nip07signer
|
||||
});
|
||||
await ndk.connect();
|
||||
|
||||
loadActions();
|
||||
loadUsernames();
|
||||
}
|
||||
|
||||
// load
|
||||
load();
|
||||
|
||||
|
||||
|
||||
|
@ -1,13 +0,0 @@
|
||||
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);
|
||||
export const relayMaster: Writable<string> = writable("");
|
19
ui/src/lib/user.ts
Normal file
19
ui/src/lib/user.ts
Normal file
@ -0,0 +1,19 @@
|
||||
// import { ndk } from "./main.js";
|
||||
|
||||
export async function loadUsernames() {
|
||||
/*
|
||||
try to get username from other realy for each user
|
||||
that isn't loaded by the server
|
||||
*/
|
||||
|
||||
// let b = document.querySelectorAll(".user");
|
||||
// b.forEach(async (e) => {
|
||||
// if (e.innerHTML.startsWith("npub1")) {
|
||||
// let npub1 = e.innerHTML;
|
||||
// let u = ndk.getUser({ npub: npub1 });
|
||||
// let p = await u.fetchProfile();
|
||||
// if (p?.name) e.innerHTML = p.name;
|
||||
// if (p?.displayName) e.innerHTML = p.displayName;
|
||||
// }
|
||||
// });
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
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),
|
||||
}));
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
import App from "./App.svelte";
|
||||
|
||||
const app = new App({
|
||||
target: document.getElementById("app"),
|
||||
});
|
||||
|
||||
export default app;
|
9
ui/src/style.css
Normal file
9
ui/src/style.css
Normal file
@ -0,0 +1,9 @@
|
||||
.inline {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.rembtn {
|
||||
margin-left: 1px;
|
||||
color: red;
|
||||
cursor: pointer;
|
||||
}
|
1
ui/src/vite-env.d.ts
vendored
Normal file
1
ui/src/vite-env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
@ -1,8 +0,0 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: ["./src/**/*.{html,js,svelte}"],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [require("@tailwindcss/forms"), require("@tailwindcss/typography")],
|
||||
};
|
34
ui/tsconfig.json
Normal file
34
ui/tsconfig.json
Normal file
@ -0,0 +1,34 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "ESNext",
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"skipLibCheck": true,
|
||||
|
||||
// added
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
|
||||
/* Bundler mode */
|
||||
// "moduleResolution": "Bundler",
|
||||
// "allowImportingTsExtensions": true,
|
||||
"noEmit": true,
|
||||
|
||||
"allowJs": true,
|
||||
"paths": {
|
||||
"lib/*": ["./src/lib/*"],
|
||||
},
|
||||
|
||||
/* Linting */
|
||||
"strict": false,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
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",
|
||||
},
|
||||
},
|
||||
});
|
3076
ui/yarn.lock
3076
ui/yarn.lock
File diff suppressed because it is too large
Load Diff
76
utils.go
76
utils.go
@ -2,22 +2,47 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"log"
|
||||
|
||||
"github.com/nbd-wtf/go-nostr"
|
||||
)
|
||||
|
||||
func isPkInWhitelist(targetPk string) bool {
|
||||
for i := 0; i < len(whitelist); i++ {
|
||||
if whitelist[i].Pk == targetPk {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
func buildHTMLTree(entries []WhitelistEntry, invitedBy string) template.HTML {
|
||||
html := "<ul>"
|
||||
|
||||
for _, entry := range entries {
|
||||
if entry.InvitedBy == invitedBy {
|
||||
user := getUserInfo(context.TODO(), entry.Pk)
|
||||
html += fmt.Sprintf(`
|
||||
<li>
|
||||
<a class="user" href="nostr:%s">%s</a>
|
||||
<a data-actionarg='[["p", "%v"]]' class="rembtn removefromrelay">x</a>
|
||||
%s
|
||||
</li>`, template.HTMLEscapeString(user.Npub),
|
||||
template.HTMLEscapeString(user.Name),
|
||||
entry.Pk,
|
||||
buildHTMLTree(entries, entry.Pk))
|
||||
}
|
||||
}
|
||||
|
||||
html += "</ul>"
|
||||
return template.HTML(html)
|
||||
}
|
||||
|
||||
func deleteFromWhitelistRecursively (ctx context.Context, target string) {
|
||||
var updatedWhitelist []User
|
||||
func isPkInWhitelist(targetPk string) bool {
|
||||
for i := 0; i < len(whitelist); i++ {
|
||||
if whitelist[i].Pk == targetPk {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func deleteFromWhitelistRecursively(ctx context.Context, target string) {
|
||||
var updatedWhitelist []WhitelistEntry
|
||||
var queue []string
|
||||
|
||||
// Remove from whitelist
|
||||
@ -26,7 +51,7 @@ func deleteFromWhitelistRecursively (ctx context.Context, target string) {
|
||||
updatedWhitelist = append(updatedWhitelist, user)
|
||||
}
|
||||
if user.InvitedBy == target {
|
||||
queue = append(queue, user.Pk);
|
||||
queue = append(queue, user.Pk)
|
||||
}
|
||||
}
|
||||
whitelist = updatedWhitelist
|
||||
@ -48,3 +73,34 @@ func deleteFromWhitelistRecursively (ctx context.Context, target string) {
|
||||
deleteFromWhitelistRecursively(ctx, pk)
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
fmt.Println("Error parsing JSON:", err)
|
||||
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
|
||||
}
|
||||
|
69
whitelist.go
69
whitelist.go
@ -9,12 +9,12 @@ import (
|
||||
"github.com/nbd-wtf/go-nostr"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
Pk string `json:"pk"`
|
||||
type WhitelistEntry struct {
|
||||
Pk string `json:"pk"`
|
||||
InvitedBy string `json:"invited_by"`
|
||||
}
|
||||
|
||||
var whitelist []User
|
||||
var whitelist []WhitelistEntry
|
||||
|
||||
func whitelistRejecter(ctx context.Context, evt *nostr.Event) (reject bool, msg string) {
|
||||
|
||||
@ -22,44 +22,51 @@ func whitelistRejecter(ctx context.Context, evt *nostr.Event) (reject bool, msg
|
||||
if !isPkInWhitelist(evt.PubKey) {
|
||||
return true, "You are not invited to this relay"
|
||||
}
|
||||
|
||||
|
||||
// 20201 = user invites new user
|
||||
if (evt.Kind == 20201) {
|
||||
if evt.Kind == 20201 {
|
||||
pTags := evt.Tags.GetAll([]string{"p"})
|
||||
for _, tag := range pTags {
|
||||
if !isPkInWhitelist(tag.Value()) {
|
||||
if nostr.IsValidPublicKeyHex(tag.Value()) {
|
||||
whitelist = append(whitelist, User{Pk: tag.Value(), InvitedBy: evt.PubKey})
|
||||
whitelist = append(whitelist, WhitelistEntry{Pk: tag.Value(), InvitedBy: evt.PubKey})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 20202 = user removes user they invited or admin removes invite
|
||||
if (evt.Kind == 20202) {
|
||||
// 20202
|
||||
// p tag = user removes user they invited or admin removes user
|
||||
// e tag = admin removes event
|
||||
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 || evt.PubKey == relayMaster) {
|
||||
/*
|
||||
1: User in whitelist
|
||||
2: Cant remove self
|
||||
3: User should have invited user OR be relay admin
|
||||
*/
|
||||
if user.Pk == tag.Value() && evt.PubKey != tag.Value() && (user.InvitedBy == evt.PubKey || evt.PubKey == relayMaster) {
|
||||
log.Println("deleting user", tag.Value())
|
||||
deleteFromWhitelistRecursively(ctx, tag.Value())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if evt.PubKey == relayMaster {
|
||||
eTags := evt.Tags.GetAll([]string{"e"})
|
||||
for _, tag := range eTags {
|
||||
filter := nostr.Filter{
|
||||
IDs: []string{tag.Value()},
|
||||
}
|
||||
events, _ := db.QueryEvents(ctx, filter)
|
||||
|
||||
// 20203 = admin deletes event
|
||||
if (evt.Kind == 20203 && evt.PubKey == relayMaster) {
|
||||
eTags := evt.Tags.GetAll([]string{"e"})
|
||||
for _, tag := range eTags {
|
||||
filter := nostr.Filter{
|
||||
IDs: []string{tag.Value()},
|
||||
}
|
||||
events, _ := db.QueryEvents(ctx, filter);
|
||||
|
||||
for ev := range events {
|
||||
err := db.DeleteEvent(ctx, ev)
|
||||
if err != nil {
|
||||
log.Println("error while deleting event", err)
|
||||
for ev := range events {
|
||||
log.Println("deleting evemt", ev.ID)
|
||||
err := db.DeleteEvent(ctx, ev)
|
||||
if err != nil {
|
||||
log.Println("error while deleting event", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -69,35 +76,35 @@ func whitelistRejecter(ctx context.Context, evt *nostr.Event) (reject bool, msg
|
||||
|
||||
}
|
||||
|
||||
func loadWhitelist () error {
|
||||
func loadWhitelist() error {
|
||||
if _, err := os.Stat("whitelist.json"); os.IsNotExist(err) {
|
||||
whitelist = []User{}
|
||||
whitelist = []WhitelistEntry{}
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user