Skip to content

Commit

Permalink
private links and edit link
Browse files Browse the repository at this point in the history
  • Loading branch information
VijeshVS committed May 28, 2024
1 parent 1f3f227 commit 4269aa9
Show file tree
Hide file tree
Showing 11 changed files with 167 additions and 37 deletions.
27 changes: 20 additions & 7 deletions app/[short_id]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,32 @@ import {
publishUserAgent,
} from "@/lib/services/redisPublicGenerate";
import { NextRequest } from "next/server";
import PrismaClientManager from "@/lib/services/pgConnect";

export async function GET(req: NextRequest) {
const path = req.nextUrl.pathname;
const shortCode = path.replace("/", "");

if (!checkIfShortCodePublic(shortCode)) {
return RESPONSE(
{
error: "Invalid input",
moreinfo: "We are currently not supporting private short codes",
},
HTTP_STATUS.BAD_REQUEST
);
// click analytics yet to be added
const prisma = PrismaClientManager.getInstance().getPrismaClient();
const link:any = await prisma.links.findFirst({
where:{
short_code:shortCode
}
})

if(!link){
return RESPONSE(
{
error: "Invalid input",
moreinfo: "Short link generated is invalid or expired",
},
HTTP_STATUS.BAD_REQUEST
);
}

return Response.redirect(link?.long_url,301)
}

const long_url = await getLongUrl(shortCode);
Expand Down
2 changes: 1 addition & 1 deletion app/app/(dashboard)/links/create/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ export default function CreatePage() {
<Label className="text-gray-400 ml-2">(Optional)</Label>
</div>
<Input
name="shortLink"
name="short_code"
value={shortLink}
onChange={(e) => setShortLink(e.target.value)}
className="mt-2 md:mt-0"
Expand Down
6 changes: 2 additions & 4 deletions app/app/(dashboard)/links/page.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
"use client"

import { DatePickerWithRange } from "@/components/DialogComponents/DatePickerWithRange";
import React, { useEffect, useMemo, useState } from 'react'
import React, { useEffect, useState } from 'react'
import { FilterDialog } from "@/components/DialogComponents/FilterDialog";
import { LinkCard } from "@/components/CardComponents/LinkCard";
import { LinkType } from "@/interfaces/types";

import { getLinks } from "@/lib/actions/getLinksAction";
import { DateRange } from "react-day-picker";
import { addDays } from "date-fns";
import { Divide } from "lucide-react";
import Loading from "./loading";


Expand Down
11 changes: 7 additions & 4 deletions components/CardComponents/LinkCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { EditLinkDialog } from "../DialogComponents/EditLinkDialog";
import { copyToClipboard } from "@/lib/utils";
import { LinkShareDialog } from "../DialogComponents/LinkShareDialog";
import { LinkType } from "@/interfaces/types";
import { useState } from "react";

const months = [
"January", "February", "March", "April", "May", "June",
Expand All @@ -29,7 +30,9 @@ export function LinkCard({
link: any;
}) {
const REDIRECT_URL = process.env.REDIRECT_URL || "https://eurl.vshetty.dev";
const shortLink:string = `${REDIRECT_URL}/${link.short_code}`
const shortLink:string = `${REDIRECT_URL}/${link.short_code}`
const [shortCode,setShortcode] = useState(link.short_code);
const [title,setTitle] = useState(link.title)

return (
<div className="flex mt-6 p-6 flex-col rounded-xl border-[0.5px] shadow-md">
Expand All @@ -40,7 +43,7 @@ export function LinkCard({
<div className="flex flex-col ml-6 w-full">
<div className="flex justify-between ">
<h1 className="text-lg font-bold hover:underline cursor-pointer">
{link.title}
{title}
</h1>
<div className="hidden md:block">
<Button onClick={()=>{copyToClipboard(shortLink)}} variant="outline">
Expand All @@ -53,7 +56,7 @@ export function LinkCard({
Share
</Button>
</LinkShareDialog>
<EditLinkDialog link={{title:link.title,shortLink:link.short_code}}>
<EditLinkDialog setShortcode={setShortcode} setParentTitle={setTitle} link={{title:link.title,shortLink:link.short_code,id:link.id}}>
<Button variant="outline" className="ml-2">
<Pencil size={15} className="mr-2" />
Edit
Expand Down Expand Up @@ -108,7 +111,7 @@ export function LinkCard({
<Share2 size={15} />
</Button>
</LinkShareDialog>
<EditLinkDialog link={{title:link.title,shortLink:link.short_code}}>
<EditLinkDialog setShortcode={setShortcode} setParentTitle={setTitle} link={{title:link.title,shortLink:link.short_code,id:link.id}}>
<Button className="ml-2" variant="outline">
<Pencil size={15} />
</Button>
Expand Down
66 changes: 52 additions & 14 deletions components/DialogComponents/EditLinkDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogClose,
DialogContent,
DialogFooter,
DialogHeader,
Expand All @@ -11,24 +12,59 @@ import {
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { EditLink } from "@/interfaces/types";
import { updateLinkAction } from "@/lib/actions/updateLinkAction";
import { HTTP_STATUS } from "@/lib/constants";
import { Lock } from "lucide-react";
import { useState } from "react";
import { toast } from "../ui/use-toast";

export function EditLinkDialog({ children,link }: {
export function EditLinkDialog({ children,link,setShortcode,setParentTitle }: {
children:React.ReactNode,
link: EditLink
link: any,
setShortcode:any,
setParentTitle:any
}) {
const [title,setTitle] = useState(link.title)
const [shortLink,setShortLink] = useState(link.shortLink)

function editLink(){
const tempLink = {
title,
shortLink
}

console.log(tempLink)
const updateLink = async () =>{
const formdata = new FormData();
formdata.append('title',title);
formdata.append('short_code',shortLink)
formdata.append('linkId',link.id)
const new_title = title;
const new_short_code = shortLink;

const res = await updateLinkAction(formdata)

if(res.status == HTTP_STATUS.OK){
toast({
title:"Link Updated Successfully !!"
})

setParentTitle(new_title)
setShortcode(new_short_code)
}
else if(res.status == HTTP_STATUS.CONFLICT){
toast({
title:"Conflict!!",
description:"The short code has already been taken",
variant:"destructive"
})
}
else if(res.status == HTTP_STATUS.BAD_REQUEST){
toast({
title:"Invalid Inputs",
variant:"destructive"
})
}
else{
toast({
title:"Some error occured",
description:"Please try again later",
variant:"destructive"
})
}
}

return (
Expand All @@ -39,8 +75,8 @@ export function EditLinkDialog({ children,link }: {
<DialogTitle>Edit Link</DialogTitle>
</DialogHeader>
<div>
<Label className="text-sm">Title</Label>
<Input value={title} onChange={(e)=>setTitle(e.target.value)} className="mt-2" />
<Label className="text-sm">Title</Label>
<Input name="title" value={title} onChange={(e)=>setTitle(e.target.value)} className="mt-2" />
<div className="flex mt-4 md:flex-row flex-col">
<div className="flex-3">
<div className="flex items-center">
Expand All @@ -51,12 +87,14 @@ export function EditLinkDialog({ children,link }: {
</div>
<div className="flex-1 ml-0 md:ml-2 md:mt-0 mt-3">
<Label className="text-sm">BackHalf</Label>
<Input value={shortLink} onChange={(e)=>setShortLink(e.target.value)} className="mt-1" />
<Input name="shortcode" value={shortLink} onChange={(e)=>setShortLink(e.target.value)} className="mt-1" />
</div>
</div>
</div>
<DialogFooter>
<Button onClick={editLink}>Save changes</Button>
<DialogClose>
<Button onClick={()=>updateLink()}>Save changes</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
Expand Down
1 change: 0 additions & 1 deletion lib/actions/createLinkAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { createPrivateLink } from "../services/privateLinkManager"

export async function createLinkAction (formdata:FormData){
const response = await createPrivateLink(formdata)
console.log(response)
return response

}
Expand Down
1 change: 0 additions & 1 deletion lib/actions/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ const register = async (formData: FormData) => {
},
});

console.log(user);

if (user) return 403;

Expand Down
9 changes: 9 additions & 0 deletions lib/actions/updateLinkAction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
'use server'

import { updatePrivateLink } from "../services/privateLinkManager"

export async function updateLinkAction (formdata:FormData){
const res = await updatePrivateLink(formdata);

return res;
}
1 change: 1 addition & 0 deletions lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const HTTP_STATUS = {
NOT_FOUND: 404,
INTERNAL_SERVER_ERROR: 500,
FORBIDDEN: 403,
CONFLICT: 409
};

export const ERROR_MESSAGES = {
Expand Down
79 changes: 75 additions & 4 deletions lib/services/privateLinkManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,49 @@ import { base62_encode } from "@/lib/services/base62";

import incrementCounter from "@/lib/services/counter";
import { getServerSession } from "next-auth";

import z from 'zod'
import { ISessionType } from "@/interfaces/url";
import authOptions from "@/lib/authOptions";
import validateURLCreateReq from "@/lib/validations/url_create";
import PrismaClientManager from "@/lib/services/pgConnect";
import { HTTP_STATUS } from "@/lib/constants";

const alphabetOnlySchema = z.string().regex(/^[a-zA-Z]+$/);


export async function createPrivateLink(formdata : FormData) {

const posgresInstance = PrismaClientManager.getInstance();
const prisma = posgresInstance.getPrismaClient();
const { title,long_url, status } = await validateURLCreateReq(formdata);
const session: ISessionType | null = await getServerSession(authOptions);

let custom_short_code:any = formdata.get('short_code')

// if custom short code exists
if(custom_short_code){

// check if duplicate
const link = await prisma.links.findFirst({
where: {
short_code:custom_short_code
}
})

if(link) return {
status: HTTP_STATUS.CONFLICT
}

// checking its regex
const res = alphabetOnlySchema.safeParse(custom_short_code);

if(!res.success || custom_short_code.startsWith("app")){
return {
status: HTTP_STATUS.BAD_REQUEST
}
}
}

if (!status) {
return {
status: HTTP_STATUS.BAD_REQUEST
Expand All @@ -29,14 +58,16 @@ export async function createPrivateLink(formdata : FormData) {
}

const shortIdLength = await incrementCounter();
const shortId = base62_encode(shortIdLength);

if(!custom_short_code)
custom_short_code = base62_encode(shortIdLength);

try {
await prisma.links.create({
data: {
title : title as string,
long_url: long_url as string,
short_code: shortId,
short_code: custom_short_code,
created_at: new Date(),
user: {
connect: {
Expand All @@ -46,7 +77,7 @@ export async function createPrivateLink(formdata : FormData) {
},
});
return {
short_url: shortId,
short_url: custom_short_code,
status: HTTP_STATUS.CREATED
}

Expand All @@ -57,3 +88,43 @@ export async function createPrivateLink(formdata : FormData) {

}
}

export async function updatePrivateLink(formdata : FormData){
const prisma = PrismaClientManager.getInstance().getPrismaClient();

const title:any = formdata.get('title')
const shortcode:any = formdata.get('short_code')
const linkId:any = formdata.get('linkId')

try{
const link = await prisma.links.findFirst({
where:{
short_code:shortcode
}
})

if(link && link.id != linkId){
return {status: HTTP_STATUS.CONFLICT}
}

await prisma.links.update({
where:{
id:Number.parseInt(linkId)
},
data:{
title:title,
short_code:shortcode
}
})

return {
status: HTTP_STATUS.OK
}

}
catch(e){
return {
status: HTTP_STATUS.INTERNAL_SERVER_ERROR
}
}
}
1 change: 0 additions & 1 deletion lib/validations/url_create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ const validateURLCreateReq = async (formdata: FormData) => {

return { title, long_url, status: errors.success, msg: errors.error };
} catch (e) {
console.log(e)
return {
title: "",
long_url: "",
Expand Down

0 comments on commit 4269aa9

Please sign in to comment.