-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
225 additions
and
6 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { NextRequest, NextResponse } from 'next/server'; | ||
import prisma from '@/lib/prisma'; | ||
|
||
export async function GET(req: NextRequest) { | ||
try { | ||
const url = new URL(req.url); | ||
const alias = url.searchParams.get('q'); | ||
|
||
if (!alias) { | ||
return NextResponse.json({ error: 'Alias parameter is required' }, { status: 400 }); | ||
} | ||
|
||
// Find the short URL using the alias | ||
const short = await prisma.shortURL.findUnique({ | ||
where: { alias }, | ||
}); | ||
|
||
if (!short) { | ||
return NextResponse.json({ error: 'Not Found' }, { status: 404 }); | ||
} | ||
|
||
// Redirect to the long URL | ||
return NextResponse.json(short.longURL); | ||
} catch (error) { | ||
console.error(error); | ||
return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 }); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { NextResponse } from 'next/server'; | ||
import type { NextRequest } from 'next/server'; | ||
import prisma from '@/lib/prisma'; | ||
import anonUrl from '@/schemas/urlNonAlias'; | ||
import { nanoid } from 'nanoid'; | ||
|
||
const getIpAddress = (req: NextRequest): string | null => { | ||
return req.headers.get('x-real-ip') || req.headers.get('x-forwarded-for') || req.ip || null; | ||
}; | ||
|
||
export async function POST(req: NextRequest) { | ||
try { | ||
const { longURL } = await req.json(); | ||
const alias = nanoid(7); | ||
|
||
// Validate the URL | ||
const result = anonUrl.safeParse({ url: longURL }); | ||
if (!result.success) { | ||
return NextResponse.json({ error: result.error.errors }, { status: 400 }); | ||
} | ||
|
||
// create random user in the database | ||
const user = await prisma.user.upsert({ | ||
where: { | ||
clerkId: 'anon_' + getIpAddress(req)?.toString(), | ||
}, | ||
update: {}, | ||
create: { | ||
clerkId: 'anon_' + getIpAddress(req)?.toString() ?? 'anon', | ||
email: 'anon_' + getIpAddress(req)?.toString() ?? 'anon', | ||
ipAddress: getIpAddress(req), | ||
}, | ||
}); | ||
|
||
|
||
// Check if the alias is already taken | ||
const existingShort = await prisma.shortURL.findUnique({ where: { alias } }); | ||
if (existingShort) { | ||
return NextResponse.json({ error: 'Alias already taken' }, { status: 400 }); | ||
} | ||
|
||
// Create the short URL | ||
const short = await prisma.shortURL.create({ | ||
data: { | ||
longURL, | ||
alias, | ||
userId: user.id, | ||
}, | ||
}); | ||
|
||
return NextResponse.json(short, { status: 201 }); | ||
} catch (error) { | ||
return NextResponse.json({ error: error + 'Internal Server Error' }, { status: 500 }); | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
'use client'; | ||
import '../globals.css'; | ||
import React, { useEffect } from 'react'; | ||
|
||
export default function TestPage() { | ||
const [longURL, setLongURL] = React.useState(''); | ||
const [shortURL, setShortURL] = React.useState(''); | ||
const [error, setError] = React.useState(''); | ||
const [alias, setAlias] = React.useState(''); | ||
|
||
async function shorten() { | ||
const response = await fetch('/api/random', { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
}, | ||
body: JSON.stringify({ | ||
longURL, | ||
}), | ||
}); | ||
|
||
const result = await response.json(); | ||
|
||
if (response.ok) { | ||
setShortURL(result.shortURL); | ||
localStorage.setItem('shortURL', result.shortURL); | ||
setError(''); | ||
} else { | ||
setShortURL(''); | ||
setError(result.error); | ||
localStorage.removeItem('shortURL'); | ||
} | ||
} | ||
|
||
async function getDestination() { | ||
// fethc using the random api but add the alias to the url after the '?' | ||
//we cant use json here because we are not sending any data | ||
const response = await fetch(`/api/metadata?q=${alias}`, { | ||
method: 'GET', | ||
}); | ||
|
||
const result = await response.json(); | ||
|
||
if (response.ok) { | ||
setShortURL(result.shortURL); | ||
localStorage.setItem('shortURL', result.shortURL); | ||
setError(''); | ||
} else { | ||
setShortURL(''); | ||
setError(result.error); | ||
localStorage.removeItem('shortURL'); | ||
} | ||
} | ||
|
||
return ( | ||
<div className="container mx-auto py-8"> | ||
<div className="max-w-lg mx-auto bg-white p-8 rounded shadow-md"> | ||
<h1 className="text-2xl font-bold mb-4">Create a Short URL</h1> | ||
<input | ||
type="text" | ||
placeholder="Long URL" | ||
value={longURL} | ||
onChange={(e) => setLongURL(e.target.value)} | ||
className="w-full px-3 py-2 mb-4 border border-gray-300 rounded text-black" | ||
/> | ||
<button | ||
onClick={shorten} | ||
className="w-full px-3 py-2 mb-4 bg-blue-500 text-white rounded" | ||
> | ||
Shorten | ||
</button> | ||
</div> | ||
<div className="max-w-lg mx-auto bg-white p-8 rounded shadow-md"> | ||
<h1 className="text-2xl font-bold mb-4">Get Destination of Short URL</h1> | ||
<input | ||
type="text" | ||
placeholder="Short URL" | ||
value={alias} | ||
onChange={(e) => setAlias(e.target.value)} | ||
className="w-full px-3 py-2 mb-4 border border-gray-300 rounded text-black" | ||
/> | ||
<button | ||
onClick={getDestination} | ||
className="w-full px-3 py-2 mb-4 bg-blue-500 text-white rounded" | ||
> | ||
Get Destination | ||
</button> | ||
</div> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { z } from 'zod'; | ||
|
||
const anonUrl = z.object({ | ||
url: z.string().url('URL must be a valid URL'), | ||
}); | ||
|
||
export default anonUrl; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,4 +23,4 @@ | |
}, | ||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], | ||
"exclude": ["node_modules"] | ||
} | ||
} |