mirror of
https://github.com/github-tijlxyz/khatru-pyramid.git
synced 2025-04-19 18:31:18 +00:00
actually blocking writes from non-whitelisted people, fixes in eventstore and some other UI tweaks.
This commit is contained in:
parent
6565b4dcf5
commit
19bc00da24
@ -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 <button />
|
||||
on mouseleave set my innerText to @name then show the next <button />`,
|
||||
),
|
||||
).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 <button />
|
||||
on mouseleave set my innerText to @name then show the next <button />`,
|
||||
),
|
||||
).Class("font-mono py-1")
|
||||
}
|
||||
|
2
go.mod
2
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
|
||||
|
13
handler.go
13
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())
|
||||
}
|
||||
|
19
main.go
19
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)
|
||||
|
47
pages.go
47
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()",
|
||||
)
|
||||
}
|
||||
|
14
relay.go
Normal file
14
relay.go
Normal file
@ -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"
|
||||
}
|
14
whitelist.go
14
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()
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user