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) }