fix login issue, speedup page with jsdelivr and <nostr-name>, canInviteMore() function.

This commit is contained in:
fiatjaf 2025-04-05 09:56:41 -03:00
parent e949ed698d
commit a05cd3dfd6
9 changed files with 81 additions and 55 deletions

18
go.mod
View File

@ -1,13 +1,13 @@
module github.com/github-tijlxyz/khatru-pyramid
go 1.23.1
go 1.24.1
require (
github.com/a-h/templ v0.3.819
github.com/fiatjaf/eventstore v0.16.2
github.com/fiatjaf/khatru v0.16.0
github.com/a-h/templ v0.3.857
github.com/fiatjaf/eventstore v0.16.4
github.com/fiatjaf/khatru v0.17.7
github.com/kelseyhightower/envconfig v1.4.0
github.com/nbd-wtf/go-nostr v0.50.4
github.com/nbd-wtf/go-nostr v0.51.8
github.com/rs/zerolog v1.33.0
golang.org/x/sync v0.12.0
)
@ -21,11 +21,11 @@ require (
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/bytedance/sonic v1.13.1 // indirect
github.com/bytedance/sonic v1.13.2 // indirect
github.com/bytedance/sonic/loader v0.2.4 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cloudwego/base64x v0.1.5 // indirect
github.com/coder/websocket v1.8.12 // indirect
github.com/coder/websocket v1.8.13 // indirect
github.com/decred/dcrd/crypto/blake256 v1.1.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
github.com/dgraph-io/ristretto v1.0.0 // indirect
@ -51,9 +51,9 @@ require (
github.com/tidwall/pretty v1.2.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.59.0 // indirect
github.com/valyala/fasthttp v1.60.0 // indirect
golang.org/x/arch v0.15.0 // indirect
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
golang.org/x/net v0.37.0 // indirect
golang.org/x/net v0.38.0 // indirect
golang.org/x/sys v0.31.0 // indirect
)

17
go.sum
View File

@ -6,6 +6,8 @@ github.com/PowerDNS/lmdb-go v1.9.3 h1:AUMY2pZT8WRpkEv39I9Id3MuoHd+NZbTVpNhruVkPT
github.com/PowerDNS/lmdb-go v1.9.3/go.mod h1:TE0l+EZK8Z1B4dx070ZxkWTlp8RG1mjN0/+FkFRQMtU=
github.com/a-h/templ v0.3.819 h1:KDJ5jTFN15FyJnmSmo2gNirIqt7hfvBD2VXVDTySckM=
github.com/a-h/templ v0.3.819/go.mod h1:iDJKJktpttVKdWoTkRNNLcllRI+BlpopJc+8au3gOUo=
github.com/a-h/templ v0.3.857 h1:6EqcJuGZW4OL+2iZ3MD+NnIcG7nGkaQeF2Zq5kf9ZGg=
github.com/a-h/templ v0.3.857/go.mod h1:qhrhAkRFubE7khxLZHsBFHfX+gWwVNKbzKeF9GlPV4M=
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=
@ -14,6 +16,7 @@ github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3IS
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=
github.com/btcsuite/btcd v0.24.2 h1:aLmxPguqxza+4ag8R1I2nnJjSu2iFn/kqtHTIImswcY=
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.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ=
@ -37,6 +40,8 @@ github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtE
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
github.com/bytedance/sonic v1.13.1 h1:Jyd5CIvdFnkOWuKXr+wm4Nyk2h0yAFsr8ucJgEasO3g=
github.com/bytedance/sonic v1.13.1/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ=
github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
@ -47,6 +52,8 @@ github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJ
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo=
github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs=
github.com/coder/websocket v1.8.13 h1:f3QZdXy7uGVz+4uCJy2nTZyM0yTBj8yANEHhqlXZ9FE=
github.com/coder/websocket v1.8.13/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -70,8 +77,12 @@ github.com/fasthttp/websocket v1.5.12 h1:e4RGPpWW2HTbL3zV0Y/t7g0ub294LkiuXXUuTOU
github.com/fasthttp/websocket v1.5.12/go.mod h1:I+liyL7/4moHojiOgUOIKEWm9EIxHqxZChS+aMFltyg=
github.com/fiatjaf/eventstore v0.16.2 h1:h4rHwSwPcqAKqWUsAbYWUhDeSgm2Kp+PBkJc3FgBYu4=
github.com/fiatjaf/eventstore v0.16.2/go.mod h1:0gU8fzYO/bG+NQAVlHtJWOlt3JKKFefh5Xjj2d1dLIs=
github.com/fiatjaf/eventstore v0.16.4 h1:pENYeuhawxMxlJk8HpRy3pb2oap0fwbphzUgsy7QPws=
github.com/fiatjaf/eventstore v0.16.4/go.mod h1:0gU8fzYO/bG+NQAVlHtJWOlt3JKKFefh5Xjj2d1dLIs=
github.com/fiatjaf/khatru v0.16.0 h1:xgGwnnOqE3989wEWm7c/z6Y6g4X92BFe/Xp1UWQ3Zmc=
github.com/fiatjaf/khatru v0.16.0/go.mod h1:TLcMgPy3IAPh40VGYq6m+gxEMpDKHj+sumqcuvbSogc=
github.com/fiatjaf/khatru v0.17.7 h1:1NB2qe/KPF2hIrlzrZOXHj4xIQsulJw1mVlFXYzmhkk=
github.com/fiatjaf/khatru v0.17.7/go.mod h1:4KW6mom+7ajwrhj5IvLJTBKj6peV8bdZjU6XoDVrX2Q=
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/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
@ -126,6 +137,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/nbd-wtf/go-nostr v0.50.4 h1:KFMLxL07FPUzrCgllc2AKPP6INip+0MhAy6ZJxCwOyo=
github.com/nbd-wtf/go-nostr v0.50.4/go.mod h1:IoEUVJKvV2308WFhVu8f2OwGC32oEYpFYnV86EH8dqA=
github.com/nbd-wtf/go-nostr v0.51.8 h1:CIoS+YqChcm4e1L1rfMZ3/mIwTz4CwApM2qx7MHNzmE=
github.com/nbd-wtf/go-nostr v0.51.8/go.mod h1:d6+DfvMWYG5pA3dmNMBJd6WCHVDDhkXbHqvfljf0Gzg=
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=
@ -173,6 +186,8 @@ github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6Kllzaw
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.59.0 h1:Qu0qYHfXvPk1mSLNqcFtEk6DpxgA26hy6bmydotDpRI=
github.com/valyala/fasthttp v1.59.0/go.mod h1:GTxNb9Bc6r2a9D0TWNSPwDz78UxnTGBViY3xZNEqyYU=
github.com/valyala/fasthttp v1.60.0 h1:kBRYS0lOhVJ6V+bYN8PqAHELKHtXqwq9zNMLKx1MBsw=
github.com/valyala/fasthttp v1.60.0/go.mod h1:iY4kDgV3Gc6EqhRZ8icqcmlG6bqhcDXfuHgTO4FXCvc=
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/arch v0.15.0 h1:QtOrQd0bTUnhNVNndMpLHNWrDmYzZ2KDqSrEymqInZw=
@ -189,6 +204,8 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=

View File

@ -21,7 +21,7 @@ func addToWhitelistHandler(w http.ResponseWriter, r *http.Request) {
pubkey = value.(string)
}
if loggedUser != s.RelayPubkey && hasInvitedAtLeast(loggedUser, s.MaxInvitesPerPerson) {
if !canInviteMore(loggedUser) {
http.Error(w, fmt.Sprintf("cannot invite more than %d", s.MaxInvitesPerPerson), 403)
return
}
@ -108,13 +108,13 @@ window.relayGroups = [{
}]
window.hideRelaySettings = true
</script>
<script type="module" crossorigin src="https://unpkg.com/jouble/dist/index.js"></script>
<link rel="stylesheet" crossorigin href="https://unpkg.com/jouble/dist/index.css">
<script type="module" crossorigin src="https://cdn.jsdelivr.net/npm/jouble@0.0.6/dist/index.js"></script>
<link rel="stylesheet" crossorigin href="https://cdn.jsdelivr.net/npm/jouble@0.0.6/dist/index.css">
</head>
<body>
<div id="root"></div>
<script src="https://unpkg.com/window.nostr.js/dist/window.nostr.js"></script>
<script src="https://cdn.jsdelivr.net/npm/window.nostr.js@0.4.7/dist/window.nostr.min.js"></script>
</body>
</html>
`)

View File

@ -2,14 +2,12 @@ package main
import (
"fmt"
"github.com/nbd-wtf/go-nostr/sdk"
)
templ inviteTreePage(loggedUser string) {
@layout(loggedUser) {
<div>
if loggedUser != "" && (loggedUser == s.RelayPubkey || !hasInvitedAtLeast(loggedUser, s.MaxInvitesPerPerson)) {
if canInviteMore(loggedUser) {
<form
hx-post="/add-to-whitelist"
hx-trigger="submit"
@ -43,7 +41,9 @@ templ inviteTreeComponent(inviter string, loggedUser string) {
for pubkey, invitedBy := range whitelist {
if invitedBy == inviter {
<li class="ml-6">
@userNameComponent(sys.FetchProfileMetadata(ctx, pubkey))
<a href={ templ.URL("https://njump.me/p/" + pubkey) } target="_blank" class="font-mono py-1">
<nostr-name pubkey={ pubkey }>{ pubkey }</nostr-name>
</a>
if isAncestorOf(loggedUser, pubkey) && loggedUser != "" {
<button
class="rounded-md text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 px-2 ml-2 bg-red-100 hover:bg-red-300"
@ -61,9 +61,3 @@ templ inviteTreeComponent(inviter string, loggedUser string) {
}
</ul>
}
templ userNameComponent(profile sdk.ProfileMetadata) {
<a href={ templ.URL("https://nosta.me/" + profile.Npub()) } target="_blank" class="font-mono py-1">
<span title={ profile.Npub() }>{ profile.ShortName() }</span>
</a>
}

View File

@ -8,10 +8,11 @@ templ layout(loggedUser string) {
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>{ s.RelayName }</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/htmx.org@1.9.6"></script>
<script src="https://unpkg.com/hyperscript.org@0.9.12"></script>
<script src="https://cdn.jsdelivr.net/npm/htmx.org@1.9.12/dist/htmx.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/hyperscript.org@0.9.14/dist/_hyperscript.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/nostr-web-components@0.0.12/dist/nostr-name.js"></script>
</head>
<body class="max-w-screen-lg px-3 mx-auto">
<body class="px-3 mx-auto">
<div class="mx-auto my-6 text-center">
<h1 class="font-bold text-2xl">{ s.RelayName }</h1>
if s.RelayDescription != "" {
@ -28,24 +29,23 @@ templ layout(loggedUser string) {
<a
href="#"
class="text-gray-600 hover:bg-gray-200 rounded-md px-3 py-2 font-medium"
_="
_={ `
on click if my innerText is equal to 'login'
get window.nostr.signEvent({created_at: Math.round(Date.now()/1000), kind: 27235, tags: [['domain', '{ s.Domain }']], content: ''})
get window.nostr.signEvent({created_at: Math.round(Date.now()/1000), kind: 27235, tags: [['domain', '`+ s.Domain+ `']], content: ''})
then get JSON.stringify(it)
then set cookies['nip98'] to it
otherwise
call cookies.clear('nip98')
end
then call location.reload()
on load
get cookies['nip98']
then if it is undefined
set my innerText to 'login'
otherwise
set my innerText to 'logout'
"
></a>
` }
>
if loggedUser != "" {
logout
} else {
login
}
</a>
</nav>
<main class="m-4">
{ children... }
@ -54,6 +54,7 @@ templ layout(loggedUser string) {
powered by
<a href="https://github.com/github-tijlxyz/khatru-pyramid" class="hover:underline cursor-pointer text-blue-500">khatru-pyramid</a>
</p>
<script src="https://cdn.jsdelivr.net/npm/window.nostr.js@0.4.7/dist/window.nostr.min.js"></script>
</body>
</html>
}

View File

@ -156,7 +156,7 @@ func getLoggedUser(r *http.Request) string {
if evtj, err := url.QueryUnescape(cookie.Value); err == nil {
var evt nostr.Event
if err := json.Unmarshal([]byte(evtj), &evt); err == nil {
if tag := evt.Tags.GetFirst([]string{"domain", ""}); tag != nil && (*tag)[1] == s.Domain {
if tag := evt.Tags.Find("domain"); tag != nil && tag[1] == s.Domain {
if ok, _ := evt.CheckSignature(); ok {
return evt.PubKey
}

View File

@ -11,7 +11,7 @@ import (
func allowPubKeyHandler(ctx context.Context, pubkey, reason string) error {
loggedUser := khatru.GetAuthed(ctx)
if loggedUser != s.RelayPubkey && hasInvitedAtLeast(loggedUser, s.MaxInvitesPerPerson) {
if !canInviteMore(loggedUser) {
return fmt.Errorf("cannot invite more than %d", s.MaxInvitesPerPerson)
}
if err := addToWhitelist(pubkey, loggedUser); err != nil {

View File

@ -9,9 +9,9 @@ templ reportsPage(reports chan *nostr.Event, loggedUser string) {
<div>
for report := range reports {
<div>
if e := report.Tags.GetFirst([]string{"e", ""}); e != nil {
if e := report.Tags.Find("e"); e != nil {
@eventReportComponent(e, report)
} else if p := report.Tags.GetFirst([]string{"p", ""}); p != nil {
} else if p := report.Tags.Find("p"); p != nil {
@profileReportComponent(p, report)
}
</div>
@ -21,13 +21,13 @@ templ reportsPage(reports chan *nostr.Event, loggedUser string) {
}
}
templ eventReportComponent(e *nostr.Tag, report *nostr.Event) {
if res, _ := sys.StoreRelay.QuerySync(ctx, nostr.Filter{IDs: []string{(*e)[1]}}); len(res) > 0 {
templ eventReportComponent(e nostr.Tag, report *nostr.Event) {
if res, _ := sys.StoreRelay.QuerySync(ctx, nostr.Filter{IDs: []string{e[1]}}); len(res) > 0 {
<div>
<div class="font-lg">
<span class="font-semibold">
if len(*e) >= 3 {
{ (*e)[2] }
if len(e) >= 3 {
{ e[2] }
}
</span>
{ " report" }
@ -42,13 +42,13 @@ templ eventReportComponent(e *nostr.Tag, report *nostr.Event) {
}
}
templ profileReportComponent(p *nostr.Tag, report *nostr.Event) {
if isPublicKeyInWhitelist((*p)[1]) {
templ profileReportComponent(p nostr.Tag, report *nostr.Event) {
if isPublicKeyInWhitelist(p[1]) {
<div>
<div class="font-lg">
<span class="font-semibold">
if len(*p) >= 3 {
{ (*p)[2] }
if len(p) >= 3 {
{ p[2] }
}
</span>
{ " report" }
@ -57,7 +57,9 @@ templ profileReportComponent(p *nostr.Tag, report *nostr.Event) {
<div class="p-3">{ report.Content }</div>
<div>
profile reported:
@userNameComponent(sys.FetchProfileMetadata(ctx, (*p)[1]))
<a href={ templ.URL("https://njump.me/p/" + report.PubKey) } target="_blank" class="font-mono py-1">
<nostr-name pubkey={ report.PubKey }>{ report.PubKey }</nostr-name>
</a>
</div>
</div>
}

View File

@ -33,17 +33,29 @@ func isPublicKeyInWhitelist(pubkey string) bool {
return ok
}
func hasInvitedAtLeast(ancestor string, target int) bool {
func canInviteMore(pubkey string) bool {
if pubkey == "" {
return false
}
if pubkey == s.RelayPubkey {
return true
}
if !isPublicKeyInWhitelist(pubkey) {
return false
}
count := 0
for _, inviter := range whitelist {
if inviter == ancestor {
if inviter == pubkey {
count++
}
if count >= target {
return true
if count >= s.MaxInvitesPerPerson {
return false
}
}
return false
return true
}
func isAncestorOf(ancestor string, target string) bool {