mirror of
https://github.com/github-tijlxyz/khatru-pyramid.git
synced 2025-04-19 18:31:18 +00:00
cookie-based nip-98-like login.
This commit is contained in:
parent
0506a198c1
commit
f850a485cb
21
handler.go
21
handler.go
@ -3,35 +3,44 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/nbd-wtf/go-nostr/nip19"
|
||||
"github.com/theplant/htmlgo"
|
||||
)
|
||||
|
||||
// embed ui files
|
||||
|
||||
func inviteTreeHandler(w http.ResponseWriter, r *http.Request) {
|
||||
content := inviteTreePageHTML(r.Context(), InviteTreePageParams{})
|
||||
content := inviteTreePageHTML(r.Context(), InviteTreePageParams{
|
||||
LoggedUser: getLoggedUser(r),
|
||||
})
|
||||
htmlgo.Fprint(w, baseHTML(content), r.Context())
|
||||
}
|
||||
|
||||
func addToWhitelistHandler(w http.ResponseWriter, r *http.Request) {
|
||||
loggedUser := getLoggedUser(r)
|
||||
|
||||
pubkey := r.PostFormValue("pubkey")
|
||||
if err := addToWhitelist(r.Context(), pubkey, s.RelayPubkey); err != nil {
|
||||
if pfx, value, err := nip19.Decode(pubkey); err == nil && pfx == "npub" {
|
||||
pubkey = value.(string)
|
||||
}
|
||||
|
||||
if err := addToWhitelist(r.Context(), pubkey, loggedUser); err != nil {
|
||||
http.Error(w, "failed to add to whitelist: "+err.Error(), 500)
|
||||
return
|
||||
}
|
||||
content := buildInviteTree(r.Context(), s.RelayPubkey)
|
||||
content := buildInviteTree(r.Context(), s.RelayPubkey, loggedUser)
|
||||
htmlgo.Fprint(w, content, r.Context())
|
||||
}
|
||||
|
||||
func removeFromWhitelistHandler(w http.ResponseWriter, r *http.Request) {
|
||||
loggedUser := getLoggedUser(r)
|
||||
pubkey := r.PostFormValue("pubkey")
|
||||
if err := removeFromWhitelist(r.Context(), pubkey); err != nil {
|
||||
if err := removeFromWhitelist(r.Context(), pubkey, loggedUser); err != nil {
|
||||
http.Error(w, "failed to remove from whitelist: "+err.Error(), 500)
|
||||
return
|
||||
}
|
||||
content := buildInviteTree(r.Context(), s.RelayPubkey)
|
||||
content := buildInviteTree(r.Context(), s.RelayPubkey, loggedUser)
|
||||
htmlgo.Fprint(w, content, r.Context())
|
||||
}
|
||||
|
||||
|
15
main.go
15
main.go
@ -1,12 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
"github.com/fiatjaf/khatru"
|
||||
"github.com/fiatjaf/khatru/plugins/storage/badgern"
|
||||
"github.com/kelseyhightower/envconfig"
|
||||
"github.com/nbd-wtf/go-nostr"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
@ -67,3 +70,15 @@ func main() {
|
||||
log.Fatal().Err(err).Msg("failed to serve")
|
||||
}
|
||||
}
|
||||
|
||||
func getLoggedUser(r *http.Request) string {
|
||||
if cookie, _ := r.Cookie("nip98"); cookie != nil {
|
||||
if evtj, err := url.QueryUnescape(cookie.Value); err == nil {
|
||||
var evt nostr.Event
|
||||
if err := json.Unmarshal([]byte(evtj), &evt); err == nil {
|
||||
return evt.PubKey
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
31
pages.go
31
pages.go
@ -6,7 +6,7 @@ import (
|
||||
. "github.com/theplant/htmlgo"
|
||||
)
|
||||
|
||||
const buttonClass = "rounded-md bg-white p-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50"
|
||||
const buttonClass = "rounded-md text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300"
|
||||
|
||||
func baseHTML(inside HTMLComponent) HTMLComponent {
|
||||
navItemClass := "text-gray-600 hover:bg-gray-200 rounded-md px-3 py-2 font-medium"
|
||||
@ -29,6 +29,7 @@ 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: [['u', location.href]], content: ''}) then get JSON.stringify(it) then set cookies['nip98'] to it"),
|
||||
).Class("flex flex-1 items-center justify-center"),
|
||||
Main(inside).Class("m-4"),
|
||||
).Class("mx-4 my-6"),
|
||||
@ -68,29 +69,41 @@ func homePageHTML(ctx context.Context, params HomePageParams) HTMLComponent {
|
||||
)
|
||||
}
|
||||
|
||||
type InviteTreePageParams struct{}
|
||||
type InviteTreePageParams struct {
|
||||
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),
|
||||
Button("invite").Class(buttonClass+" p-2 bg-white hover:bg-gray-50"),
|
||||
Div(
|
||||
buildInviteTree(ctx, s.RelayPubkey),
|
||||
buildInviteTree(ctx, s.RelayPubkey, params.LoggedUser),
|
||||
).Id("tree").Class("mt-3"),
|
||||
).Attr("hx-post", "/add-to-whitelist", "hx-trigger", "submit", "hx-target", "#tree")
|
||||
}
|
||||
|
||||
func buildInviteTree(ctx context.Context, invitedBy string) HTMLComponent {
|
||||
func buildInviteTree(ctx context.Context, invitedBy string, loggedUser string) HTMLComponent {
|
||||
children := make([]HTMLComponent, 0, len(whitelist))
|
||||
for _, entry := range whitelist {
|
||||
if entry.InvitedBy == invitedBy {
|
||||
user := getUserInfo(ctx, entry.PublicKey)
|
||||
button := Span("")
|
||||
if invitedBy == loggedUser {
|
||||
button = Button("remove").
|
||||
Class(buttonClass+" px-2 bg-red-100 hover:bg-red-300").
|
||||
Attr("hx-post", "/remove-from-whitelist",
|
||||
"hx-trigger", "click",
|
||||
"hx-target", "#tree",
|
||||
"hx-vals", `{"pubkey": "`+entry.PublicKey+`"}`)
|
||||
}
|
||||
|
||||
children = append(children,
|
||||
Li(
|
||||
A().Href("nostr:"+user.Npub).Text(user.Name).Class("font-mono"),
|
||||
Button("remove").Class(buttonClass).Attr("hx-post", "/remove-from-whitelist", "hx-trigger", "click", "hx-target", "#tree"),
|
||||
buildInviteTree(ctx, entry.PublicKey),
|
||||
).Class("ml-3"),
|
||||
A().Href("nostr:"+user.Npub).Text(user.Name).Class("font-mono py-1"),
|
||||
button,
|
||||
buildInviteTree(ctx, entry.PublicKey, loggedUser),
|
||||
).Class("ml-4"),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
12
whitelist.go
12
whitelist.go
@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/nbd-wtf/go-nostr"
|
||||
@ -71,18 +72,21 @@ func whitelistRejecter(ctx context.Context, evt *nostr.Event) (reject bool, msg
|
||||
return false, ""
|
||||
}
|
||||
|
||||
func addToWhitelist(ctx context.Context, pubkey string, invitedBy string) error {
|
||||
if nostr.IsValidPublicKeyHex(pubkey) && !isPublicKeyInWhitelist(pubkey) {
|
||||
whitelist = append(whitelist, WhitelistEntry{PublicKey: pubkey, InvitedBy: invitedBy})
|
||||
func addToWhitelist(ctx context.Context, pubkey string, inviter string) error {
|
||||
if nostr.IsValidPublicKeyHex(pubkey) && isPublicKeyInWhitelist(inviter) && !isPublicKeyInWhitelist(pubkey) {
|
||||
whitelist = append(whitelist, WhitelistEntry{PublicKey: pubkey, InvitedBy: inviter})
|
||||
}
|
||||
return saveWhitelist()
|
||||
}
|
||||
|
||||
func removeFromWhitelist(ctx context.Context, pubkey string) error {
|
||||
func removeFromWhitelist(ctx context.Context, pubkey string, deleter string) error {
|
||||
idx := slices.IndexFunc(whitelist, func(we WhitelistEntry) bool { return we.PublicKey == pubkey })
|
||||
if idx == -1 {
|
||||
return nil
|
||||
}
|
||||
if whitelist[idx].InvitedBy != deleter {
|
||||
return fmt.Errorf("can't remove a user you haven't invited")
|
||||
}
|
||||
|
||||
whitelist = append(whitelist[0:idx], whitelist[idx+1:]...)
|
||||
return saveWhitelist()
|
||||
|
Loading…
x
Reference in New Issue
Block a user