diff --git a/app/api/auth/[...nextauth]/route.ts b/app/api/auth/[...nextauth]/route.ts new file mode 100644 index 0000000..e4b4310 --- /dev/null +++ b/app/api/auth/[...nextauth]/route.ts @@ -0,0 +1,5 @@ +import { handlers } from '@/auth'; + +export const { GET, POST } = handlers; + +export const runtime = 'edge'; diff --git a/app/sign-in/page.tsx b/app/sign-in/page.tsx new file mode 100644 index 0000000..b98cc25 --- /dev/null +++ b/app/sign-in/page.tsx @@ -0,0 +1,77 @@ +import { GithubIcon } from 'lucide-react'; +import Image from 'next/image'; +import React from 'react'; + +import { signIn } from '@/auth'; +import { Button } from '@/components/ui/button'; + +const SignInPage = () => { + return ( +
+
+

+ Welcome Back! +

+

+ Sign in to access your personalized dashboard, manage your tasks, and + stay on top of your projects. Let's get started! +

+ hero +
+ +
+

+ Sign in to your account +

+
{ + 'use server'; + + await signIn('google', { redirectTo: '/dashboard' }); + }} + > + +
+
{ + 'use server'; + + await signIn('github', { redirectTo: '/dashboard' }); + }} + > + +
+
+
+ ); +}; + +export default SignInPage; diff --git a/auth.ts b/auth.ts new file mode 100644 index 0000000..a54d73a --- /dev/null +++ b/auth.ts @@ -0,0 +1,18 @@ +import { PrismaAdapter } from '@auth/prisma-adapter'; +import NextAuth from 'next-auth'; +import Github from 'next-auth/providers/github'; +import Google from 'next-auth/providers/google'; + +import prisma from './prisma/client'; + +export const { handlers, signIn, signOut, auth } = NextAuth({ + providers: [Google, Github], + adapter: PrismaAdapter(prisma), + pages: { signIn: '/sign-in' }, + session: { strategy: 'jwt' }, + callbacks: { + authorized: async ({ auth: session }) => { + return !!session; + }, + }, +}); diff --git a/middleware.ts b/middleware.ts new file mode 100644 index 0000000..5966cee --- /dev/null +++ b/middleware.ts @@ -0,0 +1,16 @@ +import { NextResponse } from 'next/server'; + +import { auth } from '@/auth'; + +export default auth((req) => { + if (!req.auth && !['/sign-in', '/'].includes(req.nextUrl.pathname)) { + const newUrl = new URL('/sign-in', req.nextUrl.origin); + return NextResponse.redirect(newUrl); + } + + return NextResponse.next(); +}); + +export const config = { + matcher: ['/dashboard/(.*)'], +}; diff --git a/package.json b/package.json index c898079..bd4ab80 100644 --- a/package.json +++ b/package.json @@ -15,10 +15,12 @@ "node": ">=18.18.0" }, "dependencies": { + "@auth/prisma-adapter": "^2.4.2", "@dnd-kit/core": "^6.1.0", "@dnd-kit/utilities": "^3.2.2", "@hookform/resolvers": "^3.9.0", - "@prisma/client": "^5.17.0", + "@prisma/client": "5.18.0", + "@prisma/extension-accelerate": "^1.1.0", "@radix-ui/react-alert-dialog": "^1.1.1", "@radix-ui/react-dropdown-menu": "^2.1.1", "@radix-ui/react-label": "^2.1.0", @@ -34,6 +36,7 @@ "lucide-react": "^0.414.0", "nanoid": "^5.0.7", "next": "14.2.5", + "next-auth": "^5.0.0-beta.9", "nextjs-toploader": "^1.6.12", "prisma": "^5.17.0", "react": "^18", diff --git a/prisma/client.ts b/prisma/client.ts index e5d7482..4bc0ef8 100644 --- a/prisma/client.ts +++ b/prisma/client.ts @@ -1,7 +1,8 @@ -import { PrismaClient } from "@prisma/client"; +import { PrismaClient } from '@prisma/client/edge'; +import { withAccelerate } from '@prisma/extension-accelerate'; const prismaClientSingleton = () => { - return new PrismaClient(); + return new PrismaClient().$extends(withAccelerate()); }; declare const globalThis: { @@ -12,4 +13,4 @@ const prisma = globalThis.prismaGlobal ?? prismaClientSingleton(); export default prisma; -if (process.env.NODE_ENV !== "production") globalThis.prismaGlobal = prisma; +if (process.env.NODE_ENV !== 'production') globalThis.prismaGlobal = prisma; diff --git a/prisma/migrations/20240817143611_added_user_models/migration.sql b/prisma/migrations/20240817143611_added_user_models/migration.sql new file mode 100644 index 0000000..9a11d83 --- /dev/null +++ b/prisma/migrations/20240817143611_added_user_models/migration.sql @@ -0,0 +1,61 @@ +-- CreateTable +CREATE TABLE "User" ( + "id" TEXT NOT NULL, + "name" TEXT, + "email" TEXT NOT NULL, + "emailVerified" TIMESTAMP(3), + "image" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "User_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Account" ( + "userId" TEXT NOT NULL, + "type" TEXT NOT NULL, + "provider" TEXT NOT NULL, + "providerAccountId" TEXT NOT NULL, + "refresh_token" TEXT, + "access_token" TEXT, + "expires_at" INTEGER, + "token_type" TEXT, + "scope" TEXT, + "id_token" TEXT, + "session_state" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Account_pkey" PRIMARY KEY ("provider","providerAccountId") +); + +-- CreateTable +CREATE TABLE "Session" ( + "sessionToken" TEXT NOT NULL, + "userId" TEXT NOT NULL, + "expires" TIMESTAMP(3) NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL +); + +-- CreateTable +CREATE TABLE "VerificationToken" ( + "identifier" TEXT NOT NULL, + "token" TEXT NOT NULL, + "expires" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "VerificationToken_pkey" PRIMARY KEY ("identifier","token") +); + +-- CreateIndex +CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); + +-- CreateIndex +CREATE UNIQUE INDEX "Session_sessionToken_key" ON "Session"("sessionToken"); + +-- AddForeignKey +ALTER TABLE "Account" ADD CONSTRAINT "Account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Session" ADD CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20240817154924_jugar/migration.sql b/prisma/migrations/20240817154924_jugar/migration.sql new file mode 100644 index 0000000..7212c74 --- /dev/null +++ b/prisma/migrations/20240817154924_jugar/migration.sql @@ -0,0 +1,13 @@ +/* + Warnings: + + - The primary key for the `Account` table will be changed. If it partially fails, the table could be left without primary key constraint. + - You are about to drop the column `providerAccountId` on the `Account` table. All the data in the column will be lost. + - Added the required column `provider_account_id` to the `Account` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "Account" DROP CONSTRAINT "Account_pkey", +DROP COLUMN "providerAccountId", +ADD COLUMN "provider_account_id" TEXT NOT NULL, +ADD CONSTRAINT "Account_pkey" PRIMARY KEY ("provider", "provider_account_id"); diff --git a/prisma/migrations/20240817155853_revert_jugar/migration.sql b/prisma/migrations/20240817155853_revert_jugar/migration.sql new file mode 100644 index 0000000..ab2ee66 --- /dev/null +++ b/prisma/migrations/20240817155853_revert_jugar/migration.sql @@ -0,0 +1,13 @@ +/* + Warnings: + + - The primary key for the `Account` table will be changed. If it partially fails, the table could be left without primary key constraint. + - You are about to drop the column `provider_account_id` on the `Account` table. All the data in the column will be lost. + - Added the required column `providerAccountId` to the `Account` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "Account" DROP CONSTRAINT "Account_pkey", +DROP COLUMN "provider_account_id", +ADD COLUMN "providerAccountId" TEXT NOT NULL, +ADD CONSTRAINT "Account_pkey" PRIMARY KEY ("provider", "providerAccountId"); diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 833e30c..531b655 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -1,5 +1,6 @@ generator client { - provider = "prisma-client-js" + provider = "prisma-client-js" + previewFeatures = ["driverAdapters"] } datasource db { @@ -48,3 +49,56 @@ enum Label { FEATURE DOCUMENTATION } + +// users + +model User { + id String @id @default(cuid()) + name String? + email String @unique + emailVerified DateTime? + image String? + accounts Account[] + sessions Session[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model Account { + userId String + type String + provider String + providerAccountId String + refresh_token String? + access_token String? + expires_at Int? + token_type String? + scope String? + id_token String? + session_state String? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@id([provider, providerAccountId]) +} + +model Session { + sessionToken String @unique + userId String + expires DateTime + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model VerificationToken { + identifier String + token String + expires DateTime + + @@id([identifier, token]) +} diff --git a/public/google_icon.svg b/public/google_icon.svg new file mode 100644 index 0000000..c0669b3 --- /dev/null +++ b/public/google_icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/sign-in-image.svg b/public/sign-in-image.svg new file mode 100644 index 0000000..3d15845 --- /dev/null +++ b/public/sign-in-image.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index af76f05..39654d3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,6 +7,26 @@ resolved "https://registry.yarnpkg.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz#7bf68b20c0a350f936915fcae06f58e32007ce30" integrity sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw== +"@auth/core@0.34.2": + version "0.34.2" + resolved "https://registry.yarnpkg.com/@auth/core/-/core-0.34.2.tgz#645fd1f972842ca473d110a34a5a36838209bf10" + integrity sha512-KywHKRgLiF3l7PLyL73fjLSIBe1YNcA6sMeew4yMP6cfCWGXZrkkXd32AjRi1hlJ9nvovUBGZHvbn+LijO6ZeQ== + dependencies: + "@panva/hkdf" "^1.1.1" + "@types/cookie" "0.6.0" + cookie "0.6.0" + jose "^5.1.3" + oauth4webapi "^2.10.4" + preact "10.11.3" + preact-render-to-string "5.2.3" + +"@auth/prisma-adapter@^2.4.2": + version "2.4.2" + resolved "https://registry.yarnpkg.com/@auth/prisma-adapter/-/prisma-adapter-2.4.2.tgz#e1a30de0a3df2fbc6eda348b83b9338672a33b08" + integrity sha512-QQwnGYfDiyTcAxMVhTrim+lLFFA3TKq3nIrbPtGZXlkiuNQ5t0rUg//Km7Wv21pD5bxhy4aRPlfq7TdFKk3XIw== + dependencies: + "@auth/core" "0.34.2" + "@dnd-kit/accessibility@^3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@dnd-kit/accessibility/-/accessibility-3.1.0.tgz#1054e19be276b5f1154ced7947fc0cb5d99192e0" @@ -235,6 +255,11 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@panva/hkdf@^1.1.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@panva/hkdf/-/hkdf-1.2.1.tgz#cb0d111ef700136f4580349ff0226bf25c853f23" + integrity sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw== + "@pkgjs/parseargs@^0.11.0": version "0.11.0" resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" @@ -245,7 +270,7 @@ resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31" integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA== -"@prisma/client@^5.17.0": +"@prisma/client@5.18.0": version "5.18.0" resolved "https://registry.yarnpkg.com/@prisma/client/-/client-5.18.0.tgz#526e4281a448f214c0ff81d65c39243608c98294" integrity sha512-BWivkLh+af1kqC89zCJYkHsRcyWsM8/JHpsDMM76DjP3ZdEquJhXa4IeX+HkWPnwJ5FanxEJFZZDTWiDs/Kvyw== @@ -270,6 +295,11 @@ "@prisma/fetch-engine" "5.18.0" "@prisma/get-platform" "5.18.0" +"@prisma/extension-accelerate@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@prisma/extension-accelerate/-/extension-accelerate-1.1.0.tgz#c82bb5c82185630912a5272e01f4c7b76c8a9d87" + integrity sha512-sESjhBZ4ywQjAVpKzsfhxyNu+9txIM5I6M1MPBaJBq/xDlqmniIAhlwIEt9KLtO80zqPxqbZYes18zrkgYqNiQ== + "@prisma/fetch-engine@5.18.0": version "5.18.0" resolved "https://registry.yarnpkg.com/@prisma/fetch-engine/-/fetch-engine-5.18.0.tgz#5b343e2b36b27e2713901ddd032ddd6932b3d55f" @@ -713,6 +743,11 @@ dependencies: "@types/tern" "*" +"@types/cookie@0.6.0": + version "0.6.0" + resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.6.0.tgz#eac397f28bf1d6ae0ae081363eca2f425bedf0d5" + integrity sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA== + "@types/debug@^4.0.0": version "4.1.12" resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.12.tgz#a155f21690871953410df4b6b6f53187f0500917" @@ -1374,6 +1409,11 @@ confusing-browser-globals@^1.0.10: resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz#ae40e9b57cdd3915408a2805ebd3a5585608dc81" integrity sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA== +cookie@0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051" + integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== + cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -2697,6 +2737,11 @@ jiti@^1.21.0: resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.6.tgz#6c7f7398dd4b3142767f9a168af2f317a428d268" integrity sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w== +jose@^5.1.3: + version "5.6.3" + resolved "https://registry.yarnpkg.com/jose/-/jose-5.6.3.tgz#415688bc84875461c86dfe271ea6029112a23e27" + integrity sha512-1Jh//hEEwMhNYPDDLwXHa2ePWgWiFNNUadVmguAAw2IJ6sj9mNxV5tGXJNqlMkJAybF6Lgw1mISDxTePP/187g== + "js-tokens@^3.0.0 || ^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -3263,6 +3308,13 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== +next-auth@^5.0.0-beta.9: + version "5.0.0-beta.20" + resolved "https://registry.yarnpkg.com/next-auth/-/next-auth-5.0.0-beta.20.tgz#949d85fbbe3fe39b51808b8172deffc0b41e33d7" + integrity sha512-+48SjV9k9AtUU3JbEIa4PXNjKIewfFjVGL7Xs2RKkuQ5QqegDNIQiIG8sLk6/qo7RTScQYIGKgeQ5IuQRtrTQg== + dependencies: + "@auth/core" "0.34.2" + next@14.2.5: version "14.2.5" resolved "https://registry.yarnpkg.com/next/-/next-14.2.5.tgz#afe4022bb0b752962e2205836587a289270efbea" @@ -3311,6 +3363,11 @@ nprogress@^0.2.0: resolved "https://registry.yarnpkg.com/nprogress/-/nprogress-0.2.0.tgz#cb8f34c53213d895723fcbab907e9422adbcafb1" integrity sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA== +oauth4webapi@^2.10.4: + version "2.11.1" + resolved "https://registry.yarnpkg.com/oauth4webapi/-/oauth4webapi-2.11.1.tgz#8d79e6b0d54ead203094f185a11031f3f9978465" + integrity sha512-aNzOnL98bL6izG97zgnZs1PFEyO4WDVRhz2Pd066NPak44w5ESLRCYmJIyey8avSBPOMtBjhF3ZDDm7bIb7UOg== + object-assign@^4.0.1, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -3597,6 +3654,18 @@ postcss@^8, postcss@^8.4.23, postcss@^8.4.4: picocolors "^1.0.1" source-map-js "^1.2.0" +preact-render-to-string@5.2.3: + version "5.2.3" + resolved "https://registry.yarnpkg.com/preact-render-to-string/-/preact-render-to-string-5.2.3.tgz#23d17376182af720b1060d5a4099843c7fe92fe4" + integrity sha512-aPDxUn5o3GhWdtJtW0svRC2SS/l8D9MAgo2+AWml+BhDImb27ALf04Q2d+AHqUUOc6RdSXFIBVa2gxzgMKgtZA== + dependencies: + pretty-format "^3.8.0" + +preact@10.11.3: + version "10.11.3" + resolved "https://registry.yarnpkg.com/preact/-/preact-10.11.3.tgz#8a7e4ba19d3992c488b0785afcc0f8aa13c78d19" + integrity sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg== + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -3619,6 +3688,11 @@ prettier@^3.3.3: resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.3.tgz#30c54fe0be0d8d12e6ae61dbb10109ea00d53105" integrity sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew== +pretty-format@^3.8.0: + version "3.8.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-3.8.0.tgz#bfbed56d5e9a776645f4b1ff7aa1a3ac4fa3c385" + integrity sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew== + prisma@^5.17.0: version "5.18.0" resolved "https://registry.yarnpkg.com/prisma/-/prisma-5.18.0.tgz#5ef69c802a075b7596231ea57003496873610b9e"