Files
2026-07-20 09:02:42 +02:00

182 lines
8.2 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""analytics - stateless API proxy for Google Analytics (GA4) and Sklik.
Runs behind the AppFactory Caddy reverse proxy at /apps/<app-id>. ROOT_PATH is
injected as an env var; FastAPI's ``root_path`` makes Swagger UI and the OpenAPI
``servers`` use the proxy prefix so "Try it out" hits /apps/<app-id>/... .
The service stores no secrets. Every credential is supplied per request in an
X- header and used only to talk to the upstream API (see AGENTS.md and
``app.credentials``).
"""
import os
from fastapi import FastAPI
from . import config
from .errors import register_exception_handlers
from .logging_config import get_logger
from .routers import ga_admin, ga_data, googleads, gsc, meta, sklik
logger = get_logger(__name__)
ROOT_PATH = os.getenv("ROOT_PATH", "")
DESCRIPTION = """
Stateless proxy exposing **Google Analytics 4**, **Google Search Console**,
**Google Ads** and **Sklik** (Seznam) APIs.
Veškeré přihlašovací údaje se posílají v každém requestu jako `X-` hlavičky
služba si nic neukládá. Vyplníte je v Swaggeru po kliknutí na **Try it out**.
| Služba | Hlavička | Povinné |
| --- | --- | --- |
| Google Analytics | `X-GA-Access-Token` **nebo** `X-GA-Credentials` | jedna z nich |
| Google Analytics | `X-GA-Quota-Project` | ne |
| Search Console | `X-GSC-Access-Token` **nebo** `X-GSC-Credentials` | jedna z nich |
| Search Console | `X-GSC-Quota-Project` | ne |
| Google Ads | `X-GAds-Developer-Token` | ano |
| Google Ads | `X-GAds-Access-Token` **nebo** `X-GAds-Credentials` | jedna z nich |
| Google Ads | `X-GAds-Login-Customer-Id`, `X-GAds-Quota-Project` | ne |
| Sklik | `X-Sklik-Token` | ano |
| Sklik | `X-Sklik-User-Id` | ne (jen pro agenturní/MCC přístup) |
| Sklik zápis | `X-Idempotency-Key` | ne (doporučeno, viz níže) |
Tři Google služby používají stejný princip přihlášení (Google OAuth) liší se
jen prefixem hlavičky a oprávněním (scope). **Jeden service account lze použít
pro všechny tři** (stačí mu udělit přístup v dané službě a povolit příslušné
API). U Google Ads navíc vždy potřebujete *developer token*.
> 💡 Hotový OAuth2 *access token* lze u všech tří Google služeb poslat dvěma
> způsoby: buď v původní hlavičce `X-…-Access-Token`, **nebo** ve standardní
> hlavičce `Authorization: Bearer <token>`. Obě cesty jsou rovnocenné; pokud
> pošlete obě, vyhrává `X-…-Access-Token`. Pořadí priority je
> `X-…-Access-Token` → `Authorization: Bearer` → `X-…-Credentials`.
---
## Kde vzít přihlašovací údaje
### 🔹 Společné pro všechny Google služby přístup přes service account
1. [Google Cloud Console](https://console.cloud.google.com/) → vytvořte nebo
vyberte projekt.
2. **APIs & Services → Library** → povolte API podle toho, co budete volat:
*Google Analytics Data API* + *Google Analytics Admin API*,
*Google Search Console API*, *Google Ads API*.
3. **IAM & Admin → Service Accounts → Create service account**.
4. U účtu **Keys → Add key → Create new key → JSON** stáhne se klíč.
5. Klíč zakódujte do **base64** a vložte do příslušné `*-Credentials` hlavičky:
- Windows PowerShell:
`[Convert]::ToBase64String([IO.File]::ReadAllBytes("klic.json"))`
- Linux/macOS: `base64 -w0 klic.json`
Service account (jeho `client_email` z JSON) pak musíte **přidat jako uživatele
v dané službě** viz níže. Místo service accountu lze vždy poslat i hotový
OAuth2 *access token* (např. z
[OAuth Playground](https://developers.google.com/oauthplayground/) se správným
scope); platí ~1 hodinu. Token pošlete buď v `*-Access-Token`, nebo ve
standardní hlavičce `Authorization: Bearer <token>`.
### 🔹 Google Analytics 4 (`X-GA-*`)
- **ID property** (`property_id` v URL): GA4 → **Administrace → Nastavení
property** → *ID property*, např. `123456789`.
- Přístup: service account `client_email` přidejte v **Administrace → Správa
přístupu k property** jako **Viewer**. Scope: `analytics.readonly`.
### 🔹 Google Search Console (`X-GSC-*`)
- **siteUrl**: adresa property, buď URL-prefix (`https://example.com/`) nebo
doménová property (`sc-domain:example.com`). Posílá se jako parametr `siteUrl`.
- Přístup: v [Search Console](https://search.google.com/search-console) →
**Nastavení → Uživatelé a oprávnění** přidejte `client_email` service accountu
(role *Full* nebo *Restricted*). Scope: `webmasters.readonly`.
### 🔹 Google Ads (`X-GAds-*`)
- **Developer token** (`X-GAds-Developer-Token`, povinný): v **Google Ads
manager (MCC) účtu → Tools → API Center**. Token musí mít schválený přístup.
- **customer_id** (v URL): 10místné číslo účtu (bez pomlček).
- **login-customer-id** (`X-GAds-Login-Customer-Id`, volitelné): ID manager
(MCC) účtu, přes který přistupujete k podřízenému účtu.
- Přístup: nejjednodušší je poslat hotový OAuth2 *access token* se scope
`https://www.googleapis.com/auth/adwords` v `X-GAds-Access-Token`.
Service account funguje jen s *domain-wide delegation*.
### 🔹 Sklik (`X-Sklik-Token`)
1. Přihlaste se na [sklik.cz](https://www.sklik.cz/).
2. Vpravo nahoře **své uživatelské jméno → Nastavení**.
3. Sekce **Přístup k API Drak** → **Zobrazit token**.
4. Token zkopírujte do hlavičky `X-Sklik-Token`.
> ⚠️ Každé vygenerování nového tokenu **zneplatní ten předchozí**. Token je
> vázaný na účet, pod kterým jste přihlášeni. Pro správu cizích účtů
> (agentura/MCC) vložte cílové `userId` do hlavičky `X-Sklik-User-Id`.
---
## ⚠️ Sklik správa kampaní (zápis)
Plné CRUD nad kampaněmi, sestavami a inzeráty. Tělo je **pole struktur** podle
[dokumentace Skliku](https://api.sklik.cz/drak/campaigns.create.html); dávka je
all-or-nothing.
Entita = `campaigns`, `groups`, `ads` nebo `keywords`.
| Operace | Endpoint |
| --- | --- |
| Založení | `POST /sklik/{entita}` |
| Úprava | `PUT /sklik/{entita}` (povinné `id`) |
| Smazání | `DELETE /sklik/{entita}?ids=1,2` |
| Obnovení | `POST /sklik/{entita}/restore?ids=1,2` |
- **Kampaně, sestavy a inzeráty se zakládají pauznuté.** Sklik má výchozí
`status: active`, takže neuvedený stav by znamenal živou kampaň proxy proto
při zakládání vynutí `status: "suspend"` a jinou hodnotu ignoruje.
- **Klíčová slova jsou výjimka** a pauznutá se nezakládají. Samo o sobě nic
neutratí (utrácení hlídá kampaň/sestava/inzerát nad ním) a pauznuté klíčové
slovo by způsobilo, že po ručním spuštění kampaně se nic nestane.
- **`PUT` status nevynucuje** tím se kampaň pozastavuje (`suspend`) i
spouští (`active`). Pokud má aktivace zůstat výhradně ruční, nastavte
`SKLIK_BLOCK_ACTIVATION=true`; pak je `active` odmítnuto s 403.
- **Mazání je vratné** Sklik entitu jen označí jako smazanou, `/restore` ji vrátí.
- **U inzerátů** platí, že změna kreativy (nadpisy, popis, URL) starý inzerát
smaže a založí nový **inzerát dostane nové `id`**.
- **Částky jsou v haléřích** (100 = 1 Kč). `dayBudget: 20000` = 200 Kč/den.
- **`X-Idempotency-Key`** (volitelné, doporučené): při opakovaném odeslání se
stejným klíčem se vrátí původní výsledek místo zopakování zápisu.
- **Stropy rozpočtů** jsou ve výchozím stavu **vypnuté**. Zapínají se
proměnnými `SKLIK_MAX_DAY_BUDGET_HALERS`, `SKLIK_MAX_TOTAL_BUDGET_HALERS`
a `SKLIK_MAX_CPC_HALERS`. Co je aktuálně nastavené, ukáže
`GET /sklik/write-limits`.
Číst strukturu účtu lze přes `GET /sklik/campaigns`, `/sklik/groups`,
`/sklik/ads` (stránkování řeší proxy), statistiky přes `POST
/sklik/report/{entity}`.
---
Podrobnosti k jednotlivým endpointům jsou v `documentation/` v repozitáři.
""".strip()
app = FastAPI(
title=config.APP_NAME,
version=config.APP_VERSION,
description=DESCRIPTION,
root_path=ROOT_PATH,
)
register_exception_handlers(app)
app.include_router(meta.router)
app.include_router(ga_data.router)
app.include_router(ga_admin.router)
app.include_router(gsc.router)
app.include_router(googleads.router)
app.include_router(sklik.router)
logger.info(
"analytics started (version=%s, root_path=%r)", config.APP_VERSION, ROOT_PATH
)