Public status page for DatoCMS services. Built with Astro, deployed on Netlify.
- Framework: Astro with TypeScript
- Interactive components: Web Components (custom elements) — zero framework JS
- Charts: Chartist v1
- Styling: Global CSS with custom properties (no preprocessor)
- Hosting: Netlify (static site + serverless functions via @astrojs/netlify)
- Data: JSON files in
data/incidents/anddata/maintenances/ - Metrics: AWS CloudWatch (response time, success rate) + StatusCake (uptime monitoring)
- Environment variables: Type-safe via
astro:envschema inastro.config.mjs - Node version: 24 (see
.nvmrc)
├── src/
│ ├── content.config.ts # Astro content collections (incidents + maintenances)
│ ├── lib/ # Business logic (incidents model, i18n, markdown, timeLink)
│ ├── styles/global.css # All styles with CSS custom properties
│ ├── layouts/BaseLayout.astro
│ ├── components/ # Astro components with inline <script> web components
│ └── pages/
│ ├── api/ # Server endpoints (cloudwatch, component-status, feeds)
│ ├── history/ # Paginated history ([...page].astro)
│ ├── incidents/ # Individual incident pages ([slug].astro)
│ ├── history.rss.ts # RSS feed
│ ├── history.atom.ts # Atom feed
│ ├── history.json.ts # JSON feed
│ ├── 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 with env schema
├── netlify.toml # Netlify build config
└── .env # Environment variables (not committed)
{
"name": "Incident Title",
"impact": "major|minor|none",
"components": ["cda", "cma", ...],
"updates": [{ "content": "...", "status": "investigating|identified|monitoring|resolved", "date": "ISO8601" }]
}Same structure but includes scheduledTime (ISO8601) and minutes (duration). Updates use statuses: scheduled, in_progress, completed.
isMaintenanceis determined by presence ofscheduledTimefield- Incident date returns
scheduledTimefor maintenances, first update date for incidents - All incidents sorted by date descending (newest first)
isUnresolved: for incidents checksstatus !== 'resolved'; for maintenances checksstatus !== 'completed' && status !== 'scheduled'
cda, cma, assets, administrativeAreas, dashboard, site. Component labels mapped via src/lib/i18n.ts.
| Endpoint | Purpose |
|---|---|
/api/cloudwatch?graph=...&time=... |
CDA response time and API success rate from AWS CloudWatch |
/api/component-status?days=... |
Uptime/downtime per component from StatusCake API |
/api/feeds |
Aggregated third-party RSS feeds |
npm install
npm run dev # Starts local dev server at localhost:4321
npm run build # Production build to ./dist/
npm run preview # Preview build locallyDefined in astro.config.mjs under env.schema using astro:env. Imported in server code via import { VAR } from 'astro:env/server':
CLOUDWATCH_AWS_REGION— AWS region (default: us-east-1)CLOUDWATCH_AWS_ACCESS_KEY_ID— AWS access keyCLOUDWATCH_AWS_SECRET_ACCESS_KEY— AWS secret keySTATUSCAKE_API_TOKEN— StatusCake API token
A static version of the site is automatically deployed to GitHub Pages on every git push via a Husky pre-push hook. It lacks Components Status, System Metrics, and Third-Party Components (those require server endpoints), but incidents and history work fine.
To activate the fallback:
- Update
GITHUB_PAGES_CNAMEfromstatus2.datocms.comtostatus.datocms.com - Commit and push — this triggers a rebuild and deploys to the
gh-pagesbranch with the updated CNAME - Update DNS — go to Cloudflare DNS for datocms.com and change the
statusCNAME record target fromdatocms-status.netlify.comtodatocms.github.io
To revert back to Netlify once it's up:
- Revert
GITHUB_PAGES_CNAMEback tostatus2.datocms.com, commit and push - Revert DNS — change the
statusCNAME record back todatocms-status.netlify.com
- Incident file names:
YYYY-MM-DD-slug.json - All dates in ISO 8601 UTC
- Interactive components use Web Components (custom elements) with inline
<script>in.astrofiles - No React or other UI framework — vanilla JS only
- CSS uses custom properties (e.g.
--color-green,--color-border)