Files
wr.do/app/(protected)/dashboard/scrape/scrapes.tsx
2025-10-30 17:11:49 +08:00

590 lines
18 KiB
TypeScript

"use client";
import { useState } from "react";
import JsonView from "@uiw/react-json-view";
import { githubLightTheme } from "@uiw/react-json-view/githubLight";
import { vscodeTheme } from "@uiw/react-json-view/vscode";
import { useTranslations } from "next-intl";
import { useTheme } from "next-themes";
import { toast } from "sonner";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import BlurImage from "@/components/shared/blur-image";
export interface MetaScrapingProps {
title: string;
description: string;
image: string;
icon: string;
url: string;
lang: string;
author: string;
timestamp: string;
payload: string;
}
export interface MarkdownScrapingProps {
url: string;
content: string;
format: string;
timestamp: string;
payload: string;
}
export function ScreenshotScraping({
user,
}: {
user: { id: string; apiKey: string };
}) {
const t = useTranslations("Scrape");
const { theme } = useTheme();
const [protocol, setProtocol] = useState("https://");
const [isShoting, setIsShoting] = useState(false);
const [currentScreenshotLink, setCurrentScreenshotLink] =
useState("vmail.dev");
const [screenshotInfo, setScreenshotInfo] = useState({
tmp_url: "",
payload: "",
});
const handleScrapingScreenshot = async () => {
if (currentScreenshotLink) {
setIsShoting(true);
const payload = `/api/v1/scraping/screenshot?url=${protocol}${currentScreenshotLink}&key=${user.apiKey}`;
const res = await fetch(payload);
if (!res.ok || res.status !== 200) {
const data = await res.json();
toast.error(data.statusText);
} else {
const blob = await res.blob();
const imageUrl = URL.createObjectURL(blob);
setScreenshotInfo({
tmp_url: imageUrl,
payload: `${window.location.origin}${payload}`,
});
toast.success("Success!");
}
setIsShoting(false);
}
};
return (
<>
<CodeLight content={`https://wr.do/api/v1/scraping/screenshot`} />
<Card className="bg-gray-50 dark:bg-gray-900">
<CardHeader>
<CardTitle>{t("Playground")}</CardTitle>
<CardDescription>
{t(
"Automate your website screenshots and turn them into stunning visuals for your applications",
)}
.
</CardDescription>
</CardHeader>
<CardContent>
<div className="flex items-center">
<Select
onValueChange={(value: string) => {
setProtocol(value);
}}
name="protocol"
defaultValue="https://"
>
<SelectTrigger className="h-10 w-24 rounded-r-none bg-transparent shadow-inner">
<SelectValue placeholder="Protocol" />
</SelectTrigger>
<SelectContent>
<SelectItem key="https" value="https://">
https://
</SelectItem>
<SelectItem key="http" value="http://">
http://
</SelectItem>
</SelectContent>
</Select>
<Input
type="text"
placeholder="www.example.com"
className="h-10 rounded-none border focus:border-primary active:border-primary"
value={currentScreenshotLink}
size={100}
onChange={(e) => setCurrentScreenshotLink(e.target.value)}
/>
<Button
variant="blue"
onClick={handleScrapingScreenshot}
disabled={isShoting}
className="w-28 rounded-l-none"
>
{isShoting ? t("Scraping") : t("Start")}
</Button>
</div>
<div className="mt-4 rounded-md border p-3">
<JsonView
className="max-w-2xl overflow-auto p-2"
style={theme === "dark" ? vscodeTheme : githubLightTheme}
value={screenshotInfo}
displayObjectSize={false}
displayDataTypes={false}
// shortenTextAfterLength={50}
/>
{screenshotInfo.tmp_url && (
<BlurImage
src={screenshotInfo.tmp_url}
alt="ligth preview landing"
className="my-4 flex rounded-md border object-contain object-center shadow-md"
width={1500}
height={750}
priority
// placeholder="blur"
/>
)}
</div>
</CardContent>
</Card>
</>
);
}
export function MetaScraping({
user,
}: {
user: { id: string; apiKey: string };
}) {
const t = useTranslations("Scrape");
const { theme } = useTheme();
const [currentLink, setCurrentLink] = useState("wr.do");
const [protocol, setProtocol] = useState("https://");
const [metaInfo, setMetaInfo] = useState<MetaScrapingProps>({
title: "",
description: "",
image: "",
icon: "",
url: "",
lang: "",
author: "",
timestamp: "",
payload: "",
});
const [isScraping, setIsScraping] = useState(false);
const handleScrapingMeta = async () => {
if (currentLink) {
setIsScraping(true);
const res = await fetch(
`/api/v1/scraping/meta?url=${protocol}${currentLink}&key=${user.apiKey}`,
);
if (!res.ok || res.status !== 200) {
const data = await res.json();
toast.error(data.statusText);
} else {
const data = await res.json();
setMetaInfo(data);
toast.success("Success!");
}
setIsScraping(false);
}
};
return (
<>
<CodeLight content={`https://wr.do/api/v1/scraping/meta`} />
<Card className="bg-gray-50 dark:bg-gray-900">
<CardHeader>
<CardTitle>{t("Playground")}</CardTitle>
<CardDescription>
{t("Scrape the meta data of a website")}.
</CardDescription>
</CardHeader>
<CardContent>
<div className="flex items-center">
<Select
onValueChange={(value: string) => {
setProtocol(value);
}}
name="protocol"
defaultValue={"https://"}
>
<SelectTrigger className="h-10 w-24 rounded-r-none bg-transparent shadow-inner">
<SelectValue placeholder="Protocol" />
</SelectTrigger>
<SelectContent>
<SelectItem key="https" value="https://">
https://
</SelectItem>
<SelectItem key="http" value="http://">
http://
</SelectItem>
</SelectContent>
</Select>
<Input
type="text"
placeholder="www.example.com"
className="h-10 rounded-none border focus:border-primary active:border-primary"
value={currentLink}
size={100}
onChange={(e) => setCurrentLink(e.target.value)}
/>
<Button
variant="blue"
onClick={handleScrapingMeta}
disabled={isScraping}
className="w-28 rounded-l-none"
>
{isScraping ? t("Scraping") : t("Start")}
</Button>
</div>
<div className="mt-4 rounded-md border p-3">
<JsonView
style={theme === "dark" ? vscodeTheme : githubLightTheme}
value={metaInfo}
displayObjectSize={false}
displayDataTypes={false}
/>
</div>
</CardContent>
</Card>
</>
);
}
export function MarkdownScraping({
user,
}: {
user: { id: string; apiKey: string };
}) {
const t = useTranslations("Scrape");
const { theme } = useTheme();
const [currentLink, setCurrentLink] = useState("wr.do");
const [protocol, setProtocol] = useState("https://");
const [metaInfo, setMetaInfo] = useState<MarkdownScrapingProps>({
url: "",
content: "",
format: "",
timestamp: "",
payload: "",
});
const [isScraping, setIsScraping] = useState(false);
const handleScrapingMeta = async () => {
if (currentLink) {
setIsScraping(true);
const res = await fetch(
`/api/v1/scraping/markdown?url=${protocol}${currentLink}&key=${user.apiKey}`,
);
if (!res.ok || res.status !== 200) {
const data = await res.json();
toast.error(data.statusText);
} else {
const data = await res.json();
setMetaInfo(data);
toast.success("Success!");
}
setIsScraping(false);
}
};
return (
<>
<CodeLight content={`https://wr.do/api/v1/scraping/markdown`} />
<Card className="bg-gray-50 dark:bg-gray-900">
<CardHeader>
<CardTitle>Markdown</CardTitle>
</CardHeader>
<CardContent>
<div className="flex items-center">
<Select
onValueChange={(value: string) => {
setProtocol(value);
}}
name="protocol"
defaultValue={"https://"}
>
<SelectTrigger className="h-10 w-24 rounded-r-none bg-transparent shadow-inner">
<SelectValue placeholder="Protocol" />
</SelectTrigger>
<SelectContent>
<SelectItem key="https" value="https://">
https://
</SelectItem>
<SelectItem key="http" value="http://">
http://
</SelectItem>
</SelectContent>
</Select>
<Input
type="text"
placeholder="www.example.com"
className="h-10 rounded-none border focus:border-primary active:border-primary"
value={currentLink}
size={100}
onChange={(e) => setCurrentLink(e.target.value)}
/>
<Button
variant="blue"
onClick={handleScrapingMeta}
disabled={isScraping}
className="w-28 rounded-l-none"
>
{isScraping ? t("Scraping") : t("Start")}
</Button>
</div>
<div className="mt-4 rounded-md border p-3">
<JsonView
style={theme === "dark" ? vscodeTheme : githubLightTheme}
value={metaInfo}
displayObjectSize={false}
displayDataTypes={false}
/>
</div>
</CardContent>
</Card>
</>
);
}
export function TextScraping({
user,
}: {
user: { id: string; apiKey: string };
}) {
const t = useTranslations("Scrape");
const { theme } = useTheme();
const [currentLink, setCurrentLink] = useState("wr.do");
const [protocol, setProtocol] = useState("https://");
const [metaInfo, setMetaInfo] = useState<MarkdownScrapingProps>({
url: "",
content: "",
format: "",
timestamp: "",
payload: "",
});
const [isScraping, setIsScraping] = useState(false);
const handleScrapingMeta = async () => {
if (currentLink) {
setIsScraping(true);
const res = await fetch(
`/api/v1/scraping/text?url=${protocol}${currentLink}&key=${user.apiKey}`,
);
if (!res.ok || res.status !== 200) {
const data = await res.json();
toast.error(data.statusText);
} else {
const data = await res.json();
setMetaInfo(data);
toast.success("Success!");
}
setIsScraping(false);
}
};
return (
<>
<CodeLight content={`https://wr.do/api/v1/scraping/text`} />
<Card className="bg-gray-50 dark:bg-gray-900">
<CardHeader>
<CardTitle>{t("Text")}</CardTitle>
</CardHeader>
<CardContent>
<div className="flex items-center">
<Select
onValueChange={(value: string) => {
setProtocol(value);
}}
name="protocol"
defaultValue={"https://"}
>
<SelectTrigger className="h-10 w-24 rounded-r-none bg-transparent shadow-inner">
<SelectValue placeholder="Protocol" />
</SelectTrigger>
<SelectContent>
<SelectItem key="https" value="https://">
https://
</SelectItem>
<SelectItem key="http" value="http://">
http://
</SelectItem>
</SelectContent>
</Select>
<Input
type="text"
placeholder="www.example.com"
className="h-10 rounded-none border focus:border-primary active:border-primary"
value={currentLink}
size={100}
onChange={(e) => setCurrentLink(e.target.value)}
/>
<Button
variant="blue"
onClick={handleScrapingMeta}
disabled={isScraping}
className="w-28 rounded-l-none"
>
{isScraping ? t("Scraping") : t("Start")}
</Button>
</div>
<div className="mt-4 rounded-md border p-3">
<JsonView
style={theme === "dark" ? vscodeTheme : githubLightTheme}
value={metaInfo}
displayObjectSize={false}
displayDataTypes={false}
/>
</div>
</CardContent>
</Card>
</>
);
}
export function QrCodeScraping({
user,
}: {
user: { id: string; apiKey: string };
}) {
const t = useTranslations("Scrape");
const { theme } = useTheme();
const [protocol, setProtocol] = useState("https://");
const [isShoting, setIsShoting] = useState(false);
const [currentScreenshotLink, setCurrentScreenshotLink] =
useState("vmail.dev");
const [qrInfo, setQrInfo] = useState({
// tmp_url: "",
payload: "",
});
const handleScrapingScreenshot = async () => {
if (currentScreenshotLink) {
setIsShoting(true);
const payload = `/api/v1/scraping/qrcode?url=${protocol}${currentScreenshotLink}&key=${user.apiKey}`;
const res = await fetch(payload);
if (!res.ok || res.status !== 200) {
toast.error(res.statusText);
} else {
setQrInfo({
payload: `${window.location.origin}${payload}`,
});
toast.success("Success!");
}
setIsShoting(false);
}
};
return (
<>
<CodeLight content={`https://wr.do/api/v1/scraping/qrcode`} />
<Card className="bg-gray-50 dark:bg-gray-900">
<CardHeader>
<CardTitle>{t("Playground")}</CardTitle>
</CardHeader>
<CardContent>
<div className="flex items-center">
<Select
onValueChange={(value: string) => {
setProtocol(value);
}}
name="protocol"
defaultValue="https://"
>
<SelectTrigger className="h-10 w-24 rounded-r-none bg-transparent shadow-inner">
<SelectValue placeholder="Protocol" />
</SelectTrigger>
<SelectContent>
<SelectItem key="https" value="https://">
https://
</SelectItem>
<SelectItem key="http" value="http://">
http://
</SelectItem>
</SelectContent>
</Select>
<Input
type="text"
placeholder="www.example.com"
className="h-10 rounded-none border focus:border-primary active:border-primary"
value={currentScreenshotLink}
size={100}
onChange={(e) => setCurrentScreenshotLink(e.target.value)}
/>
<Button
variant="blue"
onClick={handleScrapingScreenshot}
disabled={isShoting}
className="rounded-l-none"
>
{isShoting ? "Scraping..." : "Send"}
</Button>
</div>
<div className="mt-4 rounded-md border p-3">
<JsonView
className="max-w-2xl overflow-auto p-2"
style={theme === "dark" ? vscodeTheme : githubLightTheme}
value={qrInfo}
displayObjectSize={false}
displayDataTypes={false}
// shortenTextAfterLength={50}
/>
{qrInfo.payload && (
<BlurImage
src={qrInfo.payload}
alt="ligth preview landing"
className="my-4 flex rounded-md border object-contain object-center shadow-md"
width={150}
height={150}
priority
// placeholder="blur"
/>
)}
</div>
</CardContent>
</Card>
</>
);
}
export const CodeLight = ({ content }: { content: string }) => {
const code = content.trim();
return (
<div className="mx-auto w-full">
<pre className="overflow-x-auto rounded-lg bg-gray-900 p-4 text-gray-100">
<code className="block font-mono text-sm">
{code.split("\n").map((line, i) => (
<div key={i} className="group relative">
{/* Line number */}
<span className="inline-block w-8 select-none text-gray-500">
{i + 1}
</span>
{/* Code content */}
<span className="text-blue-400">{line}</span>
</div>
))}
</code>
</pre>
</div>
);
};