Setting up webmention with Cloudflare and AstroJS

Posted on Tue, 08 Aug 2023 , edited on Fri, 24 Nov 2023 #webmention #astro #cloudflare #github #POSSE #snippet

This is part of effort to own my content. Rather than posting the content elsewhere, post it on my platform instead (this blog).

Basic Idea

  1. get our data from webmentions.io, and store within src/content/webmentions folder,
  2. create new Astro collection called webmentions,
  3. consume the data - get the collection from step 2 and call wherever,
  4. push to GitHub,
  5. link Cloudflare with GitHub

1. Get data from webmentions.io

I’m using script from Jan Monschke with a bit of modification. This script will be executed by GitHub action and auto commit to specified folder (in this case, src/content/webmentions folder).

import * as https from "https";
import * as fs from "fs";
import { getSlugFromPathName, getFileName } from "../utils/common.js";

const { WEBMENTIONS_IO_TOKEN } = process.env;
const { HOSTNAME } = "ahmad.build";

async function fetchMentions() {
	const url = `https://webmention.io/api/mentions.jf2?domain=${HOSTNAME}&token=${WEBMENTIONS_IO_TOKEN}&per-page=999`;

	return new Promise((resolve, reject) => {
		// create Promise here
	}).then((response) => {
		return response.children;
	});
}

fetchMentions().then((webmentions) => {
	console.log(`Fetched ${webmentions.length} webmentions`);
	webmentions.forEach((webmention) => {
		// check if mention exist on disk. If exist, fetch new and overwrite. Else, create new mention.
		// write the mention to disk
	});
});

WEBMENTIONS_IO_TOKEN value need to be set on GitHub environment.

2. Create new Astro collection

To create new collection, we need to edit src/content/config.ts. Remember, Astro allow user to have multiple collections, e.g. notes, webmentions, activity etc. Case below, I’m creating new collection called webmentions.

const webmentionsSchema = z.object({
	url: z.string(),
	published: z.coerce.date(),
	"wm-property": z.string(),
	"wm-received": z.coerce.date(),
	content: z
		.object({
			text: z.string().optional(),
			html: z.string().optional(),
		})
		.optional(),
	author: z.object({
		type: z.string(),
		name: z.string(),
		photo: z.string(),
		url: z.string(),
	}),
});

const webmentions = defineCollection({
	type: "data",
	schema: z.array(webmentionsSchema),
});

3. Consume the webmention data

Now that the collection is ready, we can customize it to our liking. In my case, I’m getting like and reply. For that, create 2 component : WebmentionList.astro and WebmentionLike.astro

WebmentionList.astro
---

const { wm } = Astro.props;

let likeCount: any = [];
wm.map((wm: any) => {
	if (wm["wm-property"] === "like-of") {
		likeCount.push(wm);
	}
});

const hasLike = wm.some(function (wm: any) {
	return wm["wm-property"] === "like-of";
});
---
{
	wm.length > 0 && (
		<div class="webmentions">
			<div class="wm-header">
				<h3 id="webmentions">Webmentions</h3>

				{hasLike && (
					<div class="wm-like">
						<ul>
							{wm.map(
								(wm: any) =>
									wm["wm-property"] == "like-of" && <WebmentionLike {wm} />
							)}
						</ul>
						<WebmentionLikeCount {wm} />
					</div>
				)}
			</div>

			<ul class="wm-list">
				{wm.map((wm: any) => (
					<WebmentionCard {wm} />
				))}
			</ul>
		</div>
	)
}
WebmentionLike.astro
---
const { wm } = Astro.props;
const { photo, name, url } = wm.author;
---

{
	(
		<li>
			<a href={url}>
				<img class="avatar" width="32" height="32" src={photo} title={name} />
			</a>
		</li>
	)
}

4. Push to GitHub

Second last step is to push the code to GitHub. Don’t forget to set WEBMENTIONS_IO_TOKEN on GitHub environment.

5. Linked Cloudflare with GitHub

Last step is to create new site on Cloudflare and link the site with GitHub repository on previous step.

Credit

  1. https://sebastiandedeyne.com/adding-webmentions-to-my-blog/
  2. https://janmonschke.com/adding-webmentions-to-your-static-blog/