Skip to content

Commit 18a66f7

Browse files
authored
refactor: move og checker to backend (#111)
1 parent 4413a8f commit 18a66f7

File tree

5 files changed

+83
-40
lines changed

5 files changed

+83
-40
lines changed

Diff for: apps/dashboard/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"react-dom": "19.0.0-rc-66855b96-20241106",
3636
"satori": "^0.10.14",
3737
"sonner": "^1.4.41",
38+
"use-debounce": "^10.0.4",
3839
"zundo": "^2.1.0",
3940
"zustand": "^4.5.2"
4041
},

Diff for: apps/dashboard/src/app/api/og/check/route.tsx

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import * as htmlparser2 from "htmlparser2";
2+
import { META_KEYS, type MetaTags } from "../../../../lib/meta";
3+
4+
export async function GET(request: Request) {
5+
const { searchParams } = new URL(request.url);
6+
const url = searchParams.get("url");
7+
8+
if (!url) {
9+
return new Response("Missing URL", { status: 400 });
10+
}
11+
12+
try {
13+
const response = await fetch(url);
14+
const text = await response.text();
15+
16+
// @ts-expect-error missing values
17+
const tempData: Record<MetaTags, string> = {};
18+
19+
const parser = new htmlparser2.Parser({
20+
onopentag(name, attributes) {
21+
if (
22+
name === "meta" &&
23+
META_KEYS.includes(
24+
(attributes.name || attributes.property) as MetaTags,
25+
)
26+
) {
27+
tempData[(attributes.name || attributes.property) as MetaTags] =
28+
attributes.content;
29+
}
30+
},
31+
});
32+
33+
parser.write(text);
34+
parser.end();
35+
36+
return new Response(JSON.stringify(tempData), {
37+
headers: {
38+
"Content-Type": "application/json",
39+
},
40+
});
41+
} catch (error) {
42+
console.error(error);
43+
return new Response("Failed to fetch URL", { status: 500 });
44+
}
45+
}

Diff for: apps/dashboard/src/components/Splash/Tools/OpenGraphImageChecker.tsx

+11-40
Original file line numberDiff line numberDiff line change
@@ -8,33 +8,19 @@ import {
88
TextField,
99
Tooltip,
1010
} from "@radix-ui/themes";
11-
import { useEffect, useState } from "react";
12-
import * as htmlparser2 from "htmlparser2";
11+
import { useState } from "react";
12+
import { useDebouncedCallback } from "use-debounce";
1313
import { usePreviewControls } from "../../../lib/hooks/usePreviewControls";
1414
import { InfoIcon } from "../../icons/InfoIcon";
1515
import { OgImage } from "../../OgImage";
16-
17-
const META_TAGS = {
18-
"og:title": "The title of your object as it should appear within the graph.",
19-
"og:description": "A one to two sentence description of your object.",
20-
"og:site_name": "The name which should be displayed for the overall site.",
21-
"og:url":
22-
"The canonical URL of your object that will be used as its permanent ID in the graph.",
23-
"og:image":
24-
"An image URL which should represent your object within the graph.",
25-
} as const;
26-
27-
const META_KEYS = Object.keys(META_TAGS) as MetaTags[];
28-
29-
type MetaTags = keyof typeof META_TAGS;
16+
import { META_KEYS, META_TAGS, type MetaTags } from "../../../lib/meta";
3017

3118
export function OpenGraphImageChecker() {
32-
const [url, setUrl] = useState("");
3319
const [loading, setLoading] = useState(false);
3420
const [data, setData] = useState<Record<MetaTags, string> | undefined>();
3521
const { preview, PreviewControls } = usePreviewControls();
3622

37-
useEffect(() => {
23+
const debounced = useDebouncedCallback((url: string) => {
3824
try {
3925
let finalUrl = url;
4026

@@ -50,28 +36,13 @@ export function OpenGraphImageChecker() {
5036

5137
setLoading(true);
5238

53-
fetch(maybeUrl)
39+
fetch(`/api/og/check?url=${encodeURIComponent(maybeUrl.toString())}`)
5440
.then(async (response) => {
55-
const text = await response.text();
56-
// @ts-expect-error missing values
57-
const tempData: Record<MetaTags, string> = {};
58-
59-
const parser = new htmlparser2.Parser({
60-
onopentag(name, attributes) {
61-
if (
62-
name === "meta" &&
63-
META_KEYS.includes(
64-
(attributes.name || attributes.property) as MetaTags,
65-
)
66-
) {
67-
tempData[(attributes.name || attributes.property) as MetaTags] =
68-
attributes.content;
69-
}
70-
},
71-
});
41+
const tempData = (await response.json()) as Record<MetaTags, string>;
7242

73-
parser.write(text);
74-
parser.end();
43+
if (!response.ok) {
44+
throw new Error("Failed to fetch URL");
45+
}
7546

7647
setLoading(false);
7748
setData(tempData);
@@ -85,7 +56,7 @@ export function OpenGraphImageChecker() {
8556
setLoading(false);
8657
setData(undefined);
8758
}
88-
}, [url]);
59+
}, 200);
8960

9061
return (
9162
<Flex direction="column" gap="4">
@@ -99,7 +70,7 @@ export function OpenGraphImageChecker() {
9970
<Flex direction="column" gap="6">
10071
<TextField.Root
10172
onChange={(event) => {
102-
setUrl(event.target.value);
73+
debounced(event.target.value);
10374
}}
10475
placeholder="https://ogstudio.app"
10576
/>

Diff for: apps/dashboard/src/lib/meta.ts

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export const META_TAGS = {
2+
"og:title": "The title of your object as it should appear within the graph.",
3+
"og:description": "A one to two sentence description of your object.",
4+
"og:site_name": "The name which should be displayed for the overall site.",
5+
"og:url":
6+
"The canonical URL of your object that will be used as its permanent ID in the graph.",
7+
"og:image":
8+
"An image URL which should represent your object within the graph.",
9+
} as const;
10+
11+
export const META_KEYS = Object.keys(META_TAGS) as MetaTags[];
12+
13+
export type MetaTags = keyof typeof META_TAGS;

Diff for: pnpm-lock.yaml

+13
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)