From 5b10fa9879f5173aaf58d20fab23c80ed17b7ad9 Mon Sep 17 00:00:00 2001 From: Max Norman <27299284+maxn990@users.noreply.github.com> Date: Wed, 17 Sep 2025 13:24:16 -0400 Subject: [PATCH 1/3] cleanup --- apps/backend/src/auth/auth.controller.spec.ts | 18 -- apps/backend/src/auth/auth.controller.ts | 92 --------- apps/backend/src/auth/auth.module.ts | 19 -- apps/backend/src/auth/auth.service.spec.ts | 18 -- apps/backend/src/auth/auth.service.ts | 179 ------------------ apps/backend/src/auth/aws-exports.ts | 7 - .../src/auth/dtos/confirm-password.dto.ts | 12 -- apps/backend/src/auth/dtos/delete-user.dto.ts | 6 - .../src/auth/dtos/forgot-password.dto.ts | 6 - .../src/auth/dtos/refresh-token.dto.ts | 9 - .../src/auth/dtos/sign-in-response.dto.ts | 19 -- apps/backend/src/auth/dtos/sign-in.dto.ts | 9 - apps/backend/src/auth/dtos/sign-up.dto.ts | 15 -- apps/backend/src/auth/dtos/verify-user.dto.ts | 9 - apps/backend/src/auth/jwt.strategy.ts | 31 --- .../interceptors/current-user.interceptor.ts | 35 ---- apps/backend/src/users/types.ts | 4 - apps/backend/src/users/user.entity.ts | 21 -- apps/backend/src/users/users.controller.ts | 33 ---- apps/backend/src/users/users.module.ts | 15 -- apps/backend/src/users/users.service.ts | 63 ------ 21 files changed, 620 deletions(-) delete mode 100644 apps/backend/src/auth/auth.controller.spec.ts delete mode 100644 apps/backend/src/auth/auth.controller.ts delete mode 100644 apps/backend/src/auth/auth.module.ts delete mode 100644 apps/backend/src/auth/auth.service.spec.ts delete mode 100644 apps/backend/src/auth/auth.service.ts delete mode 100644 apps/backend/src/auth/aws-exports.ts delete mode 100644 apps/backend/src/auth/dtos/confirm-password.dto.ts delete mode 100644 apps/backend/src/auth/dtos/delete-user.dto.ts delete mode 100644 apps/backend/src/auth/dtos/forgot-password.dto.ts delete mode 100644 apps/backend/src/auth/dtos/refresh-token.dto.ts delete mode 100644 apps/backend/src/auth/dtos/sign-in-response.dto.ts delete mode 100644 apps/backend/src/auth/dtos/sign-in.dto.ts delete mode 100644 apps/backend/src/auth/dtos/sign-up.dto.ts delete mode 100644 apps/backend/src/auth/dtos/verify-user.dto.ts delete mode 100644 apps/backend/src/auth/jwt.strategy.ts delete mode 100644 apps/backend/src/interceptors/current-user.interceptor.ts delete mode 100644 apps/backend/src/users/types.ts delete mode 100644 apps/backend/src/users/user.entity.ts delete mode 100644 apps/backend/src/users/users.controller.ts delete mode 100644 apps/backend/src/users/users.module.ts delete mode 100644 apps/backend/src/users/users.service.ts diff --git a/apps/backend/src/auth/auth.controller.spec.ts b/apps/backend/src/auth/auth.controller.spec.ts deleted file mode 100644 index 27a31e61..00000000 --- a/apps/backend/src/auth/auth.controller.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { AuthController } from './auth.controller'; - -describe('AuthController', () => { - let controller: AuthController; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [AuthController], - }).compile(); - - controller = module.get(AuthController); - }); - - it('should be defined', () => { - expect(controller).toBeDefined(); - }); -}); diff --git a/apps/backend/src/auth/auth.controller.ts b/apps/backend/src/auth/auth.controller.ts deleted file mode 100644 index bb04b6b7..00000000 --- a/apps/backend/src/auth/auth.controller.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { - BadRequestException, - Body, - Controller, - Post, - Request, - UseGuards, -} from '@nestjs/common'; - -import { SignInDto } from './dtos/sign-in.dto'; -import { SignUpDto } from './dtos/sign-up.dto'; -import { AuthService } from './auth.service'; -import { UsersService } from '../users/users.service'; -import { VerifyUserDto } from './dtos/verify-user.dto'; -import { DeleteUserDto } from './dtos/delete-user.dto'; -import { User } from '../users/user.entity'; -import { SignInResponseDto } from './dtos/sign-in-response.dto'; -import { RefreshTokenDto } from './dtos/refresh-token.dto'; -import { AuthGuard } from '@nestjs/passport'; -import { ConfirmPasswordDto } from './dtos/confirm-password.dto'; -import { ForgotPasswordDto } from './dtos/forgot-password.dto'; -import { ApiTags } from '@nestjs/swagger'; - -@ApiTags('Auth') -@Controller('auth') -export class AuthController { - constructor( - private authService: AuthService, - private usersService: UsersService, - ) {} - - @Post('/signup') - async createUser(@Body() signUpDto: SignUpDto): Promise { - // By default, creates a standard user - try { - await this.authService.signup(signUpDto); - } catch (e) { - throw new BadRequestException(e.message); - } - - const user = await this.usersService.create( - signUpDto.email, - signUpDto.firstName, - signUpDto.lastName, - ); - - return user; - } - - // TODO deprecated if verification code is replaced by link - @Post('/verify') - verifyUser(@Body() body: VerifyUserDto): void { - try { - this.authService.verifyUser(body.email, body.verificationCode); - } catch (e) { - throw new BadRequestException(e.message); - } - } - - @Post('/signin') - signin(@Body() signInDto: SignInDto): Promise { - return this.authService.signin(signInDto); - } - - @Post('/refresh') - refresh(@Body() refreshDto: RefreshTokenDto): Promise { - return this.authService.refreshToken(refreshDto); - } - - @Post('/forgotPassword') - forgotPassword(@Body() body: ForgotPasswordDto): Promise { - return this.authService.forgotPassword(body.email); - } - - @Post('/confirmPassword') - confirmPassword(@Body() body: ConfirmPasswordDto): Promise { - return this.authService.confirmForgotPassword(body); - } - - @Post('/delete') - async delete(@Body() body: DeleteUserDto): Promise { - const user = await this.usersService.findOne(body.userId); - - try { - await this.authService.deleteUser(user.email); - } catch (e) { - throw new BadRequestException(e.message); - } - - this.usersService.remove(user.id); - } -} diff --git a/apps/backend/src/auth/auth.module.ts b/apps/backend/src/auth/auth.module.ts deleted file mode 100644 index 09f5965c..00000000 --- a/apps/backend/src/auth/auth.module.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Module } from '@nestjs/common'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { PassportModule } from '@nestjs/passport'; - -import { AuthController } from './auth.controller'; -import { AuthService } from './auth.service'; -import { UsersService } from '../users/users.service'; -import { User } from '../users/user.entity'; -import { JwtStrategy } from './jwt.strategy'; - -@Module({ - imports: [ - TypeOrmModule.forFeature([User]), - PassportModule.register({ defaultStrategy: 'jwt' }), - ], - controllers: [AuthController], - providers: [AuthService, UsersService, JwtStrategy], -}) -export class AuthModule {} diff --git a/apps/backend/src/auth/auth.service.spec.ts b/apps/backend/src/auth/auth.service.spec.ts deleted file mode 100644 index 800ab662..00000000 --- a/apps/backend/src/auth/auth.service.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { AuthService } from './auth.service'; - -describe('AuthService', () => { - let service: AuthService; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [AuthService], - }).compile(); - - service = module.get(AuthService); - }); - - it('should be defined', () => { - expect(service).toBeDefined(); - }); -}); diff --git a/apps/backend/src/auth/auth.service.ts b/apps/backend/src/auth/auth.service.ts deleted file mode 100644 index d78a12dd..00000000 --- a/apps/backend/src/auth/auth.service.ts +++ /dev/null @@ -1,179 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { - AdminDeleteUserCommand, - AdminInitiateAuthCommand, - AttributeType, - CognitoIdentityProviderClient, - ConfirmForgotPasswordCommand, - ConfirmSignUpCommand, - ForgotPasswordCommand, - ListUsersCommand, - SignUpCommand, -} from '@aws-sdk/client-cognito-identity-provider'; - -import CognitoAuthConfig from './aws-exports'; -import { SignUpDto } from './dtos/sign-up.dto'; -import { SignInDto } from './dtos/sign-in.dto'; -import { SignInResponseDto } from './dtos/sign-in-response.dto'; -import { createHmac } from 'crypto'; -import { RefreshTokenDto } from './dtos/refresh-token.dto'; -import { Status } from '../users/types'; -import { ConfirmPasswordDto } from './dtos/confirm-password.dto'; - -@Injectable() -export class AuthService { - private readonly providerClient: CognitoIdentityProviderClient; - private readonly clientSecret: string; - - constructor() { - this.providerClient = new CognitoIdentityProviderClient({ - region: CognitoAuthConfig.region, - credentials: { - accessKeyId: process.env.NX_AWS_ACCESS_KEY, - secretAccessKey: process.env.NX_AWS_SECRET_ACCESS_KEY, - }, - }); - - this.clientSecret = process.env.COGNITO_CLIENT_SECRET; - } - - // Computes secret hash to authenticate this backend to Cognito - // Hash key is the Cognito client secret, message is username + client ID - // Username value depends on the command - // (see https://docs.aws.amazon.com/cognito/latest/developerguide/signing-up-users-in-your-app.html#cognito-user-pools-computing-secret-hash) - calculateHash(username: string): string { - const hmac = createHmac('sha256', this.clientSecret); - hmac.update(username + CognitoAuthConfig.clientId); - return hmac.digest('base64'); - } - - async getUser(userSub: string): Promise { - const listUsersCommand = new ListUsersCommand({ - UserPoolId: CognitoAuthConfig.userPoolId, - Filter: `sub = "${userSub}"`, - }); - - // TODO need error handling - const { Users } = await this.providerClient.send(listUsersCommand); - return Users[0].Attributes; - } - - async signup( - { firstName, lastName, email, password }: SignUpDto, - status: Status = Status.STANDARD, - ): Promise { - // Needs error handling - const signUpCommand = new SignUpCommand({ - ClientId: CognitoAuthConfig.clientId, - SecretHash: this.calculateHash(email), - Username: email, - Password: password, - UserAttributes: [ - { - Name: 'name', - Value: `${firstName} ${lastName}`, - }, - // Optional: add a custom Cognito attribute called "role" that also stores the user's status/role - // If you choose to do so, you'll have to first add this custom attribute in your user pool - { - Name: 'custom:role', - Value: status, - }, - ], - }); - - const response = await this.providerClient.send(signUpCommand); - return response.UserConfirmed; - } - - async verifyUser(email: string, verificationCode: string): Promise { - const confirmCommand = new ConfirmSignUpCommand({ - ClientId: CognitoAuthConfig.clientId, - SecretHash: this.calculateHash(email), - Username: email, - ConfirmationCode: verificationCode, - }); - - await this.providerClient.send(confirmCommand); - } - - async signin({ email, password }: SignInDto): Promise { - const signInCommand = new AdminInitiateAuthCommand({ - AuthFlow: 'ADMIN_USER_PASSWORD_AUTH', - ClientId: CognitoAuthConfig.clientId, - UserPoolId: CognitoAuthConfig.userPoolId, - AuthParameters: { - USERNAME: email, - PASSWORD: password, - SECRET_HASH: this.calculateHash(email), - }, - }); - - const response = await this.providerClient.send(signInCommand); - - return { - accessToken: response.AuthenticationResult.AccessToken, - refreshToken: response.AuthenticationResult.RefreshToken, - idToken: response.AuthenticationResult.IdToken, - }; - } - - // Refresh token hash uses a user's sub (unique ID), not their username (typically their email) - async refreshToken({ - refreshToken, - userSub, - }: RefreshTokenDto): Promise { - const refreshCommand = new AdminInitiateAuthCommand({ - AuthFlow: 'REFRESH_TOKEN_AUTH', - ClientId: CognitoAuthConfig.clientId, - UserPoolId: CognitoAuthConfig.userPoolId, - AuthParameters: { - REFRESH_TOKEN: refreshToken, - SECRET_HASH: this.calculateHash(userSub), - }, - }); - - const response = await this.providerClient.send(refreshCommand); - - return { - accessToken: response.AuthenticationResult.AccessToken, - refreshToken: refreshToken, - idToken: response.AuthenticationResult.IdToken, - }; - } - - async forgotPassword(email: string) { - const forgotCommand = new ForgotPasswordCommand({ - ClientId: CognitoAuthConfig.clientId, - Username: email, - SecretHash: this.calculateHash(email), - }); - - await this.providerClient.send(forgotCommand); - } - - async confirmForgotPassword({ - email, - confirmationCode, - newPassword, - }: ConfirmPasswordDto) { - const confirmComamnd = new ConfirmForgotPasswordCommand({ - ClientId: CognitoAuthConfig.clientId, - SecretHash: this.calculateHash(email), - Username: email, - ConfirmationCode: confirmationCode, - Password: newPassword, - }); - - await this.providerClient.send(confirmComamnd); - } - - async deleteUser(email: string): Promise { - const adminDeleteUserCommand = new AdminDeleteUserCommand({ - Username: email, - UserPoolId: CognitoAuthConfig.userPoolId, - }); - - await this.providerClient.send(adminDeleteUserCommand); - } -} diff --git a/apps/backend/src/auth/aws-exports.ts b/apps/backend/src/auth/aws-exports.ts deleted file mode 100644 index 5c3a2dec..00000000 --- a/apps/backend/src/auth/aws-exports.ts +++ /dev/null @@ -1,7 +0,0 @@ -const CognitoAuthConfig = { - userPoolId: 'USER POOL ID HERE', - clientId: 'CLIENT ID HERE', - region: 'us-east-2', -}; - -export default CognitoAuthConfig; diff --git a/apps/backend/src/auth/dtos/confirm-password.dto.ts b/apps/backend/src/auth/dtos/confirm-password.dto.ts deleted file mode 100644 index ec1d63bb..00000000 --- a/apps/backend/src/auth/dtos/confirm-password.dto.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { IsEmail, IsString } from 'class-validator'; - -export class ConfirmPasswordDto { - @IsEmail() - email: string; - - @IsString() - newPassword: string; - - @IsString() - confirmationCode: string; -} diff --git a/apps/backend/src/auth/dtos/delete-user.dto.ts b/apps/backend/src/auth/dtos/delete-user.dto.ts deleted file mode 100644 index 1a616376..00000000 --- a/apps/backend/src/auth/dtos/delete-user.dto.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { IsPositive } from 'class-validator'; - -export class DeleteUserDto { - @IsPositive() - userId: number; -} diff --git a/apps/backend/src/auth/dtos/forgot-password.dto.ts b/apps/backend/src/auth/dtos/forgot-password.dto.ts deleted file mode 100644 index bbedf083..00000000 --- a/apps/backend/src/auth/dtos/forgot-password.dto.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { IsEmail } from 'class-validator'; - -export class ForgotPasswordDto { - @IsEmail() - email: string; -} diff --git a/apps/backend/src/auth/dtos/refresh-token.dto.ts b/apps/backend/src/auth/dtos/refresh-token.dto.ts deleted file mode 100644 index f67905d3..00000000 --- a/apps/backend/src/auth/dtos/refresh-token.dto.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { IsString } from 'class-validator'; - -export class RefreshTokenDto { - @IsString() - refreshToken: string; - - @IsString() - userSub: string; -} diff --git a/apps/backend/src/auth/dtos/sign-in-response.dto.ts b/apps/backend/src/auth/dtos/sign-in-response.dto.ts deleted file mode 100644 index 571a02f8..00000000 --- a/apps/backend/src/auth/dtos/sign-in-response.dto.ts +++ /dev/null @@ -1,19 +0,0 @@ -export class SignInResponseDto { - /** - * The JWT access token to be passed in API requests - * @example eyJ... - */ - accessToken: string; - - /** - * The JWT refresh token to maintain user sessions by requesting new access tokens - * @example eyJ... - */ - refreshToken: string; - - /** - * The JWT ID token that carries the user's information - * @example eyJ... - */ - idToken: string; -} diff --git a/apps/backend/src/auth/dtos/sign-in.dto.ts b/apps/backend/src/auth/dtos/sign-in.dto.ts deleted file mode 100644 index 51cd9c95..00000000 --- a/apps/backend/src/auth/dtos/sign-in.dto.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { IsEmail, IsString } from 'class-validator'; - -export class SignInDto { - @IsEmail() - email: string; - - @IsString() - password: string; -} diff --git a/apps/backend/src/auth/dtos/sign-up.dto.ts b/apps/backend/src/auth/dtos/sign-up.dto.ts deleted file mode 100644 index 5756f186..00000000 --- a/apps/backend/src/auth/dtos/sign-up.dto.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { IsEmail, IsString } from 'class-validator'; - -export class SignUpDto { - @IsString() - firstName: string; - - @IsString() - lastName: string; - - @IsEmail() - email: string; - - @IsString() - password: string; -} diff --git a/apps/backend/src/auth/dtos/verify-user.dto.ts b/apps/backend/src/auth/dtos/verify-user.dto.ts deleted file mode 100644 index 66391605..00000000 --- a/apps/backend/src/auth/dtos/verify-user.dto.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { IsEmail, IsString } from 'class-validator'; - -export class VerifyUserDto { - @IsEmail() - email: string; - - @IsString() - verificationCode: string; -} diff --git a/apps/backend/src/auth/jwt.strategy.ts b/apps/backend/src/auth/jwt.strategy.ts deleted file mode 100644 index 44d8789d..00000000 --- a/apps/backend/src/auth/jwt.strategy.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { PassportStrategy } from '@nestjs/passport'; -import { passportJwtSecret } from 'jwks-rsa'; -import { ExtractJwt, Strategy } from 'passport-jwt'; - -import CognitoAuthConfig from './aws-exports'; - -@Injectable() -export class JwtStrategy extends PassportStrategy(Strategy) { - constructor() { - const cognitoAuthority = `https://cognito-idp.${CognitoAuthConfig.region}.amazonaws.com/${CognitoAuthConfig.userPoolId}`; - - super({ - jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), - ignoreExpiration: false, - _audience: CognitoAuthConfig.clientId, - issuer: cognitoAuthority, - algorithms: ['RS256'], - secretOrKeyProvider: passportJwtSecret({ - cache: true, - rateLimit: true, - jwksRequestsPerMinute: 5, - jwksUri: cognitoAuthority + '/.well-known/jwks.json', - }), - }); - } - - async validate(payload) { - return { idUser: payload.sub, email: payload.email }; - } -} diff --git a/apps/backend/src/interceptors/current-user.interceptor.ts b/apps/backend/src/interceptors/current-user.interceptor.ts deleted file mode 100644 index 3d5d8297..00000000 --- a/apps/backend/src/interceptors/current-user.interceptor.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { - Injectable, - NestInterceptor, - ExecutionContext, - CallHandler, -} from '@nestjs/common'; -import { AuthService } from '../auth/auth.service'; -import { UsersService } from '../users/users.service'; - -@Injectable() -export class CurrentUserInterceptor implements NestInterceptor { - constructor( - private authService: AuthService, - private usersService: UsersService, - ) {} - - async intercept(context: ExecutionContext, handler: CallHandler) { - const request = context.switchToHttp().getRequest(); - const cognitoUserAttributes = await this.authService.getUser( - request.user.idUser, - ); - const userEmail = cognitoUserAttributes.find( - (attribute) => attribute.Name === 'email', - ).Value; - const users = await this.usersService.find(userEmail); - - if (users.length > 0) { - const user = users[0]; - - request.user = user; - } - - return handler.handle(); - } -} diff --git a/apps/backend/src/users/types.ts b/apps/backend/src/users/types.ts deleted file mode 100644 index dd9a359b..00000000 --- a/apps/backend/src/users/types.ts +++ /dev/null @@ -1,4 +0,0 @@ -export enum Status { - ADMIN = 'ADMIN', - STANDARD = 'STANDARD', -} diff --git a/apps/backend/src/users/user.entity.ts b/apps/backend/src/users/user.entity.ts deleted file mode 100644 index 3224019c..00000000 --- a/apps/backend/src/users/user.entity.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Entity, Column, ObjectIdColumn, ObjectId } from 'typeorm'; - -import type { Status } from './types'; - -@Entity() -export class User { - @Column({ primary: true }) - id: number; - - @Column() - status: Status; - - @Column() - firstName: string; - - @Column() - lastName: string; - - @Column() - email: string; -} diff --git a/apps/backend/src/users/users.controller.ts b/apps/backend/src/users/users.controller.ts deleted file mode 100644 index 4d0f9e82..00000000 --- a/apps/backend/src/users/users.controller.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { - Controller, - Delete, - Get, - Param, - ParseIntPipe, - UseGuards, - UseInterceptors, -} from '@nestjs/common'; -import { UsersService } from './users.service'; -import { AuthGuard } from '@nestjs/passport'; -import { User } from './user.entity'; -import { CurrentUserInterceptor } from '../interceptors/current-user.interceptor'; -import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; - -@ApiTags('Users') -@ApiBearerAuth() -@Controller('users') -@UseGuards(AuthGuard('jwt')) -@UseInterceptors(CurrentUserInterceptor) -export class UsersController { - constructor(private usersService: UsersService) {} - - @Get('/:userId') - async getUser(@Param('userId', ParseIntPipe) userId: number): Promise { - return this.usersService.findOne(userId); - } - - @Delete('/:id') - removeUser(@Param('id') id: string) { - return this.usersService.remove(parseInt(id)); - } -} diff --git a/apps/backend/src/users/users.module.ts b/apps/backend/src/users/users.module.ts deleted file mode 100644 index 577c2371..00000000 --- a/apps/backend/src/users/users.module.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Module } from '@nestjs/common'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { UsersController } from './users.controller'; -import { UsersService } from './users.service'; -import { User } from './user.entity'; -import { JwtStrategy } from '../auth/jwt.strategy'; -import { CurrentUserInterceptor } from '../interceptors/current-user.interceptor'; -import { AuthService } from '../auth/auth.service'; - -@Module({ - imports: [TypeOrmModule.forFeature([User])], - controllers: [UsersController], - providers: [UsersService, AuthService, JwtStrategy, CurrentUserInterceptor], -}) -export class UsersModule {} diff --git a/apps/backend/src/users/users.service.ts b/apps/backend/src/users/users.service.ts deleted file mode 100644 index 018a7678..00000000 --- a/apps/backend/src/users/users.service.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { Injectable, NotFoundException } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; - -import { User } from './user.entity'; -import { Status } from './types'; - -@Injectable() -export class UsersService { - constructor(@InjectRepository(User) private repo: Repository) {} - - async create( - email: string, - firstName: string, - lastName: string, - status: Status = Status.STANDARD, - ) { - const userId = (await this.repo.count()) + 1; - const user = this.repo.create({ - id: userId, - status, - firstName, - lastName, - email, - }); - - return this.repo.save(user); - } - - findOne(id: number) { - if (!id) { - return null; - } - - return this.repo.findOneBy({ id }); - } - - find(email: string) { - return this.repo.find({ where: { email } }); - } - - async update(id: number, attrs: Partial) { - const user = await this.findOne(id); - - if (!user) { - throw new NotFoundException('User not found'); - } - - Object.assign(user, attrs); - - return this.repo.save(user); - } - - async remove(id: number) { - const user = await this.findOne(id); - - if (!user) { - throw new NotFoundException('User not found'); - } - - return this.repo.remove(user); - } -} From e6d0486127f9253b366296d02b1c861ce1b187ce Mon Sep 17 00:00:00 2001 From: amywng <147568742+amywng@users.noreply.github.com> Date: Wed, 17 Sep 2025 18:47:39 -0400 Subject: [PATCH 2/3] setup --- README.md | 10 +- apps/backend/src/app.module.ts | 11 +- apps/backend/src/data-source.ts | 4 +- .../src/label/dtos/create-label.dto.ts | 4 + .../src/label/dtos/update-single-label.dto.ts | 3 + .../src/label/label.controller.spec.ts | 0 apps/backend/src/label/label.controller.ts | 68 ++++++++ apps/backend/src/label/label.module.ts | 15 ++ apps/backend/src/label/label.service.spec.ts | 0 apps/backend/src/label/label.service.ts | 44 ++++++ apps/backend/src/label/types/label.entity.ts | 5 + .../src/migrations/1754254886189-add_task.ts | 40 ++++- apps/backend/src/task/dtos/create-task.dto.ts | 4 + .../src/task/dtos/update-labels.dto.ts | 3 + apps/backend/src/task/dtos/update-task.dto.ts | 3 + apps/backend/src/task/task.controller.spec.ts | 0 apps/backend/src/task/task.controller.ts | 147 ++++++++++++++++++ apps/backend/src/task/task.module.ts | 16 ++ apps/backend/src/task/task.service.spec.ts | 0 apps/backend/src/task/task.service.ts | 63 ++++++++ apps/backend/src/task/types/category.ts | 2 + apps/backend/src/task/types/task.entity.ts | 7 + shared | 1 - 23 files changed, 433 insertions(+), 17 deletions(-) create mode 100644 apps/backend/src/label/dtos/create-label.dto.ts create mode 100644 apps/backend/src/label/dtos/update-single-label.dto.ts create mode 100644 apps/backend/src/label/label.controller.spec.ts create mode 100644 apps/backend/src/label/label.controller.ts create mode 100644 apps/backend/src/label/label.module.ts create mode 100644 apps/backend/src/label/label.service.spec.ts create mode 100644 apps/backend/src/label/label.service.ts create mode 100644 apps/backend/src/label/types/label.entity.ts create mode 100644 apps/backend/src/task/dtos/create-task.dto.ts create mode 100644 apps/backend/src/task/dtos/update-labels.dto.ts create mode 100644 apps/backend/src/task/dtos/update-task.dto.ts create mode 100644 apps/backend/src/task/task.controller.spec.ts create mode 100644 apps/backend/src/task/task.controller.ts create mode 100644 apps/backend/src/task/task.module.ts create mode 100644 apps/backend/src/task/task.service.spec.ts create mode 100644 apps/backend/src/task/task.service.ts create mode 100644 apps/backend/src/task/types/category.ts create mode 100644 apps/backend/src/task/types/task.entity.ts delete mode 160000 shared diff --git a/README.md b/README.md index 08515a9f..463203bd 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Scaffolding +# Jumpstart @@ -32,10 +32,4 @@ To run both the frontend and backend with one command: ``` nx run-many -t serve -p frontend backend -``` - -## Other commands - -Run `git submodule update --remote` to pull the latest changes from the component library - -When cloning the repo, make sure to add the `--recurse-modules` flag to also clone the component library submodule (e.g. `git clone --recurse-submodules https://github.com/Code-4-Community/scaffolding.git` for the `scaffolding` repo) +``` \ No newline at end of file diff --git a/apps/backend/src/app.module.ts b/apps/backend/src/app.module.ts index 54fb044e..8ed15a46 100644 --- a/apps/backend/src/app.module.ts +++ b/apps/backend/src/app.module.ts @@ -1,14 +1,21 @@ +/* import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { AppController } from './app.controller'; import { AppService } from './app.service'; -import { TaskModule } from './task/task.module'; import AppDataSource from './data-source'; +// import { TaskModule } from './task/task.module'; +///import { LabelModule } from './label/label.module'; @Module({ - imports: [TypeOrmModule.forRoot(AppDataSource.options), TaskModule], + imports: [ + TypeOrmModule.forRoot(AppDataSource.options), + //TaskModule, + //LabelModule, + ], controllers: [AppController], providers: [AppService], }) export class AppModule {} +*/ \ No newline at end of file diff --git a/apps/backend/src/data-source.ts b/apps/backend/src/data-source.ts index 4cd06624..58ba04c5 100644 --- a/apps/backend/src/data-source.ts +++ b/apps/backend/src/data-source.ts @@ -1,6 +1,6 @@ import { DataSource } from 'typeorm'; import { PluralNamingStrategy } from './strategies/plural-naming.strategy'; -import { Task } from './task/types/task.entity'; +//import { Task } from './task/types/task.entity'; import * as dotenv from 'dotenv'; dotenv.config(); @@ -12,7 +12,7 @@ const AppDataSource = new DataSource({ username: process.env.NX_DB_USERNAME, password: process.env.NX_DB_PASSWORD, database: process.env.NX_DB_DATABASE, - entities: [Task], + //entities: [Task], migrations: ['apps/backend/src/migrations/*.js'], // Setting synchronize: true shouldn't be used in production - otherwise you can lose production data synchronize: false, diff --git a/apps/backend/src/label/dtos/create-label.dto.ts b/apps/backend/src/label/dtos/create-label.dto.ts new file mode 100644 index 00000000..f2a0ea8f --- /dev/null +++ b/apps/backend/src/label/dtos/create-label.dto.ts @@ -0,0 +1,4 @@ +// should include a name (string) and hex color (string) +export class CreateLabelDTO { + +} \ No newline at end of file diff --git a/apps/backend/src/label/dtos/update-single-label.dto.ts b/apps/backend/src/label/dtos/update-single-label.dto.ts new file mode 100644 index 00000000..4f18c769 --- /dev/null +++ b/apps/backend/src/label/dtos/update-single-label.dto.ts @@ -0,0 +1,3 @@ +// should include an optional name (string) and optional hex color (string) +export class UpdateSingleLabelDTO { +} \ No newline at end of file diff --git a/apps/backend/src/label/label.controller.spec.ts b/apps/backend/src/label/label.controller.spec.ts new file mode 100644 index 00000000..e69de29b diff --git a/apps/backend/src/label/label.controller.ts b/apps/backend/src/label/label.controller.ts new file mode 100644 index 00000000..7ca90908 --- /dev/null +++ b/apps/backend/src/label/label.controller.ts @@ -0,0 +1,68 @@ +/*import { + Controller, + Get, + Post, + Body, + Patch, + Param, + Delete, +} from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; +import { LabelsService } from './label.service'; +import { Label } from './types/label.entity'; +import { CreateLabelDTO } from './dtos/create-label.dto'; +import { UpdateSingleLabelDTO } from './dtos/update-single-label.dto'; + +@ApiTags('labels') +@Controller('labels') +export class LabelsController { + constructor(private readonly labelsService: LabelsService) {} + + /** Creates a new label. + * @param LabelDto - The data transfer object containing label details. + * @returns The created label. + * @throws BadRequestException if the label name is not unique + * @throws BadRequestException if label name is not provided + * @throws BadRequestException if color is not provided or is not hexadecimal + + @Post('/label') + async createLabel(@Body() labelDto: CreateLabelDTO): Promise