diff --git a/components.go b/components.go
index 31505e1..3e9260e 100644
--- a/components.go
+++ b/components.go
@@ -32,13 +32,13 @@ func userRowComponent(ctx context.Context, profile sdk.ProfileMetadata, loggedUs
}
return Li(
- userNameComponent(ctx, profile),
+ userNameComponent(profile),
button,
inviteTreeComponent(ctx, profile.PubKey, loggedUser),
).Class("ml-6")
}
-func userNameComponent(ctx context.Context, profile sdk.ProfileMetadata) HTMLComponent {
+func userNameComponent(profile sdk.ProfileMetadata) HTMLComponent {
return A().Href("nostr:" + profile.Npub()).Children(
Span(profile.ShortName()).Attr(
"npub", profile.Npub(),
diff --git a/handler.go b/handler.go
index acdbe11..929b03b 100644
--- a/handler.go
+++ b/handler.go
@@ -3,6 +3,7 @@ package main
import (
"net/http"
+ "github.com/nbd-wtf/go-nostr"
"github.com/nbd-wtf/go-nostr/nip19"
"github.com/theplant/htmlgo"
)
@@ -43,114 +44,20 @@ func removeFromWhitelistHandler(w http.ResponseWriter, r *http.Request) {
}
func reportsViewerHandler(w http.ResponseWriter, r *http.Request) {
- // var formattedReportsData template.HTML = ""
+ events, err := db.QueryEvents(r.Context(), nostr.Filter{
+ Kinds: []int{1984},
+ Limit: 52,
+ })
+ if err != nil {
+ http.Error(w, "failed to query reports: "+err.Error(), 500)
+ return
+ }
- // events, _ := db.QueryEvents(context.Background(), nostr.Filter{
- // Kinds: []int{1984},
- // Limit: 52,
- // })
-
- // type Report struct {
- // ID string
- // ByUser string
- // AboutUser string
- // AboutEvent string
- // Type string
- // Content string
- // }
-
- // for ev := range events {
- // pTag := ev.Tags.GetFirst([]string{"p"})
-
- // eTag := ev.Tags.GetFirst([]string{"e"})
- // if pTag != nil {
- // typeReport := eTag.Relay()[6:]
- // if typeReport == "" {
- // typeReport = pTag.Relay()[6:]
- // }
- // report := Report{
- // ID: ev.ID,
- // ByUser: ev.PubKey,
- // AboutUser: pTag.Value(),
- // AboutEvent: eTag.Value(),
- // Type: typeReport,
- // Content: ev.Content,
- // }
- // // get AboutEvent content, note1 ect
- // formattedReportsData += template.HTML(fmt.Sprintf(`
- //
- //
Report %v
- //
By User: %v
- //
About User: %v
`,
- // report.ID,
- // getUserInfo(context.Background(), report.ByUser).Npub,
- // getUserInfo(context.Background(), report.ByUser).Name,
- // getUserInfo(context.Background(), report.AboutUser).Npub,
- // getUserInfo(context.Background(), report.AboutUser).Name,
- // ))
- // if report.AboutEvent != "" {
- // // fetch event data
- // aboutEvents, _ := db.QueryEvents(context.TODO(), nostr.Filter{
- // IDs: []string{report.AboutEvent},
- // })
- // for aboutEvent := range aboutEvents {
- // formattedReportsData += template.HTML(fmt.Sprintf(`
- //
- // About Event:
- // Kind: %v
- // Tags: %v
- // Content: %v
- //
- // `,
- // template.HTMLEscaper(aboutEvent.Kind),
- // template.HTMLEscaper(aboutEvent.Tags),
- // template.HTMLEscaper(aboutEvent.Content),
- // ))
- // }
- // }
- // formattedReportsData += template.HTML(fmt.Sprintf(`
- //
Type: %v
`,
- // report.Type,
- // ))
- // if report.Content != "" {
- // formattedReportsData += template.HTML(fmt.Sprintf(`
- //
Content: %v
- //
- //
- //
- //
- //
- //
- //
`,
- // template.HTMLEscaper(report.Content),
- // template.HTMLEscaper(report.ID),
- // template.HTMLEscaper(report.AboutUser),
- // template.HTMLEscaper(report.ID),
- // template.HTMLEscaper(report.ByUser),
- // ))
- // }
- // }
- // }
-
- // data := map[string]interface{}{
- // "Relayname": s.RelayName,
- // "Relaydescription": s.RelayDescription,
- // "Pagetitle": "Reports Viewer",
- // "Pagecontent": formattedReportsData,
- // }
-
- // tmpl, err := template.ParseFS(dist, "ui/dist/index.html")
- // if err != nil {
- // http.Error(w, "Error parsing template: "+err.Error(), http.StatusInternalServerError)
- // return
- // }
-
- // // Execute the template with the provided data and write it to the response
- // err = tmpl.Execute(w, data)
- // if err != nil {
- // http.Error(w, "Error executing template: "+err.Error(), http.StatusInternalServerError)
- // return
- // }
+ content := reportsPageHTML(r.Context(), ReportsPageParams{
+ reports: events,
+ loggedUser: getLoggedUser(r),
+ })
+ htmlgo.Fprint(w, content, r.Context())
}
func homePageHandler(w http.ResponseWriter, r *http.Request) {
diff --git a/main.go b/main.go
index bcad25c..d5ee953 100644
--- a/main.go
+++ b/main.go
@@ -56,7 +56,11 @@ func main() {
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)
+ relay.RejectEvent = append(relay.RejectEvent,
+ rejectEventsFromUsersNotInWhitelist,
+ restrictToKinds,
+ validateAndFilterReports,
+ )
// load users registry
if err := loadWhitelist(); err != nil {
diff --git a/pages.go b/pages.go
index b050495..c3d47b2 100644
--- a/pages.go
+++ b/pages.go
@@ -3,6 +3,7 @@ package main
import (
"context"
+ "github.com/nbd-wtf/go-nostr"
sdk "github.com/nbd-wtf/nostr-sdk"
. "github.com/theplant/htmlgo"
)
@@ -62,7 +63,7 @@ func homePageHTML(ctx context.Context, params HomePageParams) HTMLComponent {
contact,
Div(
Text("relay master: "),
- userNameComponent(ctx, params.relayOwnerInfo),
+ userNameComponent(params.relayOwnerInfo),
),
Br(),
Div(
@@ -102,3 +103,71 @@ func inviteTreePageHTML(ctx context.Context, params InviteTreePageParams) HTMLCo
).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 := fetchAndStoreProfile(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...),
+ ),
+ )
+}
diff --git a/relay.go b/relay.go
index cbd158e..cfc3ee1 100644
--- a/relay.go
+++ b/relay.go
@@ -3,6 +3,7 @@ package main
import (
"context"
+ "github.com/fiatjaf/khatru/plugins"
"github.com/nbd-wtf/go-nostr"
)
@@ -10,5 +11,33 @@ func rejectEventsFromUsersNotInWhitelist(ctx context.Context, event *nostr.Event
if isPublicKeyInWhitelist(event.PubKey) {
return false, ""
}
+ if event.Kind == 1985 {
+ // we accept reports from anyone (will filter them for relevance in the next function)
+ return false, ""
+ }
return true, "not authorized"
}
+
+var restrictToKinds = plugins.RestrictToSpecifiedKinds(
+ 0, 1, 3, 5, 6, 8, 16, 1063, 1985, 9735, 10000, 10001, 10002, 30008, 30009, 30311, 31922, 31923, 31924, 31925)
+
+func validateAndFilterReports(ctx context.Context, event *nostr.Event) (reject bool, msg string) {
+ if event.Kind == 1985 {
+ if e := event.Tags.GetFirst([]string{"e", ""}); e != nil {
+ // event report: check if the target event is here
+ res, _ := sys.StoreRelay().QuerySync(ctx, nostr.Filter{IDs: []string{(*e)[1]}})
+ if len(res) == 0 {
+ return true, "we don't know anything about the target event"
+ }
+ } else if p := event.Tags.GetFirst([]string{"p", ""}); p != nil {
+ // pubkey report
+ if !isPublicKeyInWhitelist((*p)[1]) {
+ return true, "target pubkey is not a user of this relay"
+ }
+ } else {
+ return true, "invalid report"
+ }
+ }
+
+ return false, ""
+}