About this project

goatdash is a lightweight, privacy-focused dashboard for [GoatCounter](https://www.goatcounter.com/) analytics. It runs entirely client-side as vanilla JavaScript with zero dependencies, no build tooling, and no backend server of its own. Deployment is simply copying a handful of static files to any web host. ## What it does The dashboard connects to one or more GoatCounter instances via their public v0 API over HTTPS. It displays analytics data including: - **Five KPI cards**: unique visitors (with trend), pageviews, top page, tracked paths, and total events. - **Referrer breakdown**: traffic grouped by channel (direct, search engines, campaigns, other sites) with drill-down into individual referrers and the pages they brought. - **Choropleth world map**: countries shaded by visit count using a square-root scale, with hover tooltips, zoom, pan, and reset. - **Drill-down everywhere**: click any page to see its referrers, any referrer to see the pages it drove, browsers/systems/devices to their versions, countries to regions, and campaigns to their source URLs. - **Flexible date ranges**: today, 7 days, 30 days, 90 days, or a custom start/end period. ## Multi-site support goatdash is designed around multi-site setups where each site lives at its own domain but shares a single GoatCounter account. GoatCounter resolves the correct site from the `Host` header, so the dashboard queries each site cross-origin at its own domain. The sidebar lists all sites from `/api/v0/sites`, scoped to the API key's permissions. Site switching is fast thanks to background precaching of inactive sites. ## Architecture and stack - **Vanilla JS only**: no React, no bundler, no CDN calls. Seven static files total. - **No backend**: the browser speaks directly to the GoatCounter API. There is no server to patch, database to back up, or service to keep alive. - **Service worker**: caches the app shell and versioned assets for instant reloads; API responses are cached with stale-while-revalidate. - **Theme**: dark, light, or auto mode, switched via topbar buttons and applied before paint by an external `theme.js` script compatible with strict CSP (`default-src 'self'`). - **Language**: Spanish, English, or auto-detect, persisted in `localStorage`. - **Demo mode**: loads realistic sample data without an API key for exploration. ## Installation There is no install script and nothing to compile. Serve the static files from any HTTP server: ```sh python3 -m http.server 8000 ``` Requirements: a static web server and a GoatCounter instance whose v0 API is reachable over HTTPS from the browser. No Docker, Node, or build tools needed. For production multi-site deployments, configure your web server to serve the files from a dedicated domain (e.g., `stats.example.com`) and ensure `Cache-Control: no-store` is set on the index HTML. Asset files use version query strings (e.g., `app.js?v=3`) and must be bumped on each deploy to avoid stale cache issues. An optional systemd-based weekly self-updater is provided (`deploy/goatdash-update.sh`) that downloads the latest GitHub release, verifies its SHA256 checksum, backs up the current installation, and swaps in the new version. ## Configuration On first load the connect screen asks for: - The **GoatCounter URL** of your site(s) (e.g., `https://stats.cloudless.club`). - An **API key** created in GoatCounter under Settings > API, with at least Count and Read statistics permissions. Both values are stored in the browser's `localStorage` and transmitted only over HTTPS to your GoatCounter instance. Theme, language, selected site, and date range are also persisted locally. For multi-site setups, each site must have its own domain pointing to the same GoatCounter installation. GoatCounter sends `Access-Control-Allow-Origin: *`, enabling cross-origin requests without a proxy. Note that every authenticated request triggers an `OPTIONS` preflight, resulting in two round trips per API call. ## Usage Open the page and enter your GoatCounter URL and API key, or click **Try Demo** to explore with sample data. Use the segmented control to switch date ranges, the gear menu to change theme/language or disconnect, and click any metric card to drill down into related data. The refresh menu clears caches and re-fetches everything. ## Development The project consists of plain HTML, CSS, and JavaScript across `index.html`, `styles.css`, `theme.js`, `app.js`, `fixtures.js`, and `sw.js`. There is no `package.json`, bundler, or test harness. Local development: ```sh python3 -m http.server 8000 ``` Demo fixture data in `fixtures.js` mirrors the real API response shape. ## License AGPL-3.0. The world map asset (`assets/world-map.js`) is retained verbatim from Abhishekh Singh's MIT-licensed goatcounter-dashboard and remains MIT.