Add a newsletter archive so past issues can be shared and found; v2.3.0
Publish Container / build (push) Successful in 20m51s

Web-hosted copies of episode newsletters, published from Resend broadcasts
tagged with the "New Episode" topic. Gives Facebook (and anyone else) a
proper link to share instead of raw HTML email, and makes issues findable
via site search and Google.

- Admin "Newsletter Archive" tab lists sent Resend broadcasts, flags ones
  tagged "New Episode", and publishes them with one click (pulls the HTML
  straight from Resend's API)
- Public /newsletter archive index and /newsletter/:slug issue pages,
  server-rendered with per-issue Open Graph tags for social previews
- Unsubscribe merge-tag stripped from the web copy; archive + subscribe
  links appended
- Issues added to sitemap.xml and the on-site search index

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 5 committed 2026-09-28 12:17:13 -04:00
1 parent 60b23b0fba
commit fe4c09f8a3
11 files changed
+516 -4

No files matched your search

+25
View File
@@ -35,6 +35,7 @@ import {
AUDIT_LOG_FILE,
WEBHOOKS_FILE,
STUDY_SESSIONS_FILE,
NEWSLETTER_ISSUES_FILE,
EMPTY_HIT_STATS,
EMPTY_VISITOR_STATS,
DEFAULT_REPLY_TEMPLATES,
@@ -1673,3 +1674,27 @@ export function loadQrCodesFromDisk() {
state.qrScans = []
})
}
// ── Newsletter Issues ────────────────────────────────────────────────────────
export function queueNewsletterIssuesWrite() {
state.newsletterIssuesWritePromise = state.newsletterIssuesWritePromise
.then(async () => {
await mkdir(DATA_DIR, { recursive: true })
await writeFile(NEWSLETTER_ISSUES_FILE, JSON.stringify({ issues: state.newsletterIssues }, null, 2), 'utf8')
})
.catch(err => {
console.error('[newsletter-issues] failed to write:', err)
})
}
export function loadNewsletterIssuesFromDisk() {
return readFile(NEWSLETTER_ISSUES_FILE, 'utf8')
.then(raw => {
const parsed = JSON.parse(raw)
state.newsletterIssues = Array.isArray(parsed?.issues) ? parsed.issues : []
})
.catch(() => {
state.newsletterIssues = []
})
}