diff --git a/.env b/.env index 96a9f85..78f6130 100644 --- a/.env +++ b/.env @@ -1,2 +1 @@ # make your own .env.local file with any secret keys -FIREBASE_API_KEY=AIzaSyBMSlF-S6LQFa-Zv9Yc48YxFCqV5wqvjd4 \ No newline at end of file diff --git a/.gitignore b/.gitignore index fd3dbb5..9c3206d 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,8 @@ yarn-error.log* # local env files .env*.local +.env.local +*.env.local # vercel .vercel diff --git a/app/api/checkout_sessions/route.js b/app/api/checkout_sessions/route.js new file mode 100644 index 0000000..2f6de62 --- /dev/null +++ b/app/api/checkout_sessions/route.js @@ -0,0 +1,42 @@ +import { loadStripe } from '@stripe/stripe-js'; +import { NextResponse } from 'next/server'; +const stripe = new Stripe(process.env.STRPE_API_SECRET_KEY) +/* const stripe = await loadStripe(process.env.STRIPE_API_PUBLIC_KEY); */ + +const formatAmountStripe = (amount) => { + return Math.round(amount * 100) +} + +export async function POST(req) { + const params = { + submit_type: 'subscription', + payment_method_types: ['card'], + line_items: [ + { + price_data: { + currency: "usd", + product_data: { + name: "Pro subscription", + }, + unit_amount: formatAmountStripe(10), + reccuring: { + interval: "month", + interval_count: 1, + }, + }, + quantity: 1, + }, + ], + success_url: `${req.headers.origin}/result?session_id={CHECKOUT_SESSION_ID}`, + cancel_url: `${req.headers.origin}/result?session_id={CHECKOUT_SESSION_ID}`, + } + + const checkoutSession = await stripe.checkout.sessions.create(params); + + return NextResponse.json(checkoutSession, { + status: 200, + }) + +} + + diff --git a/app/api/generate/route.js b/app/api/generate/route.js index e69de29..bcf076b 100644 --- a/app/api/generate/route.js +++ b/app/api/generate/route.js @@ -0,0 +1,44 @@ +import { IM_Fell_French_Canon } from "next/font/google"; +import { NextResponse } from "next/server"; +import OpenAI, { fileFromPath } from "openai" + +const systemPrompt = `You are a flashcard creator. Your primary goal is to generate clear, concise, and effective flashcards that help users learn and retain information. Each flashcard should focus on a single concept or question, with a brief, accurate answer or explanation. Ensure that the language used is simple and accessible, catering to the user’s level of knowledge on the subject. Whenever possible, include examples, analogies, or mnemonics to aid in memory retention. Your flashcards should be well-organized and categorized, making it easy for users to review and study the material systematically. +1. Understand the Topic: Begin by thoroughly understanding the subject matter or topic for which the flashcards are being created. +2. Identify Key Concepts: Break down the topic into its core concepts, terms, or questions that are essential for understanding the material. +3. Formulate Questions: For each key concept, create a clear and concise question that targets the specific knowledge or skill the user needs to acquire. +4. Craft Answers: Write brief, accurate, and focused answers or explanations for each question, ensuring that the language is easy to understand. +5. Use Examples: Where applicable, include examples that illustrate the concept or answer in a practical, relatable way. +6. Incorporate Mnemonics: Add mnemonic devices, analogies, or memory aids to help users remember challenging or complex information. +7. Ensure Clarity: Review each flashcard to ensure that the questions and answers are clear, unambiguous, and directly related to the key concepts. +8. Organize Systematically: Arrange the flashcards in a logical order, grouping related concepts together to facilitate easier learning and review. +9. Review and Revise: After creating the flashcards, review them to check for accuracy, clarity, and completeness. Make any necessary revisions to improve their effectiveness. +10. Aim to create a balanced set of flashcards that covers the topics comprehensively + +Return in the following JSON format +{ + "flashcards":| + { + "front": str, + "back": str + }| +} +`; + +export async function POST(req){ + const openai = OpenAI() + const data = await req.text() + + const completion = await openai.chat.completion.create({ + messages: [ + {role: "system", content: systemPrompt}, + {role: "user", content: data}, + ], + + model: "gpt-4o", + respnse_format:{type: "json_object"} + }) + + const flashcards = JSON.parse(completion.choices[0].message.content) + + return NextResponse.json(flashcards.flashcard) +} \ No newline at end of file diff --git a/app/layout.js b/app/layout.js index 9aef1df..0b3bf91 100644 --- a/app/layout.js +++ b/app/layout.js @@ -1,6 +1,8 @@ import { Inter } from "next/font/google"; import "./globals.css"; +import { ClerkProvider } from '@clerk/nextjs' + const inter = Inter({ subsets: ["latin"] }); export const metadata = { @@ -10,8 +12,11 @@ export const metadata = { export default function RootLayout({ children }) { return ( - - {children} - + + + {children} + + + ); } diff --git a/app/page.js b/app/page.js index 532155e..4c7d4e0 100644 --- a/app/page.js +++ b/app/page.js @@ -1,94 +1,31 @@ import Image from "next/image"; +import getStripe from "@/utils/get_stripe" +import { SignedIn, SignedOut, UserButton } from "@clerk/nextjs"; + +import {Toolbar, Typography, Container, AppBar, Button } from "@mui/material"; export default function Home() { return ( -
-
-

- Get started by editing  - app/page.js -

-
- - By{" "} - Vercel Logo - -
-
- -
- Next.js Logo -
- -
- -

- Docs -> -

-

Find in-depth information about Next.js features and API.

-
- - -

- Learn -> -

-

Learn about Next.js in an interactive course with quizzes!

-
- - -

- Templates -> -

-

Explore starter templates for Next.js.

-
+ + + + AI Flashcard + + + - -

- Deploy -> -

-

- Instantly deploy your Next.js site to a shareable URL with Vercel. -

-
-
-
+ + + AI Flashcards + + + + + + + + + + ); } diff --git a/middleware.ts b/middleware.ts new file mode 100644 index 0000000..3c977e9 --- /dev/null +++ b/middleware.ts @@ -0,0 +1,12 @@ +import { clerkMiddleware } from "@clerk/nextjs/server"; + +export default clerkMiddleware(); + +export const config = { + matcher: [ + // Skip Next.js internals and all static files, unless found in search params + '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)', + // Always run for API routes + '/(api|trpc)(.*)', + ], +}; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index de7cfaf..7116239 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@stripe/stripe-js": "^4.3.0", "firebase": "^10.13.0", "next": "14.2.5", + "openai": "^4.55.8", "react": "^18", "react-dom": "^18" }, @@ -1841,6 +1842,16 @@ "undici-types": "~6.18.2" } }, + "node_modules/@types/node-fetch": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz", + "integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, "node_modules/@types/parse-json": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", @@ -2013,6 +2024,18 @@ "dev": true, "license": "ISC" }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/acorn": { "version": "8.12.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", @@ -2036,6 +2059,18 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/agentkeepalive": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", + "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", + "license": "MIT", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -2269,6 +2304,12 @@ "dev": true, "license": "MIT" }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -2512,6 +2553,18 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2731,6 +2784,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -3458,6 +3520,15 @@ "node": ">=0.10.0" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3664,6 +3735,39 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data-encoder": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", + "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", + "license": "MIT" + }, + "node_modules/formdata-node": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", + "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", + "license": "MIT", + "dependencies": { + "node-domexception": "1.0.0", + "web-streams-polyfill": "4.0.0-beta.3" + }, + "engines": { + "node": ">= 12.20" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -4019,6 +4123,15 @@ "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", "license": "MIT" }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, "node_modules/idb": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", @@ -4793,6 +4906,27 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -4917,6 +5051,45 @@ "tslib": "^2.0.3" } }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -5062,6 +5235,47 @@ "wrappy": "1" } }, + "node_modules/openai": { + "version": "4.55.8", + "resolved": "https://registry.npmjs.org/openai/-/openai-4.55.8.tgz", + "integrity": "sha512-k40u3QCrP10bL6c5b6b+lffJzG4QBSAqr6QDxXPThTuWaQmlKztaeW+NmSkJTsB7Ho1Cwq+IkdZZs1ZIlScXMA==", + "license": "Apache-2.0", + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7" + }, + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/openai/node_modules/@types/node": { + "version": "18.19.44", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.44.tgz", + "integrity": "sha512-ZsbGerYg72WMXUIE9fYxtvfzLEuq6q8mKERdWFnqTmOvudMxnz+CBNRoOwJ2kNpFOncrKjT1hZwxjlFgQ9qvQA==", + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/openai/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "license": "MIT" + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -6147,6 +6361,12 @@ "node": ">=8.0" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, "node_modules/ts-api-utils": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", @@ -6350,6 +6570,21 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/web-streams-polyfill": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", + "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, "node_modules/websocket-driver": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", @@ -6373,6 +6608,16 @@ "node": ">=0.8.0" } }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 578e532..1ac2fab 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "@stripe/stripe-js": "^4.3.0", "firebase": "^10.13.0", "next": "14.2.5", + "openai": "^4.55.8", "react": "^18", "react-dom": "^18" }, diff --git a/utils/get_stripe.js b/utils/get_stripe.js new file mode 100644 index 0000000..ffa3b99 --- /dev/null +++ b/utils/get_stripe.js @@ -0,0 +1,11 @@ +import { loadStripe } from "@stripe/stripe-js"; + +let stripePromise +const getStripe = () => { + if(!stripePromise){ + stripePromise = loadStripe(process.env.STRIPE_API_PUBLIC_KEY); + } + return stripePromise +} + +export default getStripe; \ No newline at end of file