diff --git a/.env b/.env deleted file mode 100644 index ee7ba66..0000000 --- a/.env +++ /dev/null @@ -1,8 +0,0 @@ -#Common -BACKEND_PORT=3300 -# Database -POSTGRES_USER=postgres -POSTGRES_PASSWORD=postgres -POSTGRES_DB=schbody -POSTGRES_PORT=5432 -DATABASE_URL=postgresql://postgres:postgres@localhost:5432/schbody?schema=public diff --git a/.idea/jsLinters/eslint.xml b/.idea/jsLinters/eslint.xml new file mode 100644 index 0000000..541945b --- /dev/null +++ b/.idea/jsLinters/eslint.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/apps/backend/src/app.module.ts b/apps/backend/src/app.module.ts index 3713aaa..c7e8200 100644 --- a/apps/backend/src/app.module.ts +++ b/apps/backend/src/app.module.ts @@ -3,9 +3,10 @@ import { PrismaModule } from 'nestjs-prisma'; import { AppController } from './app.controller'; import { AppService } from './app.service'; +import { UsersModule } from './users/users.module'; @Module({ - imports: [PrismaModule.forRoot({ isGlobal: true })], + imports: [PrismaModule.forRoot({ isGlobal: true }), UsersModule], controllers: [AppController], providers: [AppService], }) diff --git a/apps/backend/src/main.ts b/apps/backend/src/main.ts index bd4dd77..0ecd081 100644 --- a/apps/backend/src/main.ts +++ b/apps/backend/src/main.ts @@ -1,9 +1,15 @@ +import { ValidationPipe } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); + app.useGlobalPipes( + new ValidationPipe({ + whitelist: true, + }) + ); await app.listen(3001); } bootstrap(); diff --git a/apps/backend/src/users/dto/create-user.dto.ts b/apps/backend/src/users/dto/create-user.dto.ts new file mode 100644 index 0000000..b5ec2c1 --- /dev/null +++ b/apps/backend/src/users/dto/create-user.dto.ts @@ -0,0 +1,5 @@ +import { OmitType } from '@nestjs/swagger'; + +import { User } from '../entities/user.entity'; + +export class CreateUserDto extends OmitType(User, ['id']) {} diff --git a/apps/backend/src/users/dto/update-user.dto.ts b/apps/backend/src/users/dto/update-user.dto.ts new file mode 100644 index 0000000..deaf4b4 --- /dev/null +++ b/apps/backend/src/users/dto/update-user.dto.ts @@ -0,0 +1,5 @@ +import { PartialType } from '@nestjs/mapped-types'; + +import { CreateUserDto } from './create-user.dto'; + +export class UpdateUserDto extends PartialType(CreateUserDto) {} diff --git a/apps/backend/src/users/entities/user.entity.ts b/apps/backend/src/users/entities/user.entity.ts new file mode 100644 index 0000000..d7f9113 --- /dev/null +++ b/apps/backend/src/users/entities/user.entity.ts @@ -0,0 +1,29 @@ +import { Role } from '@prisma/client'; +import { IsBoolean, IsEmail, IsEnum, IsNotEmpty, IsNumber, IsOptional, IsPositive, IsString } from 'class-validator'; + +export class User { + @IsNumber() + @IsPositive() + id: number; + + @IsString() + @IsNotEmpty() + name: string; + + @IsEmail() + email: string; + + @IsString() + @IsOptional() + phone: string; + + @IsBoolean() + isDormResident: boolean; + + @IsString() + @IsOptional() + roomNumber: string; + + @IsEnum(Role) + role: Role; +} diff --git a/apps/backend/src/users/users.controller.ts b/apps/backend/src/users/users.controller.ts new file mode 100644 index 0000000..198bb74 --- /dev/null +++ b/apps/backend/src/users/users.controller.ts @@ -0,0 +1,30 @@ +import { Body, Controller, Get, Param, ParseIntPipe, Patch } from '@nestjs/common'; +import { User } from '@prisma/client'; + +import { UpdateUserDto } from './dto/update-user.dto'; +import { UsersService } from './users.service'; + +@Controller('users') +export class UsersController { + constructor(private readonly usersService: UsersService) {} + + @Get() + async findAll() { + return this.usersService.findAll(); + } + + @Get('me') + async getCurrentUser(user: User) { + return this.usersService.findOne(user.id); + } + + @Get(':id') + async findOne(@Param('id', ParseIntPipe) id: number) { + return this.usersService.findOne(id); + } + + @Patch(':id') + update(@Param('id', ParseIntPipe) id: number, @Body() updateUserDto: UpdateUserDto) { + return this.usersService.update(id, updateUserDto); + } +} diff --git a/apps/backend/src/users/users.module.ts b/apps/backend/src/users/users.module.ts new file mode 100644 index 0000000..f57cea5 --- /dev/null +++ b/apps/backend/src/users/users.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; + +import { UsersController } from './users.controller'; +import { UsersService } from './users.service'; + +@Module({ + controllers: [UsersController], + providers: [UsersService], +}) +export class UsersModule {} diff --git a/apps/backend/src/users/users.service.ts b/apps/backend/src/users/users.service.ts new file mode 100644 index 0000000..ba4c204 --- /dev/null +++ b/apps/backend/src/users/users.service.ts @@ -0,0 +1,47 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { Prisma, User } from '@prisma/client'; +import { PrismaService } from 'nestjs-prisma'; + +import { UpdateUserDto } from './dto/update-user.dto'; + +@Injectable() +export class UsersService { + constructor(private readonly prisma: PrismaService) {} + + async findAll(): Promise<{ users: User[] }> { + const users = await this.prisma.user.findMany(); + return { users }; + } + + async findOne(id: number): Promise { + const user = await this.prisma.user.findUnique({ + where: { + id, + }, + }); + + if (!user) { + throw new NotFoundException(`User with id ${id} not found`); + } + + return user; + } + + async update(id: number, updateUserDto: UpdateUserDto) { + try { + return this.prisma.user.update({ + where: { + id, + }, + data: updateUserDto, + }); + } catch (e) { + if (e instanceof Prisma.PrismaClientKnownRequestError) { + if (e.code === 'P2025') { + throw new NotFoundException(`User with id ${id} not found`); + } + } + throw e; + } + } +} diff --git a/yarn.lock b/yarn.lock index 0fb07e9..7428f19 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1037,11 +1037,6 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" -ansi-styles@^6.1.0: - version "6.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" - integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== - any-promise@^1.0.0: version "1.3.0" resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" @@ -1737,11 +1732,6 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -eastasianwidth@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" - integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== - ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -4232,7 +4222,7 @@ streamsearch@^1.1.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string-width@^4.1.0: +string-width@4.1.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3, string-width@^5.1.2: version "4.1.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.1.0.tgz#ba846d1daa97c3c596155308063e075ed1c99aff" integrity sha512-NrX+1dVVh+6Y9dnQ19pR0pP4FiEIlUvdTGn8pw6CKTNq5sgib2nIhmUNT5TAmhWmvKr3WcxBcP3E8nWezuipuQ== @@ -4241,24 +4231,6 @@ string-width@^4.1.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^5.2.0" -string-width@^4.2.0, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^5.0.1, string-width@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" - integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== - dependencies: - eastasianwidth "^0.2.0" - emoji-regex "^9.2.2" - strip-ansi "^7.0.1" - string.prototype.matchall@^4.0.10: version "4.0.11" resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz#1092a72c59268d2abaad76582dccc687c0297e0a" @@ -4908,24 +4880,15 @@ which@^2.0.1: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" - integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== +wrap-ansi@7.0.0, wrap-ansi@^6.0.1, wrap-ansi@^6.2.0, wrap-ansi@^8.1.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" - integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== - dependencies: - ansi-styles "^6.1.0" - string-width "^5.0.1" - strip-ansi "^7.0.1" - wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"