Browse Source

bug-fixes

master
Silberengel 4 weeks ago
parent
commit
963e1c940e
  1. 1
      cmd/server/main.go
  2. 89
      internal/generator/html.go
  3. 12
      static/css/main.css
  4. 23
      templates/components.html
  5. 2
      templates/ebooks.html

1
cmd/server/main.go

@ -62,6 +62,7 @@ func main() { @@ -62,6 +62,7 @@ func main() {
cfg.SEO.SiteName,
cfg.SEO.SiteURL,
cfg.SEO.DefaultImage,
nostrClient,
)
if err != nil {
log.Fatalf("Failed to initialize HTML generator: %v", err)

89
internal/generator/html.go

@ -2,10 +2,12 @@ package generator @@ -2,10 +2,12 @@ package generator
import (
"bytes"
"context"
"encoding/json"
"fmt"
"html/template"
"path/filepath"
"sync"
"time"
"gitcitadel-online/internal/asciidoc"
@ -41,6 +43,7 @@ type HTMLGenerator struct { @@ -41,6 +43,7 @@ type HTMLGenerator struct {
siteName string
siteURL string
defaultImage string
nostrClient *nostr.Client
}
// PageData represents data for a wiki page
@ -63,6 +66,7 @@ type PageData struct { @@ -63,6 +66,7 @@ type PageData struct {
Content template.HTML
Summary string
TableOfContents template.HTML
Profiles map[string]*nostr.Profile // Map of pubkey -> profile
}
// WikiPageInfo represents info about a wiki page for navigations
@ -116,7 +120,7 @@ type UserBadgeInfo struct { @@ -116,7 +120,7 @@ type UserBadgeInfo struct {
}
// NewHTMLGenerator creates a new HTML generator
func NewHTMLGenerator(templateDir string, linkBaseURL, siteName, siteURL, defaultImage string) (*HTMLGenerator, error) {
func NewHTMLGenerator(templateDir string, linkBaseURL, siteName, siteURL, defaultImage string, nostrClient *nostr.Client) (*HTMLGenerator, error) {
tmpl := template.New("base").Funcs(getTemplateFuncs())
// Load all templates
@ -148,9 +152,49 @@ func NewHTMLGenerator(templateDir string, linkBaseURL, siteName, siteURL, defaul @@ -148,9 +152,49 @@ func NewHTMLGenerator(templateDir string, linkBaseURL, siteName, siteURL, defaul
siteName: siteName,
siteURL: siteURL,
defaultImage: defaultImage,
nostrClient: nostrClient,
}, nil
}
// fetchProfilesBatch fetches profiles for multiple pubkeys in parallel
func (g *HTMLGenerator) fetchProfilesBatch(ctx context.Context, pubkeys []string) map[string]*nostr.Profile {
if g.nostrClient == nil || len(pubkeys) == 0 {
return make(map[string]*nostr.Profile)
}
profiles := make(map[string]*nostr.Profile)
var mu sync.Mutex
var wg sync.WaitGroup
// Create a context with timeout for profile fetching
profileCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
for _, pubkey := range pubkeys {
if pubkey == "" {
continue
}
wg.Add(1)
go func(pk string) {
defer wg.Done()
// Convert pubkey to npub for fetching
npub, err := nostr.PubkeyToNpub(pk)
if err != nil {
return
}
profile, err := g.nostrClient.FetchProfile(profileCtx, npub)
if err == nil && profile != nil {
mu.Lock()
profiles[pk] = profile
mu.Unlock()
}
}(pubkey)
}
wg.Wait()
return profiles
}
// ProcessAsciiDoc processes AsciiDoc content to HTML
func (g *HTMLGenerator) ProcessAsciiDoc(content string) (string, error) {
return g.asciidocProc.Process(content)
@ -158,6 +202,18 @@ func (g *HTMLGenerator) ProcessAsciiDoc(content string) (string, error) { @@ -158,6 +202,18 @@ func (g *HTMLGenerator) ProcessAsciiDoc(content string) (string, error) {
// GenerateLandingPage generates the static landing page
func (g *HTMLGenerator) GenerateLandingPage(wikiPages []WikiPageInfo, feedItems []FeedItemInfo) (string, error) {
// Collect pubkeys from feed items
pubkeys := make([]string, 0, len(feedItems))
for _, item := range feedItems {
if item.Author != "" {
pubkeys = append(pubkeys, item.Author)
}
}
// Fetch profiles for feed authors
ctx := context.Background()
profiles := g.fetchProfilesBatch(ctx, pubkeys)
data := PageData{
Title: "Home",
Description: "Welcome to " + g.siteName,
@ -169,6 +225,7 @@ func (g *HTMLGenerator) GenerateLandingPage(wikiPages []WikiPageInfo, feedItems @@ -169,6 +225,7 @@ func (g *HTMLGenerator) GenerateLandingPage(wikiPages []WikiPageInfo, feedItems
CurrentYear: time.Now().Year(),
WikiPages: wikiPages,
FeedItems: feedItems,
Profiles: profiles,
}
return g.renderTemplate("landing.html", data)
@ -202,6 +259,7 @@ func (g *HTMLGenerator) GenerateWikiPage(wiki *nostr.WikiEvent, wikiPages []Wiki @@ -202,6 +259,7 @@ func (g *HTMLGenerator) GenerateWikiPage(wiki *nostr.WikiEvent, wikiPages []Wiki
FeedItems: []FeedItemInfo{}, // Empty - feed only on landing page
Content: template.HTML(htmlContent),
Summary: wiki.Summary,
Profiles: make(map[string]*nostr.Profile), // Empty profiles for wiki pages
}
// Add structured data for article
@ -219,8 +277,12 @@ func (g *HTMLGenerator) GenerateBlogPage(blogIndex *nostr.IndexEvent, blogItems @@ -219,8 +277,12 @@ func (g *HTMLGenerator) GenerateBlogPage(blogIndex *nostr.IndexEvent, blogItems
canonicalURL := g.siteURL + "/blog"
// Format times for blog items
// Format times for blog items and collect pubkeys
formattedBlogItems := make([]BlogItemInfo, len(blogItems))
pubkeys := make([]string, 0, len(blogItems)+1)
if blogIndex.Author != "" {
pubkeys = append(pubkeys, blogIndex.Author)
}
for i, item := range blogItems {
formattedBlogItems[i] = item
if item.CreatedAt > 0 {
@ -228,8 +290,15 @@ func (g *HTMLGenerator) GenerateBlogPage(blogIndex *nostr.IndexEvent, blogItems @@ -228,8 +290,15 @@ func (g *HTMLGenerator) GenerateBlogPage(blogIndex *nostr.IndexEvent, blogItems
formattedBlogItems[i].Time = createdTime.Format("Jan 2, 2006")
formattedBlogItems[i].TimeISO = createdTime.Format(time.RFC3339)
}
if item.Author != "" {
pubkeys = append(pubkeys, item.Author)
}
}
// Fetch profiles for blog authors
ctx := context.Background()
profiles := g.fetchProfilesBatch(ctx, pubkeys)
data := PageData{
Title: "Blog",
Description: description,
@ -243,6 +312,7 @@ func (g *HTMLGenerator) GenerateBlogPage(blogIndex *nostr.IndexEvent, blogItems @@ -243,6 +312,7 @@ func (g *HTMLGenerator) GenerateBlogPage(blogIndex *nostr.IndexEvent, blogItems
BlogSummary: blogIndex.Summary,
FeedItems: []FeedItemInfo{}, // Empty - feed only on landing page
Content: template.HTML(""),
Profiles: profiles,
}
// Add blog index metadata to template data using a map
@ -321,13 +391,21 @@ func (g *HTMLGenerator) GenerateEBooksPage(ebooks []EBookInfo, feedItems []FeedI @@ -321,13 +391,21 @@ func (g *HTMLGenerator) GenerateEBooksPage(ebooks []EBookInfo, feedItems []FeedI
// Format times for display
formattedEBooks := make([]EBookInfo, len(ebooks))
pubkeys := make([]string, 0, len(ebooks))
for i, ebook := range ebooks {
formattedEBooks[i] = ebook
createdTime := time.Unix(ebook.CreatedAt, 0)
formattedEBooks[i].Time = createdTime.Format("2006-01-02 15:04:05")
formattedEBooks[i].TimeISO = createdTime.Format(time.RFC3339)
if ebook.Author != "" {
pubkeys = append(pubkeys, ebook.Author)
}
}
// Fetch profiles for all authors
ctx := context.Background()
profiles := g.fetchProfilesBatch(ctx, pubkeys)
data := PageData{
Title: "E-Books",
Description: "Browse top-level publications (index events) from Nostr",
@ -341,6 +419,7 @@ func (g *HTMLGenerator) GenerateEBooksPage(ebooks []EBookInfo, feedItems []FeedI @@ -341,6 +419,7 @@ func (g *HTMLGenerator) GenerateEBooksPage(ebooks []EBookInfo, feedItems []FeedI
FeedItems: []FeedItemInfo{}, // Empty - feed only on landing page
EBooks: formattedEBooks,
Content: template.HTML(""), // Content comes from template
Profiles: profiles,
}
return g.renderTemplate("ebooks.html", data)
@ -383,8 +462,9 @@ func (g *HTMLGenerator) GenerateContactPage(success bool, errorMsg string, event @@ -383,8 +462,9 @@ func (g *HTMLGenerator) GenerateContactPage(success bool, errorMsg string, event
SiteName: g.siteName,
SiteURL: g.siteURL,
CurrentYear: time.Now().Year(),
WikiPages: []WikiPageInfo{}, // Will be populated if needed
FeedItems: []FeedItemInfo{}, // Empty - feed only on landing page
WikiPages: []WikiPageInfo{}, // Will be populated if needed
FeedItems: []FeedItemInfo{}, // Empty - feed only on landing page
Profiles: make(map[string]*nostr.Profile), // Empty profiles for contact page
},
Success: success,
Error: errorMsg,
@ -428,6 +508,7 @@ func (g *HTMLGenerator) GenerateContactPage(success bool, errorMsg string, event @@ -428,6 +508,7 @@ func (g *HTMLGenerator) GenerateContactPage(success bool, errorMsg string, event
"Error": data.Error,
"EventID": data.EventID,
"FormData": data.FormData,
"Profiles": make(map[string]*nostr.Profile), // Empty profiles for contact page
}
// Add repo announcement data for JavaScript

12
static/css/main.css

@ -43,18 +43,19 @@ body { @@ -43,18 +43,19 @@ body {
min-height: 100vh;
display: flex;
flex-direction: column;
padding-top: 80px; /* Space for fixed header */
}
/* Skip link for accessibility */
.skip-link {
position: absolute;
position: fixed;
top: -40px;
left: 0;
background: var(--accent-color);
color: var(--bg-primary);
padding: 8px;
text-decoration: none;
z-index: 100;
z-index: 1001; /* Above fixed header */
}
.skip-link:focus {
@ -65,9 +66,12 @@ body { @@ -65,9 +66,12 @@ body {
header {
background-color: var(--bg-secondary);
border-bottom: 1px solid var(--border-color);
position: sticky;
position: fixed;
top: 0;
z-index: 50;
left: 0;
right: 0;
width: 100%;
z-index: 1000;
}
.navbar {

23
templates/components.html

@ -76,10 +76,27 @@ @@ -76,10 +76,27 @@
</span>
{{end}}
{{/* Simple user badge - just takes a pubkey string */}}
{{/* Simple user badge - takes a pubkey string and looks up profile from .Profiles map */}}
{{define "user-badge-simple"}}
<span class="user-badge" title="{{.}}">
{{$pubkey := .}}
{{$profile := index $.Profiles $pubkey}}
<span class="user-badge" title="{{$pubkey}}">
{{if and $profile $profile.Picture}}
<img src="{{$profile.Picture}}" alt="{{if $profile.DisplayName}}{{$profile.DisplayName}}{{else if $profile.Name}}{{$profile.Name}}{{else}}User{{end}}" class="user-badge-avatar" loading="lazy">
{{else}}
<span class="user-badge-avatar-placeholder"><i data-lucide="user" class="icon-inline"></i></span>
<span class="user-badge-name">{{shortenPubkey .}}</span>
{{end}}
<span class="user-badge-name">
{{if and $profile $profile.DisplayName}}
{{$profile.DisplayName}}
{{else if and $profile $profile.Name}}
{{$profile.Name}}
{{else}}
{{shortenPubkey $pubkey}}
{{end}}
</span>
{{if and $profile $profile.Name (not $profile.DisplayName)}}
<span class="user-badge-handle">@{{$profile.Name}}</span>
{{end}}
</span>
{{end}}

2
templates/ebooks.html

@ -29,7 +29,7 @@ @@ -29,7 +29,7 @@
<time datetime="{{.TimeISO}}">{{.Time}}</time>
</td>
<td>
<a href="https://alexandria.gitcitadel.eu/naddr/{{.Naddr}}" target="_blank" rel="noopener noreferrer" class="btn btn-sm"><i data-lucide="external-link" class="icon-inline"></i> View</a>
<a href="https://alexandria.gitcitadel.eu/publication/naddr/{{.Naddr}}" target="_blank" rel="noopener noreferrer" class="btn btn-sm"><i data-lucide="external-link" class="icon-inline"></i> View</a>
</td>
</tr>
{{else}}

Loading…
Cancel
Save