Analytics API — Dokumentasi
Versi: 1.0 • Terakhir diperbarui: 2025-11-04 04:17:39 (Asia/Jakarta)
Komponen: app/routes/analytics_model.py (Blueprint Flask)
Ringkasan
Analytics API ini merekam dan menganalisis trafik web melalui tiga tabel utama: page_views, sessions, dan visitors.
Blueprint berada pada url_prefix="/analytics". Semua waktu/timestamp yang dikembalikan adalah RAW sesuai zona waktu server/DB (tidak ada konversi timezone di layer aplikasi).
Fitur inti:
- Koleksi event kunjungan (endpoint
/collect) dengan rate gate 60 menit perclient_id. - Ringkasan metrik (KPI, harian, per jam, top pages, browser share, active now).
- Akses data mentah (visitors, sessions, page_views, joined 3 tabel).
- Reset data (opsional, non-produksi).
Catatan: Tidak ada autentikasi/otorisasi di kode ini. Tambahkan lapisan auth (JWT/session) untuk produksi.
Skema Database
1) page_views
| Kolom | Tipe | Keterangan |
|---|---|---|
| id | BIGINT UNSIGNED PK | Auto increment |
| client_id | VARCHAR(64) | ID klien (mis. fingerprint/cookie) |
| session_uuid | CHAR(36) | UUID sesi (format 8-4-4-4-12) |
| path | TEXT | Path URL yang dikunjungi |
| title | TEXT NULL | Judul halaman (opsional) |
| ts | DATETIME | Waktu event (default CURRENT_TIMESTAMP()) |
| ip_hash | CHAR(64) NULL | SHA-256 dari IP |
| ip_mask | VARCHAR(45) NULL | IP yang sudah dimask (IPv4: octet ke-4=0; IPv6: empat hextet pertama + ::) |
| user_agent | TEXT NULL | User-Agent |
| browser_name | VARCHAR(80) NULL | Nama browser yang diringkas |
| device_type | VARCHAR(30) NULL | Jenis perangkat (mis. mobile/desktop) |
Index: idx_pv_client (client_id), idx_pv_ts (ts), idx_pv_ipmask (ip_mask)
2) sessions
| Kolom | Tipe | Keterangan |
|---|---|---|
| id | BIGINT UNSIGNED PK | Auto increment |
| client_id | VARCHAR(64) | Mengacu ke pengunjung |
| session_uuid | CHAR(36) UNIQUE | UUID sesi |
| started_at | DATETIME | Waktu sesi dimulai |
| last_event_at | DATETIME | Waktu event terakhir (diupdate on write) |
Index: uk_session_uuid (session_uuid), idx_sessions_client (client_id)
3) visitors
| Kolom | Tipe | Keterangan |
|---|---|---|
| id | BIGINT UNSIGNED PK | Auto increment |
| client_id | VARCHAR(64) UNIQUE | Key pengunjung |
| first_seen | DATETIME | Pertama kali terlihat |
| last_seen | DATETIME | Terakhir terlihat (diupdate on write) |
Alur Data & Privasi
Koleksi Event (POST /collect)
- Ambil body JSON dan header:
client_id(≤ 64 char) wajibpathwajibsession_uuid(opsional; auto-generate jika kosong)title,browser_name,device_type(opsional)user_agent: diprioritaskan dari HTTP headerUser-Agent, fallback dari body
- Dapatkan IP client dari
X-Forwarded-For(jika ada) atauremote_addr. - Masking dan hashing IP:
- IPv4 → octet ke-4 diganti
0(mis.192.168.10.5→192.168.10.0) - IPv6 → simpan empat hextet awal lalu
::(mis.2001:db8:85a3::8a2e:370:7334→2001:db8:85a3:0000::) - Hash →
sha256(ip)
- IPv4 → octet ke-4 diganti
- Rate gate 60 menit per
client_id: jika sudah adapage_viewspada 60 menit terakhir, tidak insert baru, tapi tetap updatevisitors&sessions. - Insert
page_viewsjika lolos gate.
Privasi
- IP tidak disimpan utuh: hanya hash dan mask.
- Data waktu disimpan apa adanya sesuai zona waktu server/DB.
Base URL
Blueprint: url_prefix="/analytics"
Jika aplikasi utama dipasang di bawah /api, maka base URL: /api/analytics.
Contoh di bawah menggunakan prefix
/api.
Endpoints: Ringkasan (Summary)
1) KPI Global
GET /api/analytics/summary/kpis?from=YYYY-MM-DD&to=YYYY-MM-DD
- Query (opsional):
from,to(YYYY-MM-DDatauYYYY-MM-DD HH:MM:SS) - Default: 30 hari terakhir (mulai 00:00:00 hingga sekarang, RAW)
- Metode: menghitung total visits, unique visitors, visits today (sejak
CURDATE()), dan active now (5 menit terakhir) - Response 200
{
"success": true,
"data": {
"range_from": "2025-10-06 00:00:00",
"range_to": "2025-11-04 10:30:21",
"visits": 1234,
"unique_visitors": 456,
"visits_today": 78,
"active_now": 5
}
}
2) Kunjungan Harian
GET /api/analytics/summary/visits_daily?from=YYYY-MM-DD&to=YYYY-MM-DD
- Default: 30 hari terakhir
- Response 200
{
"success": true,
"data": [
{"day": "2025-10-10", "visits": 20},
{"day": "2025-10-11", "visits": 35}
],
"range_from": "2025-10-06 00:00:00",
"range_to": "2025-11-04 10:30:21"
}
3) Kunjungan per Jam (24 jam)
GET /api/analytics/summary/visits_hourly?from=YYYY-MM-DD HH:MM:SS&to=YYYY-MM-DD HH:MM:SS
- Default: 24 jam terakhir (RAW)
- Bucket:
DATE_FORMAT(ts, '%Y-%m-%d %H:00:00') - Response 200
{
"success": true,
"data": [
{"hour_bucket": "2025-11-03 11:00:00", "visits": 5},
{"hour_bucket": "2025-11-03 12:00:00", "visits": 8}
],
"range_from": "2025-11-03 10:30:21",
"range_to": "2025-11-04 10:30:21"
}
4) Top Pages
GET /api/analytics/summary/top_pages?from=YYYY-MM-DD&to=YYYY-MM-DD&limit=20
- Default: 30 hari,
limit=20 - Response 200
{
"success": true,
"data": [
{"path": "/home", "visits": 120},
{"path": "/jobs", "visits": 90}
],
"range_from": "2025-10-06 00:00:00",
"range_to": "2025-11-04 10:30:21"
}
5) Browser Share
GET /api/analytics/summary/browsers?from=YYYY-MM-DD&to=YYYY-MM-DD
browser_nameyangNULLdi-normalisasi menjadi"Unknown"- Response 200
{
"success": true,
"data": [
{"browser": "Chrome", "visits": 300},
{"browser": "Safari", "visits": 120},
{"browser": "Unknown", "visits": 10}
],
"range_from": "2025-10-06 00:00:00",
"range_to": "2025-11-04 10:30:21"
}
6) Active Now
GET /api/analytics/summary/active_now
- Definisi: jumlah
client_idunik padapage_viewsselama 5 menit terakhir - Response 200
{ "success": true, "data": { "active_now": 7 } }
Endpoints: Data Mentah (Raw)
7) Visitors
GET /api/analytics/visitors?limit=100
- Query:
limit(opsional) - Sort:
ORDER BY last_seen DESC - Response 200
{ "success": true, "data": [ { "id": 1, "client_id": "abc...", "first_seen": "...", "last_seen": "..." } ] }
8) Sessions
GET /api/analytics/sessions?limit=100
- Query:
limit(opsional) - Sort:
ORDER BY last_event_at DESC - Response 200
{ "success": true, "data": [ { "id": 1, "client_id": "abc...", "session_uuid": "...", "started_at": "...", "last_event_at": "..." } ] }
9) Page Views
GET /api/analytics/page_views?limit=100
- Query:
limit(opsional) - Sort:
ORDER BY ts DESC - Response 200
{ "success": true, "data": [ { "id": 1, "client_id": "abc...", "path": "/home", "ts": "...", "...": "..." } ] }
10) Joined (3 Tabel)
GET /api/analytics/joined?limit=100&since=YYYY-MM-DD HH:MM:SS
- Query:
limit(opsional),since(opsional) — filterpv.ts >= since - Join:
page_views(pv) LEFT JOINsessions(s) LEFT JOINvisitors(v) - Response 200
{
"success": true,
"data": [
{
"pv_id": 1,
"ts": "2025-11-04 09:00:00",
"path": "/home",
"title": "Home",
"client_id": "abc",
"session_uuid": "uuid-...",
"ip_hash": "sha256...",
"ip_mask": "192.168.1.0",
"user_agent": "Mozilla/...",
"browser_name": "Chrome",
"device_type": "desktop",
"session_started_at": "2025-11-04 08:55:00",
"session_last_event_at": "2025-11-04 09:05:00",
"visitor_first_seen": "2025-10-01 10:00:00",
"visitor_last_seen": "2025-11-04 09:05:00"
}
]
}
Endpoint: Koleksi Event
11) Collect (Upsert + Insert, Rate Gate 60 mnt)
POST /api/analytics/collect
Body (JSON)
client_id(string ≤64, wajib)path(string, wajib)session_uuid(string, opsional; auto-generate jika kosong)title(string, opsional)browser_name(string ≤80, opsional)device_type(string ≤30, opsional)user_agent(string, opsional; default dari headerUser-Agent)
Response 201 (insert baru)
{ "success": true, "page_view_id": 999, "message": "Collected" }
Response 204 (rate gate)
- Jika sudah ada
page_viewsuntukclient_idyang sama dalam 60 menit terakhir. - Visitor & session tetap di-upsert, tapi tidak ada record page_view baru.
Response 400
{ "success": false, "message": "client_id & path wajib" }
Response 500
{ "success": false, "message": "penjelasan kesalahan" }
Endpoint: Reset (Non-Produksi)
12) Reset Semua Data
DELETE /api/analytics/reset
- Efek: menghapus semua data dari
page_views,sessions, danvisitors. - Hanya untuk development/testing.
Response 200
{ "success": true, "message": "Semua data dihapus" }
Contoh curl
Ganti
{{HOST}}sesuai host Anda.
KPI 7 hari ke belakang
curl -s "https://{{HOST}}/api/analytics/summary/kpis?from=$(date -d '-6 days' +%F)&to=$(date +%F)"
Visits per jam default (24 jam terakhir)
curl -s "https://{{HOST}}/api/analytics/summary/visits_hourly"
Top pages 30 hari (limit 10)
curl -s "https://{{HOST}}/api/analytics/summary/top_pages?limit=10"
Collect event sederhana
curl -s -X POST "https://{{HOST}}/api/analytics/collect" -H "Content-Type: application/json" -d '{"client_id":"abc123","path":"/home","title":"Home"}' -i
Joined data sejak timestamp tertentu
curl -s "https://{{HOST}}/api/analytics/joined?since=2025-11-01%2000:00:00&limit=50"
Validasi & Edge Cases
- Semua waktu di-parse apa adanya (RAW). Param
from/tomenerimaYYYY-MM-DD(dianggap00:00:00) atauYYYY-MM-DD HH:MM:SS. - Tidak ada pagination offset pada endpoints summary; raw endpoints mendukung
limitsederhana. - Jika aplikasi berjalan di belakang proxy/load balancer, pastikan header
X-Forwarded-Forterpercaya untuk IP asli. browser_namebisaNULL→ dinormalisasi menjadi"Unknown"pada ringkasan browser.
Rekomendasi Produksi
- Tambahkan auth (JWT) dan rate limiting (khususnya pada
/collectdan/reset). - Gunakan connection pooling untuk MySQL (sekarang koneksi per request).
- Tambahkan index tambahan:
(client_id, ts)padapage_viewsbila query frekuen. - Sediakan retention policy (mis. hapus data mentah > 180 hari).
- Audit & logging terstruktur (request-id, trace-id).
Perubahan vs Versi Sebelumnya
- Initial release dokumentasi berdasarkan implementasi
analytics_model.pytanpa modifikasi perilaku.
Struktur Respons (Konvensi)
- Sukses:
{ "success": true, ... } - Gagal:
{ "success": false, "message": "..." }