feat: add screenshot api

This commit is contained in:
oiov
2024-08-07 17:32:32 +08:00
parent 12825df032
commit 185ca7a7c8
5 changed files with 661 additions and 33 deletions
@@ -23,10 +23,11 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import BlurImage from "@/components/shared/blur-image";
export default function MetaScraping() {
const { theme } = useTheme();
const [currentLink, setCurrentLink] = useState("");
const [currentLink, setCurrentLink] = useState("wr.do");
const [protocol, setProtocol] = useState("https");
const [metaInfo, setMetaInfo] = useState({
title: "",
@@ -36,12 +37,18 @@ export default function MetaScraping() {
url: "",
lang: "",
author: "",
publisher: "",
timestamp: "",
});
const [isScraping, setIsScraping] = useState(false);
const handleScraping = async () => {
const [currentScreenshotLink, setCurrentScreenshotLink] = useState("wr.do");
const [screenshotInfo, setScreenshotInfo] = useState({
data: "",
url: "",
timestamp: "",
});
const handleScrapingMeta = async () => {
if (currentLink) {
setIsScraping(true);
const res = await fetch(
@@ -57,6 +64,23 @@ export default function MetaScraping() {
setIsScraping(false);
}
};
const handleScrapingScreenshot = async () => {
if (currentScreenshotLink) {
setIsScraping(true);
const res = await fetch(
`/api/scraping/screenshot?url=${protocol}://${currentScreenshotLink}`,
);
if (!res.ok || res.status !== 200) {
toast.error(res.statusText);
} else {
const data = await res.json();
setScreenshotInfo(data);
toast.success("Success!");
}
setIsScraping(false);
}
};
return (
<>
<Card>
@@ -92,15 +116,10 @@ export default function MetaScraping() {
value={currentLink}
size={100}
onChange={(e) => setCurrentLink(e.target.value)}
onKeyDown={(e) => {
if (e.key === "Enter") {
handleScraping();
}
}}
/>
<Button
variant="blue"
onClick={handleScraping}
onClick={handleScrapingMeta}
disabled={isScraping}
className="rounded-l-none"
>
@@ -118,6 +137,76 @@ export default function MetaScraping() {
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Screenshot</CardTitle>
<CardDescription>
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 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={isScraping}
className="rounded-l-none"
>
{isScraping ? "Scraping..." : "Send"}
</Button>
</div>
<div className="mt-4 rounded-md border p-3">
<JsonView
className="max-w-[400px] overflow-hidden"
style={theme === "dark" ? vscodeTheme : githubLightTheme}
value={screenshotInfo}
displayObjectSize={false}
displayDataTypes={false}
// shortenTextAfterLength={50}
/>
{screenshotInfo.data && (
<BlurImage
src={screenshotInfo.data}
alt="ligth preview landing"
className="my-4 flex rounded-md border object-contain object-center shadow-md dark:hidden"
width={1500}
height={750}
priority
// placeholder="blur"
/>
)}
</div>
</CardContent>
</Card>
</>
);
}
-4
View File
@@ -56,9 +56,6 @@ export async function GET(req: Request) {
const author =
$("meta[name='author']").attr("content") ||
$("meta[property='author']").attr("content");
const publisher =
$("meta[name='publisher']").attr("content") ||
$("meta[property='publisher']").attr("content");
return Response.json({
title,
@@ -68,7 +65,6 @@ export async function GET(req: Request) {
url: link,
lang,
author,
publisher,
timestamp: Date.now(),
});
} catch (error) {
+46
View File
@@ -0,0 +1,46 @@
import puppeteer from "puppeteer";
import { checkUserStatus } from "@/lib/dto/user";
import { getCurrentUser } from "@/lib/session";
import { isLink } from "@/lib/utils";
export const revalidate = 60;
export async function GET(req: Request) {
try {
const user = checkUserStatus(await getCurrentUser());
if (user instanceof Response) return user;
const url = new URL(req.url);
const link = url.searchParams.get("url");
const full = url.searchParams.get("full") || "false";
if (!link || !isLink(link)) {
return Response.json("Invalid url", {
status: 400,
statusText: "Invalid url",
});
}
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto(link, { waitUntil: "networkidle2" });
const screenshot = await page.screenshot({
encoding: "base64",
fullPage: full === "true",
});
await browser.close();
return Response.json({
data: `data:image/png;base64,${screenshot}`,
url: link,
timestamp: Date.now(),
});
} catch (error) {
console.log(error);
return Response.json("An error occurred", {
status: error.status || 500,
statusText: error.statusText || "Server error",
});
}
}
+1
View File
@@ -80,6 +80,7 @@
"next-view-transitions": "^0.3.0",
"nodemailer": "^6.9.14",
"prop-types": "^15.8.1",
"puppeteer": "^22.15.0",
"react": "18.3.1",
"react-countup": "^6.5.3",
"react-day-picker": "^8.10.1",
+516 -20
View File
File diff suppressed because it is too large Load Diff