feat: use jsx (#14863)

* feat: render rss using jsx

* fix: error page response header

* feat: render atom using jsx

* feat: export art

* feat: use jsxRenderer

* chore: upgrade hono
This commit is contained in:
DIYgod
2024-03-20 16:28:11 +00:00
committed by GitHub
parent f957e664d8
commit 40350abcbb
10 changed files with 475 additions and 473 deletions

View File

@@ -12,6 +12,7 @@ import debug from '@/middleware/debug';
import header from '@/middleware/header';
import antiHotlink from '@/middleware/anti-hotlink';
import parameter from '@/middleware/parameter';
import { jsxRenderer } from 'hono/jsx-renderer';
import logger from '@/utils/logger';
@@ -27,6 +28,13 @@ const app = new Hono();
app.use(compress());
app.use(jsxRenderer(
({ children }) => <>{children}</>,
{
docType: '<?xml version="1.0" encoding="UTF-8"?>',
stream: {}
}
));
app.use(mLogger);
app.use(sentry);
app.use(accessControl);

View File

@@ -1,4 +1,4 @@
import { rss3Ums, json, art } from '@/utils/render';
import { rss3Ums, json, RSS, Atom } from '@/utils/render';
import * as path from 'node:path';
import { config } from '@/config';
import { collapseWhitespace, convertDateToISO8601 } from '@/utils/common-utils';
@@ -26,9 +26,6 @@ const middleware: MiddlewareHandler = async (ctx, next) => {
}
}
const templateName = outputType === 'atom' ? 'atom.art' : 'rss.art';
const template = path.resolve(__dirname, `../views/${templateName}`);
if (data) {
data.title = collapseWhitespace(data.title) || '';
data.description && (data.description = collapseWhitespace(data.description) || '');
@@ -93,8 +90,10 @@ const middleware: MiddlewareHandler = async (ctx, next) => {
return ctx.body(json(result));
} else if (ctx.get('no-content')) {
return ctx.body(null);
} else if (outputType === 'atom') {
return ctx.render(<Atom data={result} />);
} else {
return ctx.body(art(template, result));
return ctx.render(<RSS data={result} />);
}
};

View File

@@ -24,6 +24,8 @@ export type DataItem = {
enclosure_title?: string;
enclosure_length?: number;
itunes_duration?: number | string;
itunes_item_image?: string;
media?: Record<string, Record<string, string>>;
_extra?: Record<string, any> & {
links?: {
@@ -45,6 +47,13 @@ export type Data = {
language?: string;
feedLink?: string;
lastBuildDate?: string;
itunes_author?: string;
itunes_category?: string;
itunes_explicit?: string | boolean;
id?: string;
atomlink?: string;
ttl?: number;
};
// namespace

View File

@@ -1,3 +1,5 @@
export { default as rss3Ums } from '@/views/rss3-ums';
export { default as json } from '@/views/json';
export { default as RSS } from '@/views/rss';
export { default as Atom } from '@/views/atom';
export { default as art } from 'art-template';

View File

@@ -1,86 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:rsshub="http://rsshub.app/xml/schemas">
<generator>RSSHub</generator>
<webMaster>i@diygod.me (DIYgod)</webMaster>
<language>{{ language || 'zh-cn' }}</language>
<id>{{ id || link }}</id>
<title><![CDATA[{{@ title || 'RSSHub' }}]]></title>
{{ if subtitle }}
<subtitle><![CDATA[{{@ subtitle }}]]></subtitle>
{{ /if }}
<updated>{{ updated }}</updated>
<link href="{{ link }}" />
<author>
<name><![CDATA[{{@ author || 'RSSHub' }}]]></name>
</author>
{{if contributor }}
{{ each contributor }}
<contributor>
<name><![CDATA[{{@ $value }}]]></name>
</contributor>
{{ /each }}
{{ /if }}
{{ if icon }}
<icon>{{ icon }}</icon>
{{ /if }}
{{ if logo }}
<logo>{{ logo }}</logo>
{{ /if }}
{{ each item $e }}
<entry>
<id>{{ $e.guid || $e.id || $e.link }}</id>
<title><![CDATA[{{@ $e.title }}]]></title>
{{ if ($e.pubDate && $e.updated) }}
<published>{{ $e.pubDate }}</published>
{{ /if }}
<updated>{{ $e.updated || $e.pubDate }}</updated>
{{ if $e.author }}
<author>
<name><![CDATA[{{@ $e.author || 'RSSHub' }}]]></name>
</author>
{{ /if }}
<link href="{{ $e.link }}" />
{{ if $e.upvotes }}
<rsshub:upvotes>{{ $e.upvotes }}</rsshub:upvotes>
{{ /if }}
{{ if $e.downvotes }}
<rsshub:downvotes>{{ $e.downvotes }}</rsshub:downvotes>
{{ /if }}
{{ if $e.comments }}
<rsshub:comments>{{ $e.comments }}</rsshub:comments>
{{ /if }}
{{ if $e.summary }}
<summary type="html"><![CDATA[{{@ $e.summary }}]]></summary>
{{ /if }}
{{ if $e.content }}
<content type="html" src="{{ $e.link }}">{{ $e.content.html || $e.content.text }}</content>
{{ else if $e.description }}
<content type="html" src="{{ $e.link }}">{{ $e.description }}</content>
{{ /if }}
{{ if typeof $e.category === 'string' }}
<category term="{{ $e.category }}"></category>
{{ else }}
{{ each $e.category $c }}
<category term="{{ $c }}"></category>
{{ /each }}
{{ /if }}
</entry>
{{ /each }}
</feed>

37
lib/views/atom.tsx Normal file
View File

@@ -0,0 +1,37 @@
import type { FC } from 'hono/jsx';
import { Data } from '@/types';
const RSS: FC<{ data: Data }> = ({ data }) => (
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:rsshub="http://rsshub.app/xml/schemas">
<title>{data.title || 'RSSHub'}</title>
<link href={data.link || 'https://docs.rsshub.app'} />
<id>{ data.id || data.link }</id>
<subtitle>{data.description || data.title} - Made with love by RSSHub(https://github.com/DIYgod/RSSHub)</subtitle>
<generator>RSSHub</generator>
<webMaster>i@diygod.me (DIYgod)</webMaster>
<language>{data.language || 'en'}</language>
<updated>{data.lastBuildDate}</updated>
<author>
<name>{data.author || 'RSSHub'}</name>
</author>
{data.item?.map((item) => (
<entry>
<title>{item.title}</title>
<content type="html" src={item.link}>{item.description}</content>
<link href={item.link} />
<id>{item.guid || item.link || item.title}</id>
{item.pubDate && <published>{item.pubDate}</published>}
{item.updated && <updated>{item.updated || item.pubDate}</updated>}
{item.author && <author><name>{item.author}</name></author>}
{typeof item.category === 'string' ? <category term={item.category}></category> : item.category?.map((c) => <category term={c}></category>)}
{item.media &&
Object.entries(item.media).map(([key, value]) => {
const Tag = `media:${key}`;
return <Tag {...value} />;
})}
</entry>
))}
</feed>
);
export default RSS;

View File

@@ -1,60 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"
{{ if itunes_author || itunes_category || (item && item.some((i) => (i.itunes_item_image || i.itunes_duration))) }}
xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
{{ /if }}
{{ if item && item.some((i) => i.media) }}
xmlns:media="http://search.yahoo.com/mrss/"
{{ /if }}
>
<channel>
<title><![CDATA[{{@ title || 'RSSHub' }}]]></title>
<link>{{ link || 'https://docs.rsshub.app' }}</link>
<atom:link href="{{ atomlink }}" rel="self" type="application/rss+xml" />
<description><![CDATA[{{@ description || title }} - Made with love by RSSHub(https://github.com/DIYgod/RSSHub)]]></description>
<generator>RSSHub</generator>
<webMaster>i@diygod.me (DIYgod)</webMaster>
{{ if itunes_author }}<itunes:author>{{ itunes_author }}</itunes:author>{{ /if }}
{{ if itunes_category }}<itunes:category text="{{ itunes_category }}"/>{{ /if }}
{{ if itunes_author }}<itunes:explicit>{{ itunes_explicit || 'false' }}</itunes:explicit>{{ /if }}
<language>{{ language || 'zh-cn' }}</language>
{{ if image }}
<image>
<url>{{ image }}</url>
<title><![CDATA[{{@ title || 'RSSHub' }}]]></title>
<link>{{ link }}</link>
</image>
{{ /if }}
<lastBuildDate>{{ lastBuildDate }}</lastBuildDate>
<ttl>{{ ttl }}</ttl>
{{ each item }}
<item>
<title><![CDATA[{{@ $value.title }}]]></title>
<description><![CDATA[{{@ $value.description || $value.title }}]]></description>
{{ if $value.pubDate }}<pubDate>{{ $value.pubDate }}</pubDate>{{ /if }}
<guid isPermaLink="false">{{ $value.guid || $value.link || $value.title }}</guid>
<link>{{ $value.link }}</link>
{{ if $value.itunes_item_image }}<itunes:image href="{{ $value.itunes_item_image }}"/>{{ /if }}
{{ if $value.enclosure_url }}<enclosure url="{{ $value.enclosure_url }}" {{ if $value.enclosure_length }}length="{{ $value.enclosure_length }}"{{ /if }} {{ if $value.enclosure_type }}type="{{ $value.enclosure_type }}"{{ /if }} />{{ /if }}
{{ if $value.itunes_duration }}<itunes:duration>{{ $value.itunes_duration }}</itunes:duration> {{ /if }}
{{ if $value.author }}<author><![CDATA[{{ $value.author }}]]></author>{{ /if }}
{{ if typeof $value.category === 'string' }}
<category>{{ $value.category }}</category>
{{ else }}
{{ each $value.category $c }}
<category>{{ $c }}</category>
{{ /each }}
{{ /if }}
{{ if $value.media }}
{{ each $value.media $tag }}
<media:{{ $index }}
{{ each $tag $data }}
{{ $index }}="{{ $data }}"
{{ /each }}
></media:{{ $index }}>
{{ /each }}
{{ /if }}
</item>
{{ /each }}
</channel>
</rss>

53
lib/views/rss.tsx Normal file
View File

@@ -0,0 +1,53 @@
import type { FC } from 'hono/jsx';
import { Data } from '@/types';
const RSS: FC<{ data: Data }> = ({ data }) => {
const hasItunes = data.itunes_author || data.itunes_category || (data.item && data.item.some((i) => i.itunes_item_image || i.itunes_duration));
const hasMedia = data.item?.some((i) => i.media);
return (
<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:itunes={hasItunes ? 'http://www.itunes.com/dtds/podcast-1.0.dtd' : undefined} xmlns:media={hasMedia ? 'http://search.yahoo.com/mrss/' : undefined} version="2.0">
<channel>
<title>{data.title || 'RSSHub'}</title>
<link>{data.link || 'https://docs.rsshub.app'}</link>
<atom:link href={data.atomlink} rel="self" type="application/rss+xml" />
<description>{data.description || data.title} - Made with love by RSSHub(https://github.com/DIYgod/RSSHub)</description>
<generator>RSSHub</generator>
<webMaster>i@diygod.me (DIYgod)</webMaster>
{data.itunes_author && <itunes:author>{data.itunes_author}</itunes:author>}
{data.itunes_category && <itunes:category text={data.itunes_category} />}
{data.itunes_author && <itunes:explicit>{data.itunes_explicit || 'false'}</itunes:explicit>}
<language>{data.language || 'en'}</language>
{data.image && (
<image>
<url>{data.image}</url>
<title>{data.title || 'RSSHub'}</title>
<link>{data.link}</link>
</image>
)}
<lastBuildDate>{data.lastBuildDate}</lastBuildDate>
<ttl>{data.ttl}</ttl>
{data.item?.map((item) => (
<item>
<title>{item.title}</title>
<description>{item.description}</description>
<link>{item.link}</link>
<guid isPermaLink="false">{item.guid || item.link || item.title}</guid>
{item.pubDate && <pubDate>{item.pubDate}</pubDate>}
{item.author && <author>{item.author}</author>}
{item.itunes_item_image && <itunes:image href={item.itunes_item_image} />}
{item.enclosure_url && <enclosure url={item.enclosure_url} length={item.enclosure_length} type={item.enclosure_type} />}
{item.itunes_duration && <itunes:duration>{item.itunes_duration}</itunes:duration>}
{typeof item.category === 'string' ? <category>{item.category}</category> : item.category?.map((c) => <category>{c}</category>)}
{item.media && Object.entries(item.media).map(([key, value]) => {
const Tag = `media:${key}`;
return <Tag {...value} />;
})}
</item>
))}
</channel>
</rss>
);
};
export default RSS;

View File

@@ -71,7 +71,7 @@
"git-rev-sync": "3.0.2",
"googleapis": "134.0.0",
"got": "11.8.6",
"hono": "4.1.2",
"hono": "4.1.3",
"html-to-text": "9.0.5",
"https-proxy-agent": "7.0.4",
"iconv-lite": "0.6.3",

682
pnpm-lock.yaml generated
View File

File diff suppressed because it is too large Load Diff