2023-02-15 22:00:06 +01:00
|
|
|
import Consola from 'consola';
|
|
|
|
|
|
|
|
|
|
import { getCookie } from 'cookies-next';
|
|
|
|
|
|
|
|
|
|
import { decode } from 'html-entities';
|
|
|
|
|
|
|
|
|
|
import { NextApiRequest, NextApiResponse } from 'next';
|
|
|
|
|
|
|
|
|
|
import Parser from 'rss-parser';
|
|
|
|
|
|
2023-03-30 23:15:08 +02:00
|
|
|
import { z } from 'zod';
|
2023-02-15 22:00:06 +01:00
|
|
|
import { getConfig } from '../../../../tools/config/getConfig';
|
|
|
|
|
import { IRssWidget } from '../../../../widgets/rss/RssWidgetTile';
|
2023-03-17 22:10:00 +01:00
|
|
|
import { Stopwatch } from '../../../../tools/shared/time/stopwatch.tool';
|
2023-02-15 22:00:06 +01:00
|
|
|
|
|
|
|
|
type CustomItem = {
|
|
|
|
|
'media:content': string;
|
|
|
|
|
enclosure: {
|
|
|
|
|
url: string;
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const parser: Parser<any, CustomItem> = new Parser({
|
|
|
|
|
customFields: {
|
|
|
|
|
item: ['media:content', 'enclosure'],
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
2023-03-30 23:15:08 +02:00
|
|
|
const getQuerySchema = z.object({
|
|
|
|
|
widgetId: z.string().uuid(),
|
|
|
|
|
});
|
|
|
|
|
|
2023-02-15 22:00:06 +01:00
|
|
|
export const Get = async (request: NextApiRequest, response: NextApiResponse) => {
|
|
|
|
|
const configName = getCookie('config-name', { req: request });
|
|
|
|
|
const config = getConfig(configName?.toString() ?? 'default');
|
|
|
|
|
|
2023-03-30 23:15:08 +02:00
|
|
|
const parseResult = getQuerySchema.safeParse(request.query);
|
|
|
|
|
|
|
|
|
|
if (!parseResult.success) {
|
|
|
|
|
response.status(400).json({ message: 'invalid query parameters, please specify the widgetId' });
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const rssWidget = config.widgets.find(
|
|
|
|
|
(x) => x.type === 'rss' && x.id === parseResult.data.widgetId
|
|
|
|
|
) as IRssWidget | undefined;
|
2023-02-15 22:00:06 +01:00
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
!rssWidget ||
|
|
|
|
|
!rssWidget.properties.rssFeedUrl ||
|
|
|
|
|
rssWidget.properties.rssFeedUrl.length < 1
|
|
|
|
|
) {
|
|
|
|
|
response.status(400).json({ message: 'required widget does not exist' });
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Consola.info('Requesting RSS feed...');
|
|
|
|
|
const stopWatch = new Stopwatch();
|
|
|
|
|
const feed = await parser.parseURL(rssWidget.properties.rssFeedUrl);
|
|
|
|
|
Consola.info(`Retrieved RSS feed after ${stopWatch.getEllapsedMilliseconds()} milliseconds`);
|
|
|
|
|
|
|
|
|
|
const orderedFeed = {
|
|
|
|
|
...feed,
|
|
|
|
|
items: feed.items
|
|
|
|
|
.map((item: { title: any; content: any }) => ({
|
|
|
|
|
...item,
|
|
|
|
|
title: item.title ? decode(item.title) : undefined,
|
|
|
|
|
content: decode(item.content),
|
|
|
|
|
enclosure: createEnclosure(item),
|
2023-02-28 20:35:57 +01:00
|
|
|
link: createLink(item),
|
2023-02-15 22:00:06 +01:00
|
|
|
}))
|
|
|
|
|
.sort((a: { pubDate: number }, b: { pubDate: number }) => {
|
|
|
|
|
if (!a.pubDate || !b.pubDate) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return a.pubDate - b.pubDate;
|
|
|
|
|
})
|
|
|
|
|
.slice(0, 20),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
response.status(200).json({
|
|
|
|
|
feed: orderedFeed,
|
|
|
|
|
success: orderedFeed?.items !== undefined,
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2023-02-28 20:35:57 +01:00
|
|
|
const createLink = (item: any) => {
|
|
|
|
|
if (item.link) {
|
|
|
|
|
return item.link;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return item.guid;
|
|
|
|
|
};
|
|
|
|
|
|
2023-02-15 22:00:06 +01:00
|
|
|
const createEnclosure = (item: any) => {
|
|
|
|
|
if (item.enclosure) {
|
|
|
|
|
return item.enclosure;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (item['media:content']) {
|
|
|
|
|
return {
|
|
|
|
|
url: item['media:content'].$.url,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return undefined;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default async (request: NextApiRequest, response: NextApiResponse) => {
|
|
|
|
|
if (request.method === 'GET') {
|
|
|
|
|
return Get(request, response);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response.status(405);
|
|
|
|
|
};
|