diff --git a/components.go b/components.go
index 200ad86..31505e1 100644
--- a/components.go
+++ b/components.go
@@ -32,16 +32,20 @@ func userRowComponent(ctx context.Context, profile sdk.ProfileMetadata, loggedUs
}
return Li(
- A().Href("nostr:"+profile.Npub()).Children(
- Span(profile.ShortName()).Attr(
- "npub", profile.Npub(),
- "name", profile.ShortName(),
- "_", `
-on mouseenter set my innerText to @npub then hide the next
-on mouseleave set my innerText to @name then show the next `,
- ),
- ).Class("font-mono py-1"),
+ userNameComponent(ctx, profile),
button,
inviteTreeComponent(ctx, profile.PubKey, loggedUser),
).Class("ml-6")
}
+
+func userNameComponent(ctx context.Context, profile sdk.ProfileMetadata) HTMLComponent {
+ return A().Href("nostr:" + profile.Npub()).Children(
+ Span(profile.ShortName()).Attr(
+ "npub", profile.Npub(),
+ "name", profile.ShortName(),
+ "_", `
+on mouseenter set my innerText to @npub then hide the next
+on mouseleave set my innerText to @name then show the next `,
+ ),
+ ).Class("font-mono py-1")
+}
diff --git a/go.mod b/go.mod
index 1ebb843..8b8cbdc 100644
--- a/go.mod
+++ b/go.mod
@@ -3,7 +3,7 @@ module github.com/github-tijlxyz/khatru-invite
go 1.21.0
require (
- github.com/fiatjaf/eventstore v0.0.2
+ github.com/fiatjaf/eventstore v0.1.0
github.com/fiatjaf/khatru v0.1.0
github.com/kelseyhightower/envconfig v1.4.0
github.com/nbd-wtf/go-nostr v0.25.0
diff --git a/handler.go b/handler.go
index 0455d96..acdbe11 100644
--- a/handler.go
+++ b/handler.go
@@ -1,7 +1,6 @@
package main
import (
- "context"
"net/http"
"github.com/nbd-wtf/go-nostr/nip19"
@@ -10,18 +9,11 @@ import (
func inviteTreeHandler(w http.ResponseWriter, r *http.Request) {
content := inviteTreePageHTML(r.Context(), InviteTreePageParams{
- LoggedUser: getLoggedUser(r),
+ loggedUser: getLoggedUser(r),
})
htmlgo.Fprint(w, baseHTML(content), r.Context())
}
-func getUserRowHandler(w http.ResponseWriter, r *http.Request) {
- pubkey := r.PostFormValue("pubkey")
- profile := fetchAndStoreProfile(r.Context(), pubkey)
- content := userRowComponent(r.Context(), profile, getLoggedUser(r))
- htmlgo.Fprint(w, content, r.Context())
-}
-
func addToWhitelistHandler(w http.ResponseWriter, r *http.Request) {
loggedUser := getLoggedUser(r)
@@ -34,6 +26,7 @@ func addToWhitelistHandler(w http.ResponseWriter, r *http.Request) {
http.Error(w, "failed to add to whitelist: "+err.Error(), 500)
return
}
+
content := inviteTreeComponent(r.Context(), "", loggedUser)
htmlgo.Fprint(w, content, r.Context())
}
@@ -162,7 +155,7 @@ func reportsViewerHandler(w http.ResponseWriter, r *http.Request) {
func homePageHandler(w http.ResponseWriter, r *http.Request) {
content := homePageHTML(r.Context(), HomePageParams{
- RelayOwnerInfo: fetchAndStoreProfile(context.Background(), s.RelayPubkey),
+ relayOwnerInfo: fetchAndStoreProfile(r.Context(), s.RelayPubkey),
})
htmlgo.Fprint(w, baseHTML(content), r.Context())
}
diff --git a/main.go b/main.go
index d164e54..bcad25c 100644
--- a/main.go
+++ b/main.go
@@ -6,7 +6,7 @@ import (
"net/url"
"os"
- "github.com/fiatjaf/eventstore/badgern"
+ "github.com/fiatjaf/eventstore/badger"
"github.com/fiatjaf/khatru"
"github.com/kelseyhightower/envconfig"
"github.com/nbd-wtf/go-nostr"
@@ -25,7 +25,7 @@ type Settings struct {
var (
s Settings
- db = badgern.BadgerBackend{}
+ db = badger.BadgerBackend{}
log = zerolog.New(os.Stderr).Output(zerolog.ConsoleWriter{Out: os.Stdout}).With().Timestamp().Logger()
whitelist = make(Whitelist)
relay = khatru.NewRelay()
@@ -52,18 +52,19 @@ func main() {
relay.Description = s.RelayDescription
relay.Contact = s.RelayContact
- // load whitelist storage
+ relay.StoreEvent = append(relay.StoreEvent, db.SaveEvent)
+ relay.QueryEvents = append(relay.QueryEvents, db.QueryEvents)
+ relay.CountEvents = append(relay.CountEvents, db.CountEvents)
+ relay.DeleteEvent = append(relay.DeleteEvent, db.DeleteEvent)
+ relay.RejectEvent = append(relay.RejectEvent, rejectEventsFromUsersNotInWhitelist)
+
+ // load users registry
if err := loadWhitelist(); err != nil {
log.Fatal().Err(err).Msg("failed to load whitelist")
return
}
- relay.StoreEvent = append(relay.StoreEvent, db.SaveEvent)
- relay.QueryEvents = append(relay.QueryEvents, db.QueryEvents)
- relay.CountEvents = append(relay.CountEvents, db.CountEvents)
- relay.DeleteEvent = append(relay.DeleteEvent, db.DeleteEvent)
-
- relay.Router().HandleFunc("/get-user-row", getUserRowHandler)
+ // http routes
relay.Router().HandleFunc("/add-to-whitelist", addToWhitelistHandler)
relay.Router().HandleFunc("/remove-from-whitelist", removeFromWhitelistHandler)
relay.Router().HandleFunc("/reports", reportsViewerHandler)
diff --git a/pages.go b/pages.go
index e10ade3..6e6a651 100644
--- a/pages.go
+++ b/pages.go
@@ -30,8 +30,11 @@ func baseHTML(inside HTMLComponent) HTMLComponent {
A().Text("information").Href("/").Class(navItemClass).Attr("hx-boost", "true", "hx-target", "main", "hx-select", "main"),
A().Text("invite tree").Href("/users").Class(navItemClass).Attr("hx-boost", "true", "hx-target", "main", "hx-select", "main"),
A().Text("reports").Href("/reports").Class(navItemClass).Attr("hx-boost", "true", "hx-target", "main", "hx-select", "main"),
- A().Text("login").Href("#").Class(navItemClass).
- Attr("_", "on click get window.nostr.signEvent({created_at: Math.round(Date.now()/1000), kind: 27235, tags: [['domain', "+s.Domain+"]], content: ''}) then get JSON.stringify(it) then set cookies['nip98'] to it"),
+ A().Text("").Href("#").Class(navItemClass).
+ Attr("_", `
+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: ''}) then get JSON.stringify(it) then set cookies['nip98'] to it otherwise call cookies.clear('nip98') end then trigger load on me
+
+on load get cookies['nip98'] then if it is undefined set my innerText to "login" otherwise set my innerText to "logout"`),
).Class("flex flex-1 items-center justify-center"),
Main(inside).Class("m-4"),
).Class("mx-4 my-6"),
@@ -39,7 +42,7 @@ func baseHTML(inside HTMLComponent) HTMLComponent {
}
type HomePageParams struct {
- RelayOwnerInfo sdk.ProfileMetadata
+ relayOwnerInfo sdk.ProfileMetadata
}
func homePageHTML(ctx context.Context, params HomePageParams) HTMLComponent {
@@ -59,33 +62,43 @@ func homePageHTML(ctx context.Context, params HomePageParams) HTMLComponent {
contact,
Div(
Text("relay master: "),
- A().Text(params.RelayOwnerInfo.Name).Href("nostr:"+params.RelayOwnerInfo.Npub()),
+ userNameComponent(ctx, params.relayOwnerInfo),
),
Br(),
Div(
Text("this relay uses"),
- A().Target("_blank").Href("https://github.com/github-tijlxyz/khatru-invite").Text("Khatru Invite"),
+ A().Target("_blank").Href("https://github.com/github-tijlxyz/khatru-invite").Text("Khatru Invite").
+ Class("underline"),
Text(" which is built with "),
- A().Target("_blank").Href("https://github.com/fiatjaf/khatru").Text("Khatru"),
- ),
+ A().Target("_blank").Href("https://github.com/fiatjaf/khatru").Text("Khatru").
+ Class("underline"),
+ ).Class("text-sm"),
)
}
type InviteTreePageParams struct {
- LoggedUser string
+ loggedUser string
}
func inviteTreePageHTML(ctx context.Context, params InviteTreePageParams) HTMLComponent {
- return Form(
- Input("pubkey").Type("text").Placeholder("npub1...").Class("w-96 rounded-md border-0 p-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600"),
- Button("invite").Class(buttonClass+" p-2 bg-white hover:bg-gray-50"),
+ inviteForm := Div()
+
+ if params.loggedUser != "" {
+ inviteForm = Form(
+ Input("pubkey").Type("text").Placeholder("npub1...").Class("w-96 rounded-md border-0 p-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600"),
+ Button("invite").Class(buttonClass+" p-2 bg-white hover:bg-gray-50"),
+ ).Attr(
+ "hx-post", "/add-to-whitelist",
+ "hx-trigger", "submit",
+ "hx-target", "#tree",
+ "_", "on htmx:afterRequest(elt, successful) if successful and elt is I call I.reset()",
+ )
+ }
+
+ return Div(
+ inviteForm,
Div(
- inviteTreeComponent(ctx, "", params.LoggedUser),
+ inviteTreeComponent(ctx, "", params.loggedUser),
).Id("tree").Class("mt-3"),
- ).Attr(
- "hx-post", "/add-to-whitelist",
- "hx-trigger", "submit",
- "hx-target", "#tree",
- "_", "on htmx:afterRequest(elt, successful) if successful and elt is I call I.reset()",
)
}
diff --git a/relay.go b/relay.go
new file mode 100644
index 0000000..cbd158e
--- /dev/null
+++ b/relay.go
@@ -0,0 +1,14 @@
+package main
+
+import (
+ "context"
+
+ "github.com/nbd-wtf/go-nostr"
+)
+
+func rejectEventsFromUsersNotInWhitelist(ctx context.Context, event *nostr.Event) (reject bool, msg string) {
+ if isPublicKeyInWhitelist(event.PubKey) {
+ return false, ""
+ }
+ return true, "not authorized"
+}
diff --git a/whitelist.go b/whitelist.go
index 7f71f8d..1010a4e 100644
--- a/whitelist.go
+++ b/whitelist.go
@@ -13,9 +13,19 @@ const WHITELIST_FILE = "users.json"
type Whitelist map[string]string // { [user_pubkey]: [invited_by] }
func addToWhitelist(pubkey string, inviter string) error {
- if nostr.IsValidPublicKeyHex(pubkey) && isPublicKeyInWhitelist(inviter) && !isPublicKeyInWhitelist(pubkey) {
- whitelist[pubkey] = inviter
+ if !isPublicKeyInWhitelist(inviter) {
+ return fmt.Errorf("pubkey %s doesn't have permission to invite", inviter)
}
+
+ if !nostr.IsValidPublicKeyHex(pubkey) {
+ return fmt.Errorf("pubkey invalid: %s", pubkey)
+ }
+
+ if isPublicKeyInWhitelist(pubkey) {
+ return fmt.Errorf("pubkey already in whitelist: %s", pubkey)
+ }
+
+ whitelist[pubkey] = inviter
return saveWhitelist()
}