mirror of
https://github.com/LucasVbr/meeting-app.git
synced 2026-05-13 17:21:53 +00:00
Edit Crud, add loaders during authentication
Took 2 hours 49 minutes
This commit is contained in:
Generated
+2006
-1
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,7 @@
|
|||||||
"@chakra-ui/react": "^2.5.1",
|
"@chakra-ui/react": "^2.5.1",
|
||||||
"@emotion/react": "^11.10.6",
|
"@emotion/react": "^11.10.6",
|
||||||
"@emotion/styled": "^11.10.6",
|
"@emotion/styled": "^11.10.6",
|
||||||
|
"@premieroctet/next-crud": "^2.2.0",
|
||||||
"@prisma/client": "^4.11.0",
|
"@prisma/client": "^4.11.0",
|
||||||
"@types/react": "18.0.28",
|
"@types/react": "18.0.28",
|
||||||
"@types/react-dom": "18.0.11",
|
"@types/react-dom": "18.0.11",
|
||||||
|
|||||||
@@ -43,15 +43,6 @@ model User {
|
|||||||
OtherUserLikes User[] @relation("Likes", fields: [OtherUserLikesID], references: [id])
|
OtherUserLikes User[] @relation("Likes", fields: [OtherUserLikesID], references: [id])
|
||||||
}
|
}
|
||||||
|
|
||||||
model Chat {
|
|
||||||
id String @id @default(auto()) @map("_id") @db.ObjectId
|
|
||||||
messages Message[]
|
|
||||||
|
|
||||||
// Les utilisateurs qui ont un chat en commun
|
|
||||||
User User[] @relation(fields: [UserId], references: [id])
|
|
||||||
UserId String[] @db.ObjectId
|
|
||||||
}
|
|
||||||
|
|
||||||
model Passion {
|
model Passion {
|
||||||
id String @id @default(auto()) @map("_id") @db.ObjectId
|
id String @id @default(auto()) @map("_id") @db.ObjectId
|
||||||
name String
|
name String
|
||||||
@@ -61,6 +52,15 @@ model Passion {
|
|||||||
User User[] @relation(fields: [UserId], references: [id])
|
User User[] @relation(fields: [UserId], references: [id])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model Chat {
|
||||||
|
id String @id @default(auto()) @map("_id") @db.ObjectId
|
||||||
|
messages Message[]
|
||||||
|
|
||||||
|
// Les utilisateurs qui ont un chat en commun
|
||||||
|
User User[] @relation(fields: [UserId], references: [id])
|
||||||
|
UserId String[] @db.ObjectId
|
||||||
|
}
|
||||||
|
|
||||||
model Message {
|
model Message {
|
||||||
id String @id @default(auto()) @map("_id") @db.ObjectId
|
id String @id @default(auto()) @map("_id") @db.ObjectId
|
||||||
message String
|
message String
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {LoginData} from '@/models/form/LoginData';
|
|||||||
export default function LoginForm() {
|
export default function LoginForm() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [loginData, setLoginData] = useState(new LoginData());
|
const [loginData, setLoginData] = useState(new LoginData());
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [invalidInput, setInvalidInput] = useState(false);
|
const [invalidInput, setInvalidInput] = useState(false);
|
||||||
|
|
||||||
const buttonWidth = {base: '100%', md: 'unset'};
|
const buttonWidth = {base: '100%', md: 'unset'};
|
||||||
@@ -26,11 +27,15 @@ export default function LoginForm() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
await signIn('credentials',
|
setIsLoading(true)
|
||||||
{...loginData, redirect: false}).then((res) => {
|
signIn('credentials', {...loginData, redirect: false})
|
||||||
|
.then((res: unknown) => {
|
||||||
const {ok: connexionSuccess} = res as SignInResponse;
|
const {ok: connexionSuccess} = res as SignInResponse;
|
||||||
|
|
||||||
if (!connexionSuccess) setInvalidInput(true);
|
if (!connexionSuccess) {
|
||||||
|
setIsLoading(false);
|
||||||
|
setInvalidInput(true);
|
||||||
|
}
|
||||||
else router.push('/dashboard');
|
else router.push('/dashboard');
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -76,7 +81,7 @@ export default function LoginForm() {
|
|||||||
justifyContent={'space-between'}>
|
justifyContent={'space-between'}>
|
||||||
<Button onClick={() => router.push('/')}
|
<Button onClick={() => router.push('/')}
|
||||||
w={buttonWidth}>Retour</Button>
|
w={buttonWidth}>Retour</Button>
|
||||||
<Button onClick={handleSubmit}
|
<Button isLoading={isLoading} onClick={handleSubmit}
|
||||||
w={buttonWidth} colorScheme="purple">Connexion</Button>
|
w={buttonWidth} colorScheme="purple">Connexion</Button>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Container>
|
</Container>
|
||||||
|
|||||||
@@ -9,12 +9,13 @@ import {
|
|||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import {useRouter} from 'next/router';
|
import {useRouter} from 'next/router';
|
||||||
import {useState} from 'react';
|
import {useState} from 'react';
|
||||||
import {RegisterData} from '@/models/form/RegisterData';
|
|
||||||
import {signIn, SignInResponse} from 'next-auth/react';
|
import {signIn, SignInResponse} from 'next-auth/react';
|
||||||
|
import {RegisterData} from '@/models/form/RegisterData';
|
||||||
|
|
||||||
export default function RegisterForm() {
|
export default function RegisterForm() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [registerData, setRegisterData] = useState(new RegisterData());
|
const [registerData, setRegisterData] = useState(new RegisterData());
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [invalidInput, setInvalidInput] = useState(false);
|
const [invalidInput, setInvalidInput] = useState(false);
|
||||||
|
|
||||||
const buttonWidth = {base: '100%', md: 'unset'};
|
const buttonWidth = {base: '100%', md: 'unset'};
|
||||||
@@ -28,31 +29,34 @@ export default function RegisterForm() {
|
|||||||
let {email, firstName, lastName, password, confirmPassword} = registerData;
|
let {email, firstName, lastName, password, confirmPassword} = registerData;
|
||||||
if (password !== confirmPassword) setInvalidInput(true);
|
if (password !== confirmPassword) setInvalidInput(true);
|
||||||
|
|
||||||
fetch('/api/user', {
|
const options = {
|
||||||
method: 'PUT',
|
method: 'POST',
|
||||||
headers: {'Content-Type': 'application/json'},
|
headers: {'Content-Type': 'application/json'},
|
||||||
body: JSON.stringify({email, firstName, lastName, password}),
|
body: JSON.stringify({email, firstName, lastName, password}),
|
||||||
}).then(() => {
|
};
|
||||||
|
|
||||||
|
setIsLoading(true);
|
||||||
|
fetch('/api/users', options).then(() => {
|
||||||
signIn('credentials', {email, password, redirect: false})
|
signIn('credentials', {email, password, redirect: false})
|
||||||
.then((res) => {
|
.then((res: unknown) => {
|
||||||
const {ok: connexionSuccess} = res as SignInResponse;
|
const {ok: connexionSuccess} = res as SignInResponse;
|
||||||
|
|
||||||
// TODO If success -> goto interactive form else login
|
|
||||||
router.push( connexionSuccess ? "/" : "/login");
|
|
||||||
});
|
|
||||||
|
|
||||||
|
// TODO If success -> goto interactive form else login
|
||||||
|
router.push(connexionSuccess ? '/' : '/login');
|
||||||
|
});
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
|
setIsLoading(false);
|
||||||
setInvalidInput(true);
|
setInvalidInput(true);
|
||||||
setRegisterData({...registerData, password: '', confirmPassword: ''});
|
setRegisterData({...registerData, password: '', confirmPassword: ''});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box flexBasis={"100%"}>
|
<Box flexBasis={'100%'}>
|
||||||
<Container>
|
<Container>
|
||||||
<Heading textAlign={"center"} size={"2xl"}>Inscription</Heading>
|
<Heading textAlign={'center'} size={'2xl'}>Inscription</Heading>
|
||||||
|
|
||||||
<Flex mt={"100px"} mb={'1rem'} gap={5}>
|
<Flex mt={'100px'} mb={'1rem'} gap={5}>
|
||||||
{/*Prénom*/}
|
{/*Prénom*/}
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<FormLabel>Prénom</FormLabel>
|
<FormLabel>Prénom</FormLabel>
|
||||||
@@ -119,8 +123,9 @@ export default function RegisterForm() {
|
|||||||
|
|
||||||
|
|
||||||
<Flex mt={'50px'} w={'100%'} justify={'space-between'} gap={5}>
|
<Flex mt={'50px'} w={'100%'} justify={'space-between'} gap={5}>
|
||||||
<Button onClick={() => router.push('/')} w={buttonWidth}>Retour</Button>
|
<Button onClick={() => router.push('/')}
|
||||||
<Button onClick={handleSubmit} w={buttonWidth} colorScheme="purple">Je
|
w={buttonWidth}>Retour</Button>
|
||||||
|
<Button isLoading={isLoading} onClick={handleSubmit} w={buttonWidth} colorScheme="purple">Je
|
||||||
m'inscris</Button>
|
m'inscris</Button>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Container>
|
</Container>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export async function hashPassword(unHashedPassword: string): Promise<string> {
|
|||||||
|
|
||||||
export async function isSamePassword(
|
export async function isSamePassword(
|
||||||
unHashedPassword: string,
|
unHashedPassword: string,
|
||||||
hashedPassword: string
|
hashedPassword: string,
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
return await bcrypt.compare(unHashedPassword, hashedPassword).
|
return await bcrypt.compare(unHashedPassword, hashedPassword).
|
||||||
then((result: boolean) => result);
|
then((result: boolean) => result);
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
const CRUD = {
|
|
||||||
CREATE: "PUT",
|
|
||||||
READ: "GET",
|
|
||||||
UPDATE: "POST",
|
|
||||||
DELETE: "DELETE",
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CRUD;
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
type CreateUserQuery = {
|
|
||||||
email: string
|
|
||||||
password: string
|
|
||||||
firstName: string
|
|
||||||
lastName: string
|
|
||||||
}
|
|
||||||
|
|
||||||
type ReadUserQuery = {
|
|
||||||
id?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
type DeleteUserQuery = {
|
|
||||||
id: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type {CreateUserQuery, ReadUserQuery, DeleteUserQuery};
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import {User} from '@/models/data_models/User';
|
import type {User} from "@prisma/client";
|
||||||
|
|
||||||
export type Session = {
|
export type Session = {
|
||||||
user: User
|
user: User
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
export type User = {
|
|
||||||
id: string
|
|
||||||
email: string
|
|
||||||
password: string
|
|
||||||
firstName: string
|
|
||||||
lastName: string
|
|
||||||
role: "USER" | "ADMIN"
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import NextCrud, {PrismaAdapter} from '@premieroctet/next-crud';
|
||||||
|
import prismaClient from '@/lib/prismaClient';
|
||||||
|
import {NextApiRequest, NextApiResponse} from 'next';
|
||||||
|
import {hashPassword} from '@/lib/PasswordTools';
|
||||||
|
|
||||||
|
type CreateUserQuery = {
|
||||||
|
email: string
|
||||||
|
password: string
|
||||||
|
firstName: string
|
||||||
|
lastName: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function handler(
|
||||||
|
req: NextApiRequest,
|
||||||
|
res: NextApiResponse
|
||||||
|
) {
|
||||||
|
|
||||||
|
const nextCrudHandler = await NextCrud({
|
||||||
|
adapter: new PrismaAdapter({prismaClient: prismaClient}),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Hash le mot de passe quand on crée un utilisateur
|
||||||
|
if (req.url === "/api/users" && req.method === "POST") {
|
||||||
|
const {email, password, firstName, lastName} = req.body as CreateUserQuery;
|
||||||
|
|
||||||
|
if (!email || !password || !firstName || !lastName)
|
||||||
|
return res.status(400).send({message: req.body});
|
||||||
|
|
||||||
|
const hashedPassword: string = await hashPassword(password);
|
||||||
|
req.body = {...req.body, password: hashedPassword}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nextCrudHandler(req, res);
|
||||||
|
}
|
||||||
@@ -3,30 +3,37 @@ import CredentialsProvider from 'next-auth/providers/credentials';
|
|||||||
import {NextApiRequest, NextApiResponse} from 'next';
|
import {NextApiRequest, NextApiResponse} from 'next';
|
||||||
import {LoginData} from '@/models/form/LoginData';
|
import {LoginData} from '@/models/form/LoginData';
|
||||||
import {isSamePassword} from '@/lib/PasswordTools';
|
import {isSamePassword} from '@/lib/PasswordTools';
|
||||||
|
import {User} from '@prisma/client';
|
||||||
import prismaClient from '@/lib/prismaClient';
|
import prismaClient from '@/lib/prismaClient';
|
||||||
import {dmmf} from '.prisma/client/edge';
|
|
||||||
|
|
||||||
export default async function auth(req: NextApiRequest, res: NextApiResponse) {
|
export default async function auth(req: NextApiRequest, res: NextApiResponse) {
|
||||||
const providers = [
|
const providers = [
|
||||||
CredentialsProvider({
|
CredentialsProvider({
|
||||||
name: 'Credentials',
|
name: 'Credentials',
|
||||||
credentials: {
|
credentials: {
|
||||||
email: {label: 'Email', type: 'text', placeholder: 'adress@email.com'},
|
email: {
|
||||||
password: {label: 'Password', type: 'password'},
|
label: 'Email',
|
||||||
|
type: 'text',
|
||||||
|
placeholder: 'adresse@email.com'
|
||||||
|
},
|
||||||
|
password: {
|
||||||
|
label: 'Password',
|
||||||
|
type: 'password',
|
||||||
|
placeholder: 'mot de passe',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
async authorize(credentials) {
|
async authorize(credentials: unknown) {
|
||||||
const {email, password} = credentials as LoginData;
|
const {email, password} = credentials as LoginData;
|
||||||
if (!email || !password) return null;
|
if (!email || !password) return null;
|
||||||
|
|
||||||
// Appel à la base de donnée
|
// Appel à la base de donnée
|
||||||
const user = await getUserByEmail(email);
|
const user = await getUserByEmail(email);
|
||||||
|
if (!user) return null;
|
||||||
|
|
||||||
// Vérification de la connexion
|
// Vérification de la connexion
|
||||||
if (user && await isSamePassword(password, user.password)) {
|
const passwordIsValid = await isSamePassword(password, user.password);
|
||||||
return user;
|
if (passwordIsValid) return user;
|
||||||
}
|
else return null;
|
||||||
|
|
||||||
return null;
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
@@ -46,8 +53,8 @@ export default async function auth(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getUserByEmail(email: string) {
|
async function getUserByEmail(email: string): Promise<null | User> {
|
||||||
return prismaClient.user.findUnique({
|
return prismaClient.user.findUnique({
|
||||||
where: {email},
|
where: {email}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
import type {NextApiRequest, NextApiResponse} from 'next';
|
|
||||||
import CRUD from '@/models/api/CRUD';
|
|
||||||
import {CreateUserQuery} from '@/models/api/UserQuery';
|
|
||||||
import {hashPassword} from '@/lib/PasswordTools';
|
|
||||||
import prismaClient from '@/lib/prismaClient';
|
|
||||||
|
|
||||||
export default function handler(
|
|
||||||
req: NextApiRequest,
|
|
||||||
res: NextApiResponse,
|
|
||||||
) {
|
|
||||||
switch (req.method) {
|
|
||||||
case CRUD.CREATE:
|
|
||||||
return createUser(req, res);
|
|
||||||
case CRUD.READ:
|
|
||||||
return readUser(req, res);
|
|
||||||
default:
|
|
||||||
return help(res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function help(res: NextApiResponse) {
|
|
||||||
res.status(400).send({message: 'error'}); // TODO add help message
|
|
||||||
}
|
|
||||||
|
|
||||||
const prisma = prismaClient;
|
|
||||||
|
|
||||||
async function createUser(req: NextApiRequest, res: NextApiResponse) {
|
|
||||||
const {email, password, firstName, lastName} = req.body as CreateUserQuery;
|
|
||||||
|
|
||||||
if (!email || !password || !firstName || !lastName)
|
|
||||||
return res.status(400).send({message: req.body});
|
|
||||||
|
|
||||||
const hashedPassword = await hashPassword(password);
|
|
||||||
|
|
||||||
const newUser = await prisma.user.create({
|
|
||||||
data: {...req.body, password: hashedPassword},
|
|
||||||
});
|
|
||||||
|
|
||||||
return res.status(201).send({message: 'createUser', newUser});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function readUser(req: NextApiRequest, res: NextApiResponse) {
|
|
||||||
const {id, email} = req.query as { id: string, email: string };
|
|
||||||
|
|
||||||
const user = (id)
|
|
||||||
? await prisma.user.findUnique({where: {id}})
|
|
||||||
: await prisma.user.findMany()
|
|
||||||
;
|
|
||||||
|
|
||||||
return res.status(200).send({message: 'readUser', user});
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,7 @@ import {Grid, GridItem, Text, Box} from '@chakra-ui/react';
|
|||||||
import {useSession} from 'next-auth/react';
|
import {useSession} from 'next-auth/react';
|
||||||
import {useRouter} from 'next/router';
|
import {useRouter} from 'next/router';
|
||||||
|
|
||||||
import type {Session} from '@/models/data_models/Session';
|
import type {Session} from '@/models/auth/Session';
|
||||||
import CardUser from '../components/layout/dashboard/card_user/CardUser';
|
import CardUser from '../components/layout/dashboard/card_user/CardUser';
|
||||||
import LeftPanel from '../components/layout/dashboard/left_panel/LeftPanel';
|
import LeftPanel from '../components/layout/dashboard/left_panel/LeftPanel';
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
|
|||||||
+1
-1
@@ -10,7 +10,7 @@ export default function Home() {
|
|||||||
<>
|
<>
|
||||||
<Head><title>{websiteName}</title></Head>
|
<Head><title>{websiteName}</title></Head>
|
||||||
|
|
||||||
<Navbar/>
|
<Navbar variant={"fixed"}/>
|
||||||
<HeroBanner/>
|
<HeroBanner/>
|
||||||
<HomePresentation/>
|
<HomePresentation/>
|
||||||
</>
|
</>
|
||||||
|
|||||||
Reference in New Issue
Block a user