From f174ba4da9841b13dc3921ec326cd61283c94a71 Mon Sep 17 00:00:00 2001 From: Pranav Bhat <96832303+psbhatbvbcs@users.noreply.github.com> Date: Fri, 26 Jan 2024 12:57:52 +0530 Subject: [PATCH 1/4] Complete functionality to delete documents --- app/api/chat/route.ts | 2 + app/api/deleteDocument/route.ts | 96 ++++++++++++++++++++++++++++++ app/dashboard/dashboard-client.tsx | 57 +++++++++++++++++- app/dashboard/layout.tsx | 8 +-- app/dashboard/page.tsx | 2 +- components/ui/Trash.tsx | 27 +++++++++ 6 files changed, 182 insertions(+), 10 deletions(-) create mode 100644 app/api/deleteDocument/route.ts create mode 100644 components/ui/Trash.tsx diff --git a/app/api/chat/route.ts b/app/api/chat/route.ts index 34dae55..f0cc9ee 100644 --- a/app/api/chat/route.ts +++ b/app/api/chat/route.ts @@ -170,6 +170,8 @@ export async function POST(req: NextRequest) { ), ).toString('base64'); + // add a chat thing to prisma + return new StreamingTextResponse(stream, { headers: { 'x-message-index': (formattedPreviousMessages.length + 1).toString(), diff --git a/app/api/deleteDocument/route.ts b/app/api/deleteDocument/route.ts new file mode 100644 index 0000000..2d253a4 --- /dev/null +++ b/app/api/deleteDocument/route.ts @@ -0,0 +1,96 @@ +import { NextResponse } from 'next/server'; +import prisma from '@/utils/prisma'; +import { getAuth } from '@clerk/nextjs/server'; + +interface DeleteFileParams { + accountId: string; + apiKey: string; + querystring: { + filePath: string; + }; +} + +async function deleteFile(params: DeleteFileParams) { + const baseUrl = 'https://api.bytescale.com'; + const path = `/v2/accounts/${params.accountId}/files`; + const entries = (obj: Record) => + Object.entries(obj).filter(([, val]) => (val ?? null) !== null) as [ + string, + string, + ][]; + const query = entries(params.querystring ?? {}) + .flatMap(([k, v]) => (Array.isArray(v) ? v.map((v2) => [k, v2]) : [[k, v]])) + .map((kv) => kv.join('=')) + .join('&'); + const response = await fetch( + `${baseUrl}${path}${query.length > 0 ? '?' : ''}${query}`, + { + method: 'DELETE', + headers: Object.fromEntries( + entries({ + Authorization: `Bearer ${params.apiKey}`, + }) as [string, string][], + ), + }, + ); + if (Math.floor(response.status / 100) !== 2) { + const result = await response.json(); + throw new Error(`Bytescale API Error: ${JSON.stringify(result)}`); + } +} + +export async function DELETE(request: Request) { + const { id, fileUrl } = await request.json(); + + const { userId } = getAuth(request as any); + + if (!userId) { + return NextResponse.json({ error: 'You must be logged in to delete data' }); + } + + try { + const document = await prisma.document.findFirst({ + where: { + id, + userId, + }, + }); + + if (!document) { + return NextResponse.json({ error: 'Document not found' }); + } + + const pathWithAccId = fileUrl.replace('https://upcdn.io/', ''); + const accId = pathWithAccId.split('/')[0]; + const path = pathWithAccId.replace(`${accId}/raw`, ''); + + deleteFile({ + accountId: accId, + apiKey: !!process.env.NEXT_SECRET_BYTESCALE_API_KEY + ? process.env.NEXT_SECRET_BYTESCALE_API_KEY + : 'no api key found', + querystring: { + filePath: path, + }, + }).then( + () => console.log('Success.'), + (error) => { + console.error(error); + return NextResponse.json({ + error: 'Could not delete document from cloud', + }); + }, + ); + + await prisma.document.delete({ + where: { + id, + }, + }); + + return NextResponse.json({ text: 'Document deleted successfully', id }); + } catch (error) { + console.log('error', error); + return NextResponse.json({ error: 'Failed to delete your data' }); + } +} diff --git a/app/dashboard/dashboard-client.tsx b/app/dashboard/dashboard-client.tsx index bf36912..e30f412 100644 --- a/app/dashboard/dashboard-client.tsx +++ b/app/dashboard/dashboard-client.tsx @@ -6,6 +6,14 @@ import { useRouter } from 'next/navigation'; import DocIcon from '@/components/ui/DocIcon'; import { formatDistanceToNow } from 'date-fns'; import { useState } from 'react'; +import TrashIcon from '@/components/ui/Trash'; + +interface Document { + id: string; + fileName: string; + fileUrl: string; + createdAt: Date; +} // Configuration for the uploader const uploader = Uploader({ @@ -14,10 +22,19 @@ const uploader = Uploader({ : 'no api key found', }); -export default function DashboardClient({ docsList }: { docsList: any }) { +export default function DashboardClient({ + initialDocsList, +}: { + initialDocsList: Document[]; +}) { const router = useRouter(); const [loading, setLoading] = useState(false); + const [docsList, setDocsList] = useState(initialDocsList); + + const updateDocsList = (newDocsList: any[]) => { + setDocsList(newDocsList); + }; const options = { maxFileCount: 1, @@ -70,6 +87,35 @@ export default function DashboardClient({ docsList }: { docsList: any }) { router.push(`/document/${data.id}`); } + async function deleteDocument(id: string, fileUrl: string) { + try { + const res = await fetch('/api/deleteDocument', { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + id, + fileUrl, + }), + }); + + if (!res.ok) { + throw new Error('Failed to delete document'); + } + + const data = await res.json(); + if (data.error) { + console.log(data.error); + } else { + console.log('Document deleted successfully'); + updateDocsList(docsList.filter((doc) => doc.id !== id)); + } + } catch (error) { + console.log('Error deleting document', error); + } + } + return (

@@ -81,7 +127,7 @@ export default function DashboardClient({ docsList }: { docsList: any }) { {docsList.map((doc: any) => (
- {formatDistanceToNow(doc.createdAt)} ago + + {formatDistanceToNow(doc.createdAt)} ago + deleteDocument(doc.id, doc.fileUrl)} + /> +
))}

diff --git a/app/dashboard/layout.tsx b/app/dashboard/layout.tsx index 300213c..692bc1a 100644 --- a/app/dashboard/layout.tsx +++ b/app/dashboard/layout.tsx @@ -1,4 +1,3 @@ -import Footer from '@/components/home/Footer'; import Header from '@/components/ui/Header'; export default function RootLayout({ @@ -7,12 +6,9 @@ export default function RootLayout({ children: React.ReactNode; }) { return ( -
+
-
{children}
-
-
-
+ {children}
); } diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx index 1f0e1cf..b4f9c9d 100644 --- a/app/dashboard/page.tsx +++ b/app/dashboard/page.tsx @@ -17,7 +17,7 @@ export default async function Page() { return (
- +
); } diff --git a/components/ui/Trash.tsx b/components/ui/Trash.tsx new file mode 100644 index 0000000..c060eeb --- /dev/null +++ b/components/ui/Trash.tsx @@ -0,0 +1,27 @@ +import React from 'react'; + +interface TrashIconProps { + onClick: () => void; +} + +const TrashIcon: React.FC = ({ onClick }) => { + return ( + <> + + + + + + ); +}; + +export default TrashIcon; From 36cb0f505db2ba4695a4dd8e694cd2ecdc130a55 Mon Sep 17 00:00:00 2001 From: Pranav Bhat <96832303+psbhatbvbcs@users.noreply.github.com> Date: Thu, 1 Feb 2024 10:21:06 +0530 Subject: [PATCH 2/4] Complete functionality to delete documents/requested changes done --- app/api/chat/route.ts | 2 -- app/dashboard/dashboard-client.tsx | 6 +----- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/app/api/chat/route.ts b/app/api/chat/route.ts index f0cc9ee..34dae55 100644 --- a/app/api/chat/route.ts +++ b/app/api/chat/route.ts @@ -170,8 +170,6 @@ export async function POST(req: NextRequest) { ), ).toString('base64'); - // add a chat thing to prisma - return new StreamingTextResponse(stream, { headers: { 'x-message-index': (formattedPreviousMessages.length + 1).toString(), diff --git a/app/dashboard/dashboard-client.tsx b/app/dashboard/dashboard-client.tsx index e30f412..02ae243 100644 --- a/app/dashboard/dashboard-client.tsx +++ b/app/dashboard/dashboard-client.tsx @@ -32,10 +32,6 @@ export default function DashboardClient({ const [loading, setLoading] = useState(false); const [docsList, setDocsList] = useState(initialDocsList); - const updateDocsList = (newDocsList: any[]) => { - setDocsList(newDocsList); - }; - const options = { maxFileCount: 1, mimeTypes: ['application/pdf'], @@ -109,7 +105,7 @@ export default function DashboardClient({ console.log(data.error); } else { console.log('Document deleted successfully'); - updateDocsList(docsList.filter((doc) => doc.id !== id)); + setDocsList(docsList.filter((doc) => doc.id !== id)); } } catch (error) { console.log('Error deleting document', error); From 77c78ad2756a526aa7ec7bb6e94dfb3be7f11893 Mon Sep 17 00:00:00 2001 From: Pranav Bhat <96832303+psbhatbvbcs@users.noreply.github.com> Date: Sat, 3 Feb 2024 23:23:07 +0530 Subject: [PATCH 3/4] pinecone deletion done --- app/api/deleteDocument/route.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/app/api/deleteDocument/route.ts b/app/api/deleteDocument/route.ts index 2d253a4..b9ca634 100644 --- a/app/api/deleteDocument/route.ts +++ b/app/api/deleteDocument/route.ts @@ -1,6 +1,7 @@ import { NextResponse } from 'next/server'; import prisma from '@/utils/prisma'; import { getAuth } from '@clerk/nextjs/server'; +import { Pinecone } from '@pinecone-database/pinecone'; interface DeleteFileParams { accountId: string; @@ -10,6 +11,17 @@ interface DeleteFileParams { }; } +const PINECONE_INDEX_NAME = process.env.PINECONE_INDEX_NAME ?? ''; + +const pinecone = new Pinecone({ + apiKey: process.env.PINECONE_API_KEY ?? '', + environment: process.env.PINECONE_ENVIRONMENT ?? '', //this is in the dashboard +}); + +if (!process.env.PINECONE_INDEX_NAME) { + throw new Error('Missing Pinecone index name in .env file'); +} + async function deleteFile(params: DeleteFileParams) { const baseUrl = 'https://api.bytescale.com'; const path = `/v2/accounts/${params.accountId}/files`; @@ -82,6 +94,14 @@ export async function DELETE(request: Request) { }, ); + const index = pinecone.Index(PINECONE_INDEX_NAME); + + try { + await index.namespace(id).deleteAll(); + } catch (error) { + console.log(error); + } + await prisma.document.delete({ where: { id, From 01ce458e40e24aadb9a4f61905ebaaaf3f7e2f04 Mon Sep 17 00:00:00 2001 From: Pranav Bhat <96832303+psbhatbvbcs@users.noreply.github.com> Date: Sat, 3 Feb 2024 23:25:50 +0530 Subject: [PATCH 4/4] pinecone deletion done --- app/api/deleteDocument/route.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/api/deleteDocument/route.ts b/app/api/deleteDocument/route.ts index b9ca634..ad27160 100644 --- a/app/api/deleteDocument/route.ts +++ b/app/api/deleteDocument/route.ts @@ -15,7 +15,7 @@ const PINECONE_INDEX_NAME = process.env.PINECONE_INDEX_NAME ?? ''; const pinecone = new Pinecone({ apiKey: process.env.PINECONE_API_KEY ?? '', - environment: process.env.PINECONE_ENVIRONMENT ?? '', //this is in the dashboard + environment: process.env.PINECONE_ENVIRONMENT ?? '', }); if (!process.env.PINECONE_INDEX_NAME) {