From 32269ff79198fffb95d7b226df8367607377d4b4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 9 Oct 2025 17:43:41 +0000 Subject: [PATCH 01/18] chore(deps): update dependency firebase to v12.4.0 (#345) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 76 +++++++++++++++++++++++-------------------------------- 1 file changed, 32 insertions(+), 44 deletions(-) diff --git a/yarn.lock b/yarn.lock index 4726b600..8596f1c1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -448,10 +448,10 @@ resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-3.2.0.tgz#13ed8212f3b9ba697611529d15347f8528058cea" integrity sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA== -"@firebase/ai@2.3.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@firebase/ai/-/ai-2.3.0.tgz#96c77dbd13adea9c2f7691ddaa91960201954684" - integrity sha512-rVZgf4FszXPSFVIeWLE8ruLU2JDmPXw4XgghcC0x/lK9veGJIyu+DvyumjreVhW/RwD3E5cNPWxQunzylhf/6w== +"@firebase/ai@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@firebase/ai/-/ai-2.4.0.tgz#fe67130f8b8770aa74c028f511dc61b67440a30a" + integrity sha512-YilG6AJ/nYpCKtxZyvEzBRAQv5bU+2tBOKX4Ps0rNNSdxN39aT37kGhjATbk1kq1z5Lq7mkWglw/ajAF3lOWUg== dependencies: "@firebase/app-check-interop-types" "0.3.3" "@firebase/component" "0.7.0" @@ -459,12 +459,12 @@ "@firebase/util" "1.13.0" tslib "^2.1.0" -"@firebase/analytics-compat@0.2.24": - version "0.2.24" - resolved "https://registry.yarnpkg.com/@firebase/analytics-compat/-/analytics-compat-0.2.24.tgz#806c34ddd5c4869006eead08bfde575972d73ce2" - integrity sha512-jE+kJnPG86XSqGQGhXXYt1tpTbCTED8OQJ/PQ90SEw14CuxRxx/H+lFbWA1rlFtFSsTCptAJtgyRBwr/f00vsw== +"@firebase/analytics-compat@0.2.25": + version "0.2.25" + resolved "https://registry.yarnpkg.com/@firebase/analytics-compat/-/analytics-compat-0.2.25.tgz#1f48bb6237bed7d6a3cf8136957aa5ceb245507b" + integrity sha512-fdzoaG0BEKbqksRDhmf4JoyZf16Wosrl0Y7tbZtJyVDOOwziE0vrFjmZuTdviL0yhak+Nco6rMsUUbkbD+qb6Q== dependencies: - "@firebase/analytics" "0.10.18" + "@firebase/analytics" "0.10.19" "@firebase/analytics-types" "0.8.3" "@firebase/component" "0.7.0" "@firebase/util" "1.13.0" @@ -475,10 +475,10 @@ resolved "https://registry.yarnpkg.com/@firebase/analytics-types/-/analytics-types-0.8.3.tgz#d08cd39a6209693ca2039ba7a81570dfa6c1518f" integrity sha512-VrIp/d8iq2g501qO46uGz3hjbDb8xzYMrbu8Tp0ovzIzrvJZ2fvmj649gTjge/b7cCCcjT0H37g1gVtlNhnkbg== -"@firebase/analytics@0.10.18": - version "0.10.18" - resolved "https://registry.yarnpkg.com/@firebase/analytics/-/analytics-0.10.18.tgz#930d43504a02fe0128a8d82f8c5361911b0dbd04" - integrity sha512-iN7IgLvM06iFk8BeFoWqvVpRFW3Z70f+Qe2PfCJ7vPIgLPjHXDE774DhCT5Y2/ZU/ZbXPDPD60x/XPWEoZLNdg== +"@firebase/analytics@0.10.19": + version "0.10.19" + resolved "https://registry.yarnpkg.com/@firebase/analytics/-/analytics-0.10.19.tgz#6bddeb9db287fa2367066855b12ec514e2914697" + integrity sha512-3wU676fh60gaiVYQEEXsbGS4HbF2XsiBphyvvqDbtC1U4/dO4coshbYktcCHq+HFaGIK07iHOh4pME0hEq1fcg== dependencies: "@firebase/component" "0.7.0" "@firebase/installations" "0.6.19" @@ -518,12 +518,12 @@ "@firebase/util" "1.13.0" tslib "^2.1.0" -"@firebase/app-compat@0.5.3": - version "0.5.3" - resolved "https://registry.yarnpkg.com/@firebase/app-compat/-/app-compat-0.5.3.tgz#efc5d6dac111af6670ece288f9ef17af48281566" - integrity sha512-rRK9YOvgsAU/+edjgubL1q1FyCMjBZZs+fAWtD36tklawkh6WZV07sNLVSceuni+a21oby6xoad+3R8dfztOrA== +"@firebase/app-compat@0.5.4": + version "0.5.4" + resolved "https://registry.yarnpkg.com/@firebase/app-compat/-/app-compat-0.5.4.tgz#eb8f47d51c57887c9979279d4d39e4ee85270fe4" + integrity sha512-T7ifGmb+awJEcp542Ek4HtNfBxcBrnuk1ggUdqyFEdsXHdq7+wVlhvE6YukTL7NS8hIkEfL7TMAPx/uCNqt30g== dependencies: - "@firebase/app" "0.14.3" + "@firebase/app" "0.14.4" "@firebase/component" "0.7.0" "@firebase/logger" "0.5.0" "@firebase/util" "1.13.0" @@ -534,10 +534,10 @@ resolved "https://registry.yarnpkg.com/@firebase/app-types/-/app-types-0.9.3.tgz#8408219eae9b1fb74f86c24e7150a148460414ad" integrity sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw== -"@firebase/app@0.14.3": - version "0.14.3" - resolved "https://registry.yarnpkg.com/@firebase/app/-/app-0.14.3.tgz#45fd8dade5a7566e53966c5a19ca98005ebae4ed" - integrity sha512-by1leTfZkwGycPKRWpc+p5/IhpnOj8zaScVi4RRm9fMoFYS3IE87Wzx1Yf/ruVYowXOEuLqYY3VmJw5tU3+0Bg== +"@firebase/app@0.14.4": + version "0.14.4" + resolved "https://registry.yarnpkg.com/@firebase/app/-/app-0.14.4.tgz#1d2ce74c09752dec9664e2f981b20335c4efbec1" + integrity sha512-pUxEGmR+uu21OG/icAovjlu1fcYJzyVhhT0rsCrn+zi+nHtrS43Bp9KPn9KGa4NMspCUE++nkyiqziuIvJdwzw== dependencies: "@firebase/component" "0.7.0" "@firebase/logger" "0.5.0" @@ -2063,7 +2063,7 @@ dependencies: "@types/express" "*" -"@types/node@*": +"@types/node@*", "@types/node@>=12.12.47", "@types/node@>=13.7.0": version "24.7.0" resolved "https://registry.yarnpkg.com/@types/node/-/node-24.7.0.tgz#a34c9f0d3401db396782e440317dd5d8373c286f" integrity sha512-IbKooQVqUBrlzWTi79E8Fw78l8k1RNtlDDNWsFZs7XonuQSJ8oNYfEeclhprUldXISRMLzBpILuKgPlIxm+/Yw== @@ -2077,13 +2077,6 @@ dependencies: undici-types "~7.10.0" -"@types/node@>=12.12.47", "@types/node@>=13.7.0": - version "24.5.2" - resolved "https://registry.yarnpkg.com/@types/node/-/node-24.5.2.tgz#52ceb83f50fe0fcfdfbd2a9fab6db2e9e7ef6446" - integrity sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ== - dependencies: - undici-types "~7.12.0" - "@types/node@^22.10.5", "@types/node@^22.8.7": version "22.18.8" resolved "https://registry.yarnpkg.com/@types/node/-/node-22.18.8.tgz#738d9dafa38f6e0c467687c158f8e1ca2d7d8eaa" @@ -4200,17 +4193,17 @@ firebase-admin@^13.0.2: "@google-cloud/storage" "^7.14.0" firebase@^12.0.0: - version "12.3.0" - resolved "https://registry.yarnpkg.com/firebase/-/firebase-12.3.0.tgz#fc24c1c91d4001e747c3b3dd452e250c4aa862ff" - integrity sha512-/JVja0IDO8zPETGv4TvvBwo7RwcQFz+RQ3JBETNtUSeqsDdI9G7fhRTkCy1sPKnLzW0xpm/kL8GOj6ncndTT3g== - dependencies: - "@firebase/ai" "2.3.0" - "@firebase/analytics" "0.10.18" - "@firebase/analytics-compat" "0.2.24" - "@firebase/app" "0.14.3" + version "12.4.0" + resolved "https://registry.yarnpkg.com/firebase/-/firebase-12.4.0.tgz#c9de52c23e707d90aa43343ab3ad1c1ce6b71301" + integrity sha512-/chNgDQ6ppPPGOQO4jctxOa/5JeQxuhaxA7Y90K0I+n/wPfoO8mRveedhVUdo7ExLcWUivnnow/ouSLYSI5Icw== + dependencies: + "@firebase/ai" "2.4.0" + "@firebase/analytics" "0.10.19" + "@firebase/analytics-compat" "0.2.25" + "@firebase/app" "0.14.4" "@firebase/app-check" "0.11.0" "@firebase/app-check-compat" "0.4.0" - "@firebase/app-compat" "0.5.3" + "@firebase/app-compat" "0.5.4" "@firebase/app-types" "0.9.3" "@firebase/auth" "1.11.0" "@firebase/auth-compat" "0.6.0" @@ -7868,11 +7861,6 @@ undici-types@~7.10.0: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.10.0.tgz#4ac2e058ce56b462b056e629cc6a02393d3ff350" integrity sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag== -undici-types@~7.12.0: - version "7.12.0" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.12.0.tgz#15c5c7475c2a3ba30659529f5cdb4674b622fafb" - integrity sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ== - undici-types@~7.14.0: version "7.14.0" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.14.0.tgz#4c037b32ca4d7d62fae042174604341588bc0840" From 822975bfc1db9cf239d613ddae0fb99f6a1fe30a Mon Sep 17 00:00:00 2001 From: joeboppell Date: Thu, 9 Oct 2025 14:07:08 -0400 Subject: [PATCH 02/18] removed indexes from migration file --- .../20250918152536_add_reservations_table.ts | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/db/migrations/20250918152536_add_reservations_table.ts b/db/migrations/20250918152536_add_reservations_table.ts index 351ad496..5c3b685c 100644 --- a/db/migrations/20250918152536_add_reservations_table.ts +++ b/db/migrations/20250918152536_add_reservations_table.ts @@ -1,14 +1,6 @@ import type { Knex } from "knex"; export async function up(knex: Knex): Promise { - await knex.schema.alterTable("locations", (table) => { - table.index("id"); - }); - - await knex.schema.alterTable("teams", (table) => { - table.index("id"); - }); - await knex.schema.createTable("reservations", (table) => { table.increments("id").primary().notNullable(); table.bigInteger("start_time").unsigned().notNullable(); @@ -46,9 +38,4 @@ export async function up(knex: Knex): Promise { export async function down(knex: Knex): Promise { await knex.schema.dropTableIfExists("reservations"); - - // Remove the index we added - await knex.schema.alterTable("locations", (table) => { - table.dropIndex("id"); - }); } From b1fd282ab898e18da96f99a5a11c7ce688dee58b Mon Sep 17 00:00:00 2001 From: joeboppell Date: Thu, 9 Oct 2025 16:31:27 -0400 Subject: [PATCH 03/18] change location role to none for reservations --- src/modules/location/location.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/location/location.controller.ts b/src/modules/location/location.controller.ts index 7252515f..d648378c 100644 --- a/src/modules/location/location.controller.ts +++ b/src/modules/location/location.controller.ts @@ -36,7 +36,7 @@ export class LocationController { ) {} @Get("/") - @Roles(Role.TEAM) + @Roles(Role.NONE) @ApiDoc({ summary: "Find All Locations", response: { From fbbc29b78efbd77ade0c5d397640286a17039cae Mon Sep 17 00:00:00 2001 From: Kanishk Sachdev Date: Thu, 9 Oct 2025 19:07:22 -0400 Subject: [PATCH 04/18] fuck this relationship on the DB --- src/entities/reservation.entity.ts | 18 ---------- .../reservation/reservation.controller.ts | 33 +++++++++---------- .../reservation/reservation.service.ts | 31 ++++++++++++++--- 3 files changed, 42 insertions(+), 40 deletions(-) diff --git a/src/entities/reservation.entity.ts b/src/entities/reservation.entity.ts index df614ce1..226f356e 100644 --- a/src/entities/reservation.entity.ts +++ b/src/entities/reservation.entity.ts @@ -11,24 +11,6 @@ export enum ReservationType { @Table({ name: "reservations", hackathonId: "hackathonId", - relationMappings: { - location: { - relation: Entity.BelongsToOneRelation, - modelClass: "location.entity.js", - join: { - from: "reservations.locationId", - to: "locations.id", - }, - }, - hackathon: { - relation: Entity.BelongsToOneRelation, - modelClass: "hackathon.entity.js", - join: { - from: "reservations.hackathonId", - to: "hackathons.id", - }, - }, - }, }) export class Reservation extends Entity { @ApiProperty() diff --git a/src/modules/reservation/reservation.controller.ts b/src/modules/reservation/reservation.controller.ts index 1f058956..8383c3ed 100644 --- a/src/modules/reservation/reservation.controller.ts +++ b/src/modules/reservation/reservation.controller.ts @@ -9,6 +9,7 @@ import { Post, Query, Req, + UnauthorizedException, UseFilters, ValidationPipe, } from "@nestjs/common"; @@ -64,12 +65,6 @@ export class UpdateReservationEntity { endTime?: number; } -class ReservationQueryParams { - @ApiProperty() - @IsString() - hackathonId: string; -} - @ApiTags("Reservations") @Controller("reservations") @UseFilters(DBExceptionFilter) @@ -84,15 +79,12 @@ export class ReservationController { ok: { type: [ReservationEntity] }, }, }) - async getReservations( - @Query(new ValidationPipe({ transform: true })) - query: ReservationQueryParams, - ) { - return this.reservationService.getReservations(query.hackathonId); + async getReservations() { + return this.reservationService.getReservations(); } @Post("/") - @Roles(Role.TEAM) + @Roles(Role.NONE) @ApiDoc({ summary: "Create Team Reservation", request: { @@ -108,7 +100,12 @@ export class ReservationController { data: CreateReservationEntity, @Req() req: any, ) { - return this.reservationService.createReservation(data, req.user?.sub); + if (!req.user || !("sub" in req.user)) { + throw new UnauthorizedException(); + } + + const userId = String(req.user.sub); + return await this.reservationService.createReservation(data, userId); } @Delete("/:id") @@ -127,9 +124,11 @@ export class ReservationController { }, }) async cancelReservation(@Param("id") reservationId: string, @Req() req: any) { - await this.reservationService.cancelReservation( - reservationId, - req.user?.sub, - ); + if (!req.user || !("sub" in req.user)) { + throw new UnauthorizedException(); + } + + const userId = String(req.user.sub); + await this.reservationService.cancelReservation(reservationId, userId); } } diff --git a/src/modules/reservation/reservation.service.ts b/src/modules/reservation/reservation.service.ts index 676347ca..58b1980c 100644 --- a/src/modules/reservation/reservation.service.ts +++ b/src/modules/reservation/reservation.service.ts @@ -13,6 +13,7 @@ import { Organizer } from "entities/organizer.entity"; import { Role } from "common/gcp"; import { FirebaseAuthService } from "common/gcp"; import { v4 as uuidv4 } from "uuid"; +import { nanoid } from "nanoid"; export interface UpdateReservationDto { reservationID: string; @@ -44,6 +45,14 @@ export class ReservationService { private readonly firebaseAuthService: FirebaseAuthService, ) {} + async getActiveHackathonId(): Promise { + const hackathon = await Hackathon.query().findOne({ active: true }); + if (!hackathon) { + throw new NotFoundException("Active hackathon not found"); + } + return hackathon.id; + } + async createReservation( data: CreateReservationDto, userId: string, @@ -52,18 +61,23 @@ export class ReservationService { const isOrganizer = await this.isUserOrganizer(userId); // If not an organizer, validate team constraints + console.log("isOrganizer:", isOrganizer); if (!isOrganizer) { + console.log("validating reservation for user:", userId); await this.validateReservation(data, userId); } else { + console.log("organizer creating reservation"); // For organizers, just validate basic constraints await this.validateBasicConstraints(data); + console.log("validated basic constraints for organizer"); await this.checkConflicts(data); } + console.log("creating reservation:", data); - const reservation = await this.reservationRepo + const reservation = this.reservationRepo .createOne({ locationId: data.locationId, - teamId: data.teamId, + teamId: data.teamId, // Organizer reservations are not team-specific startTime: data.startTime, endTime: data.endTime, hackathonId: data.hackathonId, @@ -71,7 +85,8 @@ export class ReservationService { ? ReservationType.ADMIN : ReservationType.PARTICIPANT, }) - .exec(); + .byHackathon(data.hackathonId).execute(); + console.log("created reservation:", reservation); return reservation; } @@ -89,7 +104,8 @@ export class ReservationService { // Check if user is an organizer with EXEC or higher role const organizer = await this.organizerRepo.findOne(userId).exec(); - const userPrivilege = await this.firebaseAuthService.getUserPrivilegeFromUid(userId); + const userPrivilege = + await this.firebaseAuthService.getUserPrivilegeFromUid(userId); const hasExecPrivileges = organizer && organizer.isActive && userPrivilege >= Role.EXEC; // If user has EXEC+ privileges, they can delete any reservation @@ -164,7 +180,8 @@ export class ReservationService { return updatedReservation; } - async getReservations(hackathonId: string): Promise { + async getReservations(): Promise { + const hackathonId = await this.getActiveHackathonId(); return Reservation.query() .where("hackathonId", hackathonId) .orderBy("startTime", "asc"); @@ -179,6 +196,7 @@ export class ReservationService { // 2. Validate team constraints await this.validateTeamConstraints(data.teamId, userId, data.hackathonId); + console.log("validated team constraints"); // 3. Check for conflicts (blackouts, capacity, team double booking) await this.checkConflicts(data); @@ -216,6 +234,7 @@ export class ReservationService { if (!location) { throw new NotFoundException("Location not found"); } + console.log("validated basic constraints"); } private async validateTeamConstraints( @@ -304,6 +323,8 @@ export class ReservationService { }); }); + console.log("teamConflicts:", teamConflicts); + if (teamConflicts.length > 0) { throw new BadRequestException( "Team already has a reservation during this time", From 4ff224602907ba7dab4dd8a6fbf124f4abc8537b Mon Sep 17 00:00:00 2001 From: Kanishk Sachdev Date: Thu, 9 Oct 2025 19:15:37 -0400 Subject: [PATCH 05/18] Format --- src/modules/reservation/reservation.service.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/modules/reservation/reservation.service.ts b/src/modules/reservation/reservation.service.ts index 58b1980c..04a0e5b6 100644 --- a/src/modules/reservation/reservation.service.ts +++ b/src/modules/reservation/reservation.service.ts @@ -85,7 +85,8 @@ export class ReservationService { ? ReservationType.ADMIN : ReservationType.PARTICIPANT, }) - .byHackathon(data.hackathonId).execute(); + .byHackathon(data.hackathonId) + .execute(); console.log("created reservation:", reservation); return reservation; @@ -334,6 +335,7 @@ export class ReservationService { private async isUserOrganizer(userId: string): Promise { const organizer = await this.organizerRepo.findOne(userId).exec(); - return organizer !== null && organizer.isActive; + console.log("organizer:", organizer); + return organizer !== null && organizer !== undefined && organizer.isActive; } } From a9d9c107298bd79a6e80f4685cb404a516459321 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 10 Oct 2025 04:33:13 +0000 Subject: [PATCH 06/18] chore(deps): update dependency @types/node to v22.18.9 (#346) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 8596f1c1..c0e60543 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2078,9 +2078,9 @@ undici-types "~7.10.0" "@types/node@^22.10.5", "@types/node@^22.8.7": - version "22.18.8" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.18.8.tgz#738d9dafa38f6e0c467687c158f8e1ca2d7d8eaa" - integrity sha512-pAZSHMiagDR7cARo/cch1f3rXy0AEXwsVsVH09FcyeJVAzCnGgmYis7P3JidtTUjyadhTeSo8TgRPswstghDaw== + version "22.18.9" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.18.9.tgz#5c55a22de88d7bbbb8e1e5f041fa0293b5beae82" + integrity sha512-5yBtK0k/q8PjkMXbTfeIEP/XVYnz1R9qZJ3yUicdEW7ppdDJfe+MqXEhpqDL3mtn4Wvs1u0KLEG0RXzCgNpsSg== dependencies: undici-types "~6.21.0" From 89b5aa025f4cdfea954afaeb9f1a8f4db138ac82 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 10 Oct 2025 22:00:32 +0000 Subject: [PATCH 07/18] chore(deps): update dependency @nestjs/schematics to v11.0.9 (#348) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index c0e60543..d4aef175 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1569,9 +1569,9 @@ tslib "2.8.1" "@nestjs/schematics@^11.0.0", "@nestjs/schematics@^11.0.1": - version "11.0.8" - resolved "https://registry.yarnpkg.com/@nestjs/schematics/-/schematics-11.0.8.tgz#ecc3920016bd88f462aa54d12689b2561799b7a8" - integrity sha512-HKunkzfBYLpNyL/qP5wu0OBKVPrISJLnrB4r6S53fT99pEvopDcJAeIuznSAD1Dx1njUqpbTR/uGyD0xL1y0nw== + version "11.0.9" + resolved "https://registry.yarnpkg.com/@nestjs/schematics/-/schematics-11.0.9.tgz#18a0d128c609be76410f5c7ea02680c8cd297113" + integrity sha512-0NfPbPlEaGwIT8/TCThxLzrlz3yzDNkfRNpbL7FiplKq3w4qXpJg0JYwqgMEJnLQZm3L/L/5XjoyfJHUO3qX9g== dependencies: "@angular-devkit/core" "19.2.17" "@angular-devkit/schematics" "19.2.17" From 4a2378648cc541e96c4644fd2a1f0382fd3f37bb Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 11 Oct 2025 00:30:17 +0000 Subject: [PATCH 08/18] chore(deps): update dependency ts-jest to v29.4.5 (#349) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 15 ++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index e74d29b7..76210b27 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,7 @@ "rimraf": "^6.0.1", "source-map-support": "^0.5.21", "supertest": "^7.0.0", - "ts-jest": "29.4.4", + "ts-jest": "29.4.5", "ts-loader": "^9.5.1", "ts-node": "^10.9.2", "tsconfig-paths": "^4.2.0", diff --git a/yarn.lock b/yarn.lock index d4aef175..1153ddef 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7172,6 +7172,11 @@ semver@^7.3.4, semver@^7.3.5, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semve resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== +semver@^7.7.3: + version "7.7.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946" + integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q== + send@^1.1.0, send@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/send/-/send-1.2.0.tgz#32a7554fb777b831dfa828370f773a3808d37212" @@ -7706,10 +7711,10 @@ ts-api-utils@^2.1.0: resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.1.0.tgz#595f7094e46eed364c13fd23e75f9513d29baf91" integrity sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ== -ts-jest@29.4.4: - version "29.4.4" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.4.4.tgz#fc6fefe28652ed81b8e1381ef8391901d9f81417" - integrity sha512-ccVcRABct5ZELCT5U0+DZwkXMCcOCLi2doHRrKy1nK/s7J7bch6TzJMsrY09WxgUUIP/ITfmcDS8D2yl63rnXw== +ts-jest@29.4.5: + version "29.4.5" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.4.5.tgz#a6b0dc401e521515d5342234be87f1ca96390a6f" + integrity sha512-HO3GyiWn2qvTQA4kTgjDcXiMwYQt68a1Y8+JuLRVpdIzm+UOLSHgl/XqR4c6nzJkq5rOkjc02O2I7P7l/Yof0Q== dependencies: bs-logger "^0.2.6" fast-json-stable-stringify "^2.1.0" @@ -7717,7 +7722,7 @@ ts-jest@29.4.4: json5 "^2.2.3" lodash.memoize "^4.1.2" make-error "^1.3.6" - semver "^7.7.2" + semver "^7.7.3" type-fest "^4.41.0" yargs-parser "^21.1.1" From c2fcb9a6b2bfc8f9d922ea94ad49d5d2866a1b63 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 11 Oct 2025 22:01:57 +0000 Subject: [PATCH 09/18] chore(deps): update dependency @types/node to v22.18.10 (#350) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 1153ddef..412054a1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2078,9 +2078,9 @@ undici-types "~7.10.0" "@types/node@^22.10.5", "@types/node@^22.8.7": - version "22.18.9" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.18.9.tgz#5c55a22de88d7bbbb8e1e5f041fa0293b5beae82" - integrity sha512-5yBtK0k/q8PjkMXbTfeIEP/XVYnz1R9qZJ3yUicdEW7ppdDJfe+MqXEhpqDL3mtn4Wvs1u0KLEG0RXzCgNpsSg== + version "22.18.10" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.18.10.tgz#37f0ac8d2ec41af7bba06f162c85ff409e4e3420" + integrity sha512-anNG/V/Efn/YZY4pRzbACnKxNKoBng2VTFydVu8RRs5hQjikP8CQfaeAV59VFSCzKNp90mXiVXW2QzV56rwMrg== dependencies: undici-types "~6.21.0" From ba3997298d0d82aec45e0307790c2f19f8badce7 Mon Sep 17 00:00:00 2001 From: CruidGals Date: Sun, 12 Oct 2025 00:14:49 -0400 Subject: [PATCH 10/18] Implement endpoint for new inventory item patch --- src/modules/inventory/inventory.controller.ts | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/modules/inventory/inventory.controller.ts b/src/modules/inventory/inventory.controller.ts index 8ed69cb4..750050c5 100644 --- a/src/modules/inventory/inventory.controller.ts +++ b/src/modules/inventory/inventory.controller.ts @@ -4,8 +4,11 @@ import { Body, Controller, Delete, + ForbiddenException, Get, + NotFoundException, Param, + Patch, Post, Req, UsePipes, @@ -60,6 +63,12 @@ class CreateItemDto extends IntersectionType( OptionalItemStatus, ) {} +class UpdateItemDto extends OmitType(BaseCreateItemDto, [ + "categoryId", + "holderLocationId", + "holderOrganizerId" +] as const) {} + // Movement class CreateMovementDto extends OmitType(InventoryMovementEntity, [ "id", @@ -164,6 +173,29 @@ export class InventoryController { return this.itemRepo.createOne(item).exec(); } + @Patch("items/:id") + @Roles(Role.TEAM) + @ApiDoc({ + summary: "Update an inventory item's name, asset tag, serial number, or note", + request: { body: { type: UpdateItemDto }, validate: true }, + params: [{ name: "id" }], + response: { ok: { type: InventoryItemEntity } }, + auth: Role.TEAM + }) + async updateItem(@Param("id") id: string, @Body() dto: CreateItemDto): Promise { + // Validate the dto: item should (at least) have one of the following: name, asset tag, serial number + if (!dto.name.trim() && !dto.assetTag.trim() && !dto.serialNumber.trim()) + throw new ForbiddenException("Item must have a name, asset tag, or serial number"); + + // Get the item + const item = await this.itemRepo.findOne(id).exec(); + if (!item) throw new NotFoundException("Item not found"); + + // Update the new values and patch + Object.assign(item, dto); + return this.itemRepo.patchOne(id, item).exec(); + } + @Delete("items/:id") @Roles(Role.TEAM) @ApiDoc({ From cf5344baa75e6239e6867844edf5045ed2037255 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 13 Oct 2025 21:43:44 +0000 Subject: [PATCH 11/18] chore(deps): update typescript-eslint monorepo to v8.46.1 (#353) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 128 +++++++++++++++++++++++++++--------------------------- 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/yarn.lock b/yarn.lock index 412054a1..8a210b88 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2190,78 +2190,78 @@ "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^8.19.1": - version "8.46.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.0.tgz#fc90b35d8025b5eaa66b2f6c3859cd5381a1e751" - integrity sha512-hA8gxBq4ukonVXPy0OKhiaUh/68D0E88GSmtC1iAEnGaieuDi38LhS7jdCHRLi6ErJBNDGCzvh5EnzdPwUc0DA== + version "8.46.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.1.tgz#20876354024140aabc8b400bc95735fdcade17d5" + integrity sha512-rUsLh8PXmBjdiPY+Emjz9NX2yHvhS11v0SR6xNJkm5GM1MO9ea/1GoDKlHHZGrOJclL/cZ2i/vRUYVtjRhrHVQ== dependencies: "@eslint-community/regexpp" "^4.10.0" - "@typescript-eslint/scope-manager" "8.46.0" - "@typescript-eslint/type-utils" "8.46.0" - "@typescript-eslint/utils" "8.46.0" - "@typescript-eslint/visitor-keys" "8.46.0" + "@typescript-eslint/scope-manager" "8.46.1" + "@typescript-eslint/type-utils" "8.46.1" + "@typescript-eslint/utils" "8.46.1" + "@typescript-eslint/visitor-keys" "8.46.1" graphemer "^1.4.0" ignore "^7.0.0" natural-compare "^1.4.0" ts-api-utils "^2.1.0" "@typescript-eslint/parser@^8.19.1": - version "8.46.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.46.0.tgz#9186f28c59f6e477ab8919312d2654f4f27d45c1" - integrity sha512-n1H6IcDhmmUEG7TNVSspGmiHHutt7iVKtZwRppD7e04wha5MrkV1h3pti9xQLcCMt6YWsncpoT0HMjkH1FNwWQ== - dependencies: - "@typescript-eslint/scope-manager" "8.46.0" - "@typescript-eslint/types" "8.46.0" - "@typescript-eslint/typescript-estree" "8.46.0" - "@typescript-eslint/visitor-keys" "8.46.0" + version "8.46.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.46.1.tgz#81751f46800fc6b01ce1a72760cd17f06e7f395b" + integrity sha512-6JSSaBZmsKvEkbRUkf7Zj7dru/8ZCrJxAqArcLaVMee5907JdtEbKGsZ7zNiIm/UAkpGUkaSMZEXShnN2D1HZA== + dependencies: + "@typescript-eslint/scope-manager" "8.46.1" + "@typescript-eslint/types" "8.46.1" + "@typescript-eslint/typescript-estree" "8.46.1" + "@typescript-eslint/visitor-keys" "8.46.1" debug "^4.3.4" -"@typescript-eslint/project-service@8.46.0": - version "8.46.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.46.0.tgz#1190dcc0d3494d46a85773e0c3a2838cbb2b45a7" - integrity sha512-OEhec0mH+U5Je2NZOeK1AbVCdm0ChyapAyTeXVIYTPXDJ3F07+cu87PPXcGoYqZ7M9YJVvFnfpGg1UmCIqM+QQ== +"@typescript-eslint/project-service@8.46.1": + version "8.46.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.46.1.tgz#07be0e6f27fa90a17d8e5f6996ee02329c9a8c2e" + integrity sha512-FOIaFVMHzRskXr5J4Jp8lFVV0gz5ngv3RHmn+E4HYxSJ3DgDzU7fVI1/M7Ijh1zf6S7HIoaIOtln1H5y8V+9Zg== dependencies: - "@typescript-eslint/tsconfig-utils" "^8.46.0" - "@typescript-eslint/types" "^8.46.0" + "@typescript-eslint/tsconfig-utils" "^8.46.1" + "@typescript-eslint/types" "^8.46.1" debug "^4.3.4" -"@typescript-eslint/scope-manager@8.46.0": - version "8.46.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.46.0.tgz#a41833fe387044075cb2d4cfab490a7f1dd19b61" - integrity sha512-lWETPa9XGcBes4jqAMYD9fW0j4n6hrPtTJwWDmtqgFO/4HF4jmdH/Q6wggTw5qIT5TXjKzbt7GsZUBnWoO3dqw== +"@typescript-eslint/scope-manager@8.46.1": + version "8.46.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.46.1.tgz#590dd2e65e95af646bdaf50adeae9af39e25e8c1" + integrity sha512-weL9Gg3/5F0pVQKiF8eOXFZp8emqWzZsOJuWRUNtHT+UNV2xSJegmpCNQHy37aEQIbToTq7RHKhWvOsmbM680A== dependencies: - "@typescript-eslint/types" "8.46.0" - "@typescript-eslint/visitor-keys" "8.46.0" + "@typescript-eslint/types" "8.46.1" + "@typescript-eslint/visitor-keys" "8.46.1" -"@typescript-eslint/tsconfig-utils@8.46.0", "@typescript-eslint/tsconfig-utils@^8.46.0": - version "8.46.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.0.tgz#3e33019e0b94838d37d7cc61341fbcc5bf791007" - integrity sha512-WrYXKGAHY836/N7zoK/kzi6p8tXFhasHh8ocFL9VZSAkvH956gfeRfcnhs3xzRy8qQ/dq3q44v1jvQieMFg2cw== +"@typescript-eslint/tsconfig-utils@8.46.1", "@typescript-eslint/tsconfig-utils@^8.46.1": + version "8.46.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.1.tgz#24405888560175c6c209c39df11ac06a2efef9d7" + integrity sha512-X88+J/CwFvlJB+mK09VFqx5FE4H5cXD+H/Bdza2aEWkSb8hnWIQorNcscRl4IEo1Cz9VI/+/r/jnGWkbWPx54g== -"@typescript-eslint/type-utils@8.46.0": - version "8.46.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.46.0.tgz#815efeb11b9533da68fd825628cecf283ac79829" - integrity sha512-hy+lvYV1lZpVs2jRaEYvgCblZxUoJiPyCemwbQZ+NGulWkQRy0HRPYAoef/CNSzaLt+MLvMptZsHXHlkEilaeg== +"@typescript-eslint/type-utils@8.46.1": + version "8.46.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.46.1.tgz#14d4307dd6045f6b48a888cde1513d6ec305537f" + integrity sha512-+BlmiHIiqufBxkVnOtFwjah/vrkF4MtKKvpXrKSPLCkCtAp8H01/VV43sfqA98Od7nJpDcFnkwgyfQbOG0AMvw== dependencies: - "@typescript-eslint/types" "8.46.0" - "@typescript-eslint/typescript-estree" "8.46.0" - "@typescript-eslint/utils" "8.46.0" + "@typescript-eslint/types" "8.46.1" + "@typescript-eslint/typescript-estree" "8.46.1" + "@typescript-eslint/utils" "8.46.1" debug "^4.3.4" ts-api-utils "^2.1.0" -"@typescript-eslint/types@8.46.0", "@typescript-eslint/types@^8.46.0": - version "8.46.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.46.0.tgz#20af6b332f9cd55a15fcd862fdb07d47a6131bf4" - integrity sha512-bHGGJyVjSE4dJJIO5yyEWt/cHyNwga/zXGJbJJ8TiO01aVREK6gCTu3L+5wrkb1FbDkQ+TKjMNe9R/QQQP9+rA== +"@typescript-eslint/types@8.46.1", "@typescript-eslint/types@^8.46.1": + version "8.46.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.46.1.tgz#4c5479538ec10b5508b8e982e172911c987446d8" + integrity sha512-C+soprGBHwWBdkDpbaRC4paGBrkIXxVlNohadL5o0kfhsXqOC6GYH2S/Obmig+I0HTDl8wMaRySwrfrXVP8/pQ== -"@typescript-eslint/typescript-estree@8.46.0": - version "8.46.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.0.tgz#f45a0d5f5e99b26f0280e8cff3ed3380658fd720" - integrity sha512-ekDCUfVpAKWJbRfm8T1YRrCot1KFxZn21oV76v5Fj4tr7ELyk84OS+ouvYdcDAwZL89WpEkEj2DKQ+qg//+ucg== +"@typescript-eslint/typescript-estree@8.46.1": + version "8.46.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.1.tgz#1c146573b942ebe609c156c217ceafdc7a88e6ed" + integrity sha512-uIifjT4s8cQKFQ8ZBXXyoUODtRoAd7F7+G8MKmtzj17+1UbdzFl52AzRyZRyKqPHhgzvXunnSckVu36flGy8cg== dependencies: - "@typescript-eslint/project-service" "8.46.0" - "@typescript-eslint/tsconfig-utils" "8.46.0" - "@typescript-eslint/types" "8.46.0" - "@typescript-eslint/visitor-keys" "8.46.0" + "@typescript-eslint/project-service" "8.46.1" + "@typescript-eslint/tsconfig-utils" "8.46.1" + "@typescript-eslint/types" "8.46.1" + "@typescript-eslint/visitor-keys" "8.46.1" debug "^4.3.4" fast-glob "^3.3.2" is-glob "^4.0.3" @@ -2269,22 +2269,22 @@ semver "^7.6.0" ts-api-utils "^2.1.0" -"@typescript-eslint/utils@8.46.0": - version "8.46.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.46.0.tgz#27025c5ed7cbc928440d6a30edd6ba34cc5b927a" - integrity sha512-nD6yGWPj1xiOm4Gk0k6hLSZz2XkNXhuYmyIrOWcHoPuAhjT9i5bAG+xbWPgFeNR8HPHHtpNKdYUXJl/D3x7f5g== +"@typescript-eslint/utils@8.46.1": + version "8.46.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.46.1.tgz#c572184d9227d66b10a954b90249a20c48b22452" + integrity sha512-vkYUy6LdZS7q1v/Gxb2Zs7zziuXN0wxqsetJdeZdRe/f5dwJFglmuvZBfTUivCtjH725C1jWCDfpadadD95EDQ== dependencies: "@eslint-community/eslint-utils" "^4.7.0" - "@typescript-eslint/scope-manager" "8.46.0" - "@typescript-eslint/types" "8.46.0" - "@typescript-eslint/typescript-estree" "8.46.0" + "@typescript-eslint/scope-manager" "8.46.1" + "@typescript-eslint/types" "8.46.1" + "@typescript-eslint/typescript-estree" "8.46.1" -"@typescript-eslint/visitor-keys@8.46.0": - version "8.46.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.0.tgz#23936809054c511f703713c56ddd2f46dc197845" - integrity sha512-FrvMpAK+hTbFy7vH5j1+tMYHMSKLE6RzluFJlkFNKD0p9YsUT75JlBSmr5so3QRzvMwU5/bIEdeNrxm8du8l3Q== +"@typescript-eslint/visitor-keys@8.46.1": + version "8.46.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.1.tgz#da35f1d58ec407419d68847cfd358b32746ac315" + integrity sha512-ptkmIf2iDkNUjdeu2bQqhFPV1m6qTnFFjg7PPDjxKWaMaP0Z6I9l30Jr3g5QqbZGdw8YdYvLp+XnqnWWZOg/NA== dependencies: - "@typescript-eslint/types" "8.46.0" + "@typescript-eslint/types" "8.46.1" eslint-visitor-keys "^4.2.1" "@ungap/structured-clone@^1.3.0": @@ -7167,12 +7167,12 @@ semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.3.4, semver@^7.3.5, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.7.2: +semver@^7.3.4, semver@^7.3.5, semver@^7.5.3, semver@^7.5.4, semver@^7.7.2: version "7.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== -semver@^7.7.3: +semver@^7.6.0, semver@^7.7.3: version "7.7.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946" integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q== From 649ad1e0473b277860d4de0521fac0ca16ecd8b1 Mon Sep 17 00:00:00 2001 From: joeboppell Date: Tue, 14 Oct 2025 21:32:51 -0400 Subject: [PATCH 12/18] fix file naming for photo upload to include userId --- src/modules/photo/photo.controller.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/modules/photo/photo.controller.ts b/src/modules/photo/photo.controller.ts index e0cc027e..fa0b977a 100644 --- a/src/modules/photo/photo.controller.ts +++ b/src/modules/photo/photo.controller.ts @@ -7,6 +7,7 @@ import { UseInterceptors, Req, Body, + UnauthorizedException, } from "@nestjs/common"; import { Request } from "express"; import { ApiTags } from "@nestjs/swagger"; @@ -51,7 +52,11 @@ export class PhotoController { throw new BadRequestException("Photo is required"); } - const userId = (req.user as any)?.uid; + if (!req.user || !("sub" in req.user)) { + throw new UnauthorizedException(); + } + + const userId = String(req.user.sub); const type = fileType || "default"; try { From ecaa414bcd0dcff96d5fc5a641a3685afe953d83 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 16 Oct 2025 01:58:50 +0000 Subject: [PATCH 13/18] chore(deps): update dependency passkit-generator to v3.5.2 (#356) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 8a210b88..58a3655f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6631,9 +6631,9 @@ parseurl@^1.3.3: integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== passkit-generator@^3.3.1: - version "3.5.0" - resolved "https://registry.yarnpkg.com/passkit-generator/-/passkit-generator-3.5.0.tgz#39bd4e3214dd60cb138b424275208b4ab93b523a" - integrity sha512-/Xx+AK9iSsi0CNH3sAVxh2BTQBnBzfs1mFQPXH2ZlujakUR0Bq+5AWVJKV894jY9bgZ5kKbCgkogf2mjAnhnDg== + version "3.5.2" + resolved "https://registry.yarnpkg.com/passkit-generator/-/passkit-generator-3.5.2.tgz#0747ba6aa4417fad86dd435cc924934269a098a0" + integrity sha512-/ELKMoS+lkdDr1tEhIDgVu6Mh4/QDq/wUEwHOSUh+f9Han0Nh+6WunRzK3e62A6AFL0Q7tnsE/7IsHgnvx4hcQ== dependencies: do-not-zip "^1.0.0" joi "17.4.2" From 6d73a93dfa093eaec8f80d8648cf5bfa35a62e04 Mon Sep 17 00:00:00 2001 From: joeboppell Date: Thu, 16 Oct 2025 00:45:04 -0400 Subject: [PATCH 14/18] fix inventory item patch route --- src/modules/inventory/inventory.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/inventory/inventory.controller.ts b/src/modules/inventory/inventory.controller.ts index 750050c5..419e5391 100644 --- a/src/modules/inventory/inventory.controller.ts +++ b/src/modules/inventory/inventory.controller.ts @@ -182,7 +182,7 @@ export class InventoryController { response: { ok: { type: InventoryItemEntity } }, auth: Role.TEAM }) - async updateItem(@Param("id") id: string, @Body() dto: CreateItemDto): Promise { + async updateItem(@Param("id") id: string, @Body() dto: UpdateItemDto): Promise { // Validate the dto: item should (at least) have one of the following: name, asset tag, serial number if (!dto.name.trim() && !dto.assetTag.trim() && !dto.serialNumber.trim()) throw new ForbiddenException("Item must have a name, asset tag, or serial number"); From 0c2b93e02642cb6de7d86b91bd5821b7345a7f40 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 16 Oct 2025 13:12:38 +0000 Subject: [PATCH 15/18] chore(deps): update dependency @nestjs/swagger to v11.2.1 (#357) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/yarn.lock b/yarn.lock index 58a3655f..3804abdb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1580,16 +1580,16 @@ pluralize "8.0.0" "@nestjs/swagger@^11.0.0": - version "11.2.0" - resolved "https://registry.yarnpkg.com/@nestjs/swagger/-/swagger-11.2.0.tgz#a1b10620a9f90c78edf897a9386dc4f3e014387e" - integrity sha512-5wolt8GmpNcrQv34tIPUtPoV1EeFbCetm40Ij3+M0FNNnf2RJ3FyWfuQvI8SBlcJyfaounYVTKzKHreFXsUyOg== + version "11.2.1" + resolved "https://registry.yarnpkg.com/@nestjs/swagger/-/swagger-11.2.1.tgz#590047ed74e4c8417f0b0f0260d04eea9a1d7daf" + integrity sha512-1MS7xf0pzc1mofG53xrrtrurnziafPUHkqzRm4YUVPA/egeiMaSerQBD/feiAeQ2BnX0WiLsTX4HQFO0icvOjQ== dependencies: "@microsoft/tsdoc" "0.15.1" "@nestjs/mapped-types" "2.1.0" js-yaml "4.1.0" lodash "4.17.21" - path-to-regexp "8.2.0" - swagger-ui-dist "5.21.0" + path-to-regexp "8.3.0" + swagger-ui-dist "5.29.4" "@nestjs/testing@^11.0.0": version "11.1.6" @@ -6703,6 +6703,11 @@ path-to-regexp@8.2.0, path-to-regexp@^8.0.0: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-8.2.0.tgz#73990cc29e57a3ff2a0d914095156df5db79e8b4" integrity sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ== +path-to-regexp@8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-8.3.0.tgz#aa818a6981f99321003a08987d3cec9c3474cd1f" + integrity sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA== + path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" @@ -7568,10 +7573,10 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -swagger-ui-dist@5.21.0: - version "5.21.0" - resolved "https://registry.yarnpkg.com/swagger-ui-dist/-/swagger-ui-dist-5.21.0.tgz#aed230fe6e294c9470217e67697d601e3bb8eb9d" - integrity sha512-E0K3AB6HvQd8yQNSMR7eE5bk+323AUxjtCz/4ZNKiahOlPhPJxqn3UPIGs00cyY/dhrTDJ61L7C/a8u6zhGrZg== +swagger-ui-dist@5.29.4: + version "5.29.4" + resolved "https://registry.yarnpkg.com/swagger-ui-dist/-/swagger-ui-dist-5.29.4.tgz#ba86ff7e18b6b23eecfc32a81220f032d748b468" + integrity sha512-gJFDz/gyLOCQtWwAgqs6Rk78z9ONnqTnlW11gimG9nLap8drKa3AJBKpzIQMIjl5PD2Ix+Tn+mc/tfoT2tgsng== dependencies: "@scarf/scarf" "=1.4.0" From 8d98b309a02a15bfc2c528de6046baf84c87f45a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 17 Oct 2025 08:52:00 +0000 Subject: [PATCH 16/18] chore(deps): update dependency @types/node to v22.18.11 (#359) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 3804abdb..8b43edeb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2078,9 +2078,9 @@ undici-types "~7.10.0" "@types/node@^22.10.5", "@types/node@^22.8.7": - version "22.18.10" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.18.10.tgz#37f0ac8d2ec41af7bba06f162c85ff409e4e3420" - integrity sha512-anNG/V/Efn/YZY4pRzbACnKxNKoBng2VTFydVu8RRs5hQjikP8CQfaeAV59VFSCzKNp90mXiVXW2QzV56rwMrg== + version "22.18.11" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.18.11.tgz#aa8a8ccae8cc828512df642f0d82606b89450d71" + integrity sha512-Gd33J2XIrXurb+eT2ktze3rJAfAp9ZNjlBdh4SVgyrKEOADwCbdUDaK7QgJno8Ue4kcajscsKqu6n8OBG3hhCQ== dependencies: undici-types "~6.21.0" From f7c7901219ce56e0d2f211bbc0605ea5e5b5a9ae Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 18 Oct 2025 03:43:01 +0000 Subject: [PATCH 17/18] chore(deps): update eslint monorepo to v9.38.0 (#360) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 47 +++++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/yarn.lock b/yarn.lock index 8b43edeb..5f77e3be 100644 --- a/yarn.lock +++ b/yarn.lock @@ -387,19 +387,19 @@ resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== -"@eslint/config-array@^0.21.0": - version "0.21.0" - resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.21.0.tgz#abdbcbd16b124c638081766392a4d6b509f72636" - integrity sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ== +"@eslint/config-array@^0.21.1": + version "0.21.1" + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.21.1.tgz#7d1b0060fea407f8301e932492ba8c18aff29713" + integrity sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA== dependencies: - "@eslint/object-schema" "^2.1.6" + "@eslint/object-schema" "^2.1.7" debug "^4.3.1" minimatch "^3.1.2" -"@eslint/config-helpers@^0.4.0": - version "0.4.0" - resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.4.0.tgz#e9f94ba3b5b875e32205cb83fece18e64486e9e6" - integrity sha512-WUFvV4WoIwW8Bv0KeKCIIEgdSiFOsulyN0xrMu+7z43q/hkOLXjvb5u7UC9jDxvRzcrbEmuZBX5yJZz1741jog== +"@eslint/config-helpers@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.4.1.tgz#7d173a1a35fe256f0989a0fdd8d911ebbbf50037" + integrity sha512-csZAzkNhsgwb0I/UAV6/RGFTbiakPCf0ZrGmrIxQpYvGZ00PhTkSnyKNolphgIvmnJeGw6rcGVEXfTzUnFuEvw== dependencies: "@eslint/core" "^0.16.0" @@ -425,15 +425,15 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@9.37.0", "@eslint/js@^9.17.0": - version "9.37.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.37.0.tgz#0cfd5aa763fe5d1ee60bedf84cd14f54bcf9e21b" - integrity sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg== +"@eslint/js@9.38.0", "@eslint/js@^9.17.0": + version "9.38.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.38.0.tgz#f7aa9c7577577f53302c1d795643589d7709ebd1" + integrity sha512-UZ1VpFvXf9J06YG9xQBdnzU+kthors6KjhMAl6f4gH4usHyh31rUf2DLGInT8RFYIReYXNSydgPY0V2LuWgl7A== -"@eslint/object-schema@^2.1.6": - version "2.1.6" - resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.6.tgz#58369ab5b5b3ca117880c0f6c0b0f32f6950f24f" - integrity sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA== +"@eslint/object-schema@^2.1.7": + version "2.1.7" + resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.7.tgz#6e2126a1347e86a4dedf8706ec67ff8e107ebbad" + integrity sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA== "@eslint/plugin-kit@^0.4.0": version "0.4.0" @@ -3827,23 +3827,22 @@ eslint-visitor-keys@^4.2.1: integrity sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ== eslint@^9.17.0: - version "9.37.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.37.0.tgz#ac0222127f76b09c0db63036f4fe289562072d74" - integrity sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig== + version "9.38.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.38.0.tgz#3957d2af804e5cf6cc503c618f60acc71acb2e7e" + integrity sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw== dependencies: "@eslint-community/eslint-utils" "^4.8.0" "@eslint-community/regexpp" "^4.12.1" - "@eslint/config-array" "^0.21.0" - "@eslint/config-helpers" "^0.4.0" + "@eslint/config-array" "^0.21.1" + "@eslint/config-helpers" "^0.4.1" "@eslint/core" "^0.16.0" "@eslint/eslintrc" "^3.3.1" - "@eslint/js" "9.37.0" + "@eslint/js" "9.38.0" "@eslint/plugin-kit" "^0.4.0" "@humanfs/node" "^0.16.6" "@humanwhocodes/module-importer" "^1.0.1" "@humanwhocodes/retry" "^0.4.2" "@types/estree" "^1.0.6" - "@types/json-schema" "^7.0.15" ajv "^6.12.4" chalk "^4.0.0" cross-spawn "^7.0.6" From 882ed335767f264469e10da0d1555b93a4661b4b Mon Sep 17 00:00:00 2001 From: Kanishk Sachdev Date: Sat, 18 Oct 2025 15:41:38 -0400 Subject: [PATCH 18/18] feat(photo): add approval workflow for photos and enhance metadata handling --- src/modules/photo/photo.controller.ts | 112 +++++++++++++++++++++++++- src/modules/photo/photo.service.ts | 64 ++++++++++++++- 2 files changed, 173 insertions(+), 3 deletions(-) diff --git a/src/modules/photo/photo.controller.ts b/src/modules/photo/photo.controller.ts index fa0b977a..91eae910 100644 --- a/src/modules/photo/photo.controller.ts +++ b/src/modules/photo/photo.controller.ts @@ -4,6 +4,8 @@ import { Get, InternalServerErrorException, Post, + Patch, + Param, UseInterceptors, Req, Body, @@ -75,10 +77,10 @@ export class PhotoController { @Get("/") @Roles(Role.NONE) @ApiDoc({ - summary: "Get all photos", + summary: "Get all approved photos", response: { ok: { - description: "List of all photos", + description: "List of all approved photos", schema: { type: "array", items: { @@ -103,4 +105,110 @@ export class PhotoController { throw new InternalServerErrorException("Failed to fetch photos"); } } + + @Get("/pending") + @Roles(Role.TEAM) + @ApiDoc({ + summary: "Get all photos with approval status (admin only)", + response: { + ok: { + description: "List of all photos with approval status", + schema: { + type: "array", + items: { + type: "object", + properties: { + name: { type: "string" }, + url: { type: "string" }, + createdAt: { type: "string", format: "date-time" }, + uploadedBy: { type: "string" }, + approvalStatus: { type: "string" }, + }, + }, + }, + }, + }, + }) + async getAllPendingPhotos(): Promise< + { + name: string; + url: string; + createdAt: Date; + uploadedBy: string; + approvalStatus: string; + }[] + > { + try { + return await this.photoService.getAllPendingPhotos(); + } catch (error) { + console.error("Error fetching pending photos:", error); + throw new InternalServerErrorException("Failed to fetch pending photos"); + } + } + + @Patch("/:filename/approve") + @Roles(Role.TEAM) + @ApiDoc({ + summary: "Approve a photo (admin only)", + response: { + ok: { + description: "Photo approved successfully", + }, + }, + }) + async approvePhoto( + @Param("filename") filename: string, + @Req() req: Request, + ): Promise<{ message: string }> { + if (!req.user || !("sub" in req.user)) { + throw new UnauthorizedException(); + } + + const adminId = String(req.user.sub); + + try { + await this.photoService.updatePhotoApprovalStatus( + filename, + "approved", + adminId, + ); + return { message: "Photo approved successfully" }; + } catch (error) { + console.error("Error approving photo:", error); + throw new InternalServerErrorException("Failed to approve photo"); + } + } + + @Patch("/:filename/reject") + @Roles(Role.TEAM) + @ApiDoc({ + summary: "Reject a photo (admin only)", + response: { + ok: { + description: "Photo rejected successfully", + }, + }, + }) + async rejectPhoto( + @Param("filename") filename: string, + @Req() req: Request, + ): Promise<{ message: string }> { + if (!req.user || !("sub" in req.user)) { + throw new UnauthorizedException(); + } + + const adminId = String(req.user.sub); + + try { + await this.photoService.updatePhotoApprovalStatus( + filename, + "rejected", + adminId, + ); + return { message: "Photo rejected successfully" }; + } catch (error) { + console.error("Error rejecting photo:", error); + throw new InternalServerErrorException("Failed to reject photo"); + } + } } diff --git a/src/modules/photo/photo.service.ts b/src/modules/photo/photo.service.ts index 04a499ed..10bc62ec 100644 --- a/src/modules/photo/photo.service.ts +++ b/src/modules/photo/photo.service.ts @@ -43,7 +43,14 @@ export class PhotoService { const blob = this.photoBucket.file(filename); await blob.save(file.buffer, { - metadata: { contentType: file.mimetype }, + metadata: { + contentType: file.mimetype, + metadata: { + approvalStatus: "pending", + uploadedBy: userId, + uploadedAt: new Date().toISOString(), + }, + }, }); return { photoId, photoUrl: this.getPublicPhotoUrl(filename) }; @@ -54,12 +61,67 @@ export class PhotoService { > { const [files] = await this.photoBucket.getFiles(); + // Filter to only show approved photos + const approvedFiles = files.filter((file) => { + const approvalStatus = file.metadata.metadata?.approvalStatus; + // If metadata is missing, treat as pending (backward compatibility) + // Only show if explicitly approved + return approvalStatus === "approved"; + }); + + return approvedFiles.map((file) => ({ + name: file.name, + url: this.getPublicPhotoUrl(file.name), + createdAt: file.metadata.timeCreated + ? new Date(file.metadata.timeCreated) + : new Date(), + })); + } + + async getAllPendingPhotos(): Promise< + { + name: string; + url: string; + createdAt: Date; + uploadedBy: string; + approvalStatus: string; + }[] + > { + const [files] = await this.photoBucket.getFiles(); + + // Get all photos with their approval status return files.map((file) => ({ name: file.name, url: this.getPublicPhotoUrl(file.name), createdAt: file.metadata.timeCreated ? new Date(file.metadata.timeCreated) : new Date(), + uploadedBy: String(file.metadata.metadata?.uploadedBy || "unknown"), + // If metadata is missing, treat as pending (backward compatibility) + approvalStatus: String(file.metadata.metadata?.approvalStatus || "pending"), })); } + + async updatePhotoApprovalStatus( + filename: string, + status: "approved" | "rejected", + adminId: string, + ): Promise { + const file = this.photoBucket.file(filename); + const [existingMetadata] = await file.getMetadata(); + + // Preserve existing metadata or create new structure + // This handles backward compatibility for photos without metadata + await file.setMetadata({ + metadata: { + ...existingMetadata.metadata, + approvalStatus: status, + reviewedBy: adminId, + reviewedAt: new Date().toISOString(), + // If uploadedBy/uploadedAt are missing, set defaults for backward compatibility + uploadedBy: existingMetadata.metadata?.uploadedBy || "unknown", + uploadedAt: existingMetadata.metadata?.uploadedAt || existingMetadata.timeCreated || new Date().toISOString(), + }, + }); + } }