yufan.me ships with first-party analytics backed entirely by Postgres — no third-party tracking scripts, no data leaving your server. Every page visit is ingested, enriched, and stored so you own the full dataset and can query it directly if you need to.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/syhily/yufan.me/llms.txt
Use this file to discover all available pages before exploring further.
How data is collected
Visits are recorded through a Hono resource endpoint that fires asynchronously on every page render, so analytics never block the response. The pipeline applies a layered bot filter before anything is written:- Bot detection — the
isbotlibrary checks the User-Agent string against a registry of ~6 000 known bots. The UA parser (ua-parser-js) provides a second check, catching any crawler, spider, or fetcher thatisbotmisses. - Prefetch suppression — requests with a
Purpose: prefetchorSec-Purpose: prefetchheader are discarded before bot detection runs. - Geo enrichment — if
MAXMIND_DB_PATHis set, the visitor’s IP is looked up in the GeoLite2-City database for country, region, city, and coordinates.
What is tracked
Eachaccess_log row captures the following fields:
| Field | Description |
|---|---|
path | Request path (no query string) |
referer | Raw Referer header |
referer_host | Parsed hostname from the referrer |
browser / browser_version | Parsed from User-Agent via ua-parser-js |
os / os_version | Operating system parsed from User-Agent |
device / device_type | Device model and type (desktop, mobile, tablet, …) |
visitor_hash | SHA-256 of IP + daily_salt, truncated to 32 hex chars — identifies unique visitors without storing raw IPs |
ip | Raw client IP (stored but not exposed in dashboard queries) |
language | Primary language tag from Accept-Language |
country / region / city | Geo fields from MaxMind (all NULL if not configured) |
latitude / longitude / timezone | Geo coordinates and timezone from MaxMind |
ts | Visit timestamp |
visitor_hash, not the raw IP. The salt rotates every 24 hours, so the hash cannot be used to track individuals across days.
Dashboard sections
Navigate to Admin → Analytics to access the dashboards.Overview
The overview tab shows aggregate metrics for a configurable time range:- Total visits and unique visitors (counted by
visitor_hash) - Top pages by visit count
- Top referrers by hostname
- Browser breakdown — share of visits per browser
- OS breakdown — share of visits per operating system
Realtime
The realtime tab shows:- Live visitor count — active visitors in the last few minutes
- Recent page views — a live feed of the most recent requests
MaxMind geo-enrichment
Geo columns (country, region, city, latitude, longitude, timezone) are NULL by default. To enable them:
- Download the free GeoLite2-City database from MaxMind (requires a free account).
- Place the
.mmdbfile somewhere accessible to the running process. - Set the environment variable:
Excluding admin visits
By default, visits made while logged in as an admin are excluded from analytics. This prevents your own browsing from inflating visitor counts. To include admin visits — useful during local testing to verify the pipeline is working — set:Analytics data is stored in the
access_log table. If you have TimescaleDB installed, migration 20260514000003_access_log_timescale converts it to a hypertable, which improves time-series query performance at scale. yufan.me works correctly without TimescaleDB — the migration is entirely optional.