Moved things and added title extraction
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { AdvancedUrlFormCard } from "@/components/dashboard/advanced-url-form-card"
|
||||
import { AdvancedUrlFormCard } from "@/app/(admin)/dashboard/_components/advanced-url-form-card"
|
||||
import { getSession } from "@/lib/auth/session"
|
||||
|
||||
export default async function DashboardCreatePage() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DashboardSidebar } from "@/components/dashboard/sidebar"
|
||||
import { DashboardSidebar } from "@/app/(admin)/dashboard/_components/sidebar"
|
||||
import { SidebarProvider } from "@/components/ui/sidebar"
|
||||
import { ReactNode } from "react"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { UrlsDataTable } from "@/components/dashboard/urls-data-table"
|
||||
import { UrlsDataTable } from "@/app/(admin)/dashboard/_components/urls-data-table"
|
||||
import { getSession } from "@/lib/auth/session"
|
||||
import { getAllUrls } from "@/lib/db/urls"
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { StatsCard } from "@/components/dashboard/stats-card"
|
||||
import { UrlFormCard } from "@/components/dashboard/url-form-card"
|
||||
import { StatsCard } from "@/app/(admin)/dashboard/_components/stats-card"
|
||||
import { UrlFormCard } from "@/app/(admin)/dashboard/_components/url-form-card"
|
||||
import { getSession } from "@/lib/auth/session"
|
||||
import { getDashboardStats } from "@/lib/dashboard/stats"
|
||||
import { LinkIcon, MousePointerClick, TrendingUp } from "lucide-react"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { UserDropdown } from "@/components/auth/user-dropdown"
|
||||
import { ThemeToggle } from "@/components/theme-toggle"
|
||||
import { UserDropdown } from "@/components/user-dropdown"
|
||||
import Link from "next/link"
|
||||
import { ReactNode } from "react"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { OAuthSignInButton } from "@/components/auth/signin-button"
|
||||
import { OAuthSignInButton } from "@/app/sign-in/_components/signin-button"
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { getSession } from "@/lib/auth/session"
|
||||
import { LogIn } from "lucide-react"
|
||||
@@ -11,7 +11,7 @@ export default async function SignInPage() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-slate-50 to-slate-100 dark:from-slate-900 dark:to-slate-800 p-4">
|
||||
<div className="min-h-screen flex items-center justify-center bg-background p-4">
|
||||
<Card className="w-full max-w-md shadow-xl">
|
||||
<CardHeader className="space-y-1 text-center">
|
||||
<div className="flex justify-center mb-4">
|
||||
|
||||
@@ -12,7 +12,6 @@ import { cn } from "@/lib/utils"
|
||||
interface DatePickerProps {
|
||||
value?: Date
|
||||
onChange?: (date: Date | undefined) => void
|
||||
placeholder?: string
|
||||
disabled?: boolean
|
||||
className?: string
|
||||
}
|
||||
@@ -20,7 +19,6 @@ interface DatePickerProps {
|
||||
export function DatePicker({
|
||||
value,
|
||||
onChange,
|
||||
placeholder = "Pick a date",
|
||||
disabled = false,
|
||||
className
|
||||
}: DatePickerProps) {
|
||||
@@ -37,7 +35,7 @@ export function DatePicker({
|
||||
disabled={disabled}
|
||||
>
|
||||
<CalendarIcon className="mr-2 h-4 w-4" />
|
||||
{value ? format(value, "PPP") : <span>{placeholder}</span>}
|
||||
{value ? format(value, "PPP") : <span>Pick a date</span>}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0" align="start">
|
||||
@@ -45,7 +43,6 @@ export function DatePicker({
|
||||
mode="single"
|
||||
selected={value}
|
||||
onSelect={onChange}
|
||||
initialFocus
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
|
||||
@@ -6,6 +6,7 @@ import { insertUrl } from "../db/urls"
|
||||
import { advancedUrlSchema, urlFormSchema } from "../schema/url"
|
||||
import { deleteUrl as deleteUrlDb } from "../db/urls"
|
||||
import { revalidatePath } from "next/cache"
|
||||
import { getWebsiteTitle } from "../websiteTitle"
|
||||
|
||||
type Response = {
|
||||
error: boolean
|
||||
@@ -34,6 +35,7 @@ export async function addUrl(unsafeData: unknown): Promise<Response> {
|
||||
await insertUrl({
|
||||
...data,
|
||||
slug: data.slug.length === 0 ? undefined : data.slug,
|
||||
title: await getWebsiteTitle(data.url)
|
||||
})
|
||||
|
||||
revalidatePath("/dashboard")
|
||||
@@ -66,7 +68,7 @@ export async function createAdvanceUrl(unsafeData: unknown): Promise<Response> {
|
||||
await insertUrl({
|
||||
...data,
|
||||
slug: data.slug?.length === 0 ? undefined : data.slug,
|
||||
title: data.title?.length === 0 ? undefined : data.title,
|
||||
title: data.title?.length === 0 ? await getWebsiteTitle(data.url) : data.title,
|
||||
maxVisits: data.maxVisits > 0 ? data.maxVisits : undefined,
|
||||
})
|
||||
|
||||
|
||||
31
src/lib/websiteTitle.ts
Normal file
31
src/lib/websiteTitle.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
export async function getWebsiteTitle(url: string): Promise<string | null> {
|
||||
const res = await fetch(url, { redirect: 'follow' })
|
||||
|
||||
if (!res.ok) {
|
||||
return null
|
||||
}
|
||||
|
||||
const contentType = res.headers.get('content-type')
|
||||
if (!contentType || !contentType.includes('text/html')) {
|
||||
return null
|
||||
}
|
||||
|
||||
const html = await res.text()
|
||||
|
||||
const titleMatch = html.match(/<title[^>]*>([^<]*)<\/title>/i)
|
||||
|
||||
if (titleMatch && titleMatch[1]) {
|
||||
const title = titleMatch[1]
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/&/g, '&')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, "'")
|
||||
.replace(/ /g, ' ')
|
||||
.trim()
|
||||
|
||||
return title || null
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
Reference in New Issue
Block a user