Skip to content
This repository was archived by the owner on Mar 15, 2026. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ NEXT_PUBLIC_TWITTER_LINK="https://x.com/_xLog"
NEXT_PUBLIC_GITHUB_LINK="https://github.com/Crossbell-Box/xlog"
NEXT_PUBLIC_CSB_IO="https://crossbell.io"
NEXT_PUBLIC_CSB_SCAN="https://scan.crossbell.io"
NEXT_PUBLIC_IPFS_GATEWAY="https://ipfs.4everland.xyz/ipfs/"
NEXT_PUBLIC_IPFS_GATEWAY="https://ipfs.4everland.io/ipfs/"
NEXT_PUBLIC_MIRA_LINK="https://mira.crossbell.io"
NEXT_PUBLIC_WALLET_CONNECT_V2_PROJECT_ID=

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"prisma:migrate:resolve": "prisma migrate resolve --preview-feature",
"prisma:generate": "prisma generate",
"prisma:studio": "prisma studio",
"docker:db": "docker-compose -f docker-compose.db.yml up -d",
"docker:db": "docker compose -f docker-compose.db.yml up -d",
"spell-check": "cspell lint . --no-progress",
"analyze": "NODE_ENV=production ANALYZE=true BUNDLE_ANALYZE=browser next build",
"postinstall": "prisma generate",
Expand Down
8 changes: 7 additions & 1 deletion src/app/[locale]/site/[site]/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,13 @@ export default async function SitePagePage({
</h2>
)}
<div className="xlog-short-content prose">
{page?.metadata?.content?.content}
<div
dangerouslySetInnerHTML={{
__html:
page?.metadata?.content?.content?.replace(/\n/g, "<br/>") ||
"",
}}
/>
</div>
</>
) : (
Expand Down
183 changes: 151 additions & 32 deletions src/app/[locale]/site/[site]/feed/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import RSS from "rss"

import { SITE_URL } from "~/lib/env"
import { getSiteLink } from "~/lib/helpers"
import { toGateway } from "~/lib/ipfs-parser"
import { NextServerResponse } from "~/lib/server-helper"
import { PageVisibilityEnum } from "~/lib/types"
import { getPagesBySite } from "~/models/page.model"
Expand All @@ -18,15 +19,37 @@ export async function GET(
},
) {
const site = await getSite(params.site)
const pages = await getPagesBySite({

const url = new URL(request.url)
const includeShorts = url.searchParams.get("shorts") !== "false"

const posts = await getPagesBySite({
characterId: site?.characterId,
type: "post",
visibility: PageVisibilityEnum.Published,
keepBody: true,
useHTML: true,
})

const hasAudio = pages.list?.find((page) => page.metadata?.content?.audio)
let allPages = posts.list || []

if (includeShorts) {
const shorts = await getPagesBySite({
characterId: site?.characterId,
type: "short",
visibility: PageVisibilityEnum.Published,
keepBody: true,
useHTML: true,
})

allPages = [...(posts.list || []), ...(shorts.list || [])].sort((a, b) => {
const dateA = new Date(a.metadata?.content?.date_published || a.createdAt)
const dateB = new Date(b.metadata?.content?.date_published || b.createdAt)
return dateB.getTime() - dateA.getTime()
})
}

const hasAudio = allPages.find((page) => page.metadata?.content?.audio)

const email = site?.metadata?.content?.connected_accounts
?.map((account) => {
Expand Down Expand Up @@ -71,31 +94,78 @@ export async function GET(
},
}
: {}),
items: pages.list?.map((page) => ({
id: page.characterId + "-" + page.noteId,
title: page.metadata?.content?.title || "Untitled",
summary: page.metadata?.content?.summary,
content_html: page.metadata?.content?.contentHTML,
url: `${SITE_URL}/api/redirection?characterId=${page.characterId}&noteId=${page.noteId}`,
image: page.metadata?.content?.cover,
date_published: page.metadata?.content?.date_published,
date_modified: page.updatedAt,
tags: page.metadata?.content?.tags,
author: site?.metadata?.content?.name,
...(page.metadata?.content?.audio && {
_itunes: {
image: page.metadata?.content?.cover,
summary: page.metadata?.content?.summary,
},
attachments: [
{
url: page.metadata?.content?.audio,
mime_type: "audio/mpeg",
title: page.metadata?.content?.title,
items: allPages?.map((page) => {
const isShort = page.metadata?.content?.tags?.includes("short")
const title =
page.metadata?.content?.title ||
(isShort
? `${new Date(page.metadata?.content?.date_published || page.createdAt).toLocaleDateString()}`
: "Untitled")

const allImages = isShort
? (page.metadata?.content?.images || []).map((img: string) =>
toGateway(img),
)
: page.metadata?.content?.cover
? [toGateway(page.metadata?.content?.cover)]
: []

const originalContent =
page.metadata?.content?.contentHTML ||
page.metadata?.content?.content ||
""
const imagesHtml =
isShort && allImages.length > 0
? allImages
.map(
(img: string) =>
`<img src="${img}" style="max-width: 100%; height: auto; margin: 8px 0;" />`,
)
.join("")
: ""

const contentWithImages =
isShort && imagesHtml
? `${imagesHtml}<br/>${originalContent}`
: originalContent

Comment thread
markbang marked this conversation as resolved.
return {
id: page.characterId + "-" + page.noteId,
title,
summary:
page.metadata?.content?.summary ||
(page.metadata?.content?.content
? page.metadata.content.content.slice(0, 200) + "..."
: ""),
content_html: contentWithImages,
url: `${SITE_URL}/api/redirection?characterId=${page.characterId}&noteId=${page.noteId}`,
image: allImages[0],
...(isShort &&
allImages.length > 0 && {
attachments: allImages.map((img: string) => ({
url: img,
})),
}),
date_published:
page.metadata?.content?.date_published || page.createdAt,
date_modified: page.updatedAt,
tags: page.metadata?.content?.tags,
author: site?.metadata?.content?.name,
...(page.metadata?.content?.audio && {
_itunes: {
image: page.metadata?.content?.cover,
summary: page.metadata?.content?.summary,
},
],
}),
})),
attachments: [
{
url: page.metadata?.content?.audio,
mime_type: "audio/mpeg",
title: page.metadata?.content?.title,
},
],
}),
}
}),
}

const feed = new RSS({
Expand All @@ -109,6 +179,8 @@ export async function GET(
feed_url: `${link}/feed`,
custom_namespaces: {
itunes: "http://www.itunes.com/dtds/podcast-1.0.dtd",
media: "http://search.yahoo.com/mrss/",
content: "http://purl.org/rss/1.0/modules/content/",
},
custom_elements: [
...(hasAudio
Expand Down Expand Up @@ -146,26 +218,73 @@ export async function GET(
],
})

pages.list.forEach((page) => {
allPages.forEach((page) => {
const isShort = page.metadata?.content?.tags?.includes("short")
const title =
page.metadata?.content?.title ||
(isShort
? `${new Date(page.metadata?.content?.date_published || page.createdAt).toLocaleDateString()}`
: "Untitled")

const allImages = isShort
? (page.metadata?.content?.images || []).map((img: string) =>
toGateway(img),
)
: page.metadata?.content?.cover
? [toGateway(page.metadata?.content?.cover)]
: []

const itemContent =
page.metadata?.content?.contentHTML ||
page.metadata?.content?.content ||
""

const imagesHtml =
isShort && allImages.length > 0
? allImages
.map(
(img: string) =>
`<img src="${img}" style="max-width: 100%; height: auto; margin: 8px 0;" />`,
)
.join("")
: ""

const contentWithImages =
isShort && imagesHtml ? `${imagesHtml}<br/>${itemContent}` : itemContent

const mediaContentElements = allImages.map((img: string) => ({
"media:content": { _attr: { url: img, type: "image", medium: "image" } },
}))

feed.item({
guid: page.characterId + "-" + page.noteId,
title: page.metadata?.content?.title || "Untitled",
description: page.metadata?.content?.summary,
title,
description:
page.metadata?.content?.summary ||
(page.metadata?.content?.content
? page.metadata.content.content.slice(0, 200) + "..."
: ""),
custom_elements: [
{
"content:encoded": page.metadata?.content?.contentHTML,
"content:encoded": contentWithImages,
},
...mediaContentElements,
],
url: `${SITE_URL}/api/redirection?characterId=${page.characterId}&noteId=${page.noteId}`,
date: new Date(page.metadata?.content?.date_published),
date: new Date(page.metadata?.content?.date_published || page.createdAt),
categories: page.metadata?.content?.tags,
author: site?.metadata?.content?.name,
enclosure: hasAudio
? {
url: page.metadata?.content?.audio,
type: "audio/mpeg",
}
: undefined,
: allImages[0]
? {
url: allImages[0],
type: "image/jpeg",
}
: undefined,
})
})

Expand Down
Loading