Skip to content

Commit 409d334

Browse files
authored
Merge pull request #518 from kinde-oss/docs/usehall-integration
Setup integration with Hall for AI LLM crawler metrics
2 parents 512a6d6 + ff5baf2 commit 409d334

File tree

4 files changed

+132
-4
lines changed

4 files changed

+132
-4
lines changed

customHttp.yml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ customHeaders:
55
value: >-
66
default-src 'self' *.kinde.com; style-src 'self' 'unsafe-inline'
77
https://fonts.googleapis.com; frame-src
8-
https://www.youtube-nocookie.com; child-src 'self'; connect-src 'self'
8+
https://www.youtube-nocookie.com; child-src 'self'; connect-src 'self'
99
ws https://api.management.inkeep.com https://api.inkeep.com
1010
wss://api.inkeep.com https://api.hsforms.com https://app.kinde.com
11-
https://kinde.com https://kinde-api-docs-proxy.pages.dev; base-uri
11+
https://kinde.com https://kinde-api-docs-proxy.pages.dev
12+
https://analytics.usehall.com; base-uri
1213
'none'; font-src 'self' https://fonts.gstatic.com; img-src 'self'
1314
data: https://storage.googleapis.com https://imagedelivery.net
1415
https://customer-xcbruusbiervz265.cloudflarestream.com
@@ -78,7 +79,7 @@ customHeaders:
7879
'self' ws https://api.management.inkeep.com https://api.inkeep.com
7980
wss://api.inkeep.com https://api.hsforms.com https://app.kinde.com
8081
https://kinde.com https://api-spec.kinde.com
81-
https://kinde-api-docs-proxy.pages.dev; img-src
82+
https://kinde-api-docs-proxy.pages.dev https://analytics.usehall.com; img-src
8283
https://storage.googleapis.com
8384
- pattern: /kinde-apis/frontend/*
8485
headers:
@@ -92,5 +93,5 @@ customHeaders:
9293
'self' ws https://api.management.inkeep.com https://api.inkeep.com
9394
wss://api.inkeep.com https://api.hsforms.com https://app.kinde.com
9495
https://kinde.com https://api-spec.kinde.com
95-
https://kinde-api-docs-proxy.pages.dev; img-src
96+
https://kinde-api-docs-proxy.pages.dev https://analytics.usehall.com; img-src
9697
https://storage.googleapis.com

src/components/HallAnalytics.astro

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
---
2+
// Client-side Hall Analytics Component
3+
// This works with static hosting like AWS Amplify
4+
const HALL_API_KEY = import.meta.env.HALL_API_KEY;
5+
---
6+
7+
<script define:vars={{ apiKey: HALL_API_KEY || '' }}>
8+
// Hall Analytics Client-Side Tracking
9+
(function() {
10+
'use strict';
11+
12+
const HALL_API_KEY = apiKey;
13+
14+
if (!HALL_API_KEY) {
15+
return;
16+
}
17+
18+
// Track page view when component loads
19+
function trackPageView() {
20+
const path = window.location.pathname + window.location.search;
21+
const method = 'GET';
22+
const timestamp = Date.now();
23+
24+
// Get IP from headers (will be client IP)
25+
const requestHeaders = {
26+
'User-Agent': navigator.userAgent,
27+
'Host': window.location.host,
28+
'Referer': document.referrer,
29+
'Accept-Language': navigator.language,
30+
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
31+
...(navigator.userAgentData ? {
32+
'Sec-Ch-Ua-Mobile': navigator.userAgentData.mobile ? '?1' : '?0',
33+
'Sec-Ch-Ua-Platform': `"${navigator.userAgentData.platform}"`
34+
} : {})
35+
};
36+
37+
const analyticsData = {
38+
request_path: path,
39+
request_method: method,
40+
request_ip: '127.0.0.1', // Client-side can't get real IP
41+
request_headers: requestHeaders,
42+
request_timestamp: timestamp
43+
};
44+
45+
// Send to Hall Analytics
46+
fetch('https://analytics.usehall.com/visit', {
47+
method: 'POST',
48+
headers: {
49+
'Content-Type': 'application/json',
50+
'Authorization': `Bearer ${HALL_API_KEY}`,
51+
},
52+
body: JSON.stringify(analyticsData),
53+
})
54+
.then(response => {
55+
if (!response.ok) {
56+
console.error('[Hall Analytics] Failed to track:', response.status);
57+
}
58+
})
59+
.catch(error => {
60+
console.error('[Hall Analytics] Network error:', error);
61+
});
62+
}
63+
64+
// Track initial page load
65+
if (document.readyState === 'loading') {
66+
document.addEventListener('DOMContentLoaded', trackPageView);
67+
} else {
68+
trackPageView();
69+
}
70+
71+
// Store observers for potential cleanup
72+
const observers = [];
73+
74+
// Also track when body becomes available (fallback)
75+
if (!document.body) {
76+
const bodyObserver = new MutationObserver(() => {
77+
if (document.body) {
78+
bodyObserver.disconnect();
79+
trackPageView();
80+
}
81+
});
82+
observers.push(bodyObserver);
83+
bodyObserver.observe(document.documentElement, {
84+
childList: true,
85+
subtree: true
86+
});
87+
}
88+
89+
// Track navigation (for SPA-like behavior) - only if document.body exists
90+
if (document.body) {
91+
let currentPath = window.location.pathname;
92+
const observer = new MutationObserver(() => {
93+
if (window.location.pathname !== currentPath) {
94+
currentPath = window.location.pathname;
95+
setTimeout(trackPageView, 100); // Small delay to ensure new page is loaded
96+
}
97+
});
98+
observers.push(observer);
99+
100+
observer.observe(document.body, {
101+
childList: true,
102+
subtree: true
103+
});
104+
}
105+
106+
// Cleanup on page unload
107+
window.addEventListener('beforeunload', () => {
108+
observers.forEach(observer => observer.disconnect());
109+
});
110+
111+
})();
112+
</script>

src/env.d.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
11
/// <reference path="../.astro/types.d.ts" />
22
/// <reference types="astro/client" />
33
/// <reference path="../node_modules/@astrojs/starlight/virtual.d.ts"/>
4+
5+
// Hall Analytics types (if needed for future extensions)
6+
declare namespace App {
7+
interface Locals {
8+
// Analytics data could be stored here if needed
9+
analytics?: {
10+
tracked: boolean;
11+
timestamp: number;
12+
};
13+
}
14+
}

src/starlight-overrides/Head.astro

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
import Default from "@astrojs/starlight/components/Head.astro";
33
import type {Props} from "@astrojs/starlight/props";
4+
import HallAnalytics from "../components/HallAnalytics.astro";
45
56
const hasCustomOGImage = Astro.props.entry.data.head.find((t) =>
67
t.attrs.property === "og:image" ? true : false
@@ -45,3 +46,6 @@ const frontmatter = entry.data;
4546
{frontmatter.complexity && (
4647
<meta name="complexity" content={frontmatter.complexity} />
4748
)}
49+
50+
{/* Hall Analytics */}
51+
<HallAnalytics />

0 commit comments

Comments
 (0)