Not Having My Own Server Anymore Problems
Since I canceled the virtual private server account I used to run backend services on I've found myself forced to consider alternative ways to accomplish tasks that require a server. This page at the time of writing this is hosted on Cloudflare Pages, which is comparable to Github Pages, and is free hosting for static pages. Because this website is built locally with a custom static site generator, it can easily be pushed up to one of these simple page hosting services free of charge.
A website with data that is online and allows users to interact with, add to, modify, and sometimes delete that data (like posting on a site) isn't possible to accomplish with only a static page host; hence why the old imageboard went offline and was archived when I shut off the VPS.
Under the hood that server powered a lot of things you might not have known about: like the cors proxy server that allowed me to embed content into the page when users posted Bandcamp links. I'm not going to bore you with Cross Origin Response Sharing policy, but suffice to say, there are sometimes reasons that you require a middleman server between a client app and a host server.
Latest Feature Needed a Server
One of the latest features on the site is injecting external posts from guest authors' main blogs into their author profiles here on goeshard.org. You can see an example of this in action on these two author pages:
After loading these pages and waiting a brief moment, links to posts from the author's respective site will be injected onto the page using their RSS files. However because of the whole CORS thing, I'm unable to get this to work on sites that are not configured to allow me to do it; just like Bandcamp was not configured to allow me to do it.
Only this time I don't have a server to solve this problem with. So how did I do it?
Enter the Cloudflare Worker
A Cloudflare Worker is a sister product to Pages. If you look at the pages for both products, you'll notice immediately that the pitch for Pages is very simple: the goal it accomplishes is very obvious. On the other hand, the pitch for workers is loaded with buzzwords and marketing noise.
"Cloudflare is your AI Cloud with compute, AI inference, and storage — letting you ship applications instead of managing infrastructure."
No one knows what that means, not even developers.
You can get a better idea of what a worker is useful for from looking at the examples in the documentation. Some things all of these examples have in common is:
- Server side Javascript with no database requirement.
- Server side code is a proxy between a client and another host.
That is what a worker is good for.
Example: feed.goeshard.org
Since I already have to use a cors proxy server to do this, why not just handle parsing the XML and returning JSON on the worker as well. It will end up being a neat public API.
Try these endpoints used in the views above
Here's the code that gets these responses.
import { XMLParser } from "fast-xml-parser";
const RSS = {
RisingThumb: "https://risingthumb.xyz/Writing/Blog/index.rss",
dannarchy: "https://dannarchy.com/en/rss.xml",
};
export default {
async fetch(request, env, ctx) {
// determining the key from the url: https://feed.goeshard.org/<key>
const url = new URL(request.url);
const key = url.pathname.slice(1);
// if no rss file defined for key interrupt
const rssUrl = RSS[key];
if (!rssUrl) {
return new Response("Invalid feed key", { status: 400 });
}
const opts = OPTIONS[key] || {};
// return cached response if exists
const cache = caches.default;
const cacheKey = new Request(rssUrl);
let response = await cache.match(cacheKey);
if (response) return response;
// get the XML for feed
const res = await fetch(rssUrl);
const text = await res.text();
const parser = new XMLParser({
ignoreAttributes: false,
attributeNamePrefix: "",
});
const xmlData = parser.parse(text);
const items = xmlData.rss.channel.item;
const topItems = items.slice(0, 10).map((item) => {
return {
title: item.title,
link: item.link,
pubDate: new Date(item.pubDate).toISOString(),
description: item.description,
};
});
response = new Response(JSON.stringify(topItems), {
headers: {
"Content-Type": "application/json",
// this header means any origin would be accepted.
// normally you tightly configure this for a proxy that will handle any url
// and whitelist from what domains the app can be used.
// mine only works on specific links defined by key anyway
// and I want others to be able to use this API so it's fine.
"Access-Control-Allow-Origin": "*",
},
});
// This is where we set the time to hold in cache.
// It prevents excessive requests on the involved hosts
response.headers.append("Cache-Control", "s-maxage=300");
ctx.waitUntil(cache.put(cacheKey, response.clone()));
return response;
},
};
There will eventually be more supported RSS files on the feed.
Some code is omitted which is not necessary to show an example of a CF Worker.