mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2026-05-26 20:49:55 +00:00
Add RSS and JSON feeds
This commit is contained in:
@@ -2,7 +2,14 @@
|
||||
import Hero from "$lib/Hero.svelte";
|
||||
import SvelteSeo from "svelte-seo";
|
||||
import Homepage from "Notes/Website Homepage.md";
|
||||
import { SITE_URL, SITE_TITLE } from "$lib/metadata";
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<link rel="alternate" type="application/rss+xml" title={SITE_TITLE} href={SITE_URL + "/blog/rss.xml"}>
|
||||
<link rel="alternate" type="application/feed+json" title={SITE_TITLE} href={SITE_URL + "/blog/feed.json"}>
|
||||
</svelte:head>
|
||||
|
||||
<SvelteSeo
|
||||
title="Jade Ellis"
|
||||
description="Student, Creative & Computer Scientist. See what I'm doing."
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { page } from "$app/stores";
|
||||
import { SITE_URL } from "$lib/metadata";
|
||||
import { SITE_URL, SITE_TITLE } from "$lib/metadata";
|
||||
import SvelteSeo from "svelte-seo";
|
||||
|
||||
export let data;
|
||||
@@ -8,6 +8,11 @@
|
||||
// $: console.log(data);
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<link rel="alternate" type="application/rss+xml" title={SITE_TITLE} href={SITE_URL + "/blog/rss.xml"}>
|
||||
<link rel="alternate" type="application/feed+json" title={SITE_TITLE} href={SITE_URL + "/blog/feed.json"}>
|
||||
</svelte:head>
|
||||
|
||||
<SvelteSeo
|
||||
title=""
|
||||
canonical={SITE_URL + "/blog"} />
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import { browser } from "$app/environment";
|
||||
import SvelteSeo from "svelte-seo";
|
||||
export let data;
|
||||
import { SITE_URL } from "$lib/metadata";
|
||||
import { SITE_URL, SITE_TITLE } from "$lib/metadata";
|
||||
import Toc from "$lib/Toc.svelte";
|
||||
// let GhReleasesDownload: Promise<any>;
|
||||
// if (data.ghReleaseData) {
|
||||
@@ -31,6 +31,11 @@
|
||||
};
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<link rel="alternate" type="application/rss+xml" title={SITE_TITLE} href={SITE_URL + "/blog/rss.xml"}>
|
||||
<link rel="alternate" type="application/feed+json" title={SITE_TITLE} href={SITE_URL + "/blog/feed.json"}>
|
||||
</svelte:head>
|
||||
|
||||
<SvelteSeo
|
||||
title={data.post.title}
|
||||
description={data.post.description}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
import { pages } from '../../posts'
|
||||
|
||||
import type Feed from '@json-feed-types/1_1'
|
||||
import {
|
||||
SITE_DEFAULT_DESCRIPTION,
|
||||
SITE_TITLE,
|
||||
SITE_URL,
|
||||
RSS_DEFAULT_POSTS_PER_PAGE
|
||||
} from '$lib/metadata';
|
||||
|
||||
import { error } from '@sveltejs/kit'
|
||||
// import { base } from '$app/paths';
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
export async function GET({ params, url}) {
|
||||
let dateParts = params.date.split(/[\/-]/).filter((s)=>s.length !== 0).map((p) => parseInt(p, 10))
|
||||
if (dateParts.length > 3) {
|
||||
throw error(404, 'Feed not found (bad date)')
|
||||
}
|
||||
|
||||
const selectedPages = dateParts.length ? pages
|
||||
.filter((post) => {
|
||||
let date = new Date(post.date)
|
||||
return (
|
||||
(!dateParts[0] || date.getFullYear() == dateParts[0]) &&
|
||||
(!dateParts[1] || date.getMonth()+1 == dateParts[1]) &&
|
||||
(!dateParts[2] || date.getDate() == dateParts[2])
|
||||
)
|
||||
}) : pages;
|
||||
const headers = {
|
||||
'Cache-Control': 'max-age=0, s-maxage=3600',
|
||||
'Content-Type': 'application/feed+json'
|
||||
};
|
||||
return new Response(await getJsonFeed(url.href, selectedPages), { headers });
|
||||
}
|
||||
|
||||
const AUTHOR = "Jade Ellis"
|
||||
// prettier-ignore
|
||||
async function getJsonFeed(selfUrl: string, pages: any[]): Promise<string> {
|
||||
|
||||
const feed: Feed = {
|
||||
version: 'https://jsonfeed.org/version/1.1',
|
||||
title: SITE_TITLE,
|
||||
icon: `${SITE_URL}/android-chrome-256x256.png`,
|
||||
home_page_url: SITE_URL,
|
||||
description: SITE_DEFAULT_DESCRIPTION,
|
||||
feed_url: selfUrl,
|
||||
authors: [{ name: AUTHOR }],
|
||||
items: [
|
||||
],
|
||||
}
|
||||
|
||||
for await (const post of pages) {
|
||||
const title = post.title;
|
||||
const pubDate = post.date
|
||||
const postUrl = SITE_URL + "/blog/" + post.canonical
|
||||
// const postHtml =
|
||||
const summary = post.description;
|
||||
let item: typeof feed.items[number] = {
|
||||
id: post.postUrl,
|
||||
title,
|
||||
url: postUrl,
|
||||
date_published: pubDate,
|
||||
summary,
|
||||
content_text: "",
|
||||
}
|
||||
feed.items.push(item)
|
||||
}
|
||||
|
||||
return JSON.stringify(feed)
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import { pages } from '../../posts'
|
||||
|
||||
import {
|
||||
SITE_DEFAULT_DESCRIPTION,
|
||||
SITE_TITLE,
|
||||
SITE_URL,
|
||||
RSS_DEFAULT_POSTS_PER_PAGE
|
||||
} from '$lib/metadata';
|
||||
import rssStyle from "./rss-style.xsl?url"
|
||||
import rssStyleCss from "./styles.css?url"
|
||||
import { create } from 'xmlbuilder2';
|
||||
import { error } from '@sveltejs/kit'
|
||||
// import { base } from '$app/paths';
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
export async function GET({ url, params }) {
|
||||
let dateParts = params.date.split(/[\/-]/).filter((s)=>s.length !== 0).map((p) => parseInt(p, 10))
|
||||
if (dateParts.length > 3) {
|
||||
throw error(404, 'Feed not found (bad date)')
|
||||
}
|
||||
|
||||
const selectedPages = dateParts.length ? pages
|
||||
.filter((post) => {
|
||||
console.log("filtering")
|
||||
let date = new Date(post.date)
|
||||
return (
|
||||
(!dateParts[0] || date.getFullYear() == dateParts[0]) &&
|
||||
(!dateParts[1] || date.getMonth()+1 == dateParts[1]) &&
|
||||
(!dateParts[2] || date.getDate() == dateParts[2])
|
||||
)
|
||||
}) : pages;
|
||||
const headers = {
|
||||
'Cache-Control': 'max-age=0, s-maxage=3600',
|
||||
'Content-Type': 'application/xml'
|
||||
};
|
||||
url.search = "";
|
||||
return new Response(await getRssXml(url.href, selectedPages), { headers });
|
||||
}
|
||||
|
||||
const AUTHOR = "Jade Ellis"
|
||||
// prettier-ignore
|
||||
async function getRssXml(selfUrl: string, pages: any[]): Promise<string> {
|
||||
// const rssUrl = `${SITE_URL}/rss.xml`;
|
||||
const root = create({ version: '1.0', encoding: 'utf-8' })
|
||||
.ins('xml-stylesheet', `type="text/xsl" href="${rssStyle}"`)
|
||||
.ele('feed', {
|
||||
xmlns: 'http://www.w3.org/2005/Atom',
|
||||
"xmlns:jade": 'http://jade.ellis.link',
|
||||
})
|
||||
.ele('jade:link', { rel:"stylesheet", href: rssStyleCss }).up()
|
||||
.ele('title').txt(SITE_TITLE).up()
|
||||
.ele('link', { href: SITE_URL }).up()
|
||||
.ele('link', { rel: 'self', href: selfUrl }).up()
|
||||
.ele('updated').txt(new Date().toISOString()).up()
|
||||
.ele('id').txt(SITE_URL).up()
|
||||
.ele('author')
|
||||
.ele('name').txt(AUTHOR).up()
|
||||
.up()
|
||||
.ele('subtitle').txt(SITE_DEFAULT_DESCRIPTION).up()
|
||||
|
||||
for await (const post of pages) {
|
||||
const title = post.title;
|
||||
const pubDate = post.date
|
||||
const postUrl = SITE_URL + "/blog/" + post.canonical
|
||||
// const postHtml =
|
||||
const summary = post.description;
|
||||
|
||||
root.ele('entry')
|
||||
.ele('title').txt(title).up()
|
||||
.ele('link', { href: postUrl }).up()
|
||||
.ele('published').txt(pubDate).up()
|
||||
.ele('id').txt(postUrl).up()
|
||||
// .ele('content', { type: 'html' }).txt(postHtml).up()
|
||||
.ele('summary').txt(summary).up()
|
||||
.up();
|
||||
}
|
||||
|
||||
return root.end()
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
||||
xmlns:atom="http://www.w3.org/2005/Atom" xmlns:jade="http://jade.ellis.link">
|
||||
<xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes"/>
|
||||
<xsl:template match="/">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
|
||||
<head>
|
||||
<title>
|
||||
RSS Feed |
|
||||
<xsl:value-of select="/atom:feed/atom:title"/>
|
||||
</title>
|
||||
<meta charset="utf-8"/>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<link rel="stylesheet" >
|
||||
<xsl:attribute name="href">
|
||||
<xsl:value-of select="/atom:feed/jade:link/@href[1]" />
|
||||
</xsl:attribute></link>
|
||||
</head>
|
||||
<body>
|
||||
<main class="main container">
|
||||
|
||||
<dk-alert-box type="info">
|
||||
<strong>This is an RSS feed</strong>. Subscribe by copying
|
||||
the URL from the address bar into your newsreader. Visit <a
|
||||
href="https://aboutfeeds.com">About Feeds</a> to learn more
|
||||
and get started. It’s free.
|
||||
</dk-alert-box>
|
||||
<div class="py-7">
|
||||
<h1 class="flex items-start">
|
||||
<!-- https://commons.wikimedia.org/wiki/File:Feed-icon.svg -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
|
||||
class="inline-icon"
|
||||
style="flex-shrink: 0; width: 1em; height: 1em;"
|
||||
viewBox="0 0 256 256">
|
||||
<defs>
|
||||
<linearGradient x1="0.085" y1="0.085" x2="0.915" y2="0.915"
|
||||
id="RSSg">
|
||||
<stop offset="0.0" stop-color="#E3702D"/>
|
||||
<stop offset="0.1071" stop-color="#EA7D31"/>
|
||||
<stop offset="0.3503" stop-color="#F69537"/>
|
||||
<stop offset="0.5" stop-color="#FB9E3A"/>
|
||||
<stop offset="0.7016" stop-color="#EA7C31"/>
|
||||
<stop offset="0.8866" stop-color="#DE642B"/>
|
||||
<stop offset="1.0" stop-color="#D95B29"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="256" height="256" rx="55" ry="55" x="0" y="0"
|
||||
fill="#CC5D15"/>
|
||||
<rect width="246" height="246" rx="50" ry="50" x="5" y="5"
|
||||
fill="#F49C52"/>
|
||||
<rect width="236" height="236" rx="47" ry="47" x="10" y="10"
|
||||
fill="url(#RSSg)"/>
|
||||
<circle cx="68" cy="189" r="24" fill="#FFF"/>
|
||||
<path
|
||||
d="M160 213h-34a82 82 0 0 0 -82 -82v-34a116 116 0 0 1 116 116z"
|
||||
fill="#FFF"/>
|
||||
<path
|
||||
d="M184 213A140 140 0 0 0 44 73 V 38a175 175 0 0 1 175 175z"
|
||||
fill="#FFF"/>
|
||||
</svg>
|
||||
RSS Feed Preview
|
||||
</h1>
|
||||
<h2><xsl:value-of select="/atom:feed/atom:title"/></h2>
|
||||
<p>
|
||||
<xsl:value-of select="/atom:feed/atom:subtitle"/>
|
||||
</p>
|
||||
<a>
|
||||
<xsl:attribute name="href">
|
||||
<xsl:value-of select="/atom:feed/atom:link[1]/@href"/>
|
||||
</xsl:attribute>
|
||||
Visit Website →
|
||||
</a>
|
||||
|
||||
<h2>Recent blog posts</h2>
|
||||
<xsl:for-each select="/atom:feed/atom:entry">
|
||||
<article>
|
||||
|
||||
<h3 class="text-4 font-bold">
|
||||
<a>
|
||||
<xsl:attribute name="href">
|
||||
<xsl:value-of select="atom:link/@href"/>
|
||||
</xsl:attribute>
|
||||
<xsl:value-of select="atom:title"/>
|
||||
</a>
|
||||
</h3>
|
||||
<span class="quiet">
|
||||
Published on
|
||||
<xsl:value-of select="substring(atom:published, 0, 11)" />
|
||||
</span>
|
||||
|
||||
<p class="p-summart"><xsl:value-of select="atom:summary"/></p>
|
||||
</article>
|
||||
</xsl:for-each>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
</xsl:template>
|
||||
</xsl:stylesheet>
|
||||
@@ -0,0 +1,34 @@
|
||||
@import url("$lib/styles.css");
|
||||
|
||||
body {
|
||||
padding-block: var(--spacing);
|
||||
}
|
||||
.inline-icon {
|
||||
margin-right: 0.75rem
|
||||
}
|
||||
h1 {
|
||||
/* font-size: 3.8rem; */
|
||||
line-height: 1;
|
||||
/* font-weight: 800; */
|
||||
/* margin-bottom: 4rem; */
|
||||
text-wrap: balance;
|
||||
}
|
||||
.flex {
|
||||
display: flex
|
||||
}
|
||||
|
||||
.flex-col {
|
||||
flex-direction: column
|
||||
}
|
||||
|
||||
.flex-wrap {
|
||||
flex-wrap: wrap
|
||||
}
|
||||
|
||||
.items-center {
|
||||
align-items: center
|
||||
}
|
||||
|
||||
.items-start {
|
||||
align-items: flex-start
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
import type Feed from '@json-feed-types/1_1'
|
||||
import {
|
||||
SITE_DEFAULT_DESCRIPTION,
|
||||
SITE_TITLE,
|
||||
SITE_URL,
|
||||
RSS_DEFAULT_POSTS_PER_PAGE
|
||||
} from '$lib/metadata';
|
||||
|
||||
import { create } from 'xmlbuilder2';
|
||||
// import { base } from '$app/paths';
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
export async function GET() {
|
||||
const headers = {
|
||||
'Cache-Control': 'max-age=0, s-maxage=3600',
|
||||
'Content-Type': 'application/feed+json'
|
||||
};
|
||||
return new Response(await getJsonFeed(), { headers });
|
||||
}
|
||||
|
||||
const AUTHOR = "Jade Ellis"
|
||||
// prettier-ignore
|
||||
async function getJsonFeed(): Promise<string> {
|
||||
const feedUrl = `${SITE_URL}/feed.json`;
|
||||
|
||||
const feed: Feed = {
|
||||
version: 'https://jsonfeed.org/version/1.1',
|
||||
title: SITE_TITLE,
|
||||
icon: `${SITE_URL}/android-chrome-256x256.png`,
|
||||
home_page_url: SITE_URL,
|
||||
description: SITE_DEFAULT_DESCRIPTION,
|
||||
feed_url: feedUrl,
|
||||
authors: [{ name: AUTHOR }],
|
||||
items: [
|
||||
],
|
||||
}
|
||||
// for await (const post of posts) {
|
||||
// const pubDate =
|
||||
// const postUrl =
|
||||
// const postHtml =
|
||||
// const summary = post.metadata.description;
|
||||
|
||||
// root.ele('entry')
|
||||
// .ele('title').txt(post.metadata.title).up()
|
||||
// .ele('link', { href: postUrl }).up()
|
||||
// .ele('updated').txt(pubDate).up()
|
||||
// .ele('id').txt(postUrl).up()
|
||||
// .ele('content', { type: 'html' }).txt(postHtml).up()
|
||||
// .ele('summary').txt(summary).up()
|
||||
// .up();
|
||||
// }
|
||||
|
||||
return JSON.stringify(feed)
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
import {
|
||||
SITE_DEFAULT_DESCRIPTION,
|
||||
SITE_TITLE,
|
||||
SITE_URL,
|
||||
RSS_DEFAULT_POSTS_PER_PAGE
|
||||
} from '$lib/metadata';
|
||||
|
||||
import { create } from 'xmlbuilder2';
|
||||
// import { base } from '$app/paths';
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
export async function GET() {
|
||||
const headers = {
|
||||
'Cache-Control': 'max-age=0, s-maxage=3600',
|
||||
'Content-Type': 'application/xml'
|
||||
};
|
||||
return new Response(await getRssXml(), { headers });
|
||||
}
|
||||
|
||||
const AUTHOR = "Jade Ellis"
|
||||
// prettier-ignore
|
||||
async function getRssXml(): Promise<string> {
|
||||
const rssUrl = `${SITE_URL}/rss.xml`;
|
||||
const root = create({ version: '1.0', encoding: 'utf-8' })
|
||||
.ele('feed', {
|
||||
xmlns: 'http://www.w3.org/2005/Atom',
|
||||
})
|
||||
.ele('title').txt(SITE_TITLE).up()
|
||||
.ele('link', { href: SITE_URL }).up()
|
||||
.ele('link', { rel: 'self', href: rssUrl }).up()
|
||||
.ele('updated').txt(new Date().toISOString()).up()
|
||||
.ele('id').txt(SITE_URL).up()
|
||||
.ele('author')
|
||||
.ele('name').txt(AUTHOR).up()
|
||||
.up()
|
||||
.ele('subtitle').txt(SITE_DEFAULT_DESCRIPTION).up()
|
||||
|
||||
// for await (const post of posts) {
|
||||
// const pubDate =
|
||||
// const postUrl =
|
||||
// const postHtml =
|
||||
// const summary = post.metadata.description;
|
||||
|
||||
// root.ele('entry')
|
||||
// .ele('title').txt(post.metadata.title).up()
|
||||
// .ele('link', { href: postUrl }).up()
|
||||
// .ele('updated').txt(pubDate).up()
|
||||
// .ele('id').txt(postUrl).up()
|
||||
// .ele('content', { type: 'html' }).txt(postHtml).up()
|
||||
// .ele('summary').txt(summary).up()
|
||||
// .up();
|
||||
// }
|
||||
|
||||
return root.end()
|
||||
}
|
||||
Reference in New Issue
Block a user