diff --git a/src/app/(admin)/dashboard/page.tsx b/src/app/(admin)/dashboard/page.tsx
index fa65733..601baaa 100644
--- a/src/app/(admin)/dashboard/page.tsx
+++ b/src/app/(admin)/dashboard/page.tsx
@@ -16,16 +16,10 @@ export default async function Dashboard() {
// Determine the most visited URL display value
const mostVisitedDisplay = stats.mostVisitedUrl
? stats.mostVisitedUrl.visitCount > 0
- ? `${stats.mostVisitedUrl.title} (${stats.mostVisitedUrl.visitCount})`
+ ? `${stats.mostVisitedUrl.title || stats.mostVisitedUrl.slug || "Untitled"} (${stats.mostVisitedUrl.visitCount})`
: "No visits"
: "No URLs"
- const mostVisitedDescription = stats.mostVisitedUrl
- ? stats.mostVisitedUrl.visitCount > 0
- ? `/${stats.mostVisitedUrl.slug} - ${stats.mostVisitedUrl.visitCount} visits`
- : "No URLs have been visited yet"
- : "Create your first shortened URL"
-
return (
Dashboard
@@ -46,7 +40,7 @@ export default async function Dashboard() {
title="Most Visited URL"
value={mostVisitedDisplay}
icon={}
- description={mostVisitedDescription}
+ description="Most popular shortened link"
/>
diff --git a/src/app/r/[slug]/not-found.tsx b/src/app/r/[slug]/not-found.tsx
new file mode 100644
index 0000000..cd762e1
--- /dev/null
+++ b/src/app/r/[slug]/not-found.tsx
@@ -0,0 +1,21 @@
+export default function NotFound() {
+ return (
+
+
+
404
+
+ Link Not Found
+
+
+ The link you're looking for doesn't exist or has expired.
+
+
+ Go Home
+
+
+
+ )
+}
diff --git a/src/app/r/[slug]/page.tsx b/src/app/r/[slug]/page.tsx
new file mode 100644
index 0000000..2ba5da9
--- /dev/null
+++ b/src/app/r/[slug]/page.tsx
@@ -0,0 +1,41 @@
+import { getUrlBySlug, getVisitsBySlugById, trackVisit } from "@/lib/db/urls"
+import { headers } from "next/headers"
+import { notFound, redirect } from "next/navigation"
+
+export default async function RedirectPage({
+ params
+}: {
+ params: Promise<{ slug: string }>
+}) {
+ const { slug } = await params
+
+ const urlRecord = await getUrlBySlug(slug)
+
+ if (!urlRecord) {
+ notFound()
+ }
+
+ const visits = await getVisitsBySlugById(urlRecord.id)
+
+ if (urlRecord.expDate && new Date() > urlRecord.expDate) {
+ notFound()
+ }
+
+ if (urlRecord.maxVisits && visits[0].count >= urlRecord.maxVisits) {
+ notFound()
+ }
+
+ const headersList = await headers()
+ const userAgent = headersList.get("user-agent") || "Unknown"
+ const forwardedFor = headersList.get("x-forwarded-for")
+ const realIp = headersList.get("x-real-ip")
+ const ipAddress = forwardedFor?.split(",")[0] || realIp || "Unknown"
+
+ await trackVisit({
+ urlId: urlRecord.id,
+ ipAddress: ipAddress,
+ userAgent
+ })
+
+ redirect(urlRecord.url)
+}
diff --git a/src/components/dashboard/advanced-url-form-card.tsx b/src/components/dashboard/advanced-url-form-card.tsx
index 56dd39d..ae3c5d6 100644
--- a/src/components/dashboard/advanced-url-form-card.tsx
+++ b/src/components/dashboard/advanced-url-form-card.tsx
@@ -207,7 +207,7 @@ export function AdvancedUrlFormCard() {
className="w-full"
disabled={form.formState.isSubmitting}
>
- {form.formState.isSubmitting ? "Creating..." : "Create Advanced Short Link"}
+ {form.formState.isSubmitting ? "Creating..." : "Create Short Link"}
diff --git a/src/lib/db/urls.ts b/src/lib/db/urls.ts
index 2059529..10a1f3f 100644
--- a/src/lib/db/urls.ts
+++ b/src/lib/db/urls.ts
@@ -1,6 +1,6 @@
import { eq, desc } from "drizzle-orm";
import { db } from "../drizzle/db";
-import { urls } from "../drizzle/schema";
+import { urls, visits } from "../drizzle/schema";
export function getAllUrls() {
return db.query.urls.findMany({
@@ -8,6 +8,12 @@ export function getAllUrls() {
})
}
+export function getUrlBySlug(slug: string) {
+ return db.query.urls.findFirst({
+ where: eq(urls.slug, slug)
+ })
+}
+
export function insertUrl(data: typeof urls.$inferInsert) {
return db.insert(urls).values(data)
}
@@ -18,4 +24,8 @@ export function updateUrl(id: string, data: Omit urls.id),
- ipAdress: varchar("ip_address").notNull(),
+ ipAddress: varchar("ip_address").notNull(),
userAgent: varchar("user_agent").notNull(),
createdAt,
updatedAt