~repos /website
git clone https://pyrossh.dev/repos/website.git
木 Personal website of pyrossh. Built with astrojs, shiki, vite.
80ae0539
—
pyrossh 1 year ago
Fix browsers, countries
- deno.json +2 -1
- routes/stats.tsx +22 -16
- services/analytics.ts +7 -4
deno.json
CHANGED
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
"$std/": "https://deno.land/std@0.193.0/",
|
|
26
26
|
"ua-parser-js": "https://esm.sh/ua-parser-js@1.0.37",
|
|
27
27
|
"isbot": "https://esm.sh/isbot@3.7.1",
|
|
28
|
-
"ipinfo": "https://esm.sh/node-ipinfo@3.5.1"
|
|
28
|
+
"ipinfo": "https://esm.sh/node-ipinfo@3.5.1",
|
|
29
|
+
"countries-list": "https://esm.sh/countries-list@3.0.6"
|
|
29
30
|
},
|
|
30
31
|
"compilerOptions": { "jsx": "react-jsx", "jsxImportSource": "preact" }
|
|
31
32
|
}
|
routes/stats.tsx
CHANGED
|
@@ -13,24 +13,30 @@ export const handler: Handlers<AnalyticsData> = {
|
|
|
13
13
|
|
|
14
14
|
export default function Stats({ data }: PageProps<AnalyticsData>) {
|
|
15
15
|
return (
|
|
16
|
+
<>
|
|
17
|
+
<Head>
|
|
18
|
+
<title>pyros.sh | Stats</title>
|
|
19
|
+
<meta name="description" content="Analytics of the pyros.sh website" />
|
|
20
|
+
</Head>
|
|
16
|
-
|
|
21
|
+
<div class="flex flex-1 flex-col theme-light">
|
|
17
|
-
|
|
22
|
+
<div class="py-4 text-lg font-medium text-gray-800">Dashboard</div>
|
|
18
|
-
|
|
23
|
+
<div class="charts no-select hidden">
|
|
19
|
-
|
|
24
|
+
<div class="headline">
|
|
20
|
-
|
|
25
|
+
<div class="metrics">
|
|
21
|
-
|
|
26
|
+
<Tile title="Visitors" value={data.vistors.toString()} />
|
|
22
|
-
|
|
27
|
+
<Tile title="Views" value={data.views.toString()} />
|
|
28
|
+
</div>
|
|
23
29
|
</div>
|
|
24
30
|
</div>
|
|
31
|
+
<div class="grid grid-cols-2 gap-8">
|
|
32
|
+
<List title="Pages" entries={data.pages} />
|
|
33
|
+
<List title="Countries" entries={data.countries} />
|
|
34
|
+
</div>
|
|
35
|
+
<div class="grid grid-cols-2 gap-8 mt-8">
|
|
36
|
+
<List title="OS" entries={data.os} />
|
|
37
|
+
<List title="Browsers" entries={data.browsers} />
|
|
38
|
+
</div>
|
|
25
39
|
</div>
|
|
26
|
-
<div class="grid grid-cols-2 gap-8">
|
|
27
|
-
<List title="Pages" entries={data.pages} />
|
|
28
|
-
<List title="Countries" entries={data.countries} />
|
|
29
|
-
|
|
40
|
+
</>
|
|
30
|
-
<div class="grid grid-cols-2 gap-8 mt-8">
|
|
31
|
-
<List title="OS" entries={data.os} />
|
|
32
|
-
<List title="Browsers" entries={data.browsers} />
|
|
33
|
-
</div>
|
|
34
|
-
</div>
|
|
35
41
|
);
|
|
36
42
|
}
|
services/analytics.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import UAParser from "ua-parser-js";
|
|
2
2
|
import IPinfoWrapper from "ipinfo";
|
|
3
|
+
import { getCountryData, getEmojiFlag } from "countries-list";
|
|
3
4
|
|
|
4
5
|
const ipInfoClient = new IPinfoWrapper("3f197e26fa9210");
|
|
5
6
|
const kv = await Deno.openKv();
|
|
@@ -10,7 +11,7 @@ export const recordVisit = async (
|
|
|
10
11
|
pathname: string,
|
|
11
12
|
referer: string,
|
|
12
13
|
) => {
|
|
13
|
-
const ipRes: {
|
|
14
|
+
const ipRes: { countryCode: string } = await ipInfoClient.lookupIp(hostname);
|
|
14
15
|
const parser = new UAParser(userAgent);
|
|
15
16
|
const now = new Date();
|
|
16
17
|
const prefix = [
|
|
@@ -23,7 +24,7 @@ export const recordVisit = async (
|
|
|
23
24
|
["views"],
|
|
24
25
|
["pages", pathname],
|
|
25
26
|
["referers", referer],
|
|
26
|
-
["countries", ipRes.
|
|
27
|
+
["countries", ipRes.countryCode],
|
|
27
28
|
["os", parser.getOS().name],
|
|
28
29
|
["browsers", parser.getBrowser().name],
|
|
29
30
|
].filter((arr) => arr[arr.length - 1]);
|
|
@@ -83,7 +84,7 @@ export const getAnalyticsData = async () => {
|
|
|
83
84
|
if (entry.key[2] === "visitors") {
|
|
84
85
|
data.vistors = entry.value as number;
|
|
85
86
|
}
|
|
86
|
-
if (entry.key[2] === "
|
|
87
|
+
if (entry.key[2] === "browsers") {
|
|
87
88
|
addMetric(data.browsers, entry.key[3] as string, entry.value as number);
|
|
88
89
|
}
|
|
89
90
|
if (entry.key[2] === "os") {
|
|
@@ -96,7 +97,9 @@ export const getAnalyticsData = async () => {
|
|
|
96
97
|
addMetric(data.referrers, entry.key[3] as string, entry.value as number);
|
|
97
98
|
}
|
|
98
99
|
if (entry.key[2] === "countries") {
|
|
100
|
+
const code = entry.key[3] as string;
|
|
101
|
+
const name = getEmojiFlag(code) + getCountryData(code).name;
|
|
99
|
-
addMetric(data.countries,
|
|
102
|
+
addMetric(data.countries, name, entry.value as number);
|
|
100
103
|
}
|
|
101
104
|
}
|
|
102
105
|
return data;
|