File size: 4,999 Bytes
aa7e587
9ecb1ae
 
aa7e587
 
 
 
 
 
 
9ecb1ae
aa7e587
9ecb1ae
aa7e587
9ecb1ae
aa7e587
9ecb1ae
 
 
 
 
 
 
aa7e587
9ecb1ae
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a10ad9b
9ecb1ae
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a10ad9b
 
 
 
 
 
 
 
 
9ecb1ae
 
a10ad9b
 
 
 
9ecb1ae
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
---
title: HF Space Analytics
emoji: πŸ“Š
colorFrom: yellow
colorTo: indigo
sdk: docker
pinned: false
license: mit
---

# HF Space Analytics β€” Gradio Edition

A small Gradio Space that tracks page views across your other Hugging Face Spaces. Gradio provides the dashboard, while an embedded FastAPI application provides the tracking endpoints.

## Features

- Gradio dashboard at `/`
- `POST /track` β€” record a page-view event
- `GET /stats` β€” aggregated visit counts
- `GET /events` β€” recent raw events
- `GET /tracker.js` β€” reusable browser tracker
- `GET /health` β€” health and storage check
- Daily JSONL storage in an attached Hugging Face Storage Bucket

## 1. Create the analytics Space

1. Create a new Hugging Face Space.
2. Select **Gradio** as the SDK.
3. Select **ZeroGPU** hardware. Dashboard refreshes use a one-second ZeroGPU allocation;
   tracking and API requests remain CPU-only.
4. Upload every file in this project, or push the repository with Git.
5. In the Space settings, attach a Hugging Face Storage Bucket as a read-write volume mounted at `/data`.
6. Add the optional variables and secrets below.

| Name | Type | Purpose |
|---|---|---|
| `TRACKING_KEY` | Secret | Required in tracking requests when set |
| `ADMIN_KEY` | Secret | Protects `/stats`, `/events`, and the dashboard when set |
| `ALLOWED_ORIGINS` | Variable | Comma-separated CORS origins; default `*` |
| `DATA_DIR` | Variable | Storage path; default `/data/visits` |
| `HASH_SALT` | Secret | Salt used to hash browser visitor IDs |
| `GEOIP_DB_PATH` | Variable | Path to a MaxMind GeoLite2 City database; default `/data/GeoLite2-City.mmdb` |

The top-level `app.py` starts the mounted Gradio/FastAPI application on port `7860`,
the standard port for Gradio Spaces. Do not configure a `PORT` variable for this Space;
that generic variable may be used by another process in the runtime.

## 2. Add tracking to another Space

### Static HTML or a custom frontend

Add this before `</body>`:

```html
<script
  src="https://YOUR-ANALYTICS-SPACE.hf.space/tracker.js"
  data-endpoint="https://YOUR-ANALYTICS-SPACE.hf.space/track"
  data-space="YOUR_USERNAME/YOUR_SPACE"
  data-key="YOUR_TRACKING_KEY"
  defer>
</script>
```

Omit `data-key` when `TRACKING_KEY` is not configured.

### Another Gradio Space

See [`examples/gradio_example.py`](examples/gradio_example.py). It injects the same tracker through `gr.Blocks(head=...)`.

### Direct JavaScript

```javascript
fetch("https://YOUR-ANALYTICS-SPACE.hf.space/track", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-Tracking-Key": "YOUR_TRACKING_KEY"
  },
  body: JSON.stringify({
    space: "YOUR_USERNAME/YOUR_SPACE",
    path: location.pathname,
    referrer: document.referrer || null,
    visitor_id: localStorage.getItem("hf_analytics_visitor")
  }),
  keepalive: true
});
```

## 3. Read statistics

Open the analytics Space to use the Gradio dashboard, or call the API directly:

```bash
curl "https://YOUR-ANALYTICS-SPACE.hf.space/stats?days=30"
```

With an admin key:

```bash
curl -H "X-Admin-Key: YOUR_ADMIN_KEY" \
  "https://YOUR-ANALYTICS-SPACE.hf.space/stats?days=30"
```

## Storage

The default layout is:

```text
/data/visits/
β”œβ”€β”€ 2026-08-06.jsonl
β”œβ”€β”€ 2026-08-07.jsonl
└── ...
```

Without a bucket mounted at `/data`, files are written to the Space filesystem and may disappear after a restart or rebuild.

### IP-based location

To store approximate IP-based location, provide a licensed MaxMind GeoLite2
City database at `GEOIP_DB_PATH` (the default is
`/data/GeoLite2-City.mmdb`). Each event stores only the available coarse fields
under `location`: country code, country, region, and city. The raw IP is never
stored. If the database is absent or cannot identify an address, `location` is
`null` and tracking continues normally.

## Privacy and security

- A salted hash of the connecting IP address is stored as `ip_hash`; raw IP addresses are not stored.
- Approximate IP-based location is stored only when a GeoLite2 database is configured.
- The recorded address may be the Hugging Face proxy address rather than the visitor's original IP.
- The tracker stores the browser-reported timezone and language as coarse location hints; it does not collect GPS coordinates.
- Browser visitor IDs are salted and hashed before storage.
- Paths and referrers may contain sensitive information; omit them when unnecessary.
- `TRACKING_KEY` is visible to visitors when included in browser JavaScript. Treat it only as a low-value anti-spam key, never as a Hugging Face access token.
- Set an `ADMIN_KEY` before exposing raw events or private analytics.
- Publish an appropriate privacy notice for your users and jurisdiction.

## Local development

```bash
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
DATA_DIR=./local-data python app.py
```

Then open `http://localhost:7860`.

Run tests:

```bash
pip install -r requirements-dev.txt
pytest
```