From a64a12e03d6b66f1b6c6b357482b7f41c8edf441 Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Tue, 14 Jan 2025 13:05:45 -0300 Subject: [PATCH] convert templates to .templ --- .gitignore | 1 + components.go | 45 -------------- handler.go | 18 ++---- invite_tree.templ | 69 +++++++++++++++++++++ justfile | 7 ++- layout.templ | 59 ++++++++++++++++++ pages.go | 148 ---------------------------------------------- reports.templ | 64 ++++++++++++++++++++ 8 files changed, 202 insertions(+), 209 deletions(-) delete mode 100644 components.go create mode 100644 invite_tree.templ create mode 100644 layout.templ delete mode 100644 pages.go create mode 100644 reports.templ diff --git a/.gitignore b/.gitignore index 0224a00..03cb750 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ users.json khatru-pyramid .env db +*_templ.go diff --git a/components.go b/components.go deleted file mode 100644 index 9f9880d..0000000 --- a/components.go +++ /dev/null @@ -1,45 +0,0 @@ -package main - -import ( - "context" - - sdk "github.com/nbd-wtf/go-nostr/sdk" - . "github.com/theplant/htmlgo" -) - -func inviteTreeComponent(ctx context.Context, inviter string, loggedUser string) HTMLComponent { - children := make([]HTMLComponent, 0, len(whitelist)/2) - for pubkey, invitedBy := range whitelist { - if invitedBy == inviter { - profile := sys.FetchProfileMetadata(ctx, pubkey) - children = append(children, userRowComponent(ctx, profile, loggedUser)) - } - } - return Ul(children...) -} - -func userRowComponent(ctx context.Context, profile sdk.ProfileMetadata, loggedUser string) HTMLComponent { - button := Span("") - if isAncestorOf(loggedUser, profile.PubKey) && 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": "`+profile.PubKey+`"}`, - ) - } - - return Li( - userNameComponent(profile), - button, - inviteTreeComponent(ctx, profile.PubKey, loggedUser), - ).Class("ml-6") -} - -func userNameComponent(profile sdk.ProfileMetadata) HTMLComponent { - return A().Href("https://nosta.me/" + profile.Npub()).Target("_blank").Children( - Span(profile.ShortName()).Attr("title", profile.Npub()), - ).Class("font-mono py-1") -} diff --git a/handler.go b/handler.go index 6175fd8..4fc4f5f 100644 --- a/handler.go +++ b/handler.go @@ -6,15 +6,11 @@ import ( "github.com/nbd-wtf/go-nostr" "github.com/nbd-wtf/go-nostr/nip19" - "github.com/theplant/htmlgo" ) func inviteTreeHandler(w http.ResponseWriter, r *http.Request) { loggedUser := getLoggedUser(r) - content := inviteTreePageHTML(r.Context(), InviteTreePageParams{ - loggedUser: loggedUser, - }) - htmlgo.Fprint(w, baseHTML(content, loggedUser), r.Context()) + inviteTreePage(loggedUser).Render(r.Context(), w) } func addToWhitelistHandler(w http.ResponseWriter, r *http.Request) { @@ -35,8 +31,7 @@ func addToWhitelistHandler(w http.ResponseWriter, r *http.Request) { return } - content := inviteTreeComponent(r.Context(), "", loggedUser) - htmlgo.Fprint(w, content, r.Context()) + inviteTreeComponent("", loggedUser).Render(r.Context(), w) } func removeFromWhitelistHandler(w http.ResponseWriter, r *http.Request) { @@ -46,8 +41,7 @@ func removeFromWhitelistHandler(w http.ResponseWriter, r *http.Request) { http.Error(w, "failed to remove from whitelist: "+err.Error(), 500) return } - content := inviteTreeComponent(r.Context(), "", loggedUser) - htmlgo.Fprint(w, content, r.Context()) + inviteTreeComponent("", loggedUser).Render(r.Context(), w) } // this deletes all events from users not in the relay anymore @@ -95,11 +89,7 @@ func reportsViewerHandler(w http.ResponseWriter, r *http.Request) { return } - content := reportsPageHTML(r.Context(), ReportsPageParams{ - reports: events, - loggedUser: getLoggedUser(r), - }) - htmlgo.Fprint(w, content, r.Context()) + reportsPage(events, getLoggedUser(r)).Render(r.Context(), w) } func joubleHandler(w http.ResponseWriter, r *http.Request) { diff --git a/invite_tree.templ b/invite_tree.templ new file mode 100644 index 0000000..1aeb039 --- /dev/null +++ b/invite_tree.templ @@ -0,0 +1,69 @@ +package main + +import ( + "fmt" + + "github.com/nbd-wtf/go-nostr/sdk" +) + +templ inviteTreePage(loggedUser string) { + @layout(loggedUser) { +
+ if loggedUser != "" && (loggedUser == s.RelayPubkey || !hasInvitedAtLeast(loggedUser, s.MaxInvitesPerPerson)) { +
+ + +
+ } +
+ @inviteTreeComponent("", loggedUser) +
+
+ } +} + +templ inviteTreeComponent(inviter string, loggedUser string) { + +} + +templ userNameComponent(profile sdk.ProfileMetadata) { + + { profile.ShortName() } + +} diff --git a/justfile b/justfile index bfc79dc..3547948 100644 --- a/justfile +++ b/justfile @@ -1,9 +1,12 @@ dev: - ag -l --go | entr -r godotenv go run . + fd 'go|templ' | entr -r bash -c 'just templ && godotenv go run .' -build: +build: templ CC=musl-gcc go build -ldflags='-linkmode external -extldflags "-static"' -o ./khatru-pyramid +templ: + templ generate + deploy target: build ssh root@{{target}} 'systemctl stop pyramid'; scp khatru-pyramid {{target}}:pyramid/khatru-invite diff --git a/layout.templ b/layout.templ new file mode 100644 index 0000000..5612e17 --- /dev/null +++ b/layout.templ @@ -0,0 +1,59 @@ +package main + +templ layout(loggedUser string) { + + + + + + { s.RelayName } + + + + + +
+

{ s.RelayName }

+ if s.RelayDescription != "" { +

{ s.RelayDescription }

+ } +
+ +
+ { children... } +
+

+ powered by + khatru-pyramid +

+ + +} diff --git a/pages.go b/pages.go deleted file mode 100644 index 7affcdd..0000000 --- a/pages.go +++ /dev/null @@ -1,148 +0,0 @@ -package main - -import ( - "context" - - "github.com/nbd-wtf/go-nostr" - . "github.com/theplant/htmlgo" -) - -const buttonClass = "rounded-md text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300" - -func baseHTML(inside HTMLComponent, loggedUser string) HTMLComponent { - navItemClass := "text-gray-600 hover:bg-gray-200 rounded-md px-3 py-2 font-medium" - - cleanupButton := Span("") - if loggedUser == s.RelayPubkey { - cleanupButton = A().Text("clear stuff").Href("/cleanup").Class(navItemClass) - } - - 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"), - Script("").Src("https://unpkg.com/htmx.org@1.9.6"), - Script("").Src("https://unpkg.com/hyperscript.org@0.9.12"), - ), - 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("invite tree").Href("/").Class(navItemClass).Attr("hx-boost", "true", "hx-target", "main", "hx-select", "main"), - A().Text("browse").Href("/browse").Class(navItemClass), - A().Text("reports").Href("/reports").Class(navItemClass).Attr("hx-boost", "true", "hx-target", "main", "hx-select", "main"), - cleanupButton, - 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 call location.reload() - -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"), - P( - Text("powered by "), - A().Href("https://github.com/github-tijlxyz/khatru-pyramid").Text("khatru-pyramid").Class("hover:underline cursor-pointer text-blue-500"), - ).Class("text-end my-4 text-sm"), - ).Class("my-6 mx-auto max-w-min min-w-96"), - ) -} - -type InviteTreePageParams struct { - loggedUser string -} - -func inviteTreePageHTML(ctx context.Context, params InviteTreePageParams) HTMLComponent { - inviteForm := Div() - - if params.loggedUser != "" && (params.loggedUser == s.RelayPubkey || !hasInvitedAtLeast(params.loggedUser, s.MaxInvitesPerPerson)) { - 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+" ml-2 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()", - ).Class("flex") - } - - return Div( - inviteForm, - Div( - inviteTreeComponent(ctx, "", params.loggedUser), - ).Id("tree").Class("mt-3"), - ) -} - -type ReportsPageParams struct { - reports chan *nostr.Event - loggedUser string -} - -func reportsPageHTML(ctx context.Context, params ReportsPageParams) HTMLComponent { - items := make([]HTMLComponent, 0, 52) - for report := range params.reports { - var primaryType string - var secondaryType string - var relatedContent HTMLComponent - - if e := report.Tags.GetFirst([]string{"e", ""}); e != nil { - // event report - res, _ := sys.StoreRelay.QuerySync(ctx, nostr.Filter{IDs: []string{(*e)[1]}}) - if len(res) == 0 { - sys.Store.DeleteEvent(ctx, report) - continue - } - - if len(*e) >= 3 { - primaryType = (*e)[2] - } - - relatedEvent := res[0] - relatedContent = Div( - Text("event reported: "), - Div().Text(relatedEvent.String()).Class("text-mono"), - ) - } else if p := report.Tags.GetFirst([]string{"p", ""}); p != nil { - // pubkey report - if !isPublicKeyInWhitelist((*p)[1]) { - sys.Store.DeleteEvent(ctx, report) - continue - } - - if len(*p) >= 3 { - primaryType = (*p)[2] - } - - relatedProfile := sys.FetchProfileMetadata(ctx, (*p)[1]) - relatedContent = Div( - Text("profile reported: "), - userNameComponent(relatedProfile), - ) - } else { - continue - } - - reporter := sys.FetchProfileMetadata(ctx, report.PubKey) - report := Div( - Div(Span(primaryType).Class("font-semibold"), Text(" report")).Class("font-lg"), - Div().Text(secondaryType), - Div(Text("by "), userNameComponent(reporter)), - Div().Text(report.Content).Class("p-3"), - relatedContent, - ) - - items = append(items, report) - } - return baseHTML( - Div( - H1("reports received").Class("text-xl p-4"), - Div(items...), - ), - params.loggedUser, - ) -} diff --git a/reports.templ b/reports.templ new file mode 100644 index 0000000..e6b6398 --- /dev/null +++ b/reports.templ @@ -0,0 +1,64 @@ +package main + +import "github.com/nbd-wtf/go-nostr" + +templ reportsPage(reports chan *nostr.Event, loggedUser string) { + @layout(loggedUser) { +
+

reports received

+
+ for report := range reports { +
+ if e := report.Tags.GetFirst([]string{"e", ""}); e != nil { + @eventReportComponent(e, report) + } else if p := report.Tags.GetFirst([]string{"p", ""}); p != nil { + @profileReportComponent(p, report) + } +
+ } +
+
+ } +} + +templ eventReportComponent(e *nostr.Tag, report *nostr.Event) { + if res, _ := sys.StoreRelay.QuerySync(ctx, nostr.Filter{IDs: []string{(*e)[1]}}); len(res) > 0 { +
+
+ + if len(*e) >= 3 { + { (*e)[2] } + } + + { " report" } +
+
by @userNameComponent(sys.FetchProfileMetadata(ctx, report.PubKey))
+
{ report.Content }
+
+ event reported: +
{ res[0].String() }
+
+
+ } +} + +templ profileReportComponent(p *nostr.Tag, report *nostr.Event) { + if isPublicKeyInWhitelist((*p)[1]) { +
+
+ + if len(*p) >= 3 { + { (*p)[2] } + } + + { " report" } +
+
by @userNameComponent(sys.FetchProfileMetadata(ctx, report.PubKey))
+
{ report.Content }
+
+ profile reported: + @userNameComponent(sys.FetchProfileMetadata(ctx, (*p)[1])) +
+
+ } +}