Skip to content
This repository was archived by the owner on May 3, 2025. It is now read-only.

Commit 43112e7

Browse files
committed
chore: link click counter
It doesn't quite work, but it doesn't break anything.
1 parent d93026b commit 43112e7

File tree

6 files changed

+79
-39
lines changed

6 files changed

+79
-39
lines changed

.github/workflows/deno.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ jobs:
6262
_fresh/
6363
static/
6464
routes/
65+
utils/
6566
deno.json
6667
deno.lock
6768
main.ts

islands/LinkCounter.tsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import type { JSX } from "preact/jsx-runtime";
2+
import type { ComponentProps } from "preact";
3+
4+
interface Props extends ComponentProps<"a"> {
5+
}
6+
7+
export default function LinkCounter(props: Props): JSX.Element {
8+
return (
9+
<a
10+
{...props}
11+
onClick={async () => {
12+
await fetch("/api/links", {
13+
method: "post",
14+
body: JSON.stringify({ link: props.href }),
15+
});
16+
}}
17+
>
18+
</a>
19+
);
20+
}

routes/api/links.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { define } from "../../utils.ts";
2+
import { db } from "../../utils/db.ts";
3+
4+
export const handler = define.handlers({
5+
GET: async (ctx): Promise<Response> => {
6+
const { link } = await ctx.req.json();
7+
8+
const key = ["clicks", link];
9+
10+
const clicksRes = await db.get<number>(key).catch(() => undefined);
11+
12+
if (clicksRes !== undefined) {
13+
await db.atomic().check(clicksRes).set(
14+
key,
15+
clicksRes.value! + 1,
16+
).commit();
17+
} else {
18+
await db.set(key, 1);
19+
}
20+
21+
return new Response();
22+
},
23+
});

routes/index.tsx

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,25 @@
11
import { page, type PageResponse } from "fresh";
22
import { define } from "../utils.ts";
33
import { getCookies, setCookie } from "@std/http/cookie";
4-
5-
const db = await Deno.openKv();
4+
import { db } from "../utils/db.ts";
65

76
const key = ["clicks", "main"];
87

98
export const handler = define.handlers({
109
GET: async (ctx): Promise<PageResponse<{ clicks: number }>> => {
1110
const cookie = getCookies(ctx.req.headers)["Clicked"];
1211

13-
let clicksRes;
14-
15-
try {
16-
clicksRes = await db.get<number>(key);
17-
} catch {
18-
await db.atomic().set(key, 0).commit();
19-
clicksRes = await db.get<number>(key);
20-
}
12+
const clicksRes = await db.get<number>(key).catch(() => undefined);
2113

2214
if (cookie === undefined) {
23-
await db.atomic().check(clicksRes).set(
24-
key,
25-
clicksRes.value! + 1,
26-
).commit();
15+
if (clicksRes !== undefined) {
16+
await db.atomic().check(clicksRes).set(
17+
key,
18+
clicksRes.value! + 1,
19+
).commit();
20+
} else {
21+
await db.set(key, 1);
22+
}
2723
}
2824

2925
const headers = new Headers();
@@ -33,10 +29,12 @@ export const handler = define.handlers({
3329
}
3430

3531
return page({
36-
clicks: cookie === undefined ? clicksRes.value! + 1 : clicksRes.value!,
37-
}, {
38-
headers,
39-
});
32+
clicks: clicksRes === undefined
33+
? 0
34+
: cookie === undefined
35+
? clicksRes.value! + 1
36+
: clicksRes.value!,
37+
}, { headers });
4038
},
4139
});
4240

routes/insecure.tsx

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,43 @@
11
import { page, type PageResponse } from "fresh";
22
import { define } from "../utils.ts";
33
import { getCookies, setCookie } from "@std/http/cookie";
4+
import { db } from "../utils/db.ts";
45

5-
const db = await Deno.openKv();
6-
7-
const key = ["clicks", "insecure"];
6+
export const key = ["clicks", "insecure"];
87

98
export const handler = define.handlers({
109
GET: async (ctx): Promise<PageResponse<{ clicks: number }>> => {
1110
const cookie = getCookies(ctx.req.headers)["Clicked-Insecure"];
1211

13-
let clicksRes;
14-
15-
try {
16-
clicksRes = await db.get<number>(key);
17-
} catch {
18-
await db.atomic().set(key, 0).commit();
19-
clicksRes = await db.get<number>(key);
20-
}
12+
const clicksRes = await db.get<number>(key).catch(() => undefined);
2113

2214
if (cookie === undefined) {
23-
await db.atomic().check(clicksRes).set(
24-
key,
25-
clicksRes.value! + 1,
26-
).commit();
15+
if (clicksRes !== undefined) {
16+
await db.atomic().check(clicksRes).set(
17+
key,
18+
clicksRes.value! + 1,
19+
).commit();
20+
} else {
21+
await db.set(key, 1);
22+
}
2723
}
28-
2924
const headers = new Headers();
3025

3126
if (ctx.config.mode === "production") {
3227
setCookie(headers, {
3328
name: "Clicked-Insecure",
3429
value: "true",
35-
path: "/",
30+
path: "/insecure/",
3631
});
3732
}
3833

3934
return page({
40-
clicks: cookie === undefined ? clicksRes.value! + 1 : clicksRes.value!,
41-
}, {
42-
headers,
43-
});
35+
clicks: clicksRes === undefined
36+
? 0
37+
: cookie === undefined
38+
? clicksRes.value! + 1
39+
: clicksRes.value!,
40+
}, { headers });
4441
},
4542
});
4643

utils/db.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const db = await Deno.openKv();

0 commit comments

Comments
 (0)