From 8c751548543a9b4b4dfd7fc6de0a86e13510ad27 Mon Sep 17 00:00:00 2001 From: studiokaiji Date: Fri, 20 Oct 2023 04:24:10 +0900 Subject: [PATCH 1/6] =?UTF-8?q?getContentType=E3=82=92=E4=BD=9C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hostr/cmd/tools/getContentType.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 hostr/cmd/tools/getContentType.go diff --git a/hostr/cmd/tools/getContentType.go b/hostr/cmd/tools/getContentType.go new file mode 100644 index 0000000..b785ad0 --- /dev/null +++ b/hostr/cmd/tools/getContentType.go @@ -0,0 +1,20 @@ +package tools + +import ( + "fmt" + + "github.com/studiokaiji/nostr-webhost/hostr/cmd/consts" +) + +func GetContentType(kind int) (string, error) { + switch kind { + case consts.KindWebhostHTML | consts.KindWebhostReplaceableHTML: + return "text/html; charset=utf-8", nil + case consts.KindWebhostCSS | consts.KindWebhostReplaceableCSS: + return "text/css; charset=utf-8", nil + case consts.KindWebhostJS | consts.KindWebhostReplaceableJS: + return "text/javascript; charset=utf-8", nil + default: + return "", fmt.Errorf("Invalid Kind") + } +} From e18108a518f686e1dfb2d4aba3b53b7cda5796bb Mon Sep 17 00:00:00 2001 From: studiokaiji Date: Fri, 20 Oct 2023 04:24:27 +0900 Subject: [PATCH 2/6] =?UTF-8?q?ResolvePubKey=E3=82=92=E4=BD=9C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hostr/cmd/tools/resolvePubKey.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 hostr/cmd/tools/resolvePubKey.go diff --git a/hostr/cmd/tools/resolvePubKey.go b/hostr/cmd/tools/resolvePubKey.go new file mode 100644 index 0000000..e3732dd --- /dev/null +++ b/hostr/cmd/tools/resolvePubKey.go @@ -0,0 +1,24 @@ +package tools + +import ( + "fmt" + + "github.com/nbd-wtf/go-nostr/nip19" +) + +func ResolvePubKey(npubOrHex string) (string, error) { + // npubから始まる場合はデコードする + if npubOrHex[0:4] == "npub" { + _, v, err := nip19.Decode(npubOrHex) + if err != nil { + return "", fmt.Errorf("Invalid npub") + } + return v.(string), nil + } else { + _, err := nip19.EncodePublicKey(npubOrHex) + if err != nil { + return "", fmt.Errorf("Invalid pubkey") + } + } + return npubOrHex, nil +} From f62723ed40fbc179a644aa3ac0944ba093f60685 Mon Sep 17 00:00:00 2001 From: studiokaiji Date: Fri, 20 Oct 2023 04:24:51 +0900 Subject: [PATCH 3/6] =?UTF-8?q?start=E3=82=B3=E3=83=9E=E3=83=B3=E3=83=89?= =?UTF-8?q?=E3=81=ABmode=20flag=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hostr/main.go | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/hostr/main.go b/hostr/main.go index 55691d6..24f5c43 100644 --- a/hostr/main.go +++ b/hostr/main.go @@ -17,10 +17,6 @@ import ( var cuteOstrich string func main() { - var ( - port string - ) - app := &cli.App{ Commands: []*cli.Command{ { @@ -143,15 +139,28 @@ func main() { Usage: "🕺 Wake up web server", Flags: []cli.Flag{ &cli.StringFlag{ - Name: "port", - Aliases: []string{"p"}, - Value: "3000", - Usage: "Web server port", - Destination: &port, + Name: "port", + Aliases: []string{"p"}, + Value: "3000", + Usage: "Web server port", + }, + &cli.StringFlag{ + Name: "mode", + Aliases: []string{"m"}, + Value: "normal", + Usage: "🧪 Experimental: Enabled subdomain-based access in replaceable events.", + Action: func(ctx *cli.Context, v string) error { + if v != "normal" && v != "hybrid" && v != "secure" { + return fmt.Errorf("Invalid mode flag. Must be 'normal', 'hybrid', or 'secure'.") + } + return nil + }, }, }, Action: func(ctx *cli.Context) error { - server.Start(port) + port := ctx.String("port") + mode := ctx.String("mode") + server.Start(port, mode) return nil }, }, From 4de6254ec79c56d126defc7a1c9068f44549c0b2 Mon Sep 17 00:00:00 2001 From: studiokaiji Date: Fri, 20 Oct 2023 04:26:02 +0900 Subject: [PATCH 4/6] =?UTF-8?q?subdomain=E3=81=A7=E3=81=AE=E3=83=AB?= =?UTF-8?q?=E3=83=BC=E3=83=86=E3=82=A3=E3=83=B3=E3=82=B0=E3=82=92=E5=AE=9F?= =?UTF-8?q?=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hostr/cmd/server/server.go | 198 +++++++++++++++++++++---------------- 1 file changed, 112 insertions(+), 86 deletions(-) diff --git a/hostr/cmd/server/server.go b/hostr/cmd/server/server.go index 4dc57ef..aa2e6e0 100644 --- a/hostr/cmd/server/server.go +++ b/hostr/cmd/server/server.go @@ -2,17 +2,18 @@ package server import ( "context" - "encoding/base64" "net/http" + "strings" "github.com/gin-gonic/gin" "github.com/nbd-wtf/go-nostr" "github.com/nbd-wtf/go-nostr/nip19" "github.com/studiokaiji/nostr-webhost/hostr/cmd/consts" "github.com/studiokaiji/nostr-webhost/hostr/cmd/relays" + "github.com/studiokaiji/nostr-webhost/hostr/cmd/tools" ) -func Start(port string) { +func Start(port string, mode string) { ctx := context.Background() allRelays, err := relays.GetAllRelays() @@ -27,6 +28,19 @@ func Start(port string) { r.GET("/e/:hex_or_nevent", func(ctx *gin.Context) { hexOrNevent := ctx.Param("hex_or_nevent") + subdomainPubKey := "" + + if mode == "secure" { + // modeがsecureの場合、サブドメインにnpubが含まれていないルーティングは許可しない + host := ctx.Request.Host + subdomain := strings.Split(host, ".")[0] + subdomainPubKey, err = tools.ResolvePubKey(subdomain) + if err != nil { + ctx.String(http.StatusBadRequest, "Routing without npub in the subdomain is not allowed") + return + } + } + ids := []string{} // neventからIDを取得 @@ -49,99 +63,22 @@ func Start(port string) { ids = append(ids, hexOrNevent) } - // Poolからデータを取得する - ev := pool.QuerySingle(ctx, allRelays, nostr.Filter{ + filter := nostr.Filter{ Kinds: []int{consts.KindWebhostHTML, consts.KindWebhostCSS, consts.KindWebhostJS, consts.KindWebhostPicture}, IDs: ids, - }) - if ev != nil { - switch ev.Kind { - case consts.KindWebhostHTML: - ctx.Data(http.StatusOK, "text/html; charset=utf-8", []byte(ev.Content)) - case consts.KindWebhostCSS: - ctx.Data(http.StatusOK, "text/css; charset=utf-8", []byte(ev.Content)) - case consts.KindWebhostJS: - ctx.Data(http.StatusOK, "text/javascript; charset=utf-8", []byte(ev.Content)) - case consts.KindWebhostPicture: - { - eTag := ev.Tags.GetFirst([]string{"e"}) - mTag := ev.Tags.GetFirst([]string{"m"}) - - if eTag == nil || mTag == nil { - ctx.String(http.StatusBadRequest, http.StatusText((http.StatusBadRequest))) - return - } - - evData := pool.QuerySingle(ctx, allRelays, nostr.Filter{ - IDs: []string{eTag.Value()}, - }) - - if evData == nil { - ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound)) - return - } - - data, err := base64.StdEncoding.DecodeString(evData.Content) - if err != nil { - ctx.String(http.StatusBadRequest, http.StatusText((http.StatusBadRequest))) - return - } - - ctx.Data(http.StatusOK, mTag.Value(), data) - } - default: - ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound)) - } - } else { - ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound)) } - - return - }) - - // Replaceable Event (NIP-33) - r.GET("/p/:pubKey/d/*dTag", func(ctx *gin.Context) { - // pubKeyを取得しFilterに追加 - pubKey := ctx.Param("pubKey") - // npubから始まる場合はデコードする - if pubKey[0:4] == "npub" { - _, v, err := nip19.Decode(pubKey) - if err != nil { - ctx.String(http.StatusBadRequest, "Invalid npub") - return - } - pubKey = v.(string) + if mode == "secure" { + filter.Authors = []string{subdomainPubKey} } - authors := []string{pubKey} - - // dTagを取得しFilterに追加 - // dTagの最初は`/`ではじまるのでそれをslice - dTag := ctx.Param("dTag")[1:] - - tags := nostr.TagMap{} - tags["d"] = []string{dTag} // Poolからデータを取得する - ev := pool.QuerySingle(ctx, allRelays, nostr.Filter{ - Kinds: []int{ - consts.KindWebhostReplaceableHTML, - consts.KindWebhostReplaceableCSS, - consts.KindWebhostReplaceableJS, - }, - Authors: authors, - Tags: tags, - }) + ev := pool.QuerySingle(ctx, allRelays, filter) if ev != nil { - switch ev.Kind { - case consts.KindWebhostReplaceableHTML: - ctx.Data(http.StatusOK, "text/html; charset=utf-8", []byte(ev.Content)) - case consts.KindWebhostReplaceableCSS: - ctx.Data(http.StatusOK, "text/css; charset=utf-8", []byte(ev.Content)) - case consts.KindWebhostReplaceableJS: - ctx.Data(http.StatusOK, "text/javascript; charset=utf-8", []byte(ev.Content)) - default: + contentType, err := tools.GetContentType(ev.Kind) + if err != nil { ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound)) } + ctx.Data(http.StatusOK, contentType, []byte(ev.Content)) } else { ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound)) } @@ -149,5 +86,94 @@ func Start(port string) { return }) + if mode != "secure" { + r.GET("/p/:pubKey/d/*dTag", func(ctx *gin.Context) { + // pubKeyを取得しFilterに追加 + pubKey := ctx.Param("pubKey") + pubKey, err := tools.ResolvePubKey(pubKey) + if err != nil { + ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound)) + return + } + + authors := []string{pubKey} + + // dTagを取得しFilterに追加 + // dTagの最初は`/`ではじまるのでそれをslice + dTag := ctx.Param("dTag")[1:] + + tags := nostr.TagMap{} + tags["d"] = []string{dTag} + + // Poolからデータを取得する + ev := pool.QuerySingle(ctx, allRelays, nostr.Filter{ + Kinds: []int{ + consts.KindWebhostReplaceableHTML, + consts.KindWebhostReplaceableCSS, + consts.KindWebhostReplaceableJS, + }, + Authors: authors, + Tags: tags, + }) + if ev != nil { + contentType, err := tools.GetContentType(ev.Kind) + if err != nil { + ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound)) + } + ctx.Data(http.StatusOK, contentType, []byte(ev.Content)) + } else { + ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound)) + } + + return + }) + + } + + if mode != "normal" { + r.GET("/d/*dTag", func(ctx *gin.Context) { + host := ctx.Request.Host + subdomain := strings.Split(host, ".")[0] + + // subdomainからpubKeyを取得しFilterに追加 + pubKey, err := tools.ResolvePubKey(subdomain) + if err != nil { + ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound)) + return + } + + authors := []string{pubKey} + + // dTagを取得しFilterに追加 + // dTagの最初は`/`ではじまるのでそれをslice + dTag := ctx.Param("dTag")[1:] + + tags := nostr.TagMap{} + tags["d"] = []string{dTag} + + // Poolからデータを取得する + ev := pool.QuerySingle(ctx, allRelays, nostr.Filter{ + Kinds: []int{ + consts.KindWebhostReplaceableHTML, + consts.KindWebhostReplaceableCSS, + consts.KindWebhostReplaceableJS, + }, + Authors: authors, + Tags: tags, + }) + if ev != nil { + contentType, err := tools.GetContentType(ev.Kind) + if err != nil { + ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound)) + } + ctx.Data(http.StatusOK, contentType, []byte(ev.Content)) + } else { + ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound)) + } + + return + }) + } + r.Run(":" + port) } From 240d5ded390f51306e443a09a8946d023de34a3d Mon Sep 17 00:00:00 2001 From: studiokaiji Date: Mon, 23 Oct 2023 08:04:53 +0900 Subject: [PATCH 5/6] =?UTF-8?q?ContentType=E3=81=8C=E6=AD=A3=E5=B8=B8?= =?UTF-8?q?=E3=81=AB=E5=A4=89=E3=82=8F=E3=82=89=E3=81=AA=E3=81=84=E5=95=8F?= =?UTF-8?q?=E9=A1=8C=E3=82=92=E8=A7=A3=E6=B1=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hostr/cmd/tools/getContentType.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/hostr/cmd/tools/getContentType.go b/hostr/cmd/tools/getContentType.go index b785ad0..4d25943 100644 --- a/hostr/cmd/tools/getContentType.go +++ b/hostr/cmd/tools/getContentType.go @@ -7,14 +7,13 @@ import ( ) func GetContentType(kind int) (string, error) { - switch kind { - case consts.KindWebhostHTML | consts.KindWebhostReplaceableHTML: + if kind == consts.KindWebhostHTML || kind == consts.KindWebhostReplaceableHTML { return "text/html; charset=utf-8", nil - case consts.KindWebhostCSS | consts.KindWebhostReplaceableCSS: + } else if kind == consts.KindWebhostCSS || kind == consts.KindWebhostReplaceableCSS { return "text/css; charset=utf-8", nil - case consts.KindWebhostJS | consts.KindWebhostReplaceableJS: + } else if kind == consts.KindWebhostJS || kind == consts.KindWebhostReplaceableJS { return "text/javascript; charset=utf-8", nil - default: + } else { return "", fmt.Errorf("Invalid Kind") } } From bb283c72ce96bc68c4f56b8b0d80ce04be600a1b Mon Sep 17 00:00:00 2001 From: studiokaiji Date: Mon, 23 Oct 2023 08:05:23 +0900 Subject: [PATCH 6/6] =?UTF-8?q?kind=E3=81=8C=E8=A6=8B=E3=81=A4=E3=81=8B?= =?UTF-8?q?=E3=82=89=E3=81=AA=E3=81=84=E5=A0=B4=E5=90=88=E3=81=AB=E8=A4=87?= =?UTF-8?q?=E6=95=B0=E3=81=AE=E3=83=AC=E3=82=B9=E3=83=9D=E3=83=B3=E3=82=B9?= =?UTF-8?q?=E3=81=8C=E8=B2=B7=E3=81=88=E3=82=8B=E5=95=8F=E9=A1=8C=E3=82=92?= =?UTF-8?q?=E8=A7=A3=E6=B1=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hostr/cmd/server/server.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hostr/cmd/server/server.go b/hostr/cmd/server/server.go index aa2e6e0..61043d6 100644 --- a/hostr/cmd/server/server.go +++ b/hostr/cmd/server/server.go @@ -77,8 +77,9 @@ func Start(port string, mode string) { contentType, err := tools.GetContentType(ev.Kind) if err != nil { ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound)) + } else { + ctx.Data(http.StatusOK, contentType, []byte(ev.Content)) } - ctx.Data(http.StatusOK, contentType, []byte(ev.Content)) } else { ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound)) }