Skip to content

Repository files navigation

DatoCMS Status Website

Public status page for DatoCMS services at status.datocms.com.

Built with Astro, deployed on Netlify.

Tech Stack

  • Astro with TypeScript — static pages, server endpoints, content collections
  • Web Components — interactive UI with zero framework overhead
  • Chartist — system metrics charts
  • CSS custom properties — no preprocessor
  • Netlify — hosting + serverless functions via @astrojs/netlify
  • AWS CloudWatch — response time and success rate metrics
  • StatusCake — uptime monitoring per component

Development

npm install
npm run dev       # Local dev server at localhost:4321
npm run build     # Production build to ./dist/
npm run preview   # Preview build locally

Environment Variables

Optional. Without them the site builds and runs; the metrics panels show a message instead. Fetch from Netlify:

npx netlify link     # Link to the datocms-status project
npx netlify env:list # View current values
Variable Description
CLOUDWATCH_AWS_ACCESS_KEY_ID AWS access key for CloudWatch
CLOUDWATCH_AWS_SECRET_ACCESS_KEY AWS secret key for CloudWatch
CLOUDWATCH_AWS_REGION AWS region (default: us-east-1)
STATUSCAKE_API_TOKEN StatusCake API token

Project Structure

├── src/
│   ├── content.config.ts        # Content collections (incidents + maintenances)
│   ├── lib/                     # Business logic (models, i18n, markdown)
│   ├── styles/global.css        # All styles
│   ├── layouts/BaseLayout.astro
│   ├── components/              # Astro components with inline web components
│   └── pages/
│       ├── api/                 # Server endpoints (cloudwatch, component-status, feeds)
│       ├── history/             # Paginated incident history
│       ├── incidents/           # Individual incident pages
│       ├── history.{rss,atom,json}.ts  # Feeds
│       ├── index.astro          # Homepage
│       └── 404.astro
├── data/
│   ├── incidents/               # One JSON file per incident
│   └── maintenances/            # One JSON file per maintenance
├── public/                      # Static assets (SVGs, logo)
├── astro.config.mjs             # Astro config + env schema
└── netlify.toml                 # Netlify build config

Posting an update

Run the terminal UI from the repo root:

npm run tui

The first run installs tui/ (three small packages, no native modules). The TUI never calls an LLM unless you press Ctrl+G.

  1. Pick an action: new incident, new maintenance, or an existing item. Open items and the five most recent closed ones are listed; the rest are one level down. Picking an item offers Add an update, Resolve (open items only), and History.
  2. Fill in the fields. Every field shows its valid values. Dates default to now; the picker lets you set each part with arrows or digits in any time zone and shows the resulting UTC instant.
  3. Watch the right pane: it is the exact JSON that will be written to data/.
  4. Ctrl+P starts the dev server and opens the draft in your browser. Edits hot-reload.
  5. Ctrl+S goes to Publish: write the file, commit, push. After a push the TUI waits for status.datocms.com and status2.datocms.com for up to five minutes, and reports for each host whether your text is live.

Publish from master only. Netlify and the mirror deploy master, so on any other branch the TUI can write and commit, but it does not push. An update changes only the lines it adds: the rest of the file keeps its formatting.

Key Action
Tab / Shift+Tab Next / previous field
Enter Edit the focused field or confirm a menu
Esc Leave a field, close a menu, go back
Ctrl+P Preview in the browser via the dev server
Ctrl+G Claude actions on the message: write from notes, copyedit, translate to English
Ctrl+E Open the message in $EDITOR
Ctrl+S Go to Publish
Ctrl+C Quit; asks whether to keep or discard an unpublished draft

Drafts are written to data/ as you type, so Ctrl+P works mid-edit and shows what is on screen. Quitting without publishing asks whether to keep the file.

History lists every commit that touched the item, with a side-by-side diff against the current file. Rolling back writes the old content as a new commit; history is never rewritten.

astro dev runs without the Netlify adapter, because its dev middleware starts a Deno-based edge-functions emulator this site does not use. Set NETLIFY_DEV_EMULATION=1 to opt back in. Builds are unaffected. The Claude actions shell out to the claude command and are hidden when it is not installed.

Tests: npm run test:tui. See Tests.

When the dev server shows empty sections

Restart it after you install or remove a package:

npx astro dev stop
npm run dev

The dev server keeps a cache of the packages. When the packages change while it runs, the browser gets "504 (Outdated Optimize Dep)" for the scripts, and Component Status, System Metrics and Third-Party Components stay empty. This occurs on the dev server only.

Tests

npm test              # everything below
npm run test:unit     # website logic and endpoints (Vitest)
npm run test:e2e      # website pages in a browser (Playwright)
npm run test:tui      # maintainer TUI: type-check and tests

The end-to-end run gets the browser that Playwright uses when it is missing. The first run on a machine downloads it, so that run needs the network.

Suite Where What it covers
Unit test/ Incident model, the three API endpoints, error messages, the pre-commit rule
End-to-end e2e/ Homepage, incident pages, history, feeds, and the three live panels
TUI tui/test/ Forms, date picker, publish, verification, git history

The end-to-end tests do not use the files in data/. They make fixture files with dates relative to now, build the site from them into e2e/.dist, and serve that build as a static host does. Each test gives the replies of the API endpoints itself. A test that gives none sees the site as the GitHub Pages mirror serves it.

No test calls a supplier, AWS, or StatusCake.

Pre-commit hook

A Husky hook runs the suites that the staged files can break:

Staged files Suites
Only data/ None. A status update never waits for tests.
tui/ TUI
src/lib/schema.ts, package.json, package-lock.json, .husky/, scripts/ Website and TUI
Any other file Website: type-check, unit, end-to-end

The rule is in scripts/testScope.ts. The hook tests the files in the working tree, not only the staged part of each file. Use git commit --no-verify to skip it. There is no CI: the hook is the only automatic run.

Incident Management

Incidents and maintenances are stored as JSON files in data/. The TUI above is the main way to manage them. You can also use the Claude Code slash commands below, or edit the JSON files directly. The valid values live in src/lib/schema.ts.

Data Format

Incidents (data/incidents/YYYY-MM-DD-slug.json):

{
  "name": "Increase in error rate and response time",
  "impact": "major",
  "components": ["cda", "cma"],
  "updates": [
    {
      "date": "2026-02-23T14:50:31.912Z",
      "content": "A node in our production cluster became unresponsive...",
      "status": "resolved"
    }
  ]
}

Maintenances (data/maintenances/YYYY-MM-DD-slug.json):

{
  "scheduledTime": "2025-10-14T05:00:29.209Z",
  "name": "Maintenance on our billing system",
  "minutes": "120",
  "content": "Description of what will be unavailable...",
  "components": ["billing"],
  "updates": []
}

Claude Code Commands (alternative path)

These slash commands guide you through incident management in a Claude Code chat. You describe the situation in plain language and they generate professional, user-facing copy.

Command Description
/new-incident Create a new incident. Describe what's happening, pick impact and components. Generates a title and status update.
/new-maintenance Schedule a maintenance window. Describe the work, pick time/duration and components. Generates a title and notice.
/update-incident Add a status update to an open incident or maintenance. Pick the item, new status, and describe what changed.
/resolve-incident Quickly close an open incident or maintenance with a resolution message.

Typical workflow:

/new-incident          # Something breaks — create the incident
/update-incident       # Root cause found — post an update
/resolve-incident      # All fixed — close it out

All commands show generated content for confirmation before writing, and remind you to commit and deploy.

GitHub Pages Fallback (if Netlify goes down)

A static version of the site is automatically deployed to GitHub Pages on every push to master via a Husky pre-push hook. A push from another branch does not deploy it. The hook builds the mirror from a clean copy of the commit that the push sends, so a file that nobody committed does not reach the mirror.

Under normal operation, the GitHub Pages version lives at status2.datocms.com.

It lacks Component Status, System Metrics, and Third-Party Components (those require server endpoints): each of these sections says so and links to the main host. Incidents and history work fine.

To activate the fallback:

  1. Update GITHUB_PAGES_CNAME from status2.datocms.com to status.datocms.com
  2. Commit and push — this triggers a rebuild and deploys to the gh-pages branch with the updated CNAME
  3. Go to Cloudflare DNS for datocms.com and change the status CNAME record target from datocms-status.netlify.com to datocms.github.io

To revert back to Netlify:

  1. Revert GITHUB_PAGES_CNAME back to status2.datocms.com, commit and push
  2. Change the status CNAME record back to datocms-status.netlify.com

Components

ID Label
cda Content Delivery API
cma Content Management API
assets Assets CDN (Imgix)
administrativeAreas Projects administrative interface
dashboard Account dashboard interface
site Website
billing Billing

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages