feat: resolve live Nostr references in blog posts and add embeds
Support naddr/nevent/note slugs for unindexed posts, cache naddr lookups, render Nostr embeds in markdown, and add a consistency check for events mirrors. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+14
-36
@@ -7,14 +7,17 @@
|
||||
* reading these needs the actual content, not repeated chrome.
|
||||
*/
|
||||
import { apiUrl } from "./api-base";
|
||||
import { formatMeetupCivilDateLong, getMeetupStartUtc } from "./meetupEventTime";
|
||||
import { formatMeetupCivilDateLong } from "./meetupEventTime";
|
||||
import { fetchMeetupsLive, partitionMeetups, countUpcoming } from "./meetupsData";
|
||||
|
||||
export const SITE_URL =
|
||||
process.env.NEXT_PUBLIC_SITE_URL || "https://belgianbitcoinembassy.org";
|
||||
|
||||
// Live (uncached) so the mirrors always reflect the same backend state as the
|
||||
// rendered pages — no ISR drift between /events, /events.md, and /llms.txt.
|
||||
async function fetchJson<T>(path: string, fallback: T): Promise<T> {
|
||||
try {
|
||||
const res = await fetch(apiUrl(path), { next: { revalidate: 300 } });
|
||||
const res = await fetch(apiUrl(path), { cache: "no-store" });
|
||||
if (!res.ok) return fallback;
|
||||
return (await res.json()) as T;
|
||||
} catch {
|
||||
@@ -85,14 +88,10 @@ function configuredChannels(settings: Record<string, string>) {
|
||||
export async function buildLlmsTxt(): Promise<string> {
|
||||
const [settings, meetups] = await Promise.all([
|
||||
fetchJson<Record<string, string>>("/settings/public", {}),
|
||||
fetchJson<any[]>("/meetups", []),
|
||||
fetchMeetupsLive(),
|
||||
]);
|
||||
|
||||
const now = new Date();
|
||||
const upcomingCount = (Array.isArray(meetups) ? meetups : []).filter((m) => {
|
||||
const start = getMeetupStartUtc(m.date, m.time || "00:00");
|
||||
return !Number.isNaN(start.getTime()) && start >= now;
|
||||
}).length;
|
||||
const upcomingCount = countUpcoming(meetups);
|
||||
|
||||
const channels = configuredChannels(settings);
|
||||
const channelNames = channels.map((c) => c.name).join(", ");
|
||||
@@ -101,12 +100,14 @@ export async function buildLlmsTxt(): Promise<string> {
|
||||
? `How to connect on ${channelNames}`
|
||||
: "How to connect with the community";
|
||||
|
||||
// Always state the count (including zero) so it stays machine-parseable and
|
||||
// verifiably consistent with /events and /events.md.
|
||||
const upcomingLine =
|
||||
upcomingCount > 0
|
||||
? `There ${upcomingCount === 1 ? "is" : "are"} currently ${upcomingCount} upcoming meetup${
|
||||
upcomingCount === 1 ? "" : "s"
|
||||
} scheduled.`
|
||||
: "Meetups run monthly; check the events page for the next date.";
|
||||
: "There are currently 0 upcoming meetups scheduled; the community meets monthly.";
|
||||
|
||||
return `# Belgian Bitcoin Embassy
|
||||
|
||||
@@ -135,19 +136,8 @@ Belgian Bitcoin Embassy is a volunteer-run network, not a business. Content here
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export async function buildHomeMarkdown(): Promise<string> {
|
||||
const meetups = await fetchJson<any[]>("/meetups", []);
|
||||
const now = new Date();
|
||||
const next = (Array.isArray(meetups) ? meetups : [])
|
||||
.filter((m) => {
|
||||
if (m.status && m.status !== "PUBLISHED") return false;
|
||||
const start = getMeetupStartUtc(m.date, m.time || "00:00");
|
||||
return !Number.isNaN(start.getTime()) && start >= now;
|
||||
})
|
||||
.sort(
|
||||
(a, b) =>
|
||||
getMeetupStartUtc(a.date, a.time || "00:00").getTime() -
|
||||
getMeetupStartUtc(b.date, b.time || "00:00").getTime(),
|
||||
)[0];
|
||||
const meetups = await fetchMeetupsLive();
|
||||
const next = partitionMeetups(meetups).upcoming[0];
|
||||
|
||||
let nextSection = "";
|
||||
if (next) {
|
||||
@@ -182,20 +172,8 @@ We help people in Belgium understand and adopt Bitcoin through education, meetup
|
||||
}
|
||||
|
||||
export async function buildEventsMarkdown(): Promise<string> {
|
||||
const meetups = await fetchJson<any[]>("/meetups", []);
|
||||
const list = Array.isArray(meetups) ? meetups : [];
|
||||
const now = new Date();
|
||||
|
||||
const upcoming = list.filter((m) => {
|
||||
const start = getMeetupStartUtc(m.date, m.time || "00:00");
|
||||
return !Number.isNaN(start.getTime()) && start >= now;
|
||||
});
|
||||
const past = list
|
||||
.filter((m) => {
|
||||
const start = getMeetupStartUtc(m.date, m.time || "00:00");
|
||||
return !Number.isNaN(start.getTime()) && start < now;
|
||||
})
|
||||
.reverse();
|
||||
const meetups = await fetchMeetupsLive();
|
||||
const { upcoming, past } = partitionMeetups(meetups);
|
||||
|
||||
const renderMeetup = (m: any): string => {
|
||||
const when = formatMeetupCivilDateLong(m.date);
|
||||
|
||||
Reference in New Issue
Block a user