Skip to content
Merged
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
12 changes: 12 additions & 0 deletions src/layouts/BlogLayout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,18 @@ const authorX = author?.links?.find(l => l.name === 'X')?.id;
<link rel="icon" href="/favicon.svg" sizes="any" type="image/svg+xml" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<link rel="sitemap" href="/sitemap-index.xml" />
<link
rel="alternate"
type="application/rss+xml"
title="Blogs"
href="/rss.xml"
/>
<link
rel="alternate"
type="application/rss+xml"
title={`Blogs by ${blog.data.author}`}
href={`/${blog.data.author}/rss.xml`}
/>
<SEO
charset="UTF-8"
title={title}
Expand Down
6 changes: 6 additions & 0 deletions src/layouts/Layout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ const meta = {
<link rel="icon" href="/favicon.svg" sizes="any" type="image/svg+xml" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<link rel="sitemap" href="/sitemap-index.xml" />
<link
rel="alternate"
type="application/rss+xml"
title="Blogs"
href="/rss.xml"
/>
<SEO
charset="UTF-8"
title={meta.title}
Expand Down
115 changes: 115 additions & 0 deletions src/lib/getFeed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { type RSSOptions } from '@astrojs/rss';
import { getCollection } from 'astro:content';
import siteInfo from '@/data/siteInfo';

function extractImageUrl(body: string) {
if (!body) return null;
const relativeImages = [];
const absoluteImages = [];
const imgTagRegex = /<img[^>]+src=["']([^"']+)["'][^>]*>/g;
const mdImageRegex = /!\[[^\]]*\]\(([^)]+)\)/g;

const imgMatches = [...body.matchAll(imgTagRegex)];
const mdMatches = [...body.matchAll(mdImageRegex)];
const allUrls = [
...imgMatches.map(match => match[1]),
...mdMatches.map(match => match[1]),
];

if (allUrls.length === 0) return null;

for (const url of allUrls) {
const isRelative = url.startsWith('/');
let fullUrl = url;
const ext = fullUrl.split('?')[0].split('.').pop()?.toLowerCase();
let type;

switch (ext) {
case 'jpg':
case 'jpeg':
type = 'image/jpeg';
break;
case 'png':
type = 'image/png';
break;
case 'gif':
type = 'image/gif';
break;
case 'webp':
type = 'image/webp';
break;
case 'svg':
type = 'image/svg+xml';
break;
default:
continue;
}

const imageObj = {
url: fullUrl,
type: type,
length: 0,
};

if (isRelative) {
relativeImages.push(imageObj);
} else {
absoluteImages.push(imageObj);
}
}

const images = [...relativeImages, ...absoluteImages];
return images.length > 0 ? images : null;
}

async function getFeed(
siteUrl: string,
maxItems?: number,
filter?: { tag?: string; author?: string }
) {
let blogs = await getCollection('blog');
if (filter) {
if (filter.author) {
blogs = blogs.filter(blog => blog.data.author === filter.author);
}
if (filter.tag) {
blogs = blogs.filter(blog => blog.data.tags?.includes(filter.tag!));
}
}
blogs = blogs
.sort(
(a, b) =>
new Date(b.data.pubDate).getTime() - new Date(a.data.pubDate).getTime()
)
.slice(0, maxItems || 50);
let title = siteInfo.appName;
if (filter?.author) {
title += ` - Author: ${filter.author}`;
}
if (filter?.tag) {
title += ` - Tag: ${filter.tag}`;
}
const rssOptions: RSSOptions = {
title: title,
description: siteInfo.description,
site: siteUrl,
items: blogs.map(blog => ({
title: blog.data.title,
pubDate: new Date(blog.data.pubDate),
description: blog.data.description,
link: blog.data.externalUrl
? blog.data.externalUrl
: `/blog/${blog.slug}/`,
categories: blog.data.tags,
enclosure: extractImageUrl(blog.body)?.[0] || {
url: `/og/${blog.slug}.png`,
type: 'image/png',
length: 0,
},
})),
customData: `<language>ja-jp</language>`,
};
return rssOptions;
}

export { getFeed };
21 changes: 3 additions & 18 deletions src/pages/[author]/rss.xml.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import rss from '@astrojs/rss';
import { getCollection } from 'astro:content';
import siteInfo from '@/data/siteInfo';
import { getFeed } from '@/lib/getFeed';
import member from '@/data/member';

export async function getStaticPaths() {
Expand All @@ -13,21 +12,7 @@ export async function getStaticPaths() {

export async function GET(context) {
const { author } = context.params;
const blogs = await getCollection('blog');
const feedOption = await getFeed(context.site, 50, { author });

const filteredBlogs = blogs.filter(blog => blog.data.author === author);

return rss({
title: `${siteInfo.appName} - ${author}`,
description: `${author}の記事一覧`,
site: context.site,
items: filteredBlogs.map(blog => ({
title: blog.data.title,
pubDate: blog.data.pubDate,
description: blog.data.description,
customData: blog.data.customData,
link: `/blog/${blog.slug}/`,
})),
customData: '<language>ja-jp</language>',
});
return rss(feedOption);
}
19 changes: 3 additions & 16 deletions src/pages/rss.xml.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,7 @@
import rss from '@astrojs/rss';
import { getCollection } from 'astro:content';
import siteInfo from '@/data/siteInfo';
import { getFeed } from '@/lib/getFeed';

export async function GET(context) {
const blogs = await getCollection('blog');
return rss({
title: siteInfo.appName,
description: siteInfo.description,
site: context.site,
items: blogs.map(blog => ({
title: blog.data.title,
pubDate: blog.data.pubDate,
description: blog.data.description,
customData: blog.data.customData,
link: `/blog/${blog.slug}/`,
})),
customData: `<language>ja-jp</language>`,
});
const feedOption = await getFeed(context.site);
return rss(feedOption);
}