Skip to content

Repository files navigation

Commentosaurus

A commenting system for Docusaurus pages. A small Go backend that stores comments and reactions in SQLite, and @danielpeinhopf/commentosaurus, a React component for the client-side. This repository also contains a sample Docusaurus site demonstrating the whole thing end-to-end by wiring that component in through theme swizzling.

Auth is handled upstream by a proxy (traefik, see configs/), which forwards the signed-in user's identity as X-AuthorId/X-AuthorName headers. The server trusts those headers rather than doing its own login.

Note

This project is completely vibe-coded for my own needs using Claude Code and not very well tested yet. Use at your own risk.

Repository Layout

packages/commentosaurus/  the CommentosaurusSection React component + API client
example-site/              a sample Docusaurus site consuming that package, with the comment UI wired in via theme swizzling
server/                    the Go backend for storing comments and reactions
configs/                   Traefik + OIDC auth-proxy config used in front of both

Development Quickstart (2 terminals)

The repository uses Task.

# 1. Install JS dependencies
task install

# 2. Start up Keycloak and the traefik proxy
task proxy:start

# 3. Terminal A β€” the Go backend
task server

# 4. Terminal B β€” The example site
task site

Then open http://localhost:9080, login with a user from below, visit a doc page or blog post, and post a comment.
The following user accounts exist:

Username Password Comments
admin admin Admin account with all permissions
bob bob A normal user account
jane jane A normal user account

Backend Configuration

Var Default Purpose
PORT 8080 HTTP port the Go server listens on
DB_PATH ./comments.db SQLite file path
ADMIN_AUTHOR_IDS (empty) Comma-separated author ids allowed to see every private comment and delete any comment
PROXY_SECRET (required) Shared secret the auth proxy sends as X-ProxySecret; requests without it are rejected before X-AuthorId/X-AuthorName are trusted. Must match the header value in configs/http.yml. The server refuses to start if unset
COMMENTS_ENABLED true Default for the comments feature; a page can override it (see "How comments attach to a page"). Disabled: posting/listing comments is rejected with 403, and the frontend hides the comment list and form
REACTIONS_ENABLED true Default for the reactions feature, independently of comments; overridable per page. Disabled: reacting is rejected with 403, and the frontend hides the reaction buttons
REACTION_EMOJIS πŸ‘,πŸ‘Ž,❀️,πŸŽ‰,πŸ˜• Default set of emoji a page can be reacted to with, in display order; overridable per page. Reacting with anything outside the effective set is rejected; the frontend renders exactly that set (fetched from the API, not hardcoded)
MAX_COMMENTS_PER_PAGE 10 Number of comments returned per request, oldest first -- global only, not overridable per page. When a thread has more, the frontend shows a "Load more comments" button that re-requests with a growing offset. 0 means no limit.

Deployment

The docs site and the /commentosaurus/* API must be served from the same origin, through a single reverse proxy doing path-based routing in front of both -- exactly what configs/traefik-config.yml + configs/http.yml set up. Two things depend on this:

  • CommentosaurusSection's client code only ever calls relative paths (e.g. /commentosaurus/api/comments), not an absolute API URL, so it only works same-origin.
  • The auth proxy's session cookie and the X-AuthorId/X-AuthorName/X-ProxySecret headers it attaches are scoped to that one origin. There's no cross-origin login flow, so a separately-hosted API (its own domain, with CORS) isn't supported.

How to use

Wiring Commentosaurus into your own Docusaurus site is three steps: install the package, swizzle it into your theme, and put a same-origin proxy in front that authenticates users and forwards commentosaurus traffic to the backend.

1. Install the package

npm install @danielpeinhopf/commentosaurus

The package exports the CommentosaurusSection component which we'll use in the next step.

2. Swizzle the component in

The package deliberately does not wire itself into the DocItem/BlogPostItem theme components for you. That's on the consuming site, via swizzling. Eject a wrapper for whichever page type(s) you want comments on:

npm run swizzle @docusaurus/theme-classic DocItem/Footer -- --wrap
npm run swizzle @docusaurus/theme-classic BlogPostItem/Footer -- --wrap

Then, in each generated file, call the matching hook and render CommentosaurusSection with the config it returns:

// src/theme/DocItem/Footer/index.tsx
import React from 'react';
import type {ReactNode} from 'react';
import Footer from '@theme-original/DocItem/Footer';
import {CommentosaurusSection, useDocConfig} from '@danielpeinhopf/commentosaurus';

export default function FooterWrapper(props: object): ReactNode {
  const config = useDocConfig();

  return (
    <>
      <Footer {...props} />
      <CommentosaurusSection config={config} />
    </>
  );
}
// src/theme/BlogPostItem/Footer/index.tsx
import React from 'react';
import type {ReactNode} from 'react';
import Footer from '@theme-original/BlogPostItem/Footer';
import {CommentosaurusSection, useBlogPostConfig} from '@danielpeinhopf/commentosaurus';

export default function FooterWrapper(props: object): ReactNode {
  const config = useBlogPostConfig();

  // useBlogPostConfig() returns null on list/archive/tag pages, where
  // BlogPostItem renders a truncated preview rather than the actual post.
  return (
    <>
      <Footer {...props} />
      {config && <CommentosaurusSection config={config} />}
    </>
  );
}

This adds the CommentosaurusSection to the bottom of each page.

3. Configure the proxy

CommentosaurusSection's client only ever calls relative paths (e.g. /commentosaurus/api/comments). It has no notion of an API base URL, so the docs site and the Go backend must be served from the same origin. Put a reverse proxy in front of both that:

  • Routes /commentosaurus/* to the Go backend, and everything else to the Docusaurus site
  • Authenticates the user (e.g. via OIDC) and forwards their identity as X-AuthorId/X-AuthorName headers
  • Attaches a shared-secret X-ProxySecret header, so the backend knows those identity headers actually came from the proxy and not a spoofed request

configs/traefik-config.yml + configs/http.yml set exactly this up with Traefik and the traefik-oidc-auth plugin, and docker-compose.yml runs it alongside a local Keycloak for testing (see Development Quickstart above).

Any proxy/auth stack works as long as it satisfies the three bullets above.
Traefik + OIDC is just what this repo demonstrates. Set the Go server's PROXY_SECRET env var to match the X-ProxySecret value configured on the proxy; see the Configuration table for the rest of its env vars, and Deployment above for more on why same-origin is required (and what it rules out).

How comments attach to a page

By default CommentosaurusSection uses each page's route (permalink) as the comment thread id. If you rename or move a doc/blog post, its existing comments won't follow β€” set a comment_id front-matter field to pin a stable thread id across renames:

---
comment_id: my-stable-thread-id
---

A page can also override the COMMENTS_ENABLED/REACTIONS_ENABLED/REACTION_EMOJIS defaults for itself via front matter:

---
comments_enabled: false
reactions_enabled: true
reaction_emojis: [πŸ‘, πŸš€]
---

All three are optional; an unset field falls back to the server's global default. The frontend reads these (example-site/src/theme/{DocItem,BlogPostItem}/Footer) and sends them with every request for that page, and the server resolves and enforces the effective value -- but the server has no independent notion of "pages" or their front matter, so, like pageId and a comment's body, these overrides are client-supplied and only structurally validated, not a hardened anti-abuse boundary. That's consistent with this project's overall stance on abuse today (see Spam prevention/Captcha below), not a gap specific to this feature.

MAX_COMMENTS_PER_PAGE is deliberately not in that list -- it's a global-only setting, see the Configuration table.

About

πŸ“£ A simple commenting system for Docusaurus pages using an SQLite storage backend.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages