diff --git a/src/modules/user/domain/entities/User.entity.ts b/src/modules/user/domain/entities/User.entity.ts index ee6664b..a78fa1c 100644 --- a/src/modules/user/domain/entities/User.entity.ts +++ b/src/modules/user/domain/entities/User.entity.ts @@ -17,8 +17,8 @@ export class User extends BaseEntity { @Column() password: string; - @Column({ unique: true }) - wallet: string; + @Column({ unique: true, nullable: true }) + wallet?: string; @Column({ default: false }) isVerified: boolean; diff --git a/src/modules/user/domain/interfaces/IUser.ts b/src/modules/user/domain/interfaces/IUser.ts index 410e0f7..0b7565f 100644 --- a/src/modules/user/domain/interfaces/IUser.ts +++ b/src/modules/user/domain/interfaces/IUser.ts @@ -4,7 +4,7 @@ export interface IUser { lastName: string; email: string; password: string; - wallet: string; + wallet?: string; isVerified: boolean; verificationToken?: string; verificationTokenExpires?: Date; diff --git a/src/modules/user/dto/UpdateUserDto.ts b/src/modules/user/dto/UpdateUserDto.ts index 8687e70..96585fe 100644 --- a/src/modules/user/dto/UpdateUserDto.ts +++ b/src/modules/user/dto/UpdateUserDto.ts @@ -8,6 +8,10 @@ import { } from "class-validator"; export class UpdateUserDto { + @IsOptional() + @IsString({ message: "ID must be a string" }) + id?: string; + @IsOptional() @IsString({ message: "Name must be a string" }) @MinLength(2, { message: "Name must be at least 2 characters long" }) diff --git a/src/modules/user/presentation/controllers/UserController.disabled b/src/modules/user/presentation/controllers/UserController.disabled deleted file mode 100644 index 1ef2aa5..0000000 --- a/src/modules/user/presentation/controllers/UserController.disabled +++ /dev/null @@ -1,80 +0,0 @@ -import { CreateUserDto } from "../../../../modules/user/dto/CreateUserDto"; -import { UserService } from "../../../../services/UserService"; -import { Response } from "express"; -import { UpdateUserDto } from "../../../../modules/user/dto/UpdateUserDto"; -import { AuthenticatedRequest } from "../../../../types/auth.types"; - -class UserController { - private userService = new UserService(); - - async createUser(req: AuthenticatedRequest, res: Response): Promise { - try { - const userDto = new CreateUserDto(); - Object.assign(userDto, req.body); - - const user = await this.userService.createUser(userDto); - res.status(201).json(user); - } catch (error: unknown) { - res.status(400).json({ - error: error instanceof Error ? error.message : "Unknown error", - }); - } - } - - async getUserById(req: AuthenticatedRequest, res: Response): Promise { - try { - const { id } = req.params; - const user = await this.userService.getUserById(id); - if (!user) { - res.status(404).json({ error: "User not found" }); - return; - } - res.status(200).json(user); - } catch (error: unknown) { - res.status(400).json({ - error: error instanceof Error ? error.message : "Unknown error", - }); - } - } - - async getUserByEmail( - req: AuthenticatedRequest, - res: Response - ): Promise { - try { - const { email } = req.query; - if (!email) { - res.status(400).json({ error: "Email is required" }); - return; - } - const user = await this.userService.getUserByEmail(email as string); - if (!user) { - res.status(404).json({ error: "User not found" }); - return; - } - res.status(200).json(user); - } catch (error: unknown) { - res.status(400).json({ - error: error instanceof Error ? error.message : "Unknown error", - }); - } - } - - async updateUser(req: AuthenticatedRequest, res: Response): Promise { - try { - const { id } = req.params; - const userDto = new UpdateUserDto(); - Object.assign(userDto, req.body); - userDto.id = id; - - await this.userService.updateUser(userDto); - res.status(200).json({ message: "User updated successfully" }); - } catch (error: unknown) { - res.status(400).json({ - error: error instanceof Error ? error.message : "Unknown error", - }); - } - } -} - -export default UserController; diff --git a/src/modules/user/presentation/controllers/UserController.stub.ts b/src/modules/user/presentation/controllers/UserController.stub.ts deleted file mode 100644 index ce59984..0000000 --- a/src/modules/user/presentation/controllers/UserController.stub.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Request, Response } from "express"; - -/** - * Stub controller for User functionality - * This replaces the original controller that referenced deleted services - * TODO: Implement proper user controller using new modular architecture - */ -export default class UserController { - async createUser(req: Request, res: Response) { - res.status(501).json({ - message: "User service temporarily disabled during migration", - error: "Service migration in progress" - }); - } - - async getUserById(req: Request, res: Response) { - res.status(501).json({ - message: "User service temporarily disabled during migration", - error: "Service migration in progress" - }); - } - - async getUserByEmail(req: Request, res: Response) { - res.status(501).json({ - message: "User service temporarily disabled during migration", - error: "Service migration in progress" - }); - } - - async updateUser(req: Request, res: Response) { - res.status(501).json({ - message: "User service temporarily disabled during migration", - error: "Service migration in progress" - }); - } - - async deleteUser(req: Request, res: Response) { - res.status(501).json({ - message: "User service temporarily disabled during migration", - error: "Service migration in progress" - }); - } -} \ No newline at end of file diff --git a/src/modules/user/presentation/controllers/UserController.ts b/src/modules/user/presentation/controllers/UserController.ts new file mode 100644 index 0000000..bec235f --- /dev/null +++ b/src/modules/user/presentation/controllers/UserController.ts @@ -0,0 +1,105 @@ +import { Request, Response } from "express"; +import { CreateUserUseCase } from "../../use-cases/userUseCase"; +import { GetUserByIdUseCase } from "../../use-cases/userUseCase"; +import { GetUserByEmailUseCase } from "../../use-cases/userUseCase"; +import { UpdateUserUseCase } from "../../use-cases/userUseCase"; +import { DeleteUserUseCase } from "../../use-cases/userUseCase"; +import { PrismaUserRepository } from "../../repositories/PrismaUserRepository"; +import { CreateUserDto } from "../../dto/CreateUserDto"; +import { UpdateUserDto } from "../../dto/UpdateUserDto"; + +export default class UserController { + private userRepository: PrismaUserRepository; + private createUserUseCase: CreateUserUseCase; + private getUserByIdUseCase: GetUserByIdUseCase; + private getUserByEmailUseCase: GetUserByEmailUseCase; + private updateUserUseCase: UpdateUserUseCase; + private deleteUserUseCase: DeleteUserUseCase; + + constructor() { + this.userRepository = new PrismaUserRepository(); + this.createUserUseCase = new CreateUserUseCase(this.userRepository); + this.getUserByIdUseCase = new GetUserByIdUseCase(this.userRepository); + this.getUserByEmailUseCase = new GetUserByEmailUseCase(this.userRepository); + this.updateUserUseCase = new UpdateUserUseCase(this.userRepository); + this.deleteUserUseCase = new DeleteUserUseCase(this.userRepository); + } + + async createUser(req: Request, res: Response): Promise { + try { + const userDto = new CreateUserDto(); + Object.assign(userDto, req.body); + + const user = await this.createUserUseCase.execute(userDto); + res.status(201).json(user); + } catch (error: unknown) { + res.status(400).json({ + error: error instanceof Error ? error.message : "Unknown error", + }); + } + } + + async getUserById(req: Request, res: Response): Promise { + try { + const { id } = req.params; + const user = await this.getUserByIdUseCase.execute(id); + if (!user) { + res.status(404).json({ error: "User not found" }); + return; + } + res.status(200).json(user); + } catch (error: unknown) { + res.status(400).json({ + error: error instanceof Error ? error.message : "Unknown error", + }); + } + } + + async getUserByEmail(req: Request, res: Response): Promise { + try { + const { email } = req.query; + if (!email) { + res.status(400).json({ error: "Email is required" }); + return; + } + const user = await this.getUserByEmailUseCase.execute(email as string); + if (!user) { + res.status(404).json({ error: "User not found" }); + return; + } + res.status(200).json(user); + } catch (error: unknown) { + res.status(400).json({ + error: error instanceof Error ? error.message : "Unknown error", + }); + } + } + + async updateUser(req: Request, res: Response): Promise { + try { + const { id } = req.params; + const userDto = new UpdateUserDto(); + Object.assign(userDto, req.body); + userDto.id = id; + + await this.updateUserUseCase.execute(userDto); + res.status(200).json({ message: "User updated successfully" }); + } catch (error: unknown) { + res.status(400).json({ + error: error instanceof Error ? error.message : "Unknown error", + }); + } + } + + async deleteUser(req: Request, res: Response): Promise { + try { + const { id } = req.params; + await this.deleteUserUseCase.execute(id); + res.status(200).json({ message: "User deleted successfully" }); + } catch (error: unknown) { + res.status(400).json({ + error: error instanceof Error ? error.message : "Unknown error", + }); + } + } +} diff --git a/src/modules/user/presentation/controllers/userVolunteer.controller.disabled b/src/modules/user/presentation/controllers/userVolunteer.controller.disabled deleted file mode 100644 index 8254504..0000000 --- a/src/modules/user/presentation/controllers/userVolunteer.controller.disabled +++ /dev/null @@ -1,44 +0,0 @@ -import { Request, Response } from "express"; -import { UserVolunteerService } from "../../../../services/userVolunteer.service"; -import { prisma } from "../../../../config/prisma"; - -class UserVolunteerController { - private userVolunteerService = new UserVolunteerService(prisma); - - async addUserToVolunteer(req: Request, res: Response): Promise { - try { - const { userId, volunteerId } = req.params; - const userVolunteer = await this.userVolunteerService.addUserToVolunteer( - userId, - volunteerId - ); - return res.status(201).json(userVolunteer); - } catch (error) { - return res.status(500).json({ error: (error as Error).message }); - } - } - - async getVolunteersByUserId(req: Request, res: Response): Promise { - try { - const { userId } = req.params; - const volunteers = - await this.userVolunteerService.getVolunteersByUserId(userId); - return res.status(200).json(volunteers); - } catch (error) { - return res.status(500).json({ error: (error as Error).message }); - } - } - - async getUsersByVolunteerId(req: Request, res: Response): Promise { - try { - const { volunteerId } = req.params; - const users = - await this.userVolunteerService.getUsersByVolunteerId(volunteerId); - return res.status(200).json(users); - } catch (error) { - return res.status(500).json({ error: (error as Error).message }); - } - } -} - -export default UserVolunteerController; diff --git a/src/modules/user/presentation/controllers/userVolunteer.controller.stub.ts b/src/modules/user/presentation/controllers/userVolunteer.controller.stub.ts deleted file mode 100644 index bdf3121..0000000 --- a/src/modules/user/presentation/controllers/userVolunteer.controller.stub.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Request, Response } from "express"; - -/** - * Stub controller for UserVolunteer functionality - * This replaces the original controller that referenced deleted services - * TODO: Implement proper userVolunteer controller using new modular architecture - */ -class UserVolunteerController { - async createUserVolunteer(req: Request, res: Response) { - res.status(501).json({ - message: "UserVolunteer service temporarily disabled during migration", - error: "Service migration in progress" - }); - } - - async getUserVolunteers(req: Request, res: Response) { - res.status(501).json({ - message: "UserVolunteer service temporarily disabled during migration", - error: "Service migration in progress" - }); - } - - async updateUserVolunteer(req: Request, res: Response) { - res.status(501).json({ - message: "UserVolunteer service temporarily disabled during migration", - error: "Service migration in progress" - }); - } - - async deleteUserVolunteer(req: Request, res: Response) { - res.status(501).json({ - message: "UserVolunteer service temporarily disabled during migration", - error: "Service migration in progress" - }); - } -} - -export default new UserVolunteerController(); \ No newline at end of file diff --git a/src/modules/user/presentation/controllers/userVolunteer.controller.ts b/src/modules/user/presentation/controllers/userVolunteer.controller.ts new file mode 100644 index 0000000..b26ec4a --- /dev/null +++ b/src/modules/user/presentation/controllers/userVolunteer.controller.ts @@ -0,0 +1,106 @@ +import { Request, Response } from "express"; +import { PrismaClient } from "@prisma/client"; + +const prisma = new PrismaClient(); + +export default class UserVolunteerController { + async addUserToVolunteer(req: Request, res: Response): Promise { + try { + const { userId, volunteerId } = req.params; + + // Check if the relationship already exists + const existing = await prisma.userVolunteer.findFirst({ + where: { + userId, + volunteerId, + }, + }); + + if (existing) { + res.status(400).json({ + error: "User is already assigned to this volunteer", + }); + return; + } + + const userVolunteer = await prisma.userVolunteer.create({ + data: { + userId, + volunteerId, + }, + }); + + res.status(201).json(userVolunteer); + } catch (error) { + res.status(500).json({ + error: error instanceof Error ? error.message : "Unknown error", + }); + } + } + + async getVolunteersByUserId(req: Request, res: Response): Promise { + try { + const { userId } = req.params; + + const userVolunteers = await prisma.userVolunteer.findMany({ + where: { userId }, + include: { + volunteer: true, + }, + }); + + res.status(200).json(userVolunteers); + } catch (error) { + res.status(500).json({ + error: error instanceof Error ? error.message : "Unknown error", + }); + } + } + + async getUsersByVolunteerId(req: Request, res: Response): Promise { + try { + const { volunteerId } = req.params; + + const userVolunteers = await prisma.userVolunteer.findMany({ + where: { volunteerId }, + include: { + user: true, + }, + }); + + res.status(200).json(userVolunteers); + } catch (error) { + res.status(500).json({ + error: error instanceof Error ? error.message : "Unknown error", + }); + } + } + + async removeUserFromVolunteer(req: Request, res: Response): Promise { + try { + const { userId, volunteerId } = req.params; + + const deleted = await prisma.userVolunteer.deleteMany({ + where: { + userId, + volunteerId, + }, + }); + + if (deleted.count === 0) { + res.status(404).json({ + error: "User-Volunteer relationship not found", + }); + return; + } + + res.status(200).json({ + message: "User removed from volunteer successfully", + }); + } catch (error) { + res.status(500).json({ + error: error instanceof Error ? error.message : "Unknown error", + }); + } + } +} diff --git a/src/routes/userRoutes.ts b/src/routes/userRoutes.ts index a30447a..b0d29d0 100644 --- a/src/routes/userRoutes.ts +++ b/src/routes/userRoutes.ts @@ -5,11 +5,13 @@ import { RequestHandler, NextFunction, } from "express"; -import UserController from "../modules/user/presentation/controllers/UserController.stub"; +import UserController from "../modules/user/presentation/controllers/UserController"; +import UserVolunteerController from "../modules/user/presentation/controllers/userVolunteer.controller"; import { authMiddleware } from "../middleware/authMiddleware"; import { AuthenticatedRequest } from "../types/auth.types"; const userController = new UserController(); +const userVolunteerController = new UserVolunteerController(); const router = Router(); type AuthenticatedHandler = ( @@ -44,4 +46,39 @@ router.put( wrapHandler(userController.updateUser.bind(userController)) ); +// User-Volunteer routes +router.post( + "/users/:userId/volunteers/:volunteerId", + authMiddleware, + wrapHandler( + userVolunteerController.addUserToVolunteer.bind(userVolunteerController) + ) +); + +router.get( + "/users/:userId/volunteers", + authMiddleware, + wrapHandler( + userVolunteerController.getVolunteersByUserId.bind(userVolunteerController) + ) +); + +router.get( + "/volunteers/:volunteerId/users", + authMiddleware, + wrapHandler( + userVolunteerController.getUsersByVolunteerId.bind(userVolunteerController) + ) +); + +router.delete( + "/users/:userId/volunteers/:volunteerId", + authMiddleware, + wrapHandler( + userVolunteerController.removeUserFromVolunteer.bind( + userVolunteerController + ) + ) +); + export default router;