mirror of
https://github.com/aljazceru/njump.git
synced 2025-12-17 14:24:27 +01:00
move redirectors, renderRelay and renderProfile out of render.
This commit is contained in:
4
data.go
4
data.go
@@ -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
28
main.go
@@ -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)
|
||||||
|
|||||||
3
pages.go
3
pages.go
@@ -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
29
redirect.go
Normal 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
122
render.go
@@ -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
61
render_profile.go
Normal 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
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user