move redirectors, renderRelay and renderProfile out of render.

This commit is contained in:
fiatjaf
2023-10-21 14:09:37 -03:00
parent f70b13afe7
commit ebeb9820d8
8 changed files with 123 additions and 149 deletions

View File

@@ -62,7 +62,6 @@ func (ee EnhancedEvent) ModifiedAtStr() string {
type Data struct { type Data struct {
templateId TemplateID templateId TemplateID
typ string
event *nostr.Event event *nostr.Event
relays []string relays []string
npub string npub string
@@ -112,7 +111,6 @@ func grabData(ctx context.Context, code string, isProfileSitemap bool) (*Data, e
parentNevent := "" parentNevent := ""
authorRelays := []string{} authorRelays := []string{}
var content string var content string
var typ string
var templateId TemplateID var templateId TemplateID
eventRelays := []string{} eventRelays := []string{}
@@ -133,7 +131,6 @@ func grabData(ctx context.Context, code string, isProfileSitemap bool) (*Data, e
key := "" key := ""
eventsToFetch := 100 eventsToFetch := 100
if isProfileSitemap { if isProfileSitemap {
typ = "profile_sitemap"
key = "lns:" + event.PubKey key = "lns:" + event.PubKey
eventsToFetch = 50000 eventsToFetch = 50000
} else { } else {
@@ -246,7 +243,6 @@ func grabData(ctx context.Context, code string, isProfileSitemap bool) (*Data, e
return &Data{ return &Data{
templateId: templateId, templateId: templateId,
typ: typ,
event: event, event: event,
relays: eventRelays, relays: eventRelays,
npub: npub, npub: npub,

28
main.go
View File

@@ -3,7 +3,6 @@ package main
import ( import (
"context" "context"
"embed" "embed"
"html"
"net/http" "net/http"
"os" "os"
"text/template" "text/template"
@@ -67,28 +66,6 @@ func main() {
ctx := context.Background() ctx := context.Background()
go updateArchives(ctx) go updateArchives(ctx)
// initialize templates
// use a mapping to expressly link the templates and share them between more kinds/types
templateMapping = map[string]string{
"profile_sitemap": "sitemap.xml",
}
funcMap := template.FuncMap{
"s": func() Settings { return s },
"basicFormatting": func(input string) string { return basicFormatting(input, false, false) },
"previewNotesFormatting": previewNotesFormatting,
"escapeString": html.EscapeString,
"sanitizeXSS": sanitizeXSS,
"trimProtocol": trimProtocol,
"normalizeWebsiteURL": normalizeWebsiteURL,
}
tmpls = template.Must(
template.New("tmpl").
Funcs(funcMap).
ParseFS(templates, "templates/*"),
)
// routes // routes
mux := http.NewServeMux() mux := http.NewServeMux()
mux.Handle("/njump/static/", http.StripPrefix("/njump/", http.FileServer(http.FS(static)))) mux.Handle("/njump/static/", http.StripPrefix("/njump/", http.FileServer(http.FS(static))))
@@ -101,7 +78,10 @@ func main() {
mux.HandleFunc("/njump/proxy/", proxy) mux.HandleFunc("/njump/proxy/", proxy)
mux.HandleFunc("/favicon.ico", renderFavicon) mux.HandleFunc("/favicon.ico", renderFavicon)
mux.HandleFunc("/robots.txt", renderRobots) mux.HandleFunc("/robots.txt", renderRobots)
mux.HandleFunc("/try", renderTry) mux.HandleFunc("/r/", renderRelayPage)
mux.HandleFunc("/try", redirectFromFormSubmit)
mux.HandleFunc("/e/", redirectFromESlash)
mux.HandleFunc("/p/", redirectFromPSlash)
mux.HandleFunc("/", render) mux.HandleFunc("/", render)
log.Print("listening at http://0.0.0.0:" + s.Port) log.Print("listening at http://0.0.0.0:" + s.Port)

View File

@@ -14,8 +14,7 @@ import (
type TemplateID int type TemplateID int
const ( const (
Profile TemplateID = iota Note TemplateID = iota
Note
LongForm LongForm
TelegramInstantView TelegramInstantView
Other Other

29
redirect.go Normal file
View File

@@ -0,0 +1,29 @@
package main
import (
"net/http"
"github.com/nbd-wtf/go-nostr/nip19"
)
func redirectFromFormSubmit(w http.ResponseWriter, r *http.Request) {
err := r.ParseForm()
if err != nil {
http.Error(w, "failed to parse form data", http.StatusBadRequest)
return
}
nip19entity := r.FormValue("nip19entity")
http.Redirect(w, r, "/"+nip19entity, http.StatusFound)
}
func redirectFromPSlash(w http.ResponseWriter, r *http.Request) {
code, _ := nip19.EncodePublicKey(r.URL.Path[3:])
http.Redirect(w, r, "/"+code, http.StatusFound)
return
}
func redirectFromESlash(w http.ResponseWriter, r *http.Request) {
code, _ := nip19.EncodeEvent(r.URL.Path[3:], []string{}, "")
http.Redirect(w, r, "/"+code, http.StatusFound)
return
}

122
render.go
View File

@@ -18,35 +18,25 @@ func render(w http.ResponseWriter, r *http.Request) {
fmt.Println(r.URL.Path, "#/", r.Header.Get("user-agent")) fmt.Println(r.URL.Path, "#/", r.Header.Get("user-agent"))
w.Header().Set("Content-Type", "text/html") w.Header().Set("Content-Type", "text/html")
code := r.URL.Path[1:] code := r.URL.Path[1:] // hopefully a nip19 code
isProfileSitemap := false
if strings.HasPrefix(code, "e/") { // it's the homepage
code, _ = nip19.EncodeEvent(code[2:], []string{}, "") if code == "" {
} else if strings.HasPrefix(code, "p/") { renderHomepage(w, r)
if urlSuffixMatcher.MatchString(code) {
// it's a nip05
code = code[2:]
} else {
// it's a hex pubkey
code, _ = nip19.EncodePublicKey(code[2:])
}
} else if strings.HasPrefix(code, "r/") {
hostname := code[2:]
if strings.HasPrefix(hostname, "wss:/") || strings.HasPrefix(hostname, "ws:/") {
hostname = trimProtocol(hostname)
http.Redirect(w, r, "/r/"+hostname, http.StatusFound)
} else {
renderRelayPage(w, r)
}
return return
} else if strings.HasPrefix(code, "nostr:") {
http.Redirect(w, r, "/"+code[6:], http.StatusFound)
} else if strings.HasPrefix(code, "npub") && strings.HasSuffix(code, ".xml") {
isProfileSitemap = true
code = code[:len(code)-4]
} }
if strings.HasPrefix(code, "nostr:") {
// remove the "nostr:" prefix
http.Redirect(w, r, "/"+code[6:], http.StatusFound)
return
} else if strings.HasPrefix(code, "npub") || strings.HasPrefix(code, "nprofile") {
// it's a profile
renderProfile(w, r, code)
return
}
// force note1 to become nevent1
if strings.HasPrefix(code, "note1") { if strings.HasPrefix(code, "note1") {
_, redirectHex, err := nip19.Decode(code) _, redirectHex, err := nip19.Decode(code)
if err != nil { if err != nil {
@@ -58,15 +48,10 @@ func render(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/"+redirectNevent, http.StatusFound) http.Redirect(w, r, "/"+redirectNevent, http.StatusFound)
} }
if code == "" {
renderHomepage(w, r)
return
}
host := r.Header.Get("X-Forwarded-Host") host := r.Header.Get("X-Forwarded-Host")
style := getPreviewStyle(r) style := getPreviewStyle(r)
data, err := grabData(r.Context(), code, isProfileSitemap) data, err := grabData(r.Context(), code, false)
if err != nil { if err != nil {
w.Header().Set("Cache-Control", "max-age=60") w.Header().Set("Cache-Control", "max-age=60")
http.Error(w, "error fetching event: "+err.Error(), 404) http.Error(w, "error fetching event: "+err.Error(), 404)
@@ -194,20 +179,18 @@ func render(w http.ResponseWriter, r *http.Request) {
data.content = strings.ReplaceAll(data.content, placeholderTag, "nostr:"+nreplace) data.content = strings.ReplaceAll(data.content, placeholderTag, "nostr:"+nreplace)
} }
if data.event.Kind == 30023 || data.event.Kind == 30024 { if data.event.Kind == 30023 || data.event.Kind == 30024 {
data.content = mdToHTML(data.content, data.typ == "telegram_instant_view") data.content = mdToHTML(data.content, data.templateId == TelegramInstantView)
} else { } else {
// first we run basicFormatting, which turns URLs into their appropriate HTML tags // first we run basicFormatting, which turns URLs into their appropriate HTML tags
data.content = basicFormatting(html.EscapeString(data.content), true, false) data.content = basicFormatting(html.EscapeString(data.content), true, false)
// then we render quotes as HTML, which will also apply basicFormatting to all the internal quotes // then we render quotes as HTML, which will also apply basicFormatting to all the internal quotes
data.content = renderQuotesAsHTML(r.Context(), data.content, data.typ == "telegram_instant_view") data.content = renderQuotesAsHTML(r.Context(), data.content, data.templateId == TelegramInstantView)
// we must do this because inside <blockquotes> we must treat <img>s different when telegram_instant_view // we must do this because inside <blockquotes> we must treat <img>s different when telegram_instant_view
} }
if data.typ == "telegram_instant_view" { if data.templateId == TelegramInstantView {
w.Header().Set("Cache-Control", "no-cache") w.Header().Set("Cache-Control", "no-cache")
} else if strings.Contains(data.typ, "profile") && len(data.renderableLastNotes) != 0 { } else if len(data.content) != 0 {
w.Header().Set("Cache-Control", "max-age=3600")
} else if !strings.Contains(data.typ, "profile") && len(data.content) != 0 {
w.Header().Set("Cache-Control", "max-age=604800") w.Header().Set("Cache-Control", "max-age=604800")
} else { } else {
w.Header().Set("Cache-Control", "max-age=60") w.Header().Set("Cache-Control", "max-age=60")
@@ -231,49 +214,6 @@ func render(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Link", "<"+oembed+"&format=xml>; rel=\"alternate\"; type=\"text/xml+oembed\"") w.Header().Add("Link", "<"+oembed+"&format=xml>; rel=\"alternate\"; type=\"text/xml+oembed\"")
} }
if currentTemplate, ok := templateMapping[data.typ]; ok {
// template stuff
params := map[string]any{
"style": style,
"createdAt": data.createdAt,
"modifiedAt": data.modifiedAt,
"clients": generateClientList(code, data.event),
"type": data.typ,
"title": title,
"titleizedContent": titleizedContent,
"twitterTitle": twitterTitle,
"npub": data.npub,
"npubShort": data.npubShort,
"nevent": data.nevent,
"naddr": data.naddr,
"metadata": data.metadata,
"authorLong": data.authorLong,
"subject": subject,
"description": description,
"summary": summary,
"event": data.event,
"eventJSON": string(eventJSON),
"content": data.content,
"textImageURL": textImageURL,
"videoType": data.videoType,
"image": data.image,
"video": data.video,
"proxy": "https://" + host + "/njump/proxy?src=",
"kindDescription": data.kindDescription,
"kindNIP": data.kindNIP,
"lastNotes": data.renderableLastNotes,
"seenOn": data.relays,
"parentNevent": data.parentNevent,
"authorRelays": data.authorRelays,
"oembed": oembed,
}
if err := tmpls.ExecuteTemplate(w, currentTemplate, params); err != nil {
log.Error().Err(err).Msg("error rendering")
return
}
}
// migrating to templ // migrating to templ
switch data.templateId { switch data.templateId {
case TelegramInstantView: case TelegramInstantView:
@@ -328,28 +268,6 @@ func render(w http.ResponseWriter, r *http.Request) {
Video: data.video, Video: data.video,
VideoType: data.videoType, VideoType: data.videoType,
}) })
case Profile:
err = ProfileTemplate.Render(w, &ProfilePage{
HeadCommonPartial: HeadCommonPartial{IsProfile: true},
DetailsPartial: DetailsPartial{
HideDetails: true,
CreatedAt: data.createdAt,
KindDescription: data.kindDescription,
KindNIP: data.kindNIP,
EventJSON: string(eventJSON),
Kind: data.event.Kind,
},
ClientsPartial: ClientsPartial{
Clients: generateClientList(code, data.event),
},
Metadata: data.metadata,
NormalizedAuthorWebsiteURL: normalizeWebsiteURL(data.metadata.Website),
RenderedAuthorAboutText: template.HTML(basicFormatting(html.EscapeString(data.metadata.About), false, false)),
Npub: data.npub,
AuthorRelays: data.authorRelays,
LastNotes: data.renderableLastNotes,
})
case Other: case Other:
err = OtherTemplate.Render(w, &OtherPage{ err = OtherTemplate.Render(w, &OtherPage{
HeadCommonPartial: HeadCommonPartial{IsProfile: false}, HeadCommonPartial: HeadCommonPartial{IsProfile: false},

61
render_profile.go Normal file
View File

@@ -0,0 +1,61 @@
package main
import (
"encoding/json"
"fmt"
"html"
"html/template"
"net/http"
"strings"
)
func renderProfile(w http.ResponseWriter, r *http.Request, code string) {
fmt.Println(r.URL.Path, "@.", r.Header.Get("user-agent"))
w.Header().Set("Content-Type", "text/html")
isProfileSitemap := false
if strings.HasSuffix(code, ".xml") {
isProfileSitemap = true
code = code[:len(code)-4]
}
data, err := grabData(r.Context(), code, isProfileSitemap)
if err != nil {
w.Header().Set("Cache-Control", "max-age=60")
http.Error(w, "error fetching event: "+err.Error(), 404)
return
}
if len(data.renderableLastNotes) != 0 {
w.Header().Set("Cache-Control", "max-age=3600")
}
// pretty JSON
eventJSON, _ := json.MarshalIndent(data.event, "", " ")
err = ProfileTemplate.Render(w, &ProfilePage{
HeadCommonPartial: HeadCommonPartial{IsProfile: true},
DetailsPartial: DetailsPartial{
HideDetails: true,
CreatedAt: data.createdAt,
KindDescription: data.kindDescription,
KindNIP: data.kindNIP,
EventJSON: string(eventJSON),
Kind: data.event.Kind,
},
ClientsPartial: ClientsPartial{
Clients: generateClientList(code, data.event),
},
Metadata: data.metadata,
NormalizedAuthorWebsiteURL: normalizeWebsiteURL(data.metadata.Website),
RenderedAuthorAboutText: template.HTML(basicFormatting(html.EscapeString(data.metadata.About), false, false)),
Npub: data.npub,
AuthorRelays: data.authorRelays,
LastNotes: data.renderableLastNotes,
})
if err != nil {
log.Error().Err(err).Msg("error rendering tmpl")
}
return
}

View File

@@ -11,8 +11,14 @@ import (
) )
func renderRelayPage(w http.ResponseWriter, r *http.Request) { func renderRelayPage(w http.ResponseWriter, r *http.Request) {
code := r.URL.Path[1:] hostname := r.URL.Path[3:]
hostname := code[2:]
if strings.HasPrefix(hostname, "wss:/") || strings.HasPrefix(hostname, "ws:/") {
hostname = trimProtocol(hostname)
http.Redirect(w, r, "/r/"+hostname, http.StatusFound)
return
}
isSitemap := false isSitemap := false
numResults := 1000 numResults := 1000

View File

@@ -1,15 +0,0 @@
package main
import (
"net/http"
)
func renderTry(w http.ResponseWriter, r *http.Request) {
err := r.ParseForm()
if err != nil {
http.Error(w, "Failed to parse form data", http.StatusBadRequest)
return
}
nip19entity := r.FormValue("nip19entity")
http.Redirect(w, r, "/"+nip19entity, http.StatusFound)
}