Releases: izzipizzy/gsc-hub
Release list
v0.6.2 — bounded per-site fan-out
Reliability release for large portfolios: the per-site fan-out no longer melts the socket pool, and an expired token is refreshed once instead of once per site.
Fixed
- Per-site fan-outs are bounded to 8 concurrent Search Console calls instead of one connection per property. On a ~200-site account the unbounded fan-out saturated the socket pool and the whole batch died with
UND_ERR_CONNECT_TIMEOUT, so the Sites table rendered dashes. Thanks @KuznetsovRA — #1. - An expired access token is now refreshed once per account per fan-out. The shared account row is updated in place and concurrent refreshes are deduped, so a 200-site page load no longer sends 200 token-endpoint requests — a burst Google can answer with 400s that were being read as
invalid_grantand marked the account revoked.
Changed
- Fan-out concurrency is tunable via
GSC_CONCURRENCY(default 8). URL Inspection runs narrower underGSC_INSPECT_CONCURRENCY(default 4) — it is capped by quota (2000/day, 600/min per property), not by the socket pool.
Full changelog: v0.6.1...v0.6.2
v0.6.1 — Setup wizard
Browser setup wizard — no more hand-editing .env.
On first run the app opens /setup: paste your Google Client ID / Secret (the page shows the exact redirect URI to register in GCP) and pick an access mode — loopback-only or exposed-with-login. AUTH_SECRET is auto-generated and stored in SQLite. For exposed deployments the admin is created in the browser (argon2), removing the old .env-quoting and one-shot-seed lockout traps.
Added
- Setup wizard at
/setup(Google keys + access mode) - In-browser admin creation for exposed deployments
Fixed
- Single-user (loopback) mode no longer forces a login redirect — it runs as a local admin
- Token refresh resolves the Google OAuth client credentials through the config layer (env or wizard-stored), so a wizard-configured self-host keeps refreshing tokens instead of failing ~1h after connect
Changed
- Config precedence: environment variables override wizard-stored values; env-provided fields are read-only in the wizard
Full notes in CHANGELOG.md.
v0.6.0 — SEO analytics suite
Big release: a full SEO analytics suite on top of Search Console — per-site deep-dives and a portfolio-wide view — plus Bing/IndexNow, site health checks, and optional multi-user login. All analytics are live-fetched; no GSC data is stored.
Highlights
- Per-site analytics (
/properties/[site]): Striking Distance, Keyword Cannibalization, CTR Benchmark, Content Decay, Branded split, Site Health (SSL / Safe Browsing / Core Web Vitals). - Portfolio analytics (
/properties/portfolio): the same, aggregated across every site — URL-addressable tabs, country filter, copy-queries, per-page GSC index status, async + cached. - Sparklines, one-click Privacy Blur, Bing Webmaster + IndexNow, and optional login & roles (off by default).
Full details: CHANGELOG.md · CHANGELOG.ru.md
v0.3.1 — totals bar
Added
- Totals bar on
/properties: sums Sites / Impressions / Clicks across all non-hidden sites for the selected period.
v0.3.0
Major release: deploy moved to Docker Compose + OrbStack, sitemap submitting was added, and query analytics / mobile UI were expanded.
Added
- Sitemap submit — per-site Submit sitemap (resubmits all sitemaps GSC knows, falls back to a guessed
/sitemap.xml) and a Submit all sitemaps toolbar button (parallel across visible sites). Failure reasons in the row tooltip. - Query breakdown by page and country (query × page × country) in Top queries.
- Google SERP link from a query's position cell (country-aware).
- "G" badge per site for a one-click Google
site:indexation check. - 1-day and 60-day periods (in addition to 3/7/28).
- Docker Compose + OrbStack deploy: multi-stage
Dockerfile,compose.yaml(served athttps://gsc.local, automatic TLS),.dockerignore. Container also binds127.0.0.1:5173 → 3000so OAuth can run over localhost.
Changed
- OAuth scope is now
webmasters(read-write), required for sitemap submit. - Deploy switched from pm2 to Docker Compose; secrets injected at runtime via
$env/dynamic/private. - READMEs (en/ru) and
CLAUDE.mdupdated.
Fixed
- Pinned
@auth/coreto0.41.2to match@auth/sveltekit1.11.2 (the stray0.34.3broke sign-in withbasePath?.replace is not a function).
Upgrade notes
- Connect accounts over
http://localhost:5173, nothttps://gsc.local— Google rejects OAuth redirects to the.localTLD. - Reconnect each account once to grant the read-write scope before submit works.
- The loopback port is bound to
127.0.0.1only; the app has no built-in auth and the DB holds OAuth tokens — do not expose it to the network.
Full changelog: https://github.com/izzipizzy/gsc-hub/blob/main/CHANGELOG.md
v0.2.0 — Operator Console refresh + URL Inspection
English
Visual design pass — "The Operator's Console"
- Unified toolbar pattern across all pages (breadcrumbs, segmented controls, primary action). 3d/7d/28d period and 2/4/6 columns toggles styled like Linear/Stripe Dashboard.
- Refined status badges with colored dots (active / revoked / error).
- Section dividers under Top queries and Top pages with descriptive subtitles.
- Sparkline cards now have a faint area fill (8% opacity) for visual weight.
- Query-history chart polished: dashed gridlines, thin axis, tightened legend.
- All numbers use tabular-nums for vertical alignment.
- Subtle hover affordance on table rows.
- Educational empty states ("No connected accounts yet" with a hint about local token storage).
- Reduced-motion respected via
@media (prefers-reduced-motion: reduce). - Full-bleed layout (no
max-w-*containers).
Footer
- Global footer with copyright, GitHub link, and Telegram link, all opening in new tabs.
URL Inspection (inline per-site expansion)
- Click any row in the Sites table to expand a quick URL Inspection report for the top 10 URLs of that site.
- Columns: URL, verdict (Indexed / Partial / Not indexed / Neutral), coverage state, robots.txt state, last crawl date, canonical URL with mismatch flag.
- 401 from Google marks the account
revokedautomatically; 429 surfaces "quota exceeded" without crashing the page. - Internal clicks (CSV links, Hide/Unhide, site link) don't toggle the expansion.
URL Inspection cache (12 hours)
- Results cached server-side in SQLite, keyed by
(account_id, site_url, urls_hash). Re-clicking the same site within 12h reuses the cache. - Force refresh button bypasses the cache for a deliberate fresh fetch.
- Cache age shown in the panel header ("Cached 5m ago / Fresh").
- Required because URL Inspection has a hard 2000-call/day quota per Google account.
Sitemap fallback
- Sites with no/low impressions in the current period now fall back to URLs from their sitemap.
- Source order: 1) GSC
sitemaps.list(user-submitted sitemaps, preferred); 2) direct/sitemap.xmlat site root. - Handles
<sitemapindex>by following the first child sitemap. - Homepage always included as the first URL inspected.
- Panel header shows the source: "Top by impressions" / "by impressions + sitemap fill-in" / "from sitemap (no impressions)".
Display polish
- Domain properties (
sc-domain:example.com) shown asexample.com; href still resolves tohttps://example.com/. - Page titles renamed: Properties → Sites (URL routes unchanged).
Tests
- 39 unit tests, up from 31.
Русский
Визуальный pass — «The Operator's Console»
- Единый паттерн toolbar'а на всех страницах (breadcrumbs, segmented controls, primary action). Переключатели периода 3d/7d/28d и колонок 2/4/6 в стилистике Linear/Stripe Dashboard.
- Refined status badges с цветными точками (active / revoked / error).
- Section dividers под Top queries и Top pages с описательными подписями.
- Карточки sparkline получили мягкую area fill (opacity 8%) для визуальной плотности.
- Query history chart: dashed gridlines, тонкая axis, плотная легенда.
- Все числа выводятся через
tabular-numsдля вертикального выравнивания. - Аккуратный hover на строках таблиц.
- Обучающие empty states («No connected accounts yet» с подсказкой про локальное хранение токенов).
- Reduced-motion поддерживается через
@media (prefers-reduced-motion: reduce). - Full-bleed layout (нигде
max-w-*обёрток).
Footer
- Глобальный footer с копирайтом, ссылкой на GitHub и Telegram, обе открываются в новой вкладке.
URL Inspection (inline expansion на сайт)
- Клик по строке в таблице Sites разворачивает быстрый отчёт URL Inspection для топ-10 URL'ов этого сайта.
- Колонки: URL, verdict (Indexed / Partial / Not indexed / Neutral), coverage state, robots.txt state, дата последнего crawl'а, canonical с flag'ом mismatch.
- 401 от Google автоматически помечает аккаунт
revoked; 429 показывается как «quota exceeded» без падения страницы. - Внутренние клики (CSV-ссылки, Hide/Unhide, ссылка на сайт) не дёргают expansion.
Кеш URL Inspection (12 часов)
- Результаты кешируются server-side в SQLite по ключу
(account_id, site_url, urls_hash). Повторный клик в течение 12 часов берёт из кеша. - Кнопка Force refresh обходит кеш для явного свежего запроса.
- Возраст кеша показан в шапке панели («Cached 5m ago / Fresh»).
- Кеш необходим потому что у URL Inspection жёсткий лимит 2000 вызовов/день на Google-аккаунт.
Sitemap fallback
- Для сайтов без/с малыми impressions в текущем периоде теперь используется fallback по sitemap.
- Порядок источников: 1) GSC
sitemaps.list(пользователь-сабмиттед sitemap'ы, предпочтительно); 2) прямой/sitemap.xmlв корне сайта. <sitemapindex>обрабатывается переходом по первому child-sitemap.- Главная страница всегда включена первой.
- Шапка панели показывает источник: «Top by impressions» / «by impressions + sitemap fill-in» / «from sitemap (no impressions)».
Полировка отображения
- Domain properties (
sc-domain:example.com) показываются какexample.com; href ведёт наhttps://example.com/. - Заголовки переименованы: Properties → Sites (URL-маршруты не менялись).
Тесты
- 39 unit-тестов, было 31.
v0.1.0 — initial public release
First public release.
What is gsc-hub
Local self-hosted multi-account hub for Google Search Console. Connect multiple Google accounts via OAuth, view all sites in one table, aggregate queries and pages across accounts, see per-site dashboards with sparklines and period-over-period deltas, drill into 16-month query history with one click. No external service, no GSC data leaves your machine.
Features
- Multi-account OAuth — connect any number of Google accounts; refresh tokens auto-rotate; revoked tokens detected on first 401.
- Unified sites table — all Search Console properties from all connected accounts in one full-bleed table, sortable, URL-driven (
?days=,?sort=,?dir=). - Top queries & Top pages — aggregated rollups across visible (non-hidden) sites, client-side sortable.
- Inline 16-month query history — click any query to expand a position+impressions chart spanning the last 16 months.
- Dashboard — per-site cards with sparkline of daily clicks, four metrics with deltas vs the previous period of the same length, configurable 2/4/6 columns.
- CSV exports — queries or pages, configurable period, RFC 4180 compliant.
- Hide sites locally (browser localStorage), Refresh preserves all URL state.
- No data caching — every page load is a fresh fan-out to GSC.
Stack
SvelteKit (Svelte 5) + TypeScript · Auth.js · better-sqlite3 · TailwindCSS v3 · Vitest. No chart libs, no icon fonts, system sans only.
Quickstart
```bash
git clone https://github.com/izzipizzy/gsc-hub.git
cd gsc-hub
pnpm install
cp .env.example .env
fill in GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, AUTH_SECRET
pnpm dev
```
See README.md for full setup, README.ru.md for Russian.
License
MIT.