From e7e7e0c9501e9f88d04f0709aa7200e17e9e6604 Mon Sep 17 00:00:00 2001 From: Sonny Date: Tue, 30 May 2023 01:17:50 +0200 Subject: [PATCH] feat(wip): add support for multi user in instance --- .../migration.sql | 17 +++++++ .../migration.sql | 11 +++++ prisma/schema.prisma | 48 ++++++++++++------- src/_middleware.ts | 29 +++++++++++ src/constants/paths.ts | 13 ++--- src/pages/api/auth/[...nextauth].ts | 45 +++++++++++++---- src/pages/api/category/create.ts | 19 +++++++- src/pages/signin.tsx | 8 ++-- 8 files changed, 153 insertions(+), 37 deletions(-) create mode 100644 prisma/migrations/20230529174707_add_category_link_author/migration.sql create mode 100644 prisma/migrations/20230529223602_add_link_author/migration.sql create mode 100644 src/_middleware.ts diff --git a/prisma/migrations/20230529174707_add_category_link_author/migration.sql b/prisma/migrations/20230529174707_add_category_link_author/migration.sql new file mode 100644 index 0000000..776ac93 --- /dev/null +++ b/prisma/migrations/20230529174707_add_category_link_author/migration.sql @@ -0,0 +1,17 @@ +/* + Warnings: + + - You are about to drop the column `nextCategoryId` on the `category` table. All the data in the column will be lost. + - You are about to drop the column `nextLinkId` on the `link` table. All the data in the column will be lost. + - Added the required column `authorId` to the `category` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE `category` DROP COLUMN `nextCategoryId`, + ADD COLUMN `authorId` INTEGER NOT NULL; + +-- AlterTable +ALTER TABLE `link` DROP COLUMN `nextLinkId`; + +-- AddForeignKey +ALTER TABLE `category` ADD CONSTRAINT `category_authorId_fkey` FOREIGN KEY (`authorId`) REFERENCES `user`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/migrations/20230529223602_add_link_author/migration.sql b/prisma/migrations/20230529223602_add_link_author/migration.sql new file mode 100644 index 0000000..a275419 --- /dev/null +++ b/prisma/migrations/20230529223602_add_link_author/migration.sql @@ -0,0 +1,11 @@ +/* + Warnings: + + - Added the required column `authorId` to the `link` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE `link` ADD COLUMN `authorId` INTEGER NOT NULL; + +-- AddForeignKey +ALTER TABLE `link` ADD CONSTRAINT `link_authorId_fkey` FOREIGN KEY (`authorId`) REFERENCES `user`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 5544f00..da2cd28 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -6,14 +6,19 @@ generator client { } datasource db { - provider = "mysql" - url = env("DATABASE_URL") + provider = "mysql" + url = env("DATABASE_URL") + shadowDatabaseUrl = env("SHADOW_DATABASE_URL") } model User { - id Int @id @default(autoincrement()) - google_id String @unique - email String @unique + id Int @id @default(autoincrement()) + google_id String @unique + email String @unique + + categories Category[] + links Link[] + createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@ -21,26 +26,33 @@ model User { } model Category { - id Int @id @default(autoincrement()) - name String @unique @db.VarChar(255) - links Link[] - nextCategoryId Int @default(0) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + id Int @id @default(autoincrement()) + name String @unique @db.VarChar(255) + links Link[] + + author User @relation(fields: [authorId], references: [id]) + authorId Int + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt @@map("category") } model Link { - id Int @id @default(autoincrement()) - name String @db.VarChar(255) - url String @db.Text + id Int @id @default(autoincrement()) + name String @db.VarChar(255) + url String @db.Text + category Category @relation(fields: [categoryId], references: [id]) categoryId Int - nextLinkId Int @default(0) - favorite Boolean @default(false) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + + author User @relation(fields: [authorId], references: [id]) + authorId Int + + favorite Boolean @default(false) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt @@map("link") } diff --git a/src/_middleware.ts b/src/_middleware.ts new file mode 100644 index 0000000..59120f4 --- /dev/null +++ b/src/_middleware.ts @@ -0,0 +1,29 @@ +import PATHS from "constants/paths"; + +export { default } from "next-auth/middleware"; + +// WAIT: for fix - Next.js@13.4.4 seems to be broken +// cf: https://github.com/nextauthjs/next-auth/issues/7650 +// (this file must renamed "middleware.ts") + +export const config = { + matcher: [ + PATHS.HOME, + + PATHS.LINK.CREATE, + PATHS.LINK.EDIT, + PATHS.LINK.REMOVE, + + PATHS.CATEGORY.CREATE, + PATHS.CATEGORY.EDIT, + PATHS.CATEGORY.REMOVE, + + PATHS.API.CATEGORY.CREATE, + PATHS.API.CATEGORY.EDIT, + PATHS.API.CATEGORY.REMOVE, + + PATHS.API.LINK.CREATE, + PATHS.API.LINK.EDIT, + PATHS.API.LINK.REMOVE, + ], +}; diff --git a/src/constants/paths.ts b/src/constants/paths.ts index 133dcb7..ad99d54 100644 --- a/src/constants/paths.ts +++ b/src/constants/paths.ts @@ -1,5 +1,6 @@ const PATHS = { LOGIN: "/signin", + LOGOUT: "/signout", HOME: "/", CATEGORY: { CREATE: "/category/create", @@ -13,14 +14,14 @@ const PATHS = { }, API: { CATEGORY: { - CREATE: "/category/create", - EDIT: "/category/edit", - REMOVE: "/category/remove", + CREATE: "/api/category/create", + EDIT: "/api/category/edit", + REMOVE: "/api/category/remove", }, LINK: { - CREATE: "/link/create", - EDIT: "/link/edit", - REMOVE: "/link/remove", + CREATE: "/api/link/create", + EDIT: "/api/link/edit", + REMOVE: "/api/link/remove", }, }, NOT_FOUND: "/404", diff --git a/src/pages/api/auth/[...nextauth].ts b/src/pages/api/auth/[...nextauth].ts index 35e4061..f0aab3a 100644 --- a/src/pages/api/auth/[...nextauth].ts +++ b/src/pages/api/auth/[...nextauth].ts @@ -1,11 +1,12 @@ import { PrismaClient } from "@prisma/client"; import PATHS from "constants/paths"; -import NextAuth from "next-auth"; +import NextAuth, { NextAuthOptions } from "next-auth"; import GoogleProvider from "next-auth/providers/google"; const prisma = new PrismaClient(); -export default NextAuth({ +// TODO: refactor auth +export const authOptions = { providers: [ GoogleProvider({ clientId: process.env.GOOGLE_CLIENT_ID, @@ -20,24 +21,33 @@ export default NextAuth({ }), ], callbacks: { + async session({ session }) { + // check if stored in session still exist in db + await prisma.user.findFirstOrThrow({ + where: { email: session.user.email }, + }); + return session; + }, async signIn({ account: accountParam, profile }) { - // TODO: Auth console.log( - "Connexion via", - accountParam.provider, - accountParam.providerAccountId, - profile.email, - profile.name + "[AUTH]", + "User", + profile.name, + "attempt to log in with", + accountParam.provider ); if (accountParam.provider !== "google") { + console.log("[AUTH]", "User", profile.name, "rejeced : bad provider"); return ( PATHS.LOGIN + "?error=" + encodeURI("Authentitifcation via Google requise") ); } + const email = profile?.email; if (email === "") { + console.log("[AUTH]", "User", profile.name, "rejeced : missing email"); return ( PATHS.LOGIN + "?error=" + @@ -48,6 +58,12 @@ export default NextAuth({ } const googleId = profile?.sub; if (googleId === "") { + console.log( + "[AUTH]", + "User", + profile.name, + "rejeced : missing google id" + ); return ( PATHS.LOGIN + "?error=" + @@ -74,6 +90,13 @@ export default NextAuth({ }); return true; } + + console.log( + "[AUTH]", + "User", + profile.name, + "rejeced : not authorized" + ); return ( PATHS.LOGIN + "?error=" + @@ -82,9 +105,11 @@ export default NextAuth({ ) ); } else { + console.log("[AUTH]", "User", profile.name, "success"); return true; } } catch (error) { + console.log("[AUTH]", "User", profile.name, "unhandled error"); console.error(error); return ( PATHS.LOGIN + @@ -97,8 +122,10 @@ export default NextAuth({ pages: { signIn: PATHS.LOGIN, error: PATHS.LOGIN, + signOut: PATHS.LOGOUT, }, session: { maxAge: 60 * 60 * 6, // Session de 6 heures }, -}); +} as NextAuthOptions; +export default NextAuth(authOptions); diff --git a/src/pages/api/category/create.ts b/src/pages/api/category/create.ts index f96f66d..ccd63f6 100644 --- a/src/pages/api/category/create.ts +++ b/src/pages/api/category/create.ts @@ -1,10 +1,15 @@ import { NextApiRequest, NextApiResponse } from "next"; +import { getServerSession } from "next-auth/next"; +import { authOptions } from "../auth/[...nextauth]"; + import prisma from "utils/prisma"; export default async function handler( req: NextApiRequest, res: NextApiResponse ) { + const session = await getServerSession(req, res, authOptions); // je sais plus d'où ça vient + console.log("session", session); const name = req.body?.name as string; if (!name) { @@ -30,8 +35,20 @@ export default async function handler( } try { + const { id: authorId } = await prisma.user.findFirst({ + where: { + email: session.user.email, + }, + select: { + id: true, + }, + }); + if (!authorId) { + throw new Error("Unable to find user"); + } + const category = await prisma.category.create({ - data: { name }, + data: { name, authorId }, }); return res.status(200).send({ success: "Catégorie créée avec succès", diff --git a/src/pages/signin.tsx b/src/pages/signin.tsx index 574b3b7..54dd4f7 100644 --- a/src/pages/signin.tsx +++ b/src/pages/signin.tsx @@ -1,5 +1,6 @@ +import { getServerSession } from "next-auth/next"; import { Provider } from "next-auth/providers"; -import { getProviders, getSession, signIn } from "next-auth/react"; +import { getProviders, signIn } from "next-auth/react"; import { NextSeo } from "next-seo"; import Link from "next/link"; @@ -7,6 +8,7 @@ import MessageManager from "components/MessageManager/MessageManager"; import PATHS from "constants/paths"; import styles from "styles/login.module.scss"; +import { authOptions } from "./api/auth/[...nextauth]"; interface SignInProps { providers: Provider[]; @@ -36,8 +38,8 @@ export default function SignIn({ providers }: SignInProps) { ); } -export async function getServerSideProps(context) { - const session = await getSession(context); +export async function getServerSideProps({ req, res }) { + const session = await getServerSession(req, res, authOptions); if (session) { return { redirect: {