From b4ea7b2dafa91e3ad1892dc43fdbbcea70e7fe18 Mon Sep 17 00:00:00 2001 From: Mat Manna Date: Fri, 24 Apr 2026 02:31:47 +0000 Subject: [PATCH] feat: add abaci (#31) --- src/components/AbacusCounter.astro | 27 +++++++++++++++ src/layouts/BaseLayout.astro | 2 ++ src/lib/abacus.ts | 55 ++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+) create mode 100644 src/components/AbacusCounter.astro create mode 100644 src/lib/abacus.ts diff --git a/src/components/AbacusCounter.astro b/src/components/AbacusCounter.astro new file mode 100644 index 0000000..1a1a5c0 --- /dev/null +++ b/src/components/AbacusCounter.astro @@ -0,0 +1,27 @@ +--- +import { generateAbacusKey, getAbacusHitUrl } from "../lib/abacus"; + +interface Props { + pathname: string; +} + +const { pathname } = Astro.props; +const abacusKey = generateAbacusKey(pathname); +const abacusUrl = getAbacusHitUrl(abacusKey); +--- + + diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro index 15a4d31..0832c39 100644 --- a/src/layouts/BaseLayout.astro +++ b/src/layouts/BaseLayout.astro @@ -1,4 +1,6 @@ --- +import AbacusCounter from "../components/AbacusCounter.astro"; + interface Props { title?: string; description?: string; diff --git a/src/lib/abacus.ts b/src/lib/abacus.ts new file mode 100644 index 0000000..bc4784a --- /dev/null +++ b/src/lib/abacus.ts @@ -0,0 +1,55 @@ +/** + * Generate an Abacus counter key based on the page pathname + * Examples: + * "/" → "page_index" + * "/about" → "page_about" + * "/news/article-name" → "news_article-name" + * "/opinion/better-goodbyes" → "opinion_better-goodbyes" + */ +export function generateAbacusKey(pathname: string): string { + // Remove leading/trailing slashes + const cleanPath = pathname.replace(/^\/+|\/+$/g, ""); + + if (!cleanPath) { + return "page_index"; + } + + const normalizedSegments = cleanPath + .split("/") + .map((segment) => { + try { + return decodeURIComponent(segment); + } catch { + return segment; + } + }) + .map((segment) => segment.toLowerCase().replace(/[^a-z0-9_.-]/g, "-").replace(/-+/g, "-")) + .map((segment) => segment.replace(/^-+|-+$/g, "")) + .filter(Boolean); + + if (normalizedSegments.length === 0) { + return "page_index"; + } + + if (normalizedSegments.length === 1) { + // Single-level page like "/about" → "page_about" + return `page_${normalizedSegments[0]}`.slice(0, 64); + } + + // Multi-level like "/news/article-name" → "news_article-name" + return normalizedSegments.join("_").slice(0, 64); +} + +/** + * Get the Abacus API URL for hitting a counter + */ +export function getAbacusHitUrl(key: string, namespace: string = "news.hackclub.com"): string { + return `https://abacus.jasoncameron.dev/hit/${namespace}/${key}`; +} + +/** + * Get the Abacus API URL for retrieving counter value + */ +export function getAbacusGetUrl(key: string, namespace: string = "news.hackclub.com"): string { + return `https://abacus.jasoncameron.dev/get/${namespace}/${key}`; +} -- 2.51.2