Use the node short description in link preview metadata

Hop-State: A_06FPEC5CSW59V0KHWX0W1ER
Hop-Proposal: R_06FPEC4XSJJ03M62S8AA9P0
Hop-Task: T_06FPEBTP53BQJHKGSPK894G
Hop-Attempt: AT_06FPEBTP50KTCJ3YT789R78
This commit is contained in:
2026-07-15 12:11:11 -07:00
committed by Hop
parent 0e64b83a99
commit 7143f6cfd7
4 changed files with 92 additions and 17 deletions
+9 -11
View File
@@ -13,26 +13,24 @@ const sairaCondensed = Saira_Condensed({
variable: "--font-saira", variable: "--font-saira",
}); });
import { db } from "@/db"; import { getLocalNode } from "@/lib/node/local-node";
import { buildNodeLinkMetadata, DEFAULT_NODE_DESCRIPTION } from "@/lib/node/metadata";
export async function generateMetadata(): Promise<Metadata> { export async function generateMetadata(): Promise<Metadata> {
let title = "Synapsis"; let node = null;
try { try {
const node = await db.query.nodes.findFirst(); node = await getLocalNode();
if (node?.name) {
title = node.name;
}
} catch (e) { } catch (e) {
console.error("Failed to fetch node info for metadata", e); console.error("Failed to fetch node info for metadata", e);
} }
return { return {
title: { ...buildNodeLinkMetadata(
default: title, node,
template: `%s | ${title}`, process.env.NEXT_PUBLIC_NODE_NAME || "Synapsis",
}, process.env.NEXT_PUBLIC_NODE_DESCRIPTION || DEFAULT_NODE_DESCRIPTION
description: "Synapsis is designed to function like a global signal layer rather than a culture-bound platform. Anyone can run their own node and still participate in a shared, interconnected network, with global identity, clean terminology, and a modern interface that feels current rather than experimental.", ),
manifest: "/manifest.json", manifest: "/manifest.json",
icons: { icons: {
icon: "/api/favicon", icon: "/api/favicon",
+9 -3
View File
@@ -1,14 +1,20 @@
import { db } from '@/db'; import { db } from '@/db';
export async function isLocalNodeNsfw(): Promise<boolean> { export async function getLocalNode() {
const domain = process.env.NEXT_PUBLIC_NODE_DOMAIN || process.env.NODE_DOMAIN || 'localhost:43821'; const domain = process.env.NEXT_PUBLIC_NODE_DOMAIN || process.env.NODE_DOMAIN || 'localhost:43821';
try {
let node = await db.query.nodes.findFirst({ where: { domain } }); let node = await db.query.nodes.findFirst({ where: { domain } });
if (!node) { if (!node) {
const localNodes = await db.query.nodes.findMany({ limit: 2 }); const localNodes = await db.query.nodes.findMany({ limit: 2 });
if (localNodes.length === 1) node = localNodes[0]; if (localNodes.length === 1) node = localNodes[0];
} }
return node ?? null;
}
export async function isLocalNodeNsfw(): Promise<boolean> {
try {
const node = await getLocalNode();
return node?.isNsfw === true; return node?.isNsfw === true;
} catch (error) { } catch (error) {
console.error('Local node NSFW lookup failed:', error); console.error('Local node NSFW lookup failed:', error);
+35
View File
@@ -0,0 +1,35 @@
import { describe, expect, it } from 'vitest';
import { buildNodeLinkMetadata } from './metadata';
describe('buildNodeLinkMetadata', () => {
it('uses the node short description everywhere link previews read it', () => {
const metadata = buildNodeLinkMetadata({
name: 'Example Node',
description: 'A concise description of this community.',
});
expect(metadata.description).toBe('A concise description of this community.');
expect(metadata.openGraph).toMatchObject({
title: 'Example Node',
description: 'A concise description of this community.',
});
expect(metadata.twitter).toMatchObject({
title: 'Example Node',
description: 'A concise description of this community.',
});
});
it('uses configured fallbacks when node branding is blank', () => {
const metadata = buildNodeLinkMetadata(
{ name: ' ', description: ' ' },
'Fallback Node',
'Fallback description'
);
expect(metadata.description).toBe('Fallback description');
expect(metadata.openGraph).toMatchObject({
title: 'Fallback Node',
description: 'Fallback description',
});
});
});
+36
View File
@@ -0,0 +1,36 @@
import type { Metadata } from 'next';
export const DEFAULT_NODE_DESCRIPTION = 'A swarm social network node.';
type NodeBranding = {
name?: string | null;
description?: string | null;
};
export function buildNodeLinkMetadata(
node?: NodeBranding | null,
fallbackName = 'Synapsis',
fallbackDescription = DEFAULT_NODE_DESCRIPTION
): Pick<Metadata, 'title' | 'description' | 'openGraph' | 'twitter'> {
const title = node?.name?.trim() || fallbackName;
const description = node?.description?.trim() || fallbackDescription;
return {
title: {
default: title,
template: `%s | ${title}`,
},
description,
openGraph: {
type: 'website',
siteName: title,
title,
description,
},
twitter: {
card: 'summary',
title,
description,
},
};
}