This commit is contained in:
oiov
2024-08-07 11:28:42 +08:00
parent d1f69617d5
commit 2e0bd485c8
6 changed files with 155 additions and 23 deletions
@@ -8,7 +8,7 @@ import { Donut, MapData, TopoJSONMap } from "@unovis/ts";
import { WorldMapTopoJSON } from "@unovis/ts/maps";
import { Bar, BarChart, CartesianGrid, XAxis } from "recharts";
import { isLink, timeAgo } from "@/lib/utils";
import { isLink, removeUrlSuffix, timeAgo } from "@/lib/utils";
import {
Card,
CardContent,
@@ -278,9 +278,7 @@ export function StatsList({ data, title }: { data: Stat[]; title: string }) {
className="truncate font-medium hover:opacity-70 hover:after:content-['↗']"
href={ref.dimension}
>
{ref.dimension.startsWith("http")
? ref.dimension.split("//")[1]
: ref.dimension}
{removeUrlSuffix(ref.dimension)}
</Link>
) : (
<p className="font-medium">{decodeURIComponent(ref.dimension)}</p>
+32 -19
View File
@@ -8,7 +8,13 @@ import useSWR, { useSWRConfig } from "swr";
import { siteConfig } from "@/config/site";
import { ShortUrlFormData } from "@/lib/dto/short-urls";
import { cn, expirationTime, fetcher, timeAgo } from "@/lib/utils";
import {
cn,
expirationTime,
fetcher,
removeUrlSuffix,
timeAgo,
} from "@/lib/utils";
import { Button } from "@/components/ui/button";
import {
Card,
@@ -49,8 +55,8 @@ export interface UrlListProps {
function TableColumnSekleton() {
return (
<TableRow className="grid grid-cols-3 items-center sm:grid-cols-9">
<TableCell className="col-span-1 sm:col-span-2">
<TableRow className="grid grid-cols-3 items-center sm:grid-cols-8">
<TableCell className="col-span-1">
<Skeleton className="h-5 w-20" />
</TableCell>
<TableCell className="col-span-1 sm:col-span-2">
@@ -157,8 +163,8 @@ export default function UserUrlsList({ user, action }: UrlListProps) {
)}
<Table>
<TableHeader className="bg-gray-100/50 dark:bg-primary-foreground">
<TableRow className="grid grid-cols-3 items-center sm:grid-cols-9">
<TableHead className="col-span-1 flex items-center font-bold sm:col-span-2">
<TableRow className="grid grid-cols-3 items-center sm:grid-cols-8">
<TableHead className="col-span-1 flex items-center font-bold">
Url
</TableHead>
<TableHead className="col-span-1 flex items-center font-bold sm:col-span-2">
@@ -193,9 +199,9 @@ export default function UserUrlsList({ user, action }: UrlListProps) {
<>
<TableRow
key={short.id}
className="grid animate-fade-in grid-cols-3 items-center animate-in sm:grid-cols-9"
className="grid animate-fade-in grid-cols-3 items-center animate-in sm:grid-cols-8"
>
<TableCell className="col-span-1 flex items-center gap-1 sm:col-span-2">
<TableCell className="col-span-1 flex items-center gap-1">
<Link
className="line-clamp-2 overflow-hidden overflow-ellipsis whitespace-normal text-slate-600 hover:text-blue-400 hover:underline dark:text-slate-400"
href={`/s/${short.url}`}
@@ -212,19 +218,26 @@ export default function UserUrlsList({ user, action }: UrlListProps) {
)}
/>
</TableCell>
<TableCell className="col-span-1 sm:col-span-2">
<Link
className="line-clamp-2 overflow-hidden overflow-ellipsis whitespace-normal text-slate-600 hover:text-blue-400 hover:underline dark:text-slate-400"
href={short.target}
target="_blank"
prefetch={false}
>
{short.target.startsWith("http")
? short.target.split("//")[1]
: short.target}
</Link>
<TableCell className="col-span-1 truncate sm:col-span-2">
<TooltipProvider>
<Tooltip delayDuration={200}>
<TooltipTrigger className="truncate">
{short.target}
</TooltipTrigger>
<TooltipContent>
<Link
className="line-clamp-2 overflow-hidden overflow-ellipsis whitespace-normal text-slate-600 hover:text-blue-400 hover:underline dark:text-slate-400"
href={short.target}
target="_blank"
prefetch={false}
>
{removeUrlSuffix(short.target)}
</Link>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</TableCell>
<TableCell className="col-span-1 hidden justify-center sm:flex">
<TableCell className="col-span-1 hidden justify-center truncate sm:flex">
<TooltipProvider>
<Tooltip delayDuration={200}>
<TooltipTrigger className="truncate">
+50
View File
@@ -0,0 +1,50 @@
import cheerio from "cheerio";
export const revalidate = 60;
export async function GET(req: Request) {
try {
const url = new URL(req.url);
const link = url.searchParams.get("link");
if (!link) {
return Response.json("link is required", {
status: 400,
statusText: "link is required",
});
}
const res = await fetch(link);
if (!res.ok) {
return Response.json("Failed to fetch url", {
status: 405,
statusText: "Failed to fetch url",
});
}
const html = await res.text();
console.log(html);
const $ = cheerio.load(html);
const title =
$("title").text() ||
$("meta[property='og:title']").attr("content") ||
$("meta[name='twitter:title']").attr("content");
const description =
$("meta[name='description']").attr("content") ||
$("meta[property='og:description']").attr("content") ||
$("meta[name='twitter:description']").attr("content");
const image =
$("meta[property='og:image']").attr("content") ||
$("meta[name='og:image']").attr("content") ||
$("meta[property='twitter:image']").attr("content") ||
$("meta[name='twitter:image']").attr("content");
return Response.json({ title, description, image });
} catch (error) {
console.log(error);
return Response.json(error?.statusText || error, {
status: error.status || 500,
statusText: error.statusText || "Server error",
});
}
}
+4
View File
@@ -216,3 +216,7 @@ export function isLink(str: string): boolean {
return false;
}
}
export function removeUrlSuffix(url: string): string {
return url.startsWith("http") ? url.split("//")[1] : url;
}
+1
View File
@@ -61,6 +61,7 @@
"@vercel/analytics": "^1.3.1",
"@vercel/functions": "^1.4.0",
"@vercel/og": "^0.6.2",
"cheerio": "1.0.0-rc.12",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"cmdk": "^1.0.0",
+66
View File
@@ -134,6 +134,9 @@ importers:
'@vercel/og':
specifier: ^0.6.2
version: 0.6.2
cheerio:
specifier: 1.0.0-rc.12
version: 1.0.0-rc.12
class-variance-authority:
specifier: ^0.7.0
version: 0.7.0
@@ -3170,6 +3173,9 @@ packages:
bl@4.1.0:
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
boolbase@1.0.0:
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
brace-expansion@1.1.11:
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
@@ -3243,6 +3249,13 @@ packages:
character-reference-invalid@2.0.1:
resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==}
cheerio-select@2.1.0:
resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==}
cheerio@1.0.0-rc.12:
resolution: {integrity: sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==}
engines: {node: '>= 6'}
chokidar@3.5.3:
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
engines: {node: '>= 8.10.0'}
@@ -3450,9 +3463,16 @@ packages:
resolution: {integrity: sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==}
engines: {node: '>=4'}
css-select@5.1.0:
resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==}
css-to-react-native@3.2.0:
resolution: {integrity: sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==}
css-what@6.1.0:
resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==}
engines: {node: '>= 6'}
csscolorparser@1.0.3:
resolution: {integrity: sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w==}
@@ -5174,6 +5194,9 @@ packages:
resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
nth-check@2.1.1:
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
oauth4webapi@2.10.3:
resolution: {integrity: sha512-9FkXEXfzVKzH63GUOZz1zMr3wBaICSzk6DLXx+CGdrQ10ItNk2ePWzYYc1fdmKq1ayGFb2aX97sRCoZ2s0mkDw==}
@@ -5276,6 +5299,9 @@ packages:
parse-unit@1.0.1:
resolution: {integrity: sha512-hrqldJHokR3Qj88EIlV/kAyAi/G5R2+R56TBANxNMy0uPlYcttx0jnMW6Yx5KsKPSbC3KddM/7qQm3+0wEXKxg==}
parse5-htmlparser2-tree-adapter@7.0.0:
resolution: {integrity: sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==}
parse5@7.1.2:
resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==}
@@ -9834,6 +9860,8 @@ snapshots:
inherits: 2.0.4
readable-stream: 3.6.2
boolbase@1.0.0: {}
brace-expansion@1.1.11:
dependencies:
balanced-match: 1.0.2
@@ -9909,6 +9937,25 @@ snapshots:
character-reference-invalid@2.0.1: {}
cheerio-select@2.1.0:
dependencies:
boolbase: 1.0.0
css-select: 5.1.0
css-what: 6.1.0
domelementtype: 2.3.0
domhandler: 5.0.3
domutils: 3.1.0
cheerio@1.0.0-rc.12:
dependencies:
cheerio-select: 2.1.0
dom-serializer: 2.0.0
domhandler: 5.0.3
domutils: 3.1.0
htmlparser2: 8.0.2
parse5: 7.1.2
parse5-htmlparser2-tree-adapter: 7.0.0
chokidar@3.5.3:
dependencies:
anymatch: 3.1.3
@@ -10121,12 +10168,22 @@ snapshots:
css-color-keywords@1.0.0: {}
css-select@5.1.0:
dependencies:
boolbase: 1.0.0
css-what: 6.1.0
domhandler: 5.0.3
domutils: 3.1.0
nth-check: 2.1.1
css-to-react-native@3.2.0:
dependencies:
camelize: 1.0.1
css-color-keywords: 1.0.0
postcss-value-parser: 4.2.0
css-what@6.1.0: {}
csscolorparser@1.0.3: {}
cssesc@3.0.0: {}
@@ -12393,6 +12450,10 @@ snapshots:
dependencies:
path-key: 4.0.0
nth-check@2.1.1:
dependencies:
boolbase: 1.0.0
oauth4webapi@2.10.3: {}
oauth4webapi@2.11.1: {}
@@ -12522,6 +12583,11 @@ snapshots:
parse-unit@1.0.1: {}
parse5-htmlparser2-tree-adapter@7.0.0:
dependencies:
domhandler: 5.0.3
parse5: 7.1.2
parse5@7.1.2:
dependencies:
entities: 4.5.0