mirror of
https://github.com/github-tijlxyz/khatru-pyramid.git
synced 2025-06-23 16:05:28 +00:00
rewrite things to use htmlgo (wip).
This commit is contained in:
parent
6fbde5f302
commit
32c484178f
3
go.mod
3
go.mod
@ -8,6 +8,7 @@ require (
|
|||||||
github.com/kelseyhightower/envconfig v1.4.0
|
github.com/kelseyhightower/envconfig v1.4.0
|
||||||
github.com/nbd-wtf/go-nostr v0.20.0
|
github.com/nbd-wtf/go-nostr v0.20.0
|
||||||
github.com/rs/zerolog v1.31.0
|
github.com/rs/zerolog v1.31.0
|
||||||
|
github.com/theplant/htmlgo v1.0.3
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@ -52,3 +53,5 @@ require (
|
|||||||
golang.org/x/sys v0.12.0 // indirect
|
golang.org/x/sys v0.12.0 // indirect
|
||||||
google.golang.org/protobuf v1.23.0 // indirect
|
google.golang.org/protobuf v1.23.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
|
replace github.com/fiatjaf/khatru => /home/fiatjaf/comp/khatru
|
||||||
|
5
go.sum
5
go.sum
@ -53,8 +53,6 @@ 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/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 h1:TPpQuLwJYfd4LJPXvHDYPMFWbLjsT91n3GpWtCQtdek=
|
||||||
github.com/fasthttp/websocket v1.5.3/go.mod h1:46gg/UBmTU1kUaTcwQXpUxtRwG2PvIZYeA8oL6vF3Fs=
|
github.com/fasthttp/websocket v1.5.3/go.mod h1:46gg/UBmTU1kUaTcwQXpUxtRwG2PvIZYeA8oL6vF3Fs=
|
||||||
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.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
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 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
|
||||||
@ -141,6 +139,9 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
|
|||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
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 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
|
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
|
||||||
|
github.com/theplant/htmlgo v1.0.3 h1:G7/YSf8OrOIRHVQ13avd78T/GV1kDl/jMwpQURrXB0o=
|
||||||
|
github.com/theplant/htmlgo v1.0.3/go.mod h1:pCKSFJsoVNkyW+yN2i1Mst+8130NSQzIU7L2IbnuyKg=
|
||||||
|
github.com/theplant/testingutils v0.0.0-20190603093022-26d8b4d95c61 h1:757/ruZNgTsOf5EkQBo0i3Bx/P2wgF5ljVkODeUX/uA=
|
||||||
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
|
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
|
||||||
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||||
|
269
handler.go
269
handler.go
@ -3,12 +3,10 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"embed"
|
"embed"
|
||||||
"fmt"
|
|
||||||
"html/template"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/nbd-wtf/go-nostr"
|
"github.com/theplant/htmlgo"
|
||||||
)
|
)
|
||||||
|
|
||||||
// embed ui files
|
// embed ui files
|
||||||
@ -17,177 +15,126 @@ import (
|
|||||||
var dist embed.FS
|
var dist embed.FS
|
||||||
|
|
||||||
func inviteTreeHandler(w http.ResponseWriter, r *http.Request) {
|
func inviteTreeHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
formattedInviteData := buildHTMLTree(whitelist, "")
|
content := inviteTreePageHTML(r.Context(), InviteTreePageParams{})
|
||||||
|
htmlgo.Fprint(w, baseHTML(content), r.Context())
|
||||||
data := map[string]interface{}{
|
|
||||||
"Relayname": s.RelayName,
|
|
||||||
"Relaydescription": s.RelayDescription,
|
|
||||||
"Pagetitle": "Invite Hierarchy",
|
|
||||||
"Pagecontent": `
|
|
||||||
<input type="text" id="inviteuser-input" placeholder="npub1..." /><button class="inviteuser">Invite!</button>
|
|
||||||
` + formattedInviteData,
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
func reportsViewerHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
var formattedReportsData template.HTML = ""
|
// var formattedReportsData template.HTML = ""
|
||||||
|
|
||||||
events, _ := db.QueryEvents(context.Background(), nostr.Filter{
|
// events, _ := db.QueryEvents(context.Background(), nostr.Filter{
|
||||||
Kinds: []int{1984},
|
// Kinds: []int{1984},
|
||||||
Limit: 52,
|
// Limit: 52,
|
||||||
})
|
// })
|
||||||
|
|
||||||
type Report struct {
|
// type Report struct {
|
||||||
ID string
|
// ID string
|
||||||
ByUser string
|
// ByUser string
|
||||||
AboutUser string
|
// AboutUser string
|
||||||
AboutEvent string
|
// AboutEvent string
|
||||||
Type string
|
// Type string
|
||||||
Content string
|
// Content string
|
||||||
}
|
// }
|
||||||
|
|
||||||
for ev := range events {
|
// for ev := range events {
|
||||||
pTag := ev.Tags.GetFirst([]string{"p"})
|
// pTag := ev.Tags.GetFirst([]string{"p"})
|
||||||
|
|
||||||
eTag := ev.Tags.GetFirst([]string{"e"})
|
// eTag := ev.Tags.GetFirst([]string{"e"})
|
||||||
if pTag != nil {
|
// if pTag != nil {
|
||||||
typeReport := eTag.Relay()[6:]
|
// typeReport := eTag.Relay()[6:]
|
||||||
if typeReport == "" {
|
// if typeReport == "" {
|
||||||
typeReport = pTag.Relay()[6:]
|
// typeReport = pTag.Relay()[6:]
|
||||||
}
|
// }
|
||||||
report := Report{
|
// report := Report{
|
||||||
ID: ev.ID,
|
// ID: ev.ID,
|
||||||
ByUser: ev.PubKey,
|
// ByUser: ev.PubKey,
|
||||||
AboutUser: pTag.Value(),
|
// AboutUser: pTag.Value(),
|
||||||
AboutEvent: eTag.Value(),
|
// AboutEvent: eTag.Value(),
|
||||||
Type: typeReport,
|
// Type: typeReport,
|
||||||
Content: ev.Content,
|
// Content: ev.Content,
|
||||||
}
|
// }
|
||||||
// get AboutEvent content, note1 ect
|
// // get AboutEvent content, note1 ect
|
||||||
formattedReportsData += template.HTML(fmt.Sprintf(`
|
// formattedReportsData += template.HTML(fmt.Sprintf(`
|
||||||
<div>
|
// <div>
|
||||||
<p><b>Report %v</b></p>
|
// <p><b>Report %v</b></p>
|
||||||
<p>By User: <a class="user" href="nostr:%v">%v</a></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>`,
|
// <p>About User: <a class="user" href="nostr:%v">%v</a></p>`,
|
||||||
report.ID,
|
// report.ID,
|
||||||
getUserInfo(context.Background(), report.ByUser).Npub,
|
// getUserInfo(context.Background(), report.ByUser).Npub,
|
||||||
getUserInfo(context.Background(), report.ByUser).Name,
|
// getUserInfo(context.Background(), report.ByUser).Name,
|
||||||
getUserInfo(context.Background(), report.AboutUser).Npub,
|
// getUserInfo(context.Background(), report.AboutUser).Npub,
|
||||||
getUserInfo(context.Background(), report.AboutUser).Name,
|
// getUserInfo(context.Background(), report.AboutUser).Name,
|
||||||
))
|
// ))
|
||||||
if report.AboutEvent != "" {
|
// if report.AboutEvent != "" {
|
||||||
// fetch event data
|
// // fetch event data
|
||||||
aboutEvents, _ := db.QueryEvents(context.TODO(), nostr.Filter{
|
// aboutEvents, _ := db.QueryEvents(context.TODO(), nostr.Filter{
|
||||||
IDs: []string{report.AboutEvent},
|
// IDs: []string{report.AboutEvent},
|
||||||
})
|
// })
|
||||||
for aboutEvent := range aboutEvents {
|
// for aboutEvent := range aboutEvents {
|
||||||
formattedReportsData += template.HTML(fmt.Sprintf(`
|
// formattedReportsData += template.HTML(fmt.Sprintf(`
|
||||||
<p>
|
// <p>
|
||||||
About Event: <ul>
|
// About Event: <ul>
|
||||||
<p>Kind: %v</p>
|
// <p>Kind: %v</p>
|
||||||
<p>Tags: %v</p>
|
// <p>Tags: %v</p>
|
||||||
<p>Content: %v</p>
|
// <p>Content: %v</p>
|
||||||
</ul>
|
// </ul>
|
||||||
</p>`,
|
// </p>`,
|
||||||
template.HTMLEscaper(aboutEvent.Kind),
|
// template.HTMLEscaper(aboutEvent.Kind),
|
||||||
template.HTMLEscaper(aboutEvent.Tags),
|
// template.HTMLEscaper(aboutEvent.Tags),
|
||||||
template.HTMLEscaper(aboutEvent.Content),
|
// template.HTMLEscaper(aboutEvent.Content),
|
||||||
))
|
// ))
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
formattedReportsData += template.HTML(fmt.Sprintf(`
|
// formattedReportsData += template.HTML(fmt.Sprintf(`
|
||||||
<p>Type: %v</p>`,
|
// <p>Type: %v</p>`,
|
||||||
report.Type,
|
// report.Type,
|
||||||
))
|
// ))
|
||||||
if report.Content != "" {
|
// if report.Content != "" {
|
||||||
formattedReportsData += template.HTML(fmt.Sprintf(`
|
// formattedReportsData += template.HTML(fmt.Sprintf(`
|
||||||
<p>Content: %v</p>
|
// <p>Content: %v</p>
|
||||||
<div>
|
// <div>
|
||||||
<button data-actionarg='[["e", "%v"],["p", "%v"]]' class="removefromrelay">Ban Reported User and Remove Report</button>
|
// <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='[["e", "%v"]]' class="removefromrelay">Remove This Report</button>
|
||||||
<button data-actionarg='[["p", "%v"]]' class="removefromrelay">Ban User who wrote report</button>
|
// <button data-actionarg='[["p", "%v"]]' class="removefromrelay">Ban User who wrote report</button>
|
||||||
</div>
|
// </div>
|
||||||
</div>
|
// </div>
|
||||||
<hr />`,
|
// <hr />`,
|
||||||
template.HTMLEscaper(report.Content),
|
// template.HTMLEscaper(report.Content),
|
||||||
template.HTMLEscaper(report.ID),
|
// template.HTMLEscaper(report.ID),
|
||||||
template.HTMLEscaper(report.AboutUser),
|
// template.HTMLEscaper(report.AboutUser),
|
||||||
template.HTMLEscaper(report.ID),
|
// template.HTMLEscaper(report.ID),
|
||||||
template.HTMLEscaper(report.ByUser),
|
// template.HTMLEscaper(report.ByUser),
|
||||||
))
|
// ))
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
data := map[string]interface{}{
|
// data := map[string]interface{}{
|
||||||
"Relayname": s.RelayName,
|
// "Relayname": s.RelayName,
|
||||||
"Relaydescription": s.RelayDescription,
|
// "Relaydescription": s.RelayDescription,
|
||||||
"Pagetitle": "Reports Viewer",
|
// "Pagetitle": "Reports Viewer",
|
||||||
"Pagecontent": formattedReportsData,
|
// "Pagecontent": formattedReportsData,
|
||||||
}
|
// }
|
||||||
|
|
||||||
tmpl, err := template.ParseFS(dist, "ui/dist/index.html")
|
// tmpl, err := template.ParseFS(dist, "ui/dist/index.html")
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
http.Error(w, "Error parsing template: "+err.Error(), http.StatusInternalServerError)
|
// http.Error(w, "Error parsing template: "+err.Error(), http.StatusInternalServerError)
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Execute the template with the provided data and write it to the response
|
// // Execute the template with the provided data and write it to the response
|
||||||
err = tmpl.Execute(w, data)
|
// err = tmpl.Execute(w, data)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
http.Error(w, "Error executing template: "+err.Error(), http.StatusInternalServerError)
|
// http.Error(w, "Error executing template: "+err.Error(), http.StatusInternalServerError)
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
func homePageHandler(w http.ResponseWriter, r *http.Request) {
|
func homePageHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
relayOwnerInfo := getUserInfo(context.Background(), s.RelayPubkey)
|
content := homePageHTML(r.Context(), HomePageParams{
|
||||||
|
RelayOwnerInfo: getUserInfo(context.Background(), s.RelayPubkey),
|
||||||
data := map[string]interface{}{
|
})
|
||||||
"Relayname": s.RelayName,
|
htmlgo.Fprint(w, baseHTML(content), r.Context())
|
||||||
"Relaydescription": s.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>
|
|
||||||
`, s.RelayName, s.RelayDescription, relayOwnerInfo.Npub, relayOwnerInfo.Name, s.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) {
|
func staticHandler(prefix string, w http.ResponseWriter, r *http.Request) {
|
||||||
|
9
main.go
9
main.go
@ -16,6 +16,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Settings struct {
|
type Settings struct {
|
||||||
|
Port string `envconfig:"PORT" default:"3334"`
|
||||||
RelayName string `envconfig:"RELAY_NAME" required:"true"`
|
RelayName string `envconfig:"RELAY_NAME" required:"true"`
|
||||||
RelayPubkey string `envconfig:"RELAY_PUBKEY" required:"true"`
|
RelayPubkey string `envconfig:"RELAY_PUBKEY" required:"true"`
|
||||||
RelayDescription string `envconfig:"RELAY_DESCRIPTION"`
|
RelayDescription string `envconfig:"RELAY_DESCRIPTION"`
|
||||||
@ -83,10 +84,12 @@ func main() {
|
|||||||
// ui
|
// ui
|
||||||
relay.Router().HandleFunc("/reports", reportsViewerHandler)
|
relay.Router().HandleFunc("/reports", reportsViewerHandler)
|
||||||
relay.Router().HandleFunc("/users", inviteTreeHandler)
|
relay.Router().HandleFunc("/users", inviteTreeHandler)
|
||||||
relay.Router().HandleFunc("/", redirectHandler)
|
relay.Router().HandleFunc("/", homePageHandler)
|
||||||
|
|
||||||
log.Info().Msg("running on http://127.0.0.1:3334")
|
log.Info().Msg("running on http://0.0.0.0:" + s.Port)
|
||||||
http.ListenAndServe(":3334", relay)
|
if err := http.ListenAndServe(":"+s.Port, relay); err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("failed to serve")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// save whitelist on shutdown
|
// save whitelist on shutdown
|
||||||
|
92
pages.go
Normal file
92
pages.go
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
. "github.com/theplant/htmlgo"
|
||||||
|
)
|
||||||
|
|
||||||
|
func baseHTML(inside HTMLComponent) HTMLComponent {
|
||||||
|
navItemClass := "text-gray-300 hover:bg-gray-700 hover:text-white rounded-md px-3 py-2 font-medium"
|
||||||
|
|
||||||
|
return HTML(
|
||||||
|
Head(
|
||||||
|
Meta().Charset("utf-8"),
|
||||||
|
Meta().Name("viewport").Content("width=device-width, initial-scale=1"),
|
||||||
|
Title(s.RelayName),
|
||||||
|
Script("").Src("https://cdn.tailwindcss.com"),
|
||||||
|
),
|
||||||
|
Body(
|
||||||
|
Div(
|
||||||
|
H1(s.RelayName).Class("font-bold text-2xl"),
|
||||||
|
P().Text(s.RelayDescription).Class("text-lg"),
|
||||||
|
).Class("mx-auto my-6 text-center"),
|
||||||
|
Nav(
|
||||||
|
A().Text("information").Href("/").Class(navItemClass),
|
||||||
|
A().Text("invite tree").Href("/users").Class(navItemClass),
|
||||||
|
A().Text("reports").Href("/reports").Class(navItemClass),
|
||||||
|
).Class("flex flex-1 items-center justify-center"),
|
||||||
|
Div(inside).Class("m-4"),
|
||||||
|
).Class("bg-gray-800 mx-4 my-6 text-white"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
type HomePageParams struct {
|
||||||
|
RelayOwnerInfo SimpleUserInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func homePageHTML(ctx context.Context, params HomePageParams) HTMLComponent {
|
||||||
|
contact := Div()
|
||||||
|
if s.RelayContact != "" {
|
||||||
|
contact = Div().Text("alternative contact: " + s.RelayContact)
|
||||||
|
}
|
||||||
|
|
||||||
|
description := Div()
|
||||||
|
if s.RelayDescription != "" {
|
||||||
|
description = Div().Text("description: " + s.RelayDescription)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Div(
|
||||||
|
Div().Text("name: "+s.RelayName),
|
||||||
|
description,
|
||||||
|
contact,
|
||||||
|
Div(
|
||||||
|
Text("relay master: "),
|
||||||
|
A().Text(params.RelayOwnerInfo.Name).Href("nostr:"+params.RelayOwnerInfo.Npub),
|
||||||
|
),
|
||||||
|
Br(),
|
||||||
|
Div(
|
||||||
|
Text("this relay uses"),
|
||||||
|
A().Target("_blank").Href("https://github.com/github-tijlxyz/khatru-invite").Text("Khatru Invite"),
|
||||||
|
Text(" which is built with "),
|
||||||
|
A().Target("_blank").Href("https://github.com/fiatjaf/khatru").Text("Khatru"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
type InviteTreePageParams struct{}
|
||||||
|
|
||||||
|
func inviteTreePageHTML(ctx context.Context, params InviteTreePageParams) HTMLComponent {
|
||||||
|
return Div(
|
||||||
|
Input("").Type("text").Placeholder("npub1..."),
|
||||||
|
Button("invite"),
|
||||||
|
buildInviteTree(ctx, ""),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildInviteTree(ctx context.Context, invitedBy string) HTMLComponent {
|
||||||
|
tree := Ul()
|
||||||
|
for _, entry := range whitelist {
|
||||||
|
if entry.InvitedBy == invitedBy {
|
||||||
|
user := getUserInfo(ctx, entry.PublicKey)
|
||||||
|
tree = tree.Children(
|
||||||
|
Li(
|
||||||
|
A().Href("nostr:"+user.Npub).Text(user.Name),
|
||||||
|
A().Text("remove"),
|
||||||
|
buildInviteTree(ctx, entry.PublicKey),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tree
|
||||||
|
}
|
@ -1,32 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<meta name="description" content="{{.Relaydescription}}">
|
|
||||||
<script src="./src/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>
|
|
28
utils.go
28
utils.go
@ -3,37 +3,17 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"html/template"
|
|
||||||
|
|
||||||
"github.com/nbd-wtf/go-nostr"
|
"github.com/nbd-wtf/go-nostr"
|
||||||
)
|
)
|
||||||
|
|
||||||
func buildHTMLTree(entries []WhitelistEntry, invitedBy string) template.HTML {
|
func isPublicKeyInWhitelist(pubkey string) bool {
|
||||||
html := "<ul>"
|
if pubkey == s.RelayPubkey {
|
||||||
|
return true
|
||||||
for _, entry := range entries {
|
|
||||||
if entry.InvitedBy == invitedBy {
|
|
||||||
user := getUserInfo(context.TODO(), entry.PublicKey)
|
|
||||||
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.PublicKey,
|
|
||||||
buildHTMLTree(entries, entry.PublicKey))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
html += "</ul>"
|
|
||||||
return template.HTML(html)
|
|
||||||
}
|
|
||||||
|
|
||||||
func isPkInWhitelist(targetPk string) bool {
|
|
||||||
for i := 0; i < len(whitelist); i++ {
|
for i := 0; i < len(whitelist); i++ {
|
||||||
if whitelist[i].PublicKey == targetPk {
|
if whitelist[i].PublicKey == pubkey {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ var whitelist []WhitelistEntry
|
|||||||
|
|
||||||
func whitelistRejecter(ctx context.Context, evt *nostr.Event) (reject bool, msg string) {
|
func whitelistRejecter(ctx context.Context, evt *nostr.Event) (reject bool, msg string) {
|
||||||
// check if user in whitelist
|
// check if user in whitelist
|
||||||
if !isPkInWhitelist(evt.PubKey) {
|
if !isPublicKeyInWhitelist(evt.PubKey) {
|
||||||
return true, "You are not invited to this relay"
|
return true, "You are not invited to this relay"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,13 +28,11 @@ func whitelistRejecter(ctx context.Context, evt *nostr.Event) (reject bool, msg
|
|||||||
if evt.Kind == 20201 {
|
if evt.Kind == 20201 {
|
||||||
pTags := evt.Tags.GetAll([]string{"p"})
|
pTags := evt.Tags.GetAll([]string{"p"})
|
||||||
for _, tag := range pTags {
|
for _, tag := range pTags {
|
||||||
if !isPkInWhitelist(tag.Value()) {
|
if nostr.IsValidPublicKeyHex(tag.Value()) && !isPublicKeyInWhitelist(tag.Value()) {
|
||||||
if nostr.IsValidPublicKeyHex(tag.Value()) {
|
|
||||||
whitelist = append(whitelist, WhitelistEntry{PublicKey: tag.Value(), InvitedBy: evt.PubKey})
|
whitelist = append(whitelist, WhitelistEntry{PublicKey: tag.Value(), InvitedBy: evt.PubKey})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
kind 20202
|
kind 20202
|
||||||
|
Loading…
x
Reference in New Issue
Block a user