Skip to content

Commit

Permalink
final tweaks
Browse files Browse the repository at this point in the history
  • Loading branch information
joschan21 committed Sep 26, 2023
1 parent 531061c commit ddd06e3
Show file tree
Hide file tree
Showing 12 changed files with 312 additions and 85 deletions.
15 changes: 15 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
async redirects() {
return [
{
source: '/sign-in',
destination: '/api/auth/login',
permanent: true,
},
{
source: '/sign-up',
destination: '/api/auth/register',
permanent: true,
},
]
},

webpack: (
config,
{ buildId, dev, isServer, defaultLoaders, webpack }
Expand Down
Binary file added public/thumbnail.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
182 changes: 117 additions & 65 deletions src/app/api/uploadthing/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,81 +9,133 @@ import { PDFLoader } from 'langchain/document_loaders/fs/pdf'
import { OpenAIEmbeddings } from 'langchain/embeddings/openai'
import { PineconeStore } from 'langchain/vectorstores/pinecone'
import { getPineconeClient } from '@/lib/pinecone'
import { getUserSubscriptionPlan } from '@/lib/stripe'
import { PLANS } from '@/config/stripe'

const f = createUploadthing()

export const ourFileRouter = {
pdfUploader: f({ pdf: { maxFileSize: '4MB' } })
.middleware(async ({ req }) => {
const { getUser } = getKindeServerSession()
const user = getUser()
const middleware = async () => {
const { getUser } = getKindeServerSession()
const user = getUser()

if (!user || !user.id) throw new Error('Unauthorized')
if (!user || !user.id) throw new Error('Unauthorized')

return { userId: user.id }
})
.onUploadComplete(async ({ metadata, file }) => {
console.log('onUploadComplete called')
const createdFile = await db.file.create({
const subscriptionPlan = await getUserSubscriptionPlan()

return { subscriptionPlan, userId: user.id }
}

const onUploadComplete = async ({
metadata,
file,
}: {
metadata: Awaited<ReturnType<typeof middleware>>
file: {
key: string
name: string
url: string
}
}) => {
const isFileExist = await db.file.findFirst({
where: {
key: file.key,
},
})

if (isFileExist) return

const createdFile = await db.file.create({
data: {
key: file.key,
name: file.name,
userId: metadata.userId,
url: `https://uploadthing-prod.s3.us-west-2.amazonaws.com/${file.key}`,
uploadStatus: 'PROCESSING',
},
})

try {
const response = await fetch(
`https://uploadthing-prod.s3.us-west-2.amazonaws.com/${file.key}`
)

const blob = await response.blob()

const loader = new PDFLoader(blob)

const pageLevelDocs = await loader.load()

const pagesAmt = pageLevelDocs.length

const { subscriptionPlan } = metadata
const { isSubscribed } = subscriptionPlan

const isProExceeded =
pagesAmt >
PLANS.find((plan) => plan.name === 'Pro')!.pagesPerPdf
const isFreeExceeded =
pagesAmt >
PLANS.find((plan) => plan.name === 'Free')!
.pagesPerPdf

if (
(isSubscribed && isProExceeded) ||
(!isSubscribed && isFreeExceeded)
) {
await db.file.update({
data: {
key: file.key,
name: file.name,
userId: metadata.userId,
url: `https://uploadthing-prod.s3.us-west-2.amazonaws.com/${file.key}`,
uploadStatus: 'PROCESSING',
uploadStatus: 'FAILED',
},
where: {
id: createdFile.id,
},
})
}

// vectorize and index entire document
const pinecone = await getPineconeClient()
const pineconeIndex = pinecone.Index('quill')

const embeddings = new OpenAIEmbeddings({
openAIApiKey: process.env.OPENAI_API_KEY,
})

try {
console.log('getting file')
const response = await fetch(
`https://uploadthing-prod.s3.us-west-2.amazonaws.com/${file.key}`
)
console.log('got file')
const blob = await response.blob()

const loader = new PDFLoader(blob)

const pageLevelDocs = await loader.load()

const pagesAmt = pageLevelDocs.length

// vectorize and index entire document
const pinecone = await getPineconeClient()
const pineconeIndex = pinecone.Index('quill')

const embeddings = new OpenAIEmbeddings({
openAIApiKey: process.env.OPENAI_API_KEY,
})

await PineconeStore.fromDocuments(
pageLevelDocs,
embeddings,
{
pineconeIndex,
namespace: createdFile.id,
}
)

await db.file.update({
data: {
uploadStatus: 'SUCCESS',
},
where: {
id: createdFile.id,
},
})
} catch (err) {
await db.file.update({
data: {
uploadStatus: 'FAILED',
},
where: {
id: createdFile.id,
},
})
await PineconeStore.fromDocuments(
pageLevelDocs,
embeddings,
{
pineconeIndex,
namespace: createdFile.id,
}
}),
)

await db.file.update({
data: {
uploadStatus: 'SUCCESS',
},
where: {
id: createdFile.id,
},
})
} catch (err) {
await db.file.update({
data: {
uploadStatus: 'FAILED',
},
where: {
id: createdFile.id,
},
})
}
}

export const ourFileRouter = {
freePlanUploader: f({ pdf: { maxFileSize: '4MB' } })
.middleware(middleware)
.onUploadComplete(onUploadComplete),
proPlanUploader: f({ pdf: { maxFileSize: '16MB' } })
.middleware(middleware)
.onUploadComplete(onUploadComplete),
} satisfies FileRouter

export type OurFileRouter = typeof ourFileRouter
5 changes: 4 additions & 1 deletion src/app/dashboard/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Dashboard from '@/components/Dashboard'
import { db } from '@/db'
import { getUserSubscriptionPlan } from '@/lib/stripe'
import { getKindeServerSession } from '@kinde-oss/kinde-auth-nextjs/server'
import { redirect } from 'next/navigation'

Expand All @@ -17,7 +18,9 @@ const Page = async () => {

if(!dbUser) redirect('/auth-callback?origin=dashboard')

return <Dashboard />
const subscriptionPlan = await getUserSubscriptionPlan()

return <Dashboard subscriptionPlan={subscriptionPlan} />
}

export default Page
Binary file modified src/app/favicon.ico
Binary file not shown.
15 changes: 5 additions & 10 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
import { cn } from '@/lib/utils'
import './globals.css'
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import Navbar from '@/components/Navbar'
import Providers from '@/components/Providers'
import { cn, constructMetadata } from '@/lib/utils'
import { Inter } from 'next/font/google'
import './globals.css'

import 'react-loading-skeleton/dist/skeleton.css'
import "simplebar-react/dist/simplebar.min.css"

import 'simplebar-react/dist/simplebar.min.css'

import { Toaster } from '@/components/ui/toaster'

const inter = Inter({ subsets: ['latin'] })

export const metadata: Metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
}
export const metadata = constructMetadata()

export default function RootLayout({
children,
Expand Down
9 changes: 7 additions & 2 deletions src/components/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,13 @@ import Link from 'next/link'
import { format } from 'date-fns'
import { Button } from './ui/button'
import { useState } from 'react'
import { getUserSubscriptionPlan } from '@/lib/stripe'

const Dashboard = () => {
interface PageProps {
subscriptionPlan: Awaited<ReturnType<typeof getUserSubscriptionPlan>>
}

const Dashboard = ({subscriptionPlan}: PageProps) => {
const [currentlyDeletingFile, setCurrentlyDeletingFile] =
useState<string | null>(null)

Expand Down Expand Up @@ -44,7 +49,7 @@ const Dashboard = () => {
My Files
</h1>

<UploadButton />
<UploadButton isSubscribed={subscriptionPlan.isSubscribed} />
</div>

{/* display all user files */}
Expand Down
100 changes: 100 additions & 0 deletions src/components/MobileNav.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
'use client'

import { ArrowRight, Menu } from 'lucide-react'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
import { useEffect, useState } from 'react'

const MobileNav = ({ isAuth }: { isAuth: boolean }) => {
const [isOpen, setOpen] = useState<boolean>(false)

const toggleOpen = () => setOpen((prev) => !prev)

const pathname = usePathname()

useEffect(() => {
if (isOpen) toggleOpen()
}, [pathname])

const closeOnCurrent = (href: string) => {
if (pathname === href) {
toggleOpen()
}
}

return (
<div className='sm:hidden'>
<Menu
onClick={toggleOpen}
className='relative z-50 h-5 w-5 text-zinc-700'
/>

{isOpen ? (
<div className='fixed animate-in slide-in-from-top-5 fade-in-20 inset-0 z-0 w-full'>
<ul className='absolute bg-white border-b border-zinc-200 shadow-xl grid w-full gap-3 px-10 pt-20 pb-8'>
{!isAuth ? (
<>
<li>
<Link
onClick={() =>
closeOnCurrent('/sign-up')
}
className='flex items-center w-full font-semibold text-green-600'
href='/sign-up'>
Get started
<ArrowRight className='ml-2 h-5 w-5' />
</Link>
</li>
<li className='my-3 h-px w-full bg-gray-300' />
<li>
<Link
onClick={() =>
closeOnCurrent('/sign-in')
}
className='flex items-center w-full font-semibold'
href='/sign-in'>
Sign in
</Link>
</li>
<li className='my-3 h-px w-full bg-gray-300' />
<li>
<Link
onClick={() =>
closeOnCurrent('/pricing')
}
className='flex items-center w-full font-semibold'
href='/pricing'>
Pricing
</Link>
</li>
</>
) : (
<>
<li>
<Link
onClick={() =>
closeOnCurrent('/dashboard')
}
className='flex items-center w-full font-semibold'
href='/dashboard'>
Dashboard
</Link>
</li>
<li className='my-3 h-px w-full bg-gray-300' />
<li>
<Link
className='flex items-center w-full font-semibold'
href='/sign-out'>
Sign out
</Link>
</li>
</>
)}
</ul>
</div>
) : null}
</div>
)
}

export default MobileNav
Loading

0 comments on commit ddd06e3

Please sign in to comment.