mirror of
https://github.com/studiokaiji/nostr-webhost.git
synced 2025-12-17 14:54:31 +01:00
207 lines
5.2 KiB
Go
207 lines
5.2 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"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, mode string) {
|
|
ctx := context.Background()
|
|
|
|
allRelays, err := relays.GetAllRelays()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
pool := nostr.NewSimplePool(ctx)
|
|
|
|
r := gin.Default()
|
|
|
|
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を取得
|
|
if hexOrNevent[0:6] == "nevent" {
|
|
_, res, err := nip19.Decode(hexOrNevent)
|
|
if err != nil {
|
|
ctx.String(http.StatusBadRequest, "Invalid nevent")
|
|
return
|
|
}
|
|
|
|
data, ok := res.(nostr.EventPointer)
|
|
if !ok {
|
|
ctx.String(http.StatusBadRequest, "Failed to decode nevent")
|
|
return
|
|
}
|
|
|
|
ids = append(ids, data.ID)
|
|
allRelays = append(allRelays, data.Relays...)
|
|
} else {
|
|
ids = append(ids, hexOrNevent)
|
|
}
|
|
|
|
filter := nostr.Filter{
|
|
Kinds: []int{
|
|
consts.KindWebhostHTML,
|
|
consts.KindWebhostCSS,
|
|
consts.KindWebhostJS,
|
|
consts.KindTextFile,
|
|
},
|
|
IDs: ids,
|
|
}
|
|
if mode == "secure" {
|
|
filter.Authors = []string{subdomainPubKey}
|
|
}
|
|
|
|
// Poolからデータを取得する
|
|
ev := pool.QuerySingle(ctx, allRelays, filter)
|
|
if ev != nil {
|
|
contentType, isTextFile, err := tools.GetContentType(ev)
|
|
if err != nil {
|
|
ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound))
|
|
}
|
|
|
|
content, err := tools.GetResponseContent(ev.Content, isTextFile)
|
|
if err != nil {
|
|
ctx.String(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
|
} else {
|
|
ctx.Data(http.StatusOK, contentType, content)
|
|
}
|
|
} else {
|
|
ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound))
|
|
}
|
|
|
|
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,
|
|
consts.KindReplaceableTextFile,
|
|
},
|
|
Authors: authors,
|
|
Tags: tags,
|
|
})
|
|
if ev != nil {
|
|
contentType, isTextFile, err := tools.GetContentType(ev)
|
|
if err != nil {
|
|
ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound))
|
|
}
|
|
|
|
// contentの変換
|
|
content, err := tools.GetResponseContent(ev.Content, isTextFile)
|
|
if err != nil {
|
|
ctx.String(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
|
} else {
|
|
ctx.Data(http.StatusOK, contentType, 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,
|
|
consts.KindReplaceableTextFile,
|
|
},
|
|
Authors: authors,
|
|
Tags: tags,
|
|
})
|
|
|
|
if ev != nil {
|
|
contentType, isTextFile, err := tools.GetContentType(ev)
|
|
if err != nil {
|
|
ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound))
|
|
}
|
|
|
|
content, err := tools.GetResponseContent(ev.Content, isTextFile)
|
|
if err != nil {
|
|
ctx.String(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
|
} else {
|
|
ctx.Data(http.StatusOK, contentType, content)
|
|
}
|
|
} else {
|
|
ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound))
|
|
}
|
|
|
|
return
|
|
})
|
|
}
|
|
|
|
r.Run(":" + port)
|
|
}
|