feat: implement concert and ticket type management modules with role-… - #4
Merged
Conversation
…based access control
There was a problem hiding this comment.
Pull request overview
This PR introduces a new Concert module (concert + ticket type management) into the NestJS API and wires in role-based access control and Redis-backed caching to support high-read endpoints.
Changes:
- Add Concert + TicketType controllers/services/repos (Prisma) with organizer-only create/update routes.
- Add Zod DTO schemas for concert and ticket type validation.
- Introduce cookie parsing middleware and extend Redis module exports to support caching.
Reviewed changes
Copilot reviewed 20 out of 20 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| src/ticketbox/apps/api/src/modules/redis/redis.module.ts | Exports REDIS_CLIENT token and RedisService for downstream caching. |
| src/ticketbox/apps/api/src/modules/concert/presentation/ticket-type.controller.ts | Adds public ticket-type listing and organizer-only create/update endpoints. |
| src/ticketbox/apps/api/src/modules/concert/presentation/dto/ticket-type.zod.ts | Defines Zod validation for ticket-type create/update payloads. |
| src/ticketbox/apps/api/src/modules/concert/presentation/dto/concert.zod.ts | Defines Zod validation for concert query/create/update payloads. |
| src/ticketbox/apps/api/src/modules/concert/presentation/concert.controller.ts | Adds public concert read endpoints and organizer-only create/update endpoints. |
| src/ticketbox/apps/api/src/modules/concert/infrastructure/repository/ticket-type.repository.ts | Implements Prisma-based ticket type persistence and DTO mapping. |
| src/ticketbox/apps/api/src/modules/concert/infrastructure/repository/concert.repository.ts | Implements Prisma-based concert persistence and DTO mapping (homepage/list/detail). |
| src/ticketbox/apps/api/src/modules/concert/domain/ticket-type.type.ts | Introduces TicketType domain/DTO types. |
| src/ticketbox/apps/api/src/modules/concert/domain/concert.type.ts | Introduces Concert domain/DTO types (including detail DTO). |
| src/ticketbox/apps/api/src/modules/concert/concert.module.ts | Wires controllers/services/repos and imports Redis/Auth/Session modules. |
| src/ticketbox/apps/api/src/modules/concert/application/service/ticket-type.service.ts | Adds ticket-type business logic with Redis cache-aside + invalidation. |
| src/ticketbox/apps/api/src/modules/concert/application/service/concert.service.ts | Adds concert business logic (homepage/list/detail + publish gating). |
| src/ticketbox/apps/api/src/modules/concert/application/port/service/ticket-type.service.port.ts | Adds ticket-type service port + DI token. |
| src/ticketbox/apps/api/src/modules/concert/application/port/service/concert.service.port.ts | Adds concert service port + DI token. |
| src/ticketbox/apps/api/src/modules/concert/application/port/repository/ticket-type.repository.port.ts | Adds ticket-type repository port + DI token. |
| src/ticketbox/apps/api/src/modules/concert/application/port/repository/concert.repository.port.ts | Adds concert repository port + DI token. |
| src/ticketbox/apps/api/src/app.module.ts | Registers ConcertModule and applies cookie parsing middleware globally. |
| src/ticketbox/apps/api/common/middleware/cookie-parser.middleware.ts | Adds cookie parsing middleware used by auth/public endpoints. |
| src/ticketbox/apps/api/common/guard/roles.guard.ts | Adds RolesGuard enforcing role metadata. |
| src/ticketbox/apps/api/common/decorator/roles.decorator.ts | Adds @Roles(...) decorator for role metadata. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+15
to
+16
| startAt: z.string().datetime('startAt must be a valid ISO datetime string').transform((val) => new Date(val)), | ||
| endAt: z.string().datetime('endAt must be a valid ISO datetime string').transform((val) => new Date(val)), |
Comment on lines
+11
to
+12
| saleStartAt: z.string().datetime('saleStartAt must be a valid ISO datetime string').transform((val) => new Date(val)), | ||
| saleEndAt: z.string().datetime('saleEndAt must be a valid ISO datetime string').transform((val) => new Date(val)), |
Comment on lines
+63
to
+65
| items: result.items, | ||
| meta: result.meta, | ||
| }; |
Comment on lines
+50
to
+54
| if (body.saleStartAt >= body.saleEndAt) { | ||
| throw new BadRequestException('saleStartAt must be before saleEndAt'); | ||
| } | ||
|
|
||
| const data = await this.ticketTypeService.createTicketType(concertId, body); |
Comment on lines
+68
to
+72
| if (body.saleStartAt && body.saleEndAt && body.saleStartAt >= body.saleEndAt) { | ||
| throw new BadRequestException('saleStartAt must be before saleEndAt'); | ||
| } | ||
|
|
||
| const data = await this.ticketTypeService.updateTicketType(ticketTypeId, body); |
Comment on lines
+95
to
+99
| if (body.startAt >= body.endAt) { | ||
| throw new BadRequestException('CONCERT_INVALID_DATE_RANGE'); | ||
| } | ||
|
|
||
| const data = await this.concertService.createConcert(body); |
Comment on lines
+115
to
+119
| if (body.startAt && body.endAt && body.startAt >= body.endAt) { | ||
| throw new BadRequestException('CONCERT_INVALID_DATE_RANGE'); | ||
| } | ||
|
|
||
| const data = await this.concertService.updateConcert(id, body); |
Comment on lines
+48
to
+52
| await this.redisService.set({ | ||
| key: cacheKey, | ||
| value: list as any, | ||
| ttlSeconds: 300, // 5 minutes TTL | ||
| }); |
Comment on lines
+75
to
+83
| if (accessToken) { | ||
| try { | ||
| const payload = await this.tokenService.verifyAccessToken(accessToken); | ||
| if (payload && payload.role === RoleName.ORGANIZER) { | ||
| isOrganizer = true; | ||
| } | ||
| } catch (err) { | ||
| // Invalid token, treat as public/audience | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
…based access control