Browse Source

bug-fixes

master
Silberengel 4 weeks ago
parent
commit
8c725244ac
  1. 10
      README.md
  2. 6
      internal/generator/html.go
  3. 2
      internal/nostr/profile.go
  4. 4
      internal/nostr/wiki.go
  5. 4
      internal/server/handlers.go
  6. 57
      static/css/main.css
  7. 7164
      static/js/nostr.bundle.js
  8. 19
      templates/articles.html
  9. 2
      templates/base.html
  10. 25
      templates/blog.html
  11. 5
      templates/components.html
  12. 27
      templates/contact.html
  13. 2
      templates/ebooks.html
  14. 16
      templates/feed.html
  15. 6
      templates/landing.html
  16. 1
      templates/wiki.html

10
README.md

@ -36,11 +36,17 @@ A server-generated website that fetches kind 30818 wiki events from Nostr relays @@ -36,11 +36,17 @@ A server-generated website that fetches kind 30818 wiki events from Nostr relays
```bash
npm install -g @asciidoctor/core
```
4. Copy the example config:
4. Download nostr-tools bundle (for contact form):
```bash
mkdir -p static/js
curl -L -o static/js/nostr.bundle.js https://unpkg.com/nostr-tools@latest/lib/nostr.bundle.js
```
Note: The nostr-tools library is hosted locally to avoid dependency on external CDNs.
5. Copy the example config:
```bash
cp config.yaml.example config.yaml
```
5. Edit `config.yaml` with your indices and settings
6. Edit `config.yaml` with your indices and settings
## Configuration

6
internal/generator/html.go

@ -906,13 +906,13 @@ func (g *HTMLGenerator) GenerateFeedPage(feedItems []FeedItemInfo) (string, erro @@ -906,13 +906,13 @@ func (g *HTMLGenerator) GenerateFeedPage(feedItems []FeedItemInfo) (string, erro
ctx := context.Background()
profiles := g.fetchProfilesBatch(ctx, pubkeys)
description := "Recent notes from TheForest relay"
description := "Recent notes from The Forest relay"
if len(feedItems) > 0 {
description = fmt.Sprintf("Browse %d recent notes from TheForest relay", len(feedItems))
description = fmt.Sprintf("Browse %d recent notes from The Forest relay", len(feedItems))
}
data := PageData{
Title: "TheForest Feed",
Title: "The Forest Feed",
Description: description,
CanonicalURL: canonicalURL,
OGImage: g.siteURL + g.defaultImage,

2
internal/nostr/profile.go

@ -118,7 +118,7 @@ func (c *Client) FetchProfilesBatch(ctx context.Context, pubkeys []string) (map[ @@ -118,7 +118,7 @@ func (c *Client) FetchProfilesBatch(ctx context.Context, pubkeys []string) (map[
"pubkeys": len(uniquePubkeys),
}).Debug("Batch fetching profiles")
// Fetch all profile events from fallback relays only (not theforest)
// Fetch all profile events from fallback relays only (not The Forest)
profileRelays := c.GetProfileRelays()
if len(profileRelays) == 0 {
// Fallback: if no profile relays configured, use all relays

4
internal/nostr/wiki.go

@ -57,7 +57,7 @@ func (ws *WikiService) FetchWikiIndex(ctx context.Context, naddrStr string) (*In @@ -57,7 +57,7 @@ func (ws *WikiService) FetchWikiIndex(ctx context.Context, naddrStr string) (*In
filter := naddr.ToFilter()
logFilter(filter, fmt.Sprintf("wiki index (kind %d)", ws.indexKind))
// Fetch the event from theforest only (primary relay)
// Fetch the event from The Forest only (primary relay)
primaryRelay := ws.client.GetPrimaryRelay()
if primaryRelay == "" {
return nil, fmt.Errorf("primary relay not configured")
@ -326,7 +326,7 @@ func (ws *WikiService) FetchWikiEventByDTag(ctx context.Context, pubkey, dTag st @@ -326,7 +326,7 @@ func (ws *WikiService) FetchWikiEventByDTag(ctx context.Context, pubkey, dTag st
}
logFilter(filter, fmt.Sprintf("wiki by d-tag %s", dTag))
// Fetch from theforest only (primary relay)
// Fetch from The Forest only (primary relay)
primaryRelay := ws.client.GetPrimaryRelay()
if primaryRelay == "" {
return nil, fmt.Errorf("primary relay not configured")

4
internal/server/handlers.go

@ -468,8 +468,8 @@ func (s *Server) middleware(next http.Handler) http.Handler { @@ -468,8 +468,8 @@ func (s *Server) middleware(next http.Handler) http.Handler {
w.Header().Set("X-XSS-Protection", "1; mode=block")
w.Header().Set("Referrer-Policy", "strict-origin-when-cross-origin")
// CSP header - allow unpkg.com for Lucide icons
w.Header().Set("Content-Security-Policy", "default-src 'self'; script-src 'self' 'unsafe-inline' https://unpkg.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:;")
// CSP header - allow unpkg.com for Lucide icons and jsdelivr.net for nostr-tools
w.Header().Set("Content-Security-Policy", "default-src 'self'; script-src 'self' 'unsafe-inline' https://unpkg.com https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:;")
// Log request (only in debug mode to reduce noise)
start := time.Now()

57
static/css/main.css

@ -138,6 +138,9 @@ header { @@ -138,6 +138,9 @@ header {
gap: 0.5rem;
padding: 0.5rem 0;
line-height: 1.2;
text-transform: uppercase;
letter-spacing: 0.05em;
font-weight: 600;
}
.nav-menu a:hover {
@ -229,6 +232,9 @@ header { @@ -229,6 +232,9 @@ header {
color: var(--text-primary);
text-decoration: none;
transition: background 0.2s, color 0.2s;
text-transform: uppercase;
letter-spacing: 0.05em;
font-weight: 600;
}
.dropdown-menu a:hover {
@ -291,6 +297,9 @@ header { @@ -291,6 +297,9 @@ header {
text-decoration: none;
border-radius: 4px;
transition: background 0.2s, color 0.2s;
text-transform: uppercase;
letter-spacing: 0.05em;
font-weight: 600;
}
.wiki-menu a:hover {
@ -511,7 +520,7 @@ a:focus { @@ -511,7 +520,7 @@ a:focus {
position: relative;
background-image: url('/static/GitCitadel_PFP.png');
background-size: cover;
background-position: center right;
background-position: center center;
background-repeat: no-repeat;
padding: 3rem 2rem;
border-radius: 12px;
@ -530,7 +539,7 @@ a:focus { @@ -530,7 +539,7 @@ a:focus {
right: 0;
bottom: 0;
background: linear-gradient(
to right,
to bottom,
rgba(45, 45, 45, 0.95) 0%,
rgba(45, 45, 45, 0.85) 30%,
rgba(124, 158, 255, 0.4) 60%,
@ -1018,6 +1027,7 @@ footer { @@ -1018,6 +1027,7 @@ footer {
.article-menu li {
margin-bottom: 0.5rem;
display: block;
}
.article-link {
@ -1040,10 +1050,15 @@ footer { @@ -1040,10 +1050,15 @@ footer {
}
.article-link-title {
display: flex;
align-items: center;
gap: 0.5rem;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 0.5rem;
font-size: 1em;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.article-link-meta {
@ -1089,6 +1104,23 @@ footer { @@ -1089,6 +1104,23 @@ footer {
border-bottom: 1px solid var(--border-color);
}
.article-meta {
display: flex;
flex-wrap: wrap;
gap: 1.5rem;
margin-top: 1rem;
align-items: center;
font-size: 0.95em;
}
.article-meta .article-date,
.article-meta .article-author {
display: flex;
align-items: center;
gap: 0.5rem;
color: var(--text-secondary);
}
.article-image {
margin: 1.5rem 0;
width: 100%;
@ -1736,6 +1768,17 @@ textarea:focus-visible { @@ -1736,6 +1768,17 @@ textarea:focus-visible {
background-color: var(--bg-secondary);
border-radius: 8px;
overflow: hidden;
table-layout: fixed;
}
.ebooks-table th:nth-child(1),
.ebooks-table td:nth-child(1) {
width: 66.67%;
}
.ebooks-table th:nth-child(2),
.ebooks-table td:nth-child(2) {
width: 33.33%;
}
.ebooks-table thead {
@ -1801,6 +1844,16 @@ textarea:focus-visible { @@ -1801,6 +1844,16 @@ textarea:focus-visible {
font-size: 0.9em;
}
.btn-icon-only {
padding: 0.5rem;
aspect-ratio: 1;
justify-content: center;
}
.btn-icon-only .icon-inline {
margin-right: 0;
}
.ebooks-table td .text-center {
text-align: center;
color: var(--text-secondary);

7164
static/js/nostr.bundle.js

File diff suppressed because it is too large Load Diff

19
templates/articles.html

@ -22,14 +22,6 @@ @@ -22,14 +22,6 @@
<li>
<a href="#" class="article-link" data-dtag="{{$item.DTag}}" data-index="{{$index}}"{{if eq $index 0}} data-active="true"{{end}}>
<div class="article-link-title"><span class="icon-inline">{{icon "file-text"}}</span> {{$item.Title}}</div>
{{if $item.Time}}
<div class="article-link-meta">
<span class="article-date"><span class="icon-inline">{{icon "clock"}}</span> {{$item.Time}}</span>
{{if $item.Author}}
<span class="article-author">{{template "user-badge-simple" (dict "Pubkey" $item.Author "Profiles" $.Profiles)}}</span>
{{end}}
</div>
{{end}}
</a>
</li>
{{end}}
@ -43,7 +35,16 @@ @@ -43,7 +35,16 @@
<article class="blog-article{{if eq $index 0}} active{{end}}" data-dtag="{{$item.DTag}}" id="article-{{$item.DTag}}">
<header class="article-header">
<h1 class="article-title">{{$item.Title}}</h1>
<p class="article-subtitle">Longform article</p>
{{if or $item.Time $item.Author}}
<div class="article-meta">
{{if $item.Time}}
<span class="article-date"><span class="icon-inline">{{icon "clock"}}</span> {{$item.Time}}</span>
{{end}}
{{if $item.Author}}
<span class="article-author"><span class="icon-inline">{{icon "user"}}</span> {{template "user-badge-simple" (dict "Pubkey" $item.Author "Profiles" $.Profiles)}}</span>
{{end}}
</div>
{{end}}
</header>
{{if and $item.Image (ne $item.Image "")}}
<div class="article-image">

2
templates/base.html

@ -58,7 +58,7 @@ @@ -58,7 +58,7 @@
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle">TheForest Relay <span class="dropdown-arrow"></span></a>
<a href="#" class="dropdown-toggle">The Forest Relay <span class="dropdown-arrow"></span></a>
<ul class="dropdown-menu">
<li><a href="/feed"><span class="icon-inline">{{icon "rss"}}</span> Feed</a></li>
<li><a href="/articles"><span class="icon-inline">{{icon "file-text"}}</span> Articles</a></li>

25
templates/blog.html

@ -8,18 +8,12 @@ @@ -8,18 +8,12 @@
<!-- Left Sidebar -->
<aside class="blog-sidebar">
<div class="blog-header">
{{if .BlogIndexAuthor}}
<div class="blog-author-handle">{{template "user-badge-simple" (dict "Pubkey" .BlogIndexAuthor "Profiles" $.Profiles)}}</div>
{{end}}
{{if .BlogIndexImage}}
<div class="blog-image">
<img src="{{.BlogIndexImage}}" alt="{{.BlogIndexTitle}}" />
</div>
{{end}}
<h1 class="blog-title">{{if .BlogIndexTitle}}{{.BlogIndexTitle}}{{else}}Blog{{end}}</h1>
{{if .BlogIndexAuthor}}
<p class="blog-byline">by {{template "user-badge-simple" (dict "Pubkey" .BlogIndexAuthor "Profiles" $.Profiles)}}</p>
{{end}}
{{if .BlogIndexSummary}}
<p class="blog-description">{{.BlogIndexSummary}}</p>
{{end}}
@ -35,14 +29,6 @@ @@ -35,14 +29,6 @@
<li>
<a href="#" class="article-link" data-dtag="{{$item.DTag}}" data-index="{{$index}}"{{if eq $index 0}} data-active="true"{{end}}>
<div class="article-link-title"><span class="icon-inline">{{icon "file-text"}}</span> {{$item.Title}}</div>
{{if $item.Time}}
<div class="article-link-meta">
<span class="article-date"><span class="icon-inline">{{icon "clock"}}</span> {{$item.Time}}</span>
{{if $item.Author}}
<span class="article-author">{{template "user-badge-simple" (dict "Pubkey" $item.Author "Profiles" $.Profiles)}}</span>
{{end}}
</div>
{{end}}
</a>
</li>
{{end}}
@ -56,7 +42,16 @@ @@ -56,7 +42,16 @@
<article class="blog-article{{if eq $index 0}} active{{end}}" data-dtag="{{$item.DTag}}" id="article-{{$item.DTag}}">
<header class="article-header">
<h1 class="article-title">{{$item.Title}}</h1>
<p class="article-subtitle">This entry originally appeared in this blog.</p>
{{if or $item.Time $item.Author}}
<div class="article-meta">
{{if $item.Time}}
<span class="article-date"><span class="icon-inline">{{icon "clock"}}</span> {{$item.Time}}</span>
{{end}}
{{if $item.Author}}
<span class="article-author"><span class="icon-inline">{{icon "user"}}</span> {{template "user-badge-simple" (dict "Pubkey" $item.Author "Profiles" $.Profiles)}}</span>
{{end}}
</div>
{{end}}
</header>
{{if and $item.Image (ne $item.Image "")}}
<div class="article-image">

5
templates/components.html

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
{{/* Feed Component - Reusable feed sidebar */}}
{{define "feed"}}
<div class="feed-container">
<h3><span class="icon-inline">{{icon "rss"}}</span> Recent Notes <a href="https://aitherboard.imwald.eu/feed/relay/theforest.nostr1.com" target="_blank" rel="noopener noreferrer" class="feed-link-header"><span class="icon-inline">{{icon "external-link"}}</span> View Full Feed</a></h3>
<h3><span class="icon-inline">{{icon "rss"}}</span> Recent Notes</h3>
<div class="feed-items">
{{range .FeedItems}}
<article class="feed-item">
@ -10,9 +10,6 @@ @@ -10,9 +10,6 @@
<time class="feed-time" datetime="{{.TimeISO}}"><span class="icon-inline">{{icon "clock"}}</span> {{.Time}}</time>
</header>
<div class="feed-content">{{.Content}}</div>
<footer class="feed-footer">
<a href="https://aitherboard.imwald.eu/event/{{.EventID}}" class="feed-link" target="_blank" rel="noopener noreferrer"><span class="icon-inline">{{icon "external-link"}}</span> View on Aitherboard</a>
</footer>
</article>
{{else}}
<p class="feed-empty"><span class="icon-inline">{{icon "inbox"}}</span> No recent notes available.</p>

27
templates/contact.html

@ -171,7 +171,7 @@ @@ -171,7 +171,7 @@
<script type="application/json" id="contact-relays-data">{{json .ContactRelays}}</script>
<script src="https://cdn.jsdelivr.net/npm/nostr-tools@1.18.0/lib/nostr.bundle.js"></script>
<script src="/static/js/nostr.bundle.js"></script>
<script>
(function() {
const form = document.getElementById('contact-form');
@ -361,15 +361,17 @@ @@ -361,15 +361,17 @@
const tags = [];
// Add 'a' tag for repository announcement if available
if (repoAnnouncement) {
tags.push(['a', `30617:${repoAnnouncement.pubkey}:${repoAnnouncement.dTag}`]);
tags.push(['p', repoAnnouncement.pubkey]);
if (repoAnnouncement.maintainers && repoAnnouncement.maintainers.length > 0) {
repoAnnouncement.maintainers.forEach(maintainer => {
tags.push(['p', maintainer]);
});
}
if (repoAnnouncement && repoAnnouncement.pubkey && repoAnnouncement.dTag) {
tags.push(['a', `30617:${repoAnnouncement.pubkey}:${repoAnnouncement.dTag}`]);
tags.push(['p', repoAnnouncement.pubkey]);
if (repoAnnouncement.maintainers && repoAnnouncement.maintainers.length > 0) {
repoAnnouncement.maintainers.forEach(maintainer => {
if (maintainer) {
tags.push(['p', maintainer]);
}
});
}
}
// Add required 'p' tags for contact recipients
@ -392,7 +394,10 @@ @@ -392,7 +394,10 @@
});
// Add contact relays tag
tags.push(['relays', ...contactRelays]);
// Store relays as a JSON string in the tag value
if (contactRelays && contactRelays.length > 0) {
tags.push(['relays', JSON.stringify(contactRelays)]);
}
// Create unsigned event (kind 1 for contact messages)
const unsignedEvent = {

2
templates/ebooks.html

@ -25,7 +25,7 @@ @@ -25,7 +25,7 @@
<strong>{{.Title}}</strong>
{{if .Summary}}<br><small class="text-muted">{{.Summary}}</small>{{end}}
<div style="margin-top: 0.75rem;">
<a href="https://alexandria.gitcitadel.eu/publication/naddr/{{.Naddr}}" target="_blank" rel="noopener noreferrer" class="btn btn-sm"><span class="icon-inline">{{icon "external-link"}}</span> View on Alexandria</a>
<a href="https://alexandria.gitcitadel.eu/publication/naddr/{{.Naddr}}" target="_blank" rel="noopener noreferrer" class="btn btn-sm btn-icon-only" title="View on Alexandria"><span class="icon-inline">{{icon "external-link"}}</span></a>
</div>
</td>
<td>{{template "user-badge-simple" (dict "Pubkey" .Author "Profiles" $.Profiles)}}</td>

16
templates/feed.html

@ -1,19 +1,19 @@ @@ -1,19 +1,19 @@
{{define "content"}}
<article class="feed-page">
<header class="page-header">
<h1><span class="icon-inline">{{icon "rss"}}</span> TheForest Feed</h1>
<p class="page-summary">Recent notes from TheForest relay</p>
<h1><span class="icon-inline">{{icon "rss"}}</span> The Forest Feed</h1>
<p class="page-summary">Recent notes from The Forest relay</p>
</header>
<div class="feed-about-blurb">
<h2>About TheForest Relay</h2>
<p>TheForest is a Nostr relay operated by GitCitadel. It provides a reliable, fast, and open relay service for the Nostr protocol.</p>
<h2>About The Forest 🌲 Relay</h2>
<p>The Forest is a Nostr relay operated by GitCitadel. It provides a reliable, fast, and open relay service for the Nostr protocol.</p>
<ul>
<li><strong>Relay URL: </strong> <a href="https://theforest.nostr1.com" target="_blank" rel="noopener noreferrer"><code>wss://theforest.nostr1.com</code></a></li>
<li><strong>Status: </strong> Online and operational</li>
<li><strong>Features: </strong> Supports all standard Nostr event kinds</li>
<li><strong>Relay URL : </strong> <a href="https://theforest.nostr1.com" target="_blank" rel="noopener noreferrer"><code>wss://theforest.nostr1.com</code></a></li>
<li><strong>Status : </strong> Online and operational</li>
<li><strong>Features : </strong> Supports all standard Nostr event kinds</li>
</ul>
<p>TheForest relay hosts a variety of content, including longform markdown articles (kind 30023), e-books and structured publications (kind 30040), and short-form notes (kind 1).</p>
<p>The Forest relay hosts a variety of content, including longform markdown articles (kind 30023), e-books and structured publications (kind 30040), and short-form notes (kind 1).</p>
</div>
<div class="feed-container">

6
templates/landing.html

@ -87,20 +87,20 @@ @@ -87,20 +87,20 @@
</div>
</div>
<div class="feature-card-content">
<p>Longform markdown articles from the TheForest relay.</p>
<p>Longform markdown articles from the The Forest relay.</p>
<a href="/articles" class="btn"><span class="icon-inline">{{icon "arrow-right"}}</span> View Articles</a>
</div>
</div>
<div class="feature-card">
<h3><span class="icon-inline">{{icon "rss"}}</span> Feed</h3>
<p>Browse recent notes and updates from TheForest relay.</p>
<p>Browse recent notes and updates from The Forest relay.</p>
<a href="/feed" class="btn"><span class="icon-inline">{{icon "arrow-right"}}</span> View Feed</a>
</div>
<div class="feature-card">
<h3><span class="icon-inline">{{icon "book"}}</span> E-Books</h3>
<p>Discover and download e-books from the decentralized #Alexandria library.</p>
<p>Discover and download e-books from the decentralized Alexandria library.</p>
<a href="/ebooks" class="btn"><span class="icon-inline">{{icon "arrow-right"}}</span> View E-Books</a>
</div>

1
templates/wiki.html

@ -14,7 +14,6 @@ @@ -14,7 +14,6 @@
<a href="/wiki/{{.DTag}}" class="wiki-link"><span class="icon-inline">{{icon "file-text"}}</span> {{.Title}}</a>
{{end}}
</div>
<p class="wiki-index-note wiki-sidebar-note"><span class="icon-inline">{{icon "arrow-right"}}</span> On larger screens, you can also select an article from the sidebar.</p>
{{else}}
<p><span class="icon-inline">{{icon "inbox"}}</span> No wiki articles available yet.</p>
{{end}}

Loading…
Cancel
Save