Skip to content

Commit 2455a45

Browse files
committed
Merge develop — release v0.7.10 (whitelist sync fix)
2 parents c8847ab + 47ab992 commit 2455a45

13 files changed

Lines changed: 119 additions & 28 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ This project adheres to [Semantic Versioning](https://semver.org/).
77

88
## [Unreleased]
99

10+
## [0.7.10] - 2026-06-19
11+
12+
### Fixed
13+
14+
- **Manual sync + IP whitelist:** valid `x-api-token` bypasses `GGHSTATS_WHITELIST` on protected paths so dashboard **Sync all** works remotely; clearer sync error messages (403/429) in the UI.
15+
1016
## [0.7.9] - 2026-06-16
1117

1218
### Added
@@ -327,7 +333,8 @@ This project adheres to [Semantic Versioning](https://semver.org/).
327333
- Project naming and module path finalized as `gghstats` (binary, Docker image, `GGHSTATS_*` environment variables).
328334
- Toolchain and build base image aligned to Go **1.26.1**.
329335

330-
[Unreleased]: https://github.com/hrodrig/gghstats/compare/v0.7.9...HEAD
336+
[Unreleased]: https://github.com/hrodrig/gghstats/compare/v0.7.10...HEAD
337+
[0.7.10]: https://github.com/hrodrig/gghstats/compare/v0.7.9...v0.7.10
331338
[0.7.9]: https://github.com/hrodrig/gghstats/compare/v0.7.8...v0.7.9
332339
[0.7.8]: https://github.com/hrodrig/gghstats/compare/v0.7.7...v0.7.8
333340
[0.7.7]: https://github.com/hrodrig/gghstats/compare/v0.7.6...v0.7.7

README.md

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
![gghstats — self-hosted GitHub traffic beyond the 14-day window](assets/gghstats-poster-devto.png)
44

5-
[![Version](https://img.shields.io/badge/version-0.7.9-blue)](https://github.com/hrodrig/gghstats/releases)
5+
[![Version](https://img.shields.io/badge/version-0.7.10-blue)](https://github.com/hrodrig/gghstats/releases)
66
[![Release](https://img.shields.io/github/v/release/hrodrig/gghstats)](https://github.com/hrodrig/gghstats/releases)
77
[![CI](https://github.com/hrodrig/gghstats/actions/workflows/ci.yml/badge.svg)](https://github.com/hrodrig/gghstats/actions)
88
[![codecov](https://codecov.io/gh/hrodrig/gghstats/graph/badge.svg)](https://codecov.io/gh/hrodrig/gghstats)
@@ -123,7 +123,7 @@ export GGHSTATS_GITHUB_TOKEN=ghp_xxx
123123
gghstats run --open
124124
```
125125

126-
Or extract a [Release](https://github.com/hrodrig/gghstats/releases) tarball, or `go install github.com/hrodrig/gghstats/cmd/gghstats@latest`. Pin a version: `VERSION=v0.7.9 curl -fsSL …/install.sh | sh`.
126+
Or extract a [Release](https://github.com/hrodrig/gghstats/releases) tarball, or `go install github.com/hrodrig/gghstats/cmd/gghstats@latest`. Pin a version: `VERSION=v0.7.10 curl -fsSL …/install.sh | sh`.
127127

128128
Open <http://localhost:8080> if you did not use **`--open`**. Data is stored in `./data/gghstats.db` (override with `GGHSTATS_DB`). A first sync may take a while if the default filter includes many repositories — narrow `GGHSTATS_FILTER` in [Configuration](#configuration) when you move beyond this smoke test.
129129

@@ -167,7 +167,7 @@ Open <http://localhost:8080>. The template [`.env.example`](.env.example) docume
167167
curl -fsSL https://raw.githubusercontent.com/hrodrig/gghstats/main/scripts/install.sh | sh
168168
```
169169

170-
Downloads the latest [release](https://github.com/hrodrig/gghstats/releases) archive for your OS/arch into `/usr/local/bin` (override with `BINDIR=~/bin`). Review [`scripts/install.sh`](scripts/install.sh) before piping to `sh`; pin with `VERSION=v0.7.9`.
170+
Downloads the latest [release](https://github.com/hrodrig/gghstats/releases) archive for your OS/arch into `/usr/local/bin` (override with `BINDIR=~/bin`). Review [`scripts/install.sh`](scripts/install.sh) before piping to `sh`; pin with `VERSION=v0.7.10`.
171171

172172
**From source (recommended for developers):**
173173

@@ -182,15 +182,15 @@ This installs the binary to `$GOBIN` (default `$HOME/go/bin`). Ensure `$GOBIN` i
182182
| Platform | Command / path |
183183
|----------|----------------|
184184
| **Homebrew (macOS / Linux)** | `brew install hrodrig/gghstats/gghstats`[tap](https://github.com/hrodrig/homebrew-gghstats) |
185-
| **Debian / Ubuntu** | `wget -q -O /tmp/gghstats.deb https://github.com/hrodrig/gghstats/releases/download/v0.7.9/gghstats_0.7.9_linux_amd64.deb && sudo dpkg -i /tmp/gghstats.deb` |
186-
| **Fedora / RHEL / AlmaLinux / Rocky / Oracle Linux** | `sudo dnf install https://github.com/hrodrig/gghstats/releases/download/v0.7.9/gghstats_0.7.9_linux_amd64.rpm` |
185+
| **Debian / Ubuntu** | `wget -q -O /tmp/gghstats.deb https://github.com/hrodrig/gghstats/releases/download/v0.7.10/gghstats_0.7.10_linux_amd64.deb && sudo dpkg -i /tmp/gghstats.deb` |
186+
| **Fedora / RHEL / AlmaLinux / Rocky / Oracle Linux** | `sudo dnf install https://github.com/hrodrig/gghstats/releases/download/v0.7.10/gghstats_0.7.10_linux_amd64.rpm` |
187187
| **Linux tarball** | `tar -xzf gghstats_*_linux_*.tar.gz` from [Releases](https://github.com/hrodrig/gghstats/releases); verify `checksums.txt` |
188188
| **FreeBSD** | `gghstats_*_freebsd_*.tar.gz` on [Releases](https://github.com/hrodrig/gghstats/releases); port + developer guide [`contrib/freebsd/README.md`](contrib/freebsd/README.md) (`gmake` in repo, `make` in ports tree) |
189189
| **OpenBSD** | `gghstats_*_openbsd_*.tar.gz` on [Releases](https://github.com/hrodrig/gghstats/releases); [`contrib/openbsd/README.md`](contrib/openbsd/README.md) and port [`contrib/openbsd/port/`](contrib/openbsd/port/) |
190190
| **macOS / Windows archives** | `.tar.gz` / `.zip` on [Releases](https://github.com/hrodrig/gghstats/releases) |
191-
| **OCI image** | `ghcr.io/hrodrig/gghstats:v0.7.9` or `:latest` (multi-arch) |
191+
| **OCI image** | `ghcr.io/hrodrig/gghstats:v0.7.10` or `:latest` (multi-arch) |
192192

193-
Replace `v0.7.9` and `amd64` with your [release](https://github.com/hrodrig/gghstats/releases) version and architecture (e.g. `arm64`).
193+
Replace `v0.7.10` and `amd64` with your [release](https://github.com/hrodrig/gghstats/releases) version and architecture (e.g. `arm64`).
194194

195195
After install, validate the UI locally:
196196

@@ -415,6 +415,8 @@ gghstats supports optional IP-based access control via `GGHSTATS_WHITELIST`. Whe
415415

416416
By default the whitelist applies to **all routes** except `/metrics`, `/api/v1/healthz`, and **`/api/v1/badge/*`** (README embeds). Scope it to specific paths with `GGHSTATS_WHITELIST_PATHS` (comma-separated prefixes, e.g. `/api/,/h2h`). Paths not listed remain publicly accessible. Badge URLs stay public even when `/api/` is whitelisted.
417417

418+
When **`GGHSTATS_API_TOKEN`** is set, requests that include a matching **`x-api-token`** header bypass the IP whitelist on protected paths (the token is still validated by the API handler). This lets remote operators run **`POST /api/v1/sync`** from the dashboard without opening `/api/` to the whole internet.
419+
418420
```bash
419421
# Internal network + VPN only
420422
GGHSTATS_WHITELIST=10.0.0.0/8,172.16.0.0/12,192.168.1.0/24

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.7.9
1+
0.7.10

cmd/gghstats/serve.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ func runServe(args []string) error {
132132
defer rateLimiter.Shutdown()
133133
}
134134

135-
whitelist := server.NewWhitelist(server.ParseWhitelistEnv())
135+
whitelist := server.NewWhitelist(server.ParseWhitelistEnv(), cfg.APIToken)
136136

137137
syncOpts := sync.Options{
138138
IncludePrivate: cfg.IncludePrivate,

internal/i18n/locales/de.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,10 @@
175175
"sync_last_repo": "Letzte Synchronisation ({{repo}}): {{when}}",
176176
"sync_last": "Letzte Synchronisation: {{when}}",
177177
"sync_failed": "Sync fehlgeschlagen",
178+
"sync_start_failed": "Sync konnte nicht gestartet werden",
179+
"sync_already_running": "Sync läuft bereits",
180+
"sync_ip_not_whitelisted": "Ihre IP ist für die API nicht freigegeben (GGHSTATS_WHITELIST prüfen)",
181+
"sync_rate_limited": "Rate-Limit — eine Minute warten und erneut versuchen",
178182
"sync_done": "Sync abgeschlossen",
179183
"token_required": "API-Token erforderlich",
180184
"token_save_sync": "Speichern & synchronisieren"

internal/i18n/locales/en.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,10 @@
175175
"sync_last_repo": "Last sync ({{repo}}): {{when}}",
176176
"sync_last": "Last sync: {{when}}",
177177
"sync_failed": "Sync failed",
178+
"sync_start_failed": "Could not start sync",
179+
"sync_already_running": "Sync already running",
180+
"sync_ip_not_whitelisted": "Your IP is not whitelisted for the API (check GGHSTATS_WHITELIST)",
181+
"sync_rate_limited": "Rate limited — wait a minute and try again",
178182
"sync_done": "Sync finished",
179183
"token_required": "API token required",
180184
"token_save_sync": "Save & sync"

internal/i18n/locales/es.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,10 @@
175175
"sync_last_repo": "Última sincronización ({{repo}}): {{when}}",
176176
"sync_last": "Última sincronización: {{when}}",
177177
"sync_failed": "Error de sincronización",
178+
"sync_start_failed": "No se pudo iniciar la sincronización",
179+
"sync_already_running": "Sincronización ya en curso",
180+
"sync_ip_not_whitelisted": "Tu IP no está en la lista blanca de la API (revisa GGHSTATS_WHITELIST)",
181+
"sync_rate_limited": "Límite de peticiones — espera un minuto e inténtalo de nuevo",
178182
"sync_done": "Sincronización terminada",
179183
"token_required": "Se requiere token de API",
180184
"token_save_sync": "Guardar y sincronizar"

internal/i18n/locales/fr.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,10 @@
175175
"sync_last_repo": "Dernière synchronisation ({{repo}}) : {{when}}",
176176
"sync_last": "Dernière synchronisation : {{when}}",
177177
"sync_failed": "Échec de la synchronisation",
178+
"sync_start_failed": "Impossible de démarrer la synchronisation",
179+
"sync_already_running": "Synchronisation déjà en cours",
180+
"sync_ip_not_whitelisted": "Votre IP n'est pas autorisée pour l'API (vérifiez GGHSTATS_WHITELIST)",
181+
"sync_rate_limited": "Limite de débit — attendez une minute et réessayez",
178182
"sync_done": "Synchronisation terminée",
179183
"token_required": "Jeton API requis",
180184
"token_save_sync": "Enregistrer et synchroniser"

internal/i18n/locales/pt-br.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,10 @@
175175
"sync_last_repo": "Última sincronização ({{repo}}): {{when}}",
176176
"sync_last": "Última sincronização: {{when}}",
177177
"sync_failed": "Falha na sincronização",
178+
"sync_start_failed": "Não foi possível iniciar a sincronização",
179+
"sync_already_running": "Sincronização já em andamento",
180+
"sync_ip_not_whitelisted": "Seu IP não está na lista branca da API (verifique GGHSTATS_WHITELIST)",
181+
"sync_rate_limited": "Limite de requisições — aguarde um minuto e tente novamente",
178182
"sync_done": "Sincronização concluída",
179183
"token_required": "Token da API é obrigatório",
180184
"token_save_sync": "Salvar e sincronizar"

internal/server/locale.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@ func jsI18nPayload(bundle *i18n.Bundle, locale string) map[string]string {
122122
"js.sync_last_repo",
123123
"js.sync_last",
124124
"js.sync_failed",
125+
"js.sync_start_failed",
126+
"js.sync_already_running",
127+
"js.sync_ip_not_whitelisted",
128+
"js.sync_rate_limited",
125129
"js.sync_done",
126130
"js.token_required",
127131
"js.token_save_sync",

0 commit comments

Comments
 (0)