From fc05bcc9cec407ec79a2e01d9f5a1500347cd268 Mon Sep 17 00:00:00 2001 From: Andrea Vos Date: Tue, 18 Jun 2024 16:22:05 +0000 Subject: [PATCH] Blog feed --- middleware/atom.js | 49 ++++++++++++++++++++++++++++++++++++++++++++++ nuxt.config.ts | 1 + package.json | 1 + pnpm-lock.yaml | 21 ++++++++++++++++++++ routes/blog.vue | 14 ++++++++++--- server/no-ssr.ts | 2 +- 6 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 middleware/atom.js diff --git a/middleware/atom.js b/middleware/atom.js new file mode 100644 index 000000000..6a1d531e8 --- /dev/null +++ b/middleware/atom.js @@ -0,0 +1,49 @@ +import { Feed } from 'feed'; +import parseMarkdown from '../src/parseMarkdown.js'; +import fetch from 'node-fetch'; + +export default async function ({ app, route, res }) { + if (!process.server || route.path !== '/blog.atom') { + return; + } + + const posts = await (await fetch(`${process.env.BASE_URL}/api/blog`)).json(); + + const feed = new Feed({ + title: `${app.$t('title')} • ${app.$t('links.blog')}`, + description: app.$t('description'), + id: process.env.BASE_URL, + link: `${process.env.BASE_URL}/blog.atom`, + language: app.$config.locale, + image: `${process.env.BASE_URL}/icon.png`, + favicon: `${process.env.BASE_URL}/icon.png`, + updated: new Date(posts[0].date), + }); + + for (const post of posts) { + const parsed = await parseMarkdown( + (await import(`../locale/${app.$config.locale}/blog/${post.slug}.md`)).default, + app.$translator, + ); + + feed.addItem({ + title: post.title, + id: `${process.env.BASE_URL}/${app.$config.links.blogRoute}/${post.slug}`, + link: `${process.env.BASE_URL}/${app.$config.links.blogRoute}/${post.slug}`, + description: parsed.intro, + content: parsed.content, + author: post.authors.map((author) => ({ + name: author, + link: author.startsWith('@') ? `${process.env.BASE_URL}/${author}` : undefined, + })), + date: new Date(post.date), + image: post.hero ? `${process.env.BASE_URL}${post.hero}` : undefined, + }); + } + + + res.setHeader('Content-Type', 'application/rss+xml; charset=utf-8'); + res.end(feed.atom1()); + + return new Promise(() => {}); // halt execution +} diff --git a/nuxt.config.ts b/nuxt.config.ts index 6239ab287..d2d8977d5 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -628,6 +628,7 @@ const nuxtConfig: NuxtConfig = { routes.push({ name: 'all', path: '*', component: resolve(__dirname, 'routes/pronoun.vue') }); }, + middleware: 'atom', }, hooks: { render: { diff --git a/package.json b/package.json index 97a8ce9b2..818c3de07 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "dotenv": "^8.2.0", "express": "^4.17.1", "express-session": "^1.17.1", + "feed": "^4.2.2", "generic-diff": "^1.0.1", "grant": "^5.4.22", "he-date": "^1.2.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d26d32458..34043ceb6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -74,6 +74,9 @@ dependencies: express-session: specifier: ^1.17.1 version: 1.17.3 + feed: + specifier: ^4.2.2 + version: 4.2.2 generic-diff: specifier: ^1.0.1 version: 1.0.1 @@ -8955,6 +8958,13 @@ packages: pend: 1.2.0 dev: false + /feed@4.2.2: + resolution: {integrity: sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==} + engines: {node: '>=0.4.0'} + dependencies: + xml-js: 1.6.11 + dev: false + /figgy-pudding@3.5.2: resolution: {integrity: sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==} deprecated: This module is no longer supported. @@ -14867,6 +14877,10 @@ packages: chokidar: 3.5.3 dev: true + /sax@1.4.1: + resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} + dev: false + /saxes@6.0.0: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} engines: {node: '>=v12.22.7'} @@ -17116,6 +17130,13 @@ packages: utf-8-validate: optional: true + /xml-js@1.6.11: + resolution: {integrity: sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==} + hasBin: true + dependencies: + sax: 1.4.1 + dev: false + /xml-name-validator@4.0.0: resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} engines: {node: '>=12'} diff --git a/routes/blog.vue b/routes/blog.vue index 28cd4debb..f0c4d5a2a 100644 --- a/routes/blog.vue +++ b/routes/blog.vue @@ -1,8 +1,16 @@