Skip to content

Commit

Permalink
Merge pull request #76 from WildCodeSchool/feat/account-query-profile…
Browse files Browse the repository at this point in the history
…-page

Feat/account query profile page
  • Loading branch information
MrRobo1 authored Jul 15, 2024
2 parents b1ae573 + a42a9be commit f5719d2
Show file tree
Hide file tree
Showing 25 changed files with 685 additions and 340 deletions.
28 changes: 14 additions & 14 deletions backend/src/authChecker.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { User, UserRoleType } from "entities";
import { AuthChecker } from "type-graphql";
// import { User, UserRoleType } from "entities";
// import { AuthChecker } from "type-graphql";

export const authChecker: AuthChecker<{ user: User | null }, UserRoleType> = (
{ context },
roles
): boolean => {
if (!context.user) {
return false;
}
// export const authChecker: AuthChecker<{ user: User | null }, UserRoleType> = (
// { context },
// roles
// ): boolean => {
// if (!context.user) {
// return false;
// }

if (roles.length === 0) {
return true;
}
// if (roles.length === 0) {
// return true;
// }

return roles.includes(context.user.role);
};
// return roles.includes(context.user.role);
// };
4 changes: 2 additions & 2 deletions backend/src/entities/user.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export class User extends BaseEntity {
@Field()
@Column()
@MinLength(5, {
message: "Le nom d'utilisateur doit contenir au moins 5 caractères",
message: "Le nom d'utilisateur doit contenir au moins 5 caractères", //TODO à revoir car possible de créer un compte avec moins de 5 caractères
})
username: string;

Expand Down Expand Up @@ -42,4 +42,4 @@ export class UserInfo {
email: string;
@Field({ nullable: true })
role: string;
}
}
2 changes: 1 addition & 1 deletion backend/src/fillDatabaseIfEmpty.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export async function fillDatabaseIfEmpty() {
"Ceci est une description du matériel d'escalade à louer ! Il est super cool et vous permettra de grimper en toute sécurité !";
climbing.picture =
"https://images.unsplash.com/photo-1630432328419-bee5f50d6390";
climbing.price_fixed = 199;
climbing.price_fixed = 259;
climbing.price_daily = 20;
climbing.quantity = 3;
await climbing.save();
Expand Down
36 changes: 28 additions & 8 deletions backend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ import { startStandaloneServer } from "@apollo/server/standalone";
import { createClient } from "redis";
import "reflect-metadata";
import { buildSchema } from "type-graphql";
import { authChecker } from "./authChecker";
// import { authChecker } from "./authChecker";
import dataSource from "./config/datasource";
import { fillDatabaseIfEmpty } from "./fillDatabaseIfEmpty";
import { CategoryResolver, ProductResolver, UserResolver } from "./resolvers";
import * as jwt from "jsonwebtoken";

export const redisClient = createClient({
url: "redis://redis",
Expand All @@ -28,16 +29,35 @@ const start = async () => {

const schema = await buildSchema({
resolvers: [ProductResolver, CategoryResolver, UserResolver],
authChecker: authChecker,
authChecker: ({context}) => {
if (context.email) {
return true
} else {
return false
}
},
});

const server = new ApolloServer({
schema,
});
const server = new ApolloServer({
schema,
});
const { url } = await startStandaloneServer(server, {
listen: { port: 4000 },
// A chaque requête exécuté, la fonction de contexte va s'enclencher
context: async ({ req }) => {
const token = req.headers.authorization?.split("Bearer ")[1];

const { url } = await startStandaloneServer(server, {
listen: { port: 4000 },
});
if (token) {
try {
const payload = jwt.verify(token, "mysupersecretkey");
return payload;
} catch {
console.log("invalid secret key")
}
}
return {};
},
});

console.log(`🚀 Server ready at: ${url}`);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Field, InputType } from "type-graphql";

@InputType()
export class InputUser {
export class InputUserCreate {
@Field()
username: string;

Expand Down
10 changes: 10 additions & 0 deletions backend/src/inputs/InputUserLogin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Field, InputType } from "type-graphql";

@InputType()
export class InputUserLogin {
@Field()
email: string;

@Field()
password: string;
}
6 changes: 4 additions & 2 deletions backend/src/inputs/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { InputUserCreate } from "./InputUserCreate";
import { InputCreateCategory } from "./InputCreateCategory";
import { InputCreateProduct } from "./InputCreateProduct";
import { InputUpdateProduct } from "./InputUpdateProduct";
import { InputUser } from "./InputUser";
import { InputUserLogin } from "./InputUserLogin";

export {
InputCreateCategory,
InputCreateProduct,
InputUpdateProduct,
InputUser,
InputUserLogin,
InputUserCreate
};
21 changes: 13 additions & 8 deletions backend/src/resolvers/product.resolver.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Arg, Mutation, Query, Resolver } from "type-graphql";
import { Arg, Ctx, Mutation, Query, Resolver } from "type-graphql";
import { Product } from "../entities";
import { InputCreateProduct, InputUpdateProduct } from "../inputs";
import ProductService from "../services/product.service";
Expand All @@ -11,9 +11,10 @@ export default class ProductResolver {
this.productService = new ProductService();
}

// @Authorized()
@Query(() => [Product])
async getAllproducts() {
return await this.productService.list();
async getAllproducts(@Ctx() ctx: {email:string}) {
return await this.productService.list(ctx);
}

@Query(() => [Product])
Expand All @@ -33,11 +34,15 @@ export default class ProductResolver {
return productsByCategoryId;
}

@Mutation(() => Product)
async addProduct(@Arg("infos") infos: InputCreateProduct) {
const newProduct = await this.productService.create(infos);
return newProduct;
}
// @Authorized("admin")
@Mutation(() => Product)
async addProduct(
@Arg("infos") infos: InputCreateProduct,
// @Ctx() ctx: { email: string }
) {
const newProduct = await this.productService.create(infos);
return newProduct;
}

@Mutation(() => Product)
async updateProduct(
Expand Down
35 changes: 23 additions & 12 deletions backend/src/resolvers/user.resolver.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { Arg, Ctx, Mutation, Query, Resolver } from "type-graphql";
import { User, UserInfo } from "../entities/user.entity";
import { InputUser } from "../inputs";
import { InputUserCreate, InputUserLogin } from "../inputs";
import { UserService } from "../services/user.service";

import { Arg, Ctx, Mutation, Query, Resolver } from "type-graphql";

@Resolver()
export default class UserResolver {
private userService = new UserService();
Expand All @@ -20,25 +19,29 @@ export default class UserResolver {
return this.userService.deleteUser(userId);
}

@Mutation(() => User)
async createUser(@Arg("inputUser") inputUser: InputUser): Promise<User> {
return this.userService.createUser(inputUser);
}
// @Mutation(() => User)
// async createUser(@Arg("inputUser") inputUser: InputUserCreate): Promise<User> {
// return this.userService.createUser(inputUser);
// }

@Mutation(() => String)
async register(@Arg("newUserData") newUserData: InputUser): Promise<string> {
async register(
@Arg("newUserData") newUserData: InputUserCreate
): Promise<string> {
try {
await this.userService.createUser(newUserData);
return "New user was created with success";
return "New user has been created with success";
} catch (err) {
console.log("err", err);
return "Error while creating new user";
}
}

@Mutation(() => String)
async loginUser(@Arg("inputUser") inputUser: InputUser): Promise<string> {
return this.userService.loginUser(inputUser);
@Query(() => String)
async loginUser(
@Arg("inputUserLogin") inputUserLogin: InputUserLogin
): Promise<string> {
return this.userService.loginUser(inputUserLogin);
}

@Query(() => Boolean)
Expand All @@ -55,6 +58,14 @@ export default class UserResolver {
return "Your are admin";
}

@Query(() => User, { nullable: true })
async getUserProfile(@Ctx() ctx: { email: string }): Promise<User | null> {
if (!ctx.email) {
throw new Error("User not authenticated");
}
return this.userService.getUserByEmail(ctx.email);
}

@Query(() => UserInfo)
async whoAmI(@Ctx() ctx: { email: string; role: string }) {
if (ctx.email !== undefined) {
Expand Down
2 changes: 1 addition & 1 deletion backend/src/services/product.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default class ProductService {
this.dbCategory = datasource.getRepository(Category);
}

async list() {
async list(_ctx:any) {
return this.db.find({
relations: {
category: true,
Expand Down
47 changes: 33 additions & 14 deletions backend/src/services/user.service.ts
Original file line number Diff line number Diff line change
@@ -1,49 +1,58 @@
import * as argon2 from "argon2";
import * as jwt from "jsonwebtoken";

import { InputUser } from "inputs";
import { InputUserCreate, InputUserLogin } from "inputs";
import { User, UserRoleType } from "../entities/user.entity";

export class UserService {
async createUser(inputUser: InputUser): Promise<User> {
async createUser(inputUserCreate: InputUserCreate): Promise<User> {
try {
const existingUser = await User.findOne({
where: [{ email: inputUser.email }, { username: inputUser.username }],
where: [
{ email: inputUserCreate.email },
{ username: inputUserCreate.username },
],
});

if (existingUser) {
throw new Error("User with this email or username already exists");
}

const newUser = new User();
newUser.email = inputUser.email;
newUser.username = inputUser.username;
newUser.hashedPassword = await argon2.hash(inputUser.password);
newUser.email = inputUserCreate.email;
newUser.username = inputUserCreate.username;
newUser.hashedPassword = await argon2.hash(inputUserCreate.password);
newUser.role = "user";
return await newUser.save();
} catch (error) {
console.error("Error while creating new user:", error);
console.error("Error while creating new user :", error);
throw new Error("Error while creating new user");
}
}

async loginUser(inputUser: InputUser): Promise<string> {
let payload: { email: string; role: UserRoleType };
async loginUser(inputUserLogin: InputUserLogin): Promise<string> {
let payload: { email: string; role: UserRoleType; username: string };
try {
const user = await User.findOne({ where: { email: inputUser.email } });
const user = await User.findOne({
where: { email: inputUserLogin.email },
});
if (!user) {
throw new Error("User not found");
}

if (!(await argon2.verify(user.hashedPassword, inputUser.password))) {
if (
!(await argon2.verify(user.hashedPassword, inputUserLogin.password))
) {
throw new Error("Invalid password");
}

payload = { email: user.email, role: user.role };
payload = { email: user.email, role: user.role, username: user.username };

// Signature du token avec une clé secrète
const token = jwt.sign(payload, "mysupersecretkey");
return token;
} catch (error) {
console.error("Error while login:", error);
console.error("Error while login :", error);
throw new Error("Error while login");
}
}
Expand Down Expand Up @@ -72,7 +81,7 @@ export class UserService {
await userToDelete.remove();
return "User removed";
} catch (error) {
console.error("Error while deleting user:", error);
console.error("Error while deleting user :", error);
throw new Error("Error while deleting user");
}
}
Expand All @@ -81,6 +90,16 @@ export class UserService {
return "You are admin";
}

async getUserByEmail(email: string): Promise<User | null> {
try {
const user = await User.findOne({ where: { email } });
return user;
} catch (error) {
console.error("Error while fetching user by email:", error);
throw new Error("Error while fetching user");
}
}

async whoAmI(
email: string,
role: UserRoleType
Expand Down
9 changes: 7 additions & 2 deletions frontend/src/components/Layout.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import type { Metadata } from "next";
import { ReactNode } from "react";

import { ReactNode, createContext } from "react";
import MainHeader from "./headers/MainHeader";

export const UserContext = createContext({
isLoggedIn: false,
refetchLogin: () => {},
role: "user"
});

// const montserrat = Montserrat({
// subsets: ["latin"],
// variable: "--font_montserrat",
Expand Down
Loading

0 comments on commit f5719d2

Please sign in to comment.