chore
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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",
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
Generated
+66
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user