From 97e6c9a0c2c32bf514d3a4218d239741f1dc26c8 Mon Sep 17 00:00:00 2001 From: bndw Date: Fri, 13 Feb 2026 20:50:27 -0800 Subject: feat: add HTML index page for browser viewing Add a beautiful HTML landing page when visiting relay in browser: - Shows all three protocol endpoints (gRPC, Connect, WebSocket) - Lists supported NIPs (01, 09, 11) - Displays relay features and info - Responsive design with gradient styling - Serves on GET requests (regular Accept header) - NIP-11 still served for Accept: application/nostr+json --- cmd/relay/main.go | 1 + internal/handler/websocket/handler.go | 28 ++++- internal/handler/websocket/index.go | 194 ++++++++++++++++++++++++++++++++++ 3 files changed, 219 insertions(+), 4 deletions(-) create mode 100644 internal/handler/websocket/index.go diff --git a/cmd/relay/main.go b/cmd/relay/main.go index 9cf6ad6..56668fc 100644 --- a/cmd/relay/main.go +++ b/cmd/relay/main.go @@ -51,6 +51,7 @@ func main() { mux.Handle(path, handler) wsHandler := wshandler.NewHandler(store, subManager) + wsHandler.SetIndexData(*grpcAddr, *wsAddr, *wsAddr) mux.Handle("/", wsHandler) grpcLis, err := net.Listen("tcp", *grpcAddr) diff --git a/internal/handler/websocket/handler.go b/internal/handler/websocket/handler.go index 224a2f8..2d531e9 100644 --- a/internal/handler/websocket/handler.go +++ b/internal/handler/websocket/handler.go @@ -21,8 +21,9 @@ type EventStore interface { } type Handler struct { - store EventStore - subs *subscription.Manager + store EventStore + subs *subscription.Manager + indexData IndexData } func NewHandler(store EventStore, subs *subscription.Manager) *Handler { @@ -32,9 +33,28 @@ func NewHandler(store EventStore, subs *subscription.Manager) *Handler { } } +// SetIndexData sets the addresses for the index page +func (h *Handler) SetIndexData(grpcAddr, httpAddr, wsAddr string) { + h.indexData = IndexData{ + GrpcAddr: grpcAddr, + HttpAddr: httpAddr, + WsAddr: wsAddr, + } +} + func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - if r.Method == "GET" && r.Header.Get("Accept") == "application/nostr+json" { - h.ServeNIP11(w, r) + // Handle GET requests + if r.Method == "GET" { + accept := r.Header.Get("Accept") + + // NIP-11: Relay information document + if accept == "application/nostr+json" { + h.ServeNIP11(w, r) + return + } + + // Serve HTML index for browsers + h.ServeIndex(w, r, h.indexData) return } diff --git a/internal/handler/websocket/index.go b/internal/handler/websocket/index.go new file mode 100644 index 0000000..96bcfd4 --- /dev/null +++ b/internal/handler/websocket/index.go @@ -0,0 +1,194 @@ +package websocket + +import ( + "html/template" + "net/http" +) + +var indexTemplate = template.Must(template.New("index").Parse(` + + + + + Nostr gRPC Relay + + + +
+
+

⚡ Nostr gRPC Relay

+

High-performance relay with multi-protocol support

+
+ +
+
+

Protocols

+ +
+

🔌 gRPC (Native Binary)

+

{{.GrpcAddr}}

+

High-performance binary protocol for applications

+
+ +
+

🌐 Connect (HTTP/JSON)

+

{{.HttpAddr}}/nostr.v1.NostrRelay/*

+

Browser-compatible gRPC over HTTP with JSON

+
+ +
+

🔗 WebSocket (Nostr Protocol)

+

ws://{{.WsAddr}}/

+

Standard Nostr protocol (NIP-01) for all clients

+
+
+ +
+

Supported NIPs

+
+ NIP-01 + NIP-09 + NIP-11 +
+
+ +
+

Features

+
    +
  • Binary-first storage (Protocol Buffers)
  • +
  • SQLite with WAL mode
  • +
  • Event validation (ID & signature)
  • +
  • Real-time subscriptions
  • +
  • Event deletion (NIP-09)
  • +
+
+
+ + +
+ +`)) + +type IndexData struct { + GrpcAddr string + HttpAddr string + WsAddr string +} + +func (h *Handler) ServeIndex(w http.ResponseWriter, r *http.Request, data IndexData) { + w.Header().Set("Content-Type", "text/html; charset=utf-8") + indexTemplate.Execute(w, data) +} -- cgit v1.2.3