From 7c65bfeb2c6bd92314fb15cfbf89ea00fbb38c11 Mon Sep 17 00:00:00 2001 From: Hemant Date: Mon, 31 Jul 2023 18:31:40 +0530 Subject: [PATCH] Added api limits for a free user --- app/api/code/route.ts | 12 ++++---- app/api/conversation/route.ts | 11 ++++--- app/api/image/route.ts | 12 ++++---- app/api/music/route.ts | 12 ++++---- app/api/video/route.ts | 12 ++++---- lib/api-limit.ts | 57 +++++++++++++++++++++++++++++++++++ lib/prismadb.ts | 10 ++++++ package.json | 4 +++ prisma/schema.prisma | 28 +++++++++++++++++ yarn.lock | 24 +++++++++++++++ 10 files changed, 153 insertions(+), 29 deletions(-) create mode 100644 lib/api-limit.ts create mode 100644 lib/prismadb.ts create mode 100644 prisma/schema.prisma diff --git a/app/api/code/route.ts b/app/api/code/route.ts index 8498483..1d09195 100644 --- a/app/api/code/route.ts +++ b/app/api/code/route.ts @@ -3,7 +3,7 @@ import { NextResponse } from "next/server"; import { ChatCompletionRequestMessage, Configuration, OpenAIApi } from "openai"; // import { checkSubscription } from "@/lib/subscription"; -// import { incrementApiLimit, checkApiLimit } from "@/lib/api-limit"; +import { incrementApiLimit, checkApiLimit } from "@/lib/api-limit"; const configuration = new Configuration({ apiKey: process.env.OPENAI_API_KEY, @@ -34,12 +34,12 @@ export async function POST(req: Request) { return new NextResponse("Messages are required", { status: 400 }); } - // const freeTrial = await checkApiLimit(); + const freeTrial = await checkApiLimit(); // const isPro = await checkSubscription(); - // if (!freeTrial && !isPro) { - // return new NextResponse("Free trial has expired. Please upgrade to pro.", { status: 403 }); - // } + if (!freeTrial) { + return new NextResponse("Free trial has expired. Please upgrade to pro.", { status: 403 }); + } const response = await openai.createChatCompletion({ model: "gpt-3.5-turbo", @@ -47,7 +47,7 @@ export async function POST(req: Request) { }); // if (!isPro) { - // await incrementApiLimit(); + await incrementApiLimit(); // } return NextResponse.json(response.data.choices[0].message); diff --git a/app/api/conversation/route.ts b/app/api/conversation/route.ts index 8ac042a..5c58f4b 100644 --- a/app/api/conversation/route.ts +++ b/app/api/conversation/route.ts @@ -3,7 +3,7 @@ import { NextResponse } from "next/server"; import { Configuration, OpenAIApi } from "openai"; // import { checkSubscription } from "@/lib/subscription"; -// import { incrementApiLimit, checkApiLimit } from "@/lib/api-limit"; +import { incrementApiLimit, checkApiLimit } from "@/lib/api-limit"; const configuration = new Configuration({ apiKey: process.env.OPENAI_API_KEY, @@ -30,12 +30,13 @@ export async function POST(req: Request) { return new NextResponse("Messages are required", { status: 400 }); } - // const freeTrial = await checkApiLimit(); + const freeTrial = await checkApiLimit(); // const isPro = await checkSubscription(); // if (!freeTrial && !isPro) { - // return new NextResponse("Free trial has expired. Please upgrade to pro.", { status: 403 }); - // } + if (!freeTrial) { + return new NextResponse("Free trial has expired. Please upgrade to pro.", { status: 403 }); + } const response = await openai.createChatCompletion({ model: "gpt-3.5-turbo", @@ -43,7 +44,7 @@ export async function POST(req: Request) { }); // if (!isPro) { - // await incrementApiLimit(); + await incrementApiLimit(); // } return NextResponse.json(response.data.choices[0].message); diff --git a/app/api/image/route.ts b/app/api/image/route.ts index ae09a3c..520f272 100644 --- a/app/api/image/route.ts +++ b/app/api/image/route.ts @@ -3,7 +3,7 @@ import { NextResponse } from "next/server"; import { Configuration, OpenAIApi } from "openai"; // import { checkSubscription } from "@/lib/subscription"; -// import { incrementApiLimit, checkApiLimit } from "@/lib/api-limit"; +import { incrementApiLimit, checkApiLimit } from "@/lib/api-limit"; const configuration = new Configuration({ apiKey: process.env.OPENAI_API_KEY, @@ -37,12 +37,12 @@ export async function POST(req: Request) { return new NextResponse("Resolution is required", { status: 400 }); } - // const freeTrial = await checkApiLimit(); + const freeTrial = await checkApiLimit(); // const isPro = await checkSubscription(); - // if (!freeTrial && !isPro) { - // return new NextResponse("Free trial has expired. Please upgrade to pro.", { status: 403 }); - // } + if (!freeTrial) { + return new NextResponse("Free trial has expired. Please upgrade to pro.", { status: 403 }); + } const response = await openai.createImage({ prompt, @@ -51,7 +51,7 @@ export async function POST(req: Request) { }); // if (!isPro) { - // await incrementApiLimit(); + await incrementApiLimit(); // } return NextResponse.json(response.data.data); diff --git a/app/api/music/route.ts b/app/api/music/route.ts index f881b70..0f986ef 100644 --- a/app/api/music/route.ts +++ b/app/api/music/route.ts @@ -2,8 +2,8 @@ import Replicate from "replicate"; import { auth } from "@clerk/nextjs"; import { NextResponse } from "next/server"; -// import { incrementApiLimit, checkApiLimit } from "@/lib/api-limit"; // import { checkSubscription } from "@/lib/subscription"; +import { incrementApiLimit, checkApiLimit } from "@/lib/api-limit"; const replicate = new Replicate({ auth: process.env.REPLICATE_API_TOKEN!, @@ -23,12 +23,12 @@ export async function POST(req: Request) { return new NextResponse("Prompt is required", { status: 400 }); } - // const freeTrial = await checkApiLimit(); + const freeTrial = await checkApiLimit(); // const isPro = await checkSubscription(); - // if (!freeTrial && !isPro) { - // return new NextResponse("Free trial has expired. Please upgrade to pro.", { status: 403 }); - // } + if (!freeTrial) { + return new NextResponse("Free trial has expired. Please upgrade to pro.", { status: 403 }); + } const response = await replicate.run( "riffusion/riffusion:8cf61ea6c56afd61d8f5b9ffd14d7c216c0a93844ce2d82ac1c9ecc9c7f24e05", @@ -40,7 +40,7 @@ export async function POST(req: Request) { ); // if (!isPro) { - // await incrementApiLimit(); + await incrementApiLimit(); // } return NextResponse.json(response); diff --git a/app/api/video/route.ts b/app/api/video/route.ts index dec2120..24afdfc 100644 --- a/app/api/video/route.ts +++ b/app/api/video/route.ts @@ -2,7 +2,7 @@ import Replicate from "replicate"; import { auth } from "@clerk/nextjs"; import { NextResponse } from "next/server"; -// import { incrementApiLimit, checkApiLimit } from "@/lib/api-limit"; +import { incrementApiLimit, checkApiLimit } from "@/lib/api-limit"; // import { checkSubscription } from "@/lib/subscription"; const replicate = new Replicate({ @@ -23,12 +23,12 @@ export async function POST(req: Request) { return new NextResponse("Prompt is required", { status: 400 }); } - // const freeTrial = await checkApiLimit(); + const freeTrial = await checkApiLimit(); // const isPro = await checkSubscription(); - // if (!freeTrial && !isPro) { - // return new NextResponse("Free trial has expired. Please upgrade to pro.", { status: 403 }); - // } + if (!freeTrial) { + return new NextResponse("Free trial has expired. Please upgrade to pro.", { status: 403 }); + } const response = await replicate.run( "anotherjesse/zeroscope-v2-xl:71996d331e8ede8ef7bd76eba9fae076d31792e4ddf4ad057779b443d6aea62f", @@ -40,7 +40,7 @@ export async function POST(req: Request) { ); // if (!isPro) { - // await incrementApiLimit(); + await incrementApiLimit(); // } return NextResponse.json(response); diff --git a/lib/api-limit.ts b/lib/api-limit.ts new file mode 100644 index 0000000..8edf1bb --- /dev/null +++ b/lib/api-limit.ts @@ -0,0 +1,57 @@ +import { auth } from "@clerk/nextjs"; + +import prismadb from "@/lib/prismadb"; +import { MAX_FREE_COUNTS } from "@/constants"; + +export const incrementApiLimit = async () => { + const { userId } = auth(); + + if (!userId) return + + const userApiLimit = await prismadb.userApiLimit.findUnique({ + where: { userId: userId }, + }); + + if (userApiLimit) { + await prismadb.userApiLimit.update({ + where: { userId: userId }, + data: { count: userApiLimit.count + 1 }, + }); + } else { + await prismadb.userApiLimit.create({ + data: { userId: userId, count: 1 }, + }); + } +}; + +export const checkApiLimit = async () => { + const { userId } = auth(); + + if (!userId) return false + + const userApiLimit = await prismadb.userApiLimit.findUnique({ + where: { userId: userId }, + }); + + if (!userApiLimit || userApiLimit.count < MAX_FREE_COUNTS) { + return true; + } else { + return false; + } +}; + +export const getApiLimitCount = async () => { + const { userId } = auth(); + + if (!userId) return 0 + + const userApiLimit = await prismadb.userApiLimit.findUnique({ + where: { + userId + } + }); + + if (!userApiLimit) return 0; + + return userApiLimit.count; +}; diff --git a/lib/prismadb.ts b/lib/prismadb.ts new file mode 100644 index 0000000..1f845d9 --- /dev/null +++ b/lib/prismadb.ts @@ -0,0 +1,10 @@ +import { PrismaClient } from "@prisma/client"; + +declare global { + var prisma: PrismaClient | undefined +} + +const prismadb = globalThis.prisma || new PrismaClient() +if (process.env.NODE_ENV !== "production") globalThis.prisma = prismadb + +export default prismadb; diff --git a/package.json b/package.json index 32534e3..7a0df07 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "dependencies": { "@clerk/nextjs": "^4.23.1", "@hookform/resolvers": "^3.1.1", + "@prisma/client": "^5.0.0", "@radix-ui/react-avatar": "^1.0.3", "@radix-ui/react-dialog": "^1.0.4", "@radix-ui/react-icons": "^1.3.0", @@ -40,5 +41,8 @@ "tailwindcss-animate": "^1.0.6", "typescript": "5.1.6", "typewriter-effect": "^2.20.1" + }, + "devDependencies": { + "prisma": "^5.0.0" } } diff --git a/prisma/schema.prisma b/prisma/schema.prisma new file mode 100644 index 0000000..8f8a181 --- /dev/null +++ b/prisma/schema.prisma @@ -0,0 +1,28 @@ +// This is your Prisma schema file, +// learn more about it in the docs: https://pris.ly/d/prisma-schema + +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +model UserApiLimit { + id String @id @default(cuid()) + userId String @unique + count Int @default(0) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model UserSubscription { + id String @id @default(cuid()) + userId String @unique + stripeCustomerId String? @unique @map(name: "stripe_customer_id") + stripeSubscriptionId String? @unique @map(name: "stripe_subscription_id") + stripePriceId String? @map(name: "stripe_price_id") + stripeCurrentPeriodEnd DateTime? @map(name: "stripe_current_period_end") +} diff --git a/yarn.lock b/yarn.lock index 97929ec..0e0b4d7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -322,6 +322,23 @@ picocolors "^1.0.0" tslib "^2.6.0" +"@prisma/client@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@prisma/client/-/client-5.0.0.tgz#9f0cd4164f4ffddb28bb1811c27eb7fa1e01a087" + integrity sha512-XlO5ELNAQ7rV4cXIDJUNBEgdLwX3pjtt9Q/RHqDpGf43szpNJx2hJnggfFs7TKNx0cOFsl6KJCSfqr5duEU/bQ== + dependencies: + "@prisma/engines-version" "4.17.0-26.6b0aef69b7cdfc787f822ecd7cdc76d5f1991584" + +"@prisma/engines-version@4.17.0-26.6b0aef69b7cdfc787f822ecd7cdc76d5f1991584": + version "4.17.0-26.6b0aef69b7cdfc787f822ecd7cdc76d5f1991584" + resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-4.17.0-26.6b0aef69b7cdfc787f822ecd7cdc76d5f1991584.tgz#b36eda5620872d3fac810c302a7e46cf41daa033" + integrity sha512-HHiUF6NixsldsP3JROq07TYBLEjXFKr6PdH8H4gK/XAoTmIplOJBCgrIUMrsRAnEuGyRoRLXKXWUb943+PFoKQ== + +"@prisma/engines@5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-5.0.0.tgz#5249650eabe77c458c90f2be97d8210353c2e22e" + integrity sha512-kyT/8fd0OpWmhAU5YnY7eP31brW1q1YrTGoblWrhQJDiN/1K+Z8S1kylcmtjqx5wsUGcP1HBWutayA/jtyt+sg== + "@radix-ui/number@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@radix-ui/number/-/number-1.0.1.tgz#644161a3557f46ed38a042acf4a770e826021674" @@ -3086,6 +3103,13 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== +prisma@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/prisma/-/prisma-5.0.0.tgz#f6571c46dc2478172cb7bc1bb62d74026a2c2630" + integrity sha512-KYWk83Fhi1FH59jSpavAYTt2eoMVW9YKgu8ci0kuUnt6Dup5Qy47pcB4/TLmiPAbhGrxxSz7gsSnJcCmkyPANA== + dependencies: + "@prisma/engines" "5.0.0" + prop-types@^15.0.0, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"