Files
njump/render_profile.go
2025-02-24 21:30:19 -03:00

141 lines
3.8 KiB
Go

package main
import (
"context"
"html"
"html/template"
"net/http"
"strings"
"time"
)
func renderProfile(ctx context.Context, r *http.Request, w http.ResponseWriter, code string) {
isEmbed := r.URL.Query().Get("embed") != ""
isSitemap := false
if strings.HasSuffix(code, ".xml") {
code = code[:len(code)-4]
isSitemap = true
}
isRSS := false
if strings.HasSuffix(code, ".rss") {
code = code[:len(code)-4]
isRSS = true
}
profile, err := sys.FetchProfileFromInput(ctx, code)
if err != nil {
log.Warn().Err(err).Str("code", code).Msg("error fetching profile on render_profile")
w.Header().Set("Cache-Control", "max-age=60")
w.WriteHeader(http.StatusNotFound)
errorTemplate(ErrorPageParams{Errors: err.Error(), Clients: generateClientList(999999, code)}).Render(ctx, w)
return
} else if profile.Event != nil {
internal.scheduleEventExpiration(profile.Event.ID)
}
// banned or unallowed conditions
if banned, reason := internal.isBannedPubkey(profile.PubKey); banned {
w.Header().Set("Cache-Control", "max-age=60")
log.Warn().Err(err).Str("code", code).Str("reason", reason).Msg("pubkey banned")
http.Error(w, "pubkey banned", http.StatusNotFound)
return
}
if isMaliciousBridged(profile) {
http.Error(w, "profile is malicious", http.StatusNotFound)
return
}
if is, _ := isExplicitContent(ctx, profile.Picture); is {
http.Error(w, "profile is not allowed", http.StatusNotFound)
return
}
var createdAt string
if profile.Event != nil {
createdAt = profile.Event.CreatedAt.Time().Format("2006-01-02T15:04:05Z07:00")
}
var lastNotes []EnhancedEvent
var cacheControl string = "max-age=86400"
if !isEmbed {
var justFetched bool
lastNotes, justFetched = authorLastNotes(ctx, profile.PubKey)
if justFetched && profile.Event != nil {
cacheControl = "only-if-cached"
}
}
w.Header().Set("Cache-Control", cacheControl)
if isSitemap {
w.Header().Add("content-type", "text/xml")
w.Write([]byte(XML_HEADER))
err = SitemapTemplate.Render(w, &SitemapPage{
Host: s.Domain,
ModifiedAt: createdAt,
LastNotes: lastNotes,
})
} else if isRSS {
w.Header().Add("content-type", "text/xml")
w.Write([]byte(XML_HEADER))
err = RSSTemplate.Render(w, &RSSPage{
Host: s.Domain,
ModifiedAt: createdAt,
Metadata: profile,
LastNotes: lastNotes,
})
} else {
w.Header().Add("content-type", "text/html")
nprofile := profile.Nprofile(ctx, sys, 2)
params := ProfilePageParams{
HeadParams: HeadParams{IsProfile: true},
Details: DetailsParams{
HideDetails: true,
CreatedAt: createdAt,
KindDescription: kindNames[0],
KindNIP: kindNIPs[0],
EventJSON: toJSONHTML(profile.Event),
Kind: 0,
Metadata: profile,
},
Metadata: profile,
NormalizedAuthorWebsiteURL: normalizeWebsiteURL(profile.Website),
RenderedAuthorAboutText: template.HTML(basicFormatting(html.EscapeString(profile.About), false, false, false)),
Nprofile: nprofile,
AuthorRelays: relaysPretty(ctx, profile.PubKey),
LastNotes: lastNotes,
Clients: generateClientList(0, nprofile,
func(c ClientReference, s string) string {
if c == nostrudel {
s = strings.Replace(s, "/n/", "/u/", 1)
}
if c == primalWeb {
s = strings.Replace(
strings.Replace(s, "/e/", "/p/", 1),
nprofile, profile.Npub(), 1)
}
return s
},
),
}
// give this global context a timeout because it may used inside the template to validate the nip05 address
ctx, cancel := context.WithTimeout(ctx, time.Second*3)
defer cancel()
if isEmbed {
err = embeddedProfileTemplate(params).Render(ctx, w)
} else {
err = profileTemplate(params).Render(ctx, w)
}
}
if err != nil {
log.Warn().Err(err).Msg("error rendering tmpl")
}
return
}