Skip to content

Commit

Permalink
Support default human readable URLs (#44)
Browse files Browse the repository at this point in the history
* Support default human readable URLs

* Normalize ULID slugs to uppercase
  • Loading branch information
zephraph authored Sep 12, 2024
1 parent 706585d commit 8e28f61
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 43 deletions.
7 changes: 3 additions & 4 deletions src/layouts/base.astro
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,19 @@ import { ViewTransitions } from "astro:transitions";
interface Props {
title: string;
id: string;
description?: string;
}
const { id, title } = Astro.props;
const { title, description } = Astro.props;
---

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<!-- <meta name="description" content="Astro description" /> -->
{description && <meta name="description" content={description} />}
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="/favicon.ico" />
<link rel="canonical" href=`${Astro.site}${id}` />
<meta name="generator" content={Astro.generator} />
<title>{title}</title>
<ViewTransitions />
Expand Down
6 changes: 3 additions & 3 deletions src/layouts/default.astro
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import BaseLayout from "./base.astro";
import Header from "@components/Header.astro";
interface Props {
id: string;
title: string;
description?: string;
}
const { id, title } = Astro.props;
const { title, description } = Astro.props;
---

<BaseLayout id={id} title={title}>
<BaseLayout title={title} description={description}>
<main class="lg-:container mx-auto px-6 py-2 max-w-screen-lg">
<Header />
<article class="prose lg:prose-xl mb-16">
Expand Down
5 changes: 2 additions & 3 deletions src/layouts/homepage.astro
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@ import BaseLayout from "./base.astro";
import Header from "@components/Header.astro";
interface Props {
id: string;
title: string;
}
const { id, title } = Astro.props;
const { title } = Astro.props;
---

<BaseLayout id={id} title={title}>
<BaseLayout title={title} description="Justin Bennett's personal website">
<main class="lg-:container mx-auto px-6 py-2 max-w-screen-lg">
<Header expanded={true} />
<article class="prose lg:prose-xl">
Expand Down
97 changes: 64 additions & 33 deletions src/pages/[...slug].astro
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
---
export const prerender = false;
/** 1 month in seconds */
const CACHE_TTL = 60 * 60 * 24 * 30;
// This matches to the note in my obsidian setup
// that's homepage. I probably could avoid hard coding this
// by having a mapping in the KV store, but this is
Expand All @@ -13,54 +16,82 @@ function isULID(ulid: string) {
return ulidRegex.test(ulid);
}
/**
* If a `get` KV request returns null and it was requested with a cacheTTL,
* the empty result will be cached. That's usually not what we want, so this
* function requests the slug again without a cacheTTL to bust the cache.
*/
function bustKVCache(slug: string) {
// We don't actually want to wait for this, we just want to make sure it happens
Astro.locals.runtime.ctx.waitUntil(
Astro.locals.runtime.env.KV_MAPPINGS.get(slug)
);
}
let { slug } = Astro.params;
// Automatically redirect home slug to root
if (slug === HOME_SLUG) {
return Astro.redirect("/", 308);
}
const originalSlug = slug;
// default to homepage slug
slug ??= HOME_SLUG;
let html = "";
let layout = "default";
let title = "";
if (!isULID(slug)) {
slug = (await Astro.locals.runtime.env.KV_MAPPINGS.get(slug)) as string;
if (!slug) return Astro.rewrite("/404");
slug = (await Astro.locals.runtime.env.KV_MAPPINGS.get(slug, {
cacheTtl: CACHE_TTL,
})) as string;
if (!slug) {
bustKVCache(slug);
return Astro.rewrite("/404");
}
}
slug = slug.toUpperCase();
if (isULID(slug)) {
let content = "";
// In dev read note from symlinked directory
if (process.env.NODE_ENV === "development") {
const fs = await import("node:fs/promises");
content = await fs.readFile(`./notes/${slug}.md`, "utf-8");
// In prod pull from R2
} else {
const result = await Astro.locals.runtime.env.R2_BUCKET.get(
slug.toUpperCase()
);
if (!result) return Astro.rewrite("/404");
content = await result.text();
// We've already done redirection of the home slug, so skip if it matches that
if (slug !== HOME_SLUG) {
// If the original path is different from the canonical mapping, redirect
const redirectPath = await Astro.locals.runtime.env.KV_MAPPINGS.get(slug, {
cacheTtl: CACHE_TTL,
});
if (redirectPath && redirectPath !== originalSlug) {
return Astro.redirect(redirectPath, 308);
}
if (!redirectPath) {
bustKVCache(slug);
}
if (!content) return Astro.rewrite("/404");
const { code, metadata } = await Astro.locals.render(content);
html = code;
title = metadata.frontmatter.title;
// The slice here is a hack to remove the "@layouts/" and ".astro" from the layout name
// so that the import below works correctly
layout = metadata.frontmatter.layout.slice(9, -6);
}
let content = "";
// In dev read note from symlinked directory
if (process.env.NODE_ENV === "development") {
const fs = await import("node:fs/promises");
content = await fs.readFile(`./notes/${slug}.md`, "utf-8");
// In prod pull from R2
} else {
const result = await Astro.locals.runtime.env.R2_BUCKET.get(slug);
if (!result) return Astro.rewrite("/404");
content = await result.text();
}
if (!content) return Astro.rewrite("/404");
const { code: html = "", metadata } = await Astro.locals.render(content);
if (!html) return Astro.rewrite("/404");
// The slug is used to determine the canonical URL. For every page that _isn't_ the hope page I want that
// to the the ULID. For the home page, I just want the root URL to be canonical.
slug = slug === HOME_SLUG ? "" : slug;
const { title, description, layout = "default" } = metadata.frontmatter;
// The slice here is a hack to remove the "@layouts/" and ".astro" from the layout name
// so that the import below works correctly
const layoutName = layout.slice(9, -6);
const Layout = (await import(`../layouts/${layout}.astro`)).default;
const Layout = (await import(`../layouts/${layoutName}.astro`)).default;
---

<Layout id={slug} title={title}>
<Layout title={title} description={description}>
<Fragment set:html={html} />
</Layout>

0 comments on commit 8e28f61

Please sign in to comment.