From 099ffb74a1e0fd961682c1506866f1a3cc8fa12b Mon Sep 17 00:00:00 2001 From: Anika Raemer Date: Sun, 21 Sep 2025 20:01:33 +0200 Subject: [PATCH] improve login and add migration script --- src/controllers/AuthController.ts | 15 ++-- src/dtos/LoginRequestDto.ts | 7 ++ src/dtos/LoginResponseDto.ts | 9 +++ src/endpoints/AuthPoint.ts | 7 +- .../1758477574859-CreateUserTable.ts | 77 +++++++++++++++++++ 5 files changed, 106 insertions(+), 9 deletions(-) create mode 100644 src/dtos/LoginRequestDto.ts create mode 100644 src/dtos/LoginResponseDto.ts create mode 100644 src/migrations/1758477574859-CreateUserTable.ts diff --git a/src/controllers/AuthController.ts b/src/controllers/AuthController.ts index e261d5b..ca2159b 100644 --- a/src/controllers/AuthController.ts +++ b/src/controllers/AuthController.ts @@ -1,8 +1,9 @@ import { UserRepository } from "../repositories/UserRepository"; import { encrypt } from "../utils/encryptionUtils"; import { ValidationError, UnauthorizedError } from "../errors/httpErrors"; -import { UserDto } from "../dtos/UserDto"; import { UserDtoEntityMapper } from "../mappers/UserDtoEntityMapper"; +import { LoginResponseDto } from "../dtos/LoginResponseDto"; +import { LoginRequestDto } from "../dtos/LoginRequestDto"; export class AuthController { constructor( @@ -10,7 +11,9 @@ export class AuthController { private mapper: UserDtoEntityMapper ) {} - async login(userName: string, password: string): Promise<{ token: string; user: UserDto }> { + async login(loginRequest : LoginRequestDto): Promise { + const userName :string = loginRequest.userName; + const password :string = loginRequest.password; if (!userName || !password) { throw new ValidationError("Username and password are required"); } @@ -30,9 +33,9 @@ export class AuthController { role: user.role, }); - return { - token, - user: this.mapper.toDto(user), - }; + const responseDto = new LoginResponseDto(); + responseDto.userData = this.mapper.toDto(user); + responseDto.token = token; + return responseDto; } } diff --git a/src/dtos/LoginRequestDto.ts b/src/dtos/LoginRequestDto.ts new file mode 100644 index 0000000..d1b1ae6 --- /dev/null +++ b/src/dtos/LoginRequestDto.ts @@ -0,0 +1,7 @@ +/** + * Defines a login request + */ +export class LoginRequestDto { + userName: string; + password: string; +} \ No newline at end of file diff --git a/src/dtos/LoginResponseDto.ts b/src/dtos/LoginResponseDto.ts new file mode 100644 index 0000000..9c57701 --- /dev/null +++ b/src/dtos/LoginResponseDto.ts @@ -0,0 +1,9 @@ +import { UserDto } from "./UserDto"; + +/** + * Response to a successful login + */ +export class LoginResponseDto { + userData: UserDto; + token: string; +} \ No newline at end of file diff --git a/src/endpoints/AuthPoint.ts b/src/endpoints/AuthPoint.ts index e86bf16..91abda3 100644 --- a/src/endpoints/AuthPoint.ts +++ b/src/endpoints/AuthPoint.ts @@ -7,6 +7,7 @@ import { UnauthorizedError, InternalServerError, } from "../errors/httpErrors"; +import { LoginRequestDto } from "../dtos/LoginRequestDto"; const router = Router(); @@ -16,9 +17,9 @@ const authController = new AuthController(userRepository, mapper); router.post("/login", async (req, res) => { try { - const { userName, password } = req.body; - const result = await authController.login(userName, password); - res.json(result); + const requestDto: LoginRequestDto = req.body; + const responseDto = await authController.login(requestDto)); + res.json(responseDto); } catch (err: any) { if (err instanceof ValidationError || err instanceof UnauthorizedError) { res.status(err.statusCode).json({ error: err.message }); diff --git a/src/migrations/1758477574859-CreateUserTable.ts b/src/migrations/1758477574859-CreateUserTable.ts new file mode 100644 index 0000000..b6151ea --- /dev/null +++ b/src/migrations/1758477574859-CreateUserTable.ts @@ -0,0 +1,77 @@ +import { MigrationInterface, QueryRunner, Table, TableUnique } from "typeorm"; + +export class CreateUserTable1661234567890 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.createTable( + new Table({ + name: "user", + columns: [ + { + name: "id", + type: "uuid", + isPrimary: true, + isGenerated: true, + generationStrategy: "uuid", + }, + { + name: "userName", + type: "varchar", + isNullable: false, + }, + { + name: "email", + type: "varchar", + isNullable: false, + }, + { + name: "password", + type: "varchar", + isNullable: false, + }, + { + name: "firstName", + type: "varchar", + isNullable: true, + }, + { + name: "lastName", + type: "varchar", + isNullable: true, + }, + { + name: "role", + type: "varchar", + default: "'user'", + }, + { + name: "createdAt", + type: "timestamp", + default: "now()", + }, + { + name: "updatedAt", + type: "timestamp", + default: "now()", + }, + ], + }), + true + ); + + // Add a unique constraint on userName + await queryRunner.createUniqueConstraint( + "user", + new TableUnique({ + columnNames: ["userName"], + name: "UQ_user_userName", + }) + ); + } + + public async down(queryRunner: QueryRunner): Promise { + // Drop the unique constraint first + await queryRunner.dropUniqueConstraint("user", "UQ_user_userName"); + // Drop the table + await queryRunner.dropTable("user"); + } +}