mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-28 12:30:58 +00:00
feat: add the support feature to the passenger api
This commit is contained in:
@@ -1,15 +1,207 @@
|
||||
import { Body, Controller, Get, Param, Post, UseGuards } from '@nestjs/common';
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
Param,
|
||||
Patch,
|
||||
Post,
|
||||
Query,
|
||||
Req,
|
||||
UnauthorizedException,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger';
|
||||
import { IsPublic } from '@tria-plc/api-common/modules/auth/decorators/public.decorator';
|
||||
import { SupportService } from './support.service';
|
||||
import { JwtGuard } from '../../common/jwt.guard';
|
||||
import {
|
||||
CreateConversationDto,
|
||||
CreateGuestConversationDto,
|
||||
GuestIdBodyDto,
|
||||
GuestSendMessageDto,
|
||||
ListConversationsQueryDto,
|
||||
SendMessageDto,
|
||||
UpdateStatusDto,
|
||||
} from './support.dto';
|
||||
|
||||
function userId(req: any): string {
|
||||
const id = req?.user?.id ?? req?.user?.sub;
|
||||
if (!id) throw new UnauthorizedException();
|
||||
return id;
|
||||
}
|
||||
|
||||
@ApiTags('Support')
|
||||
@Controller('support')
|
||||
export class SupportController {
|
||||
constructor(private service: SupportService) {}
|
||||
@Get('faq') @ApiOperation({ summary: 'Get FAQ categories' }) getFaqCategories() { return this.service.getFaqCategories(); }
|
||||
@Get('faq/:categoryId/articles') @ApiOperation({ summary: 'Get FAQ articles for a category' }) getFaqArticles(@Param('categoryId') id: string) { return this.service.getFaqArticles(id); }
|
||||
@Post('conversations') @UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth') @ApiOperation({ summary: 'Start a support conversation' }) startConversation(@Body('userId') userId: string) { return this.service.startConversation(userId); }
|
||||
@Post('conversations/:id/messages') @UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth') @ApiOperation({ summary: 'Send a message in a conversation' }) sendMessage(@Param('id') id: string, @Body() body: { sender: 'USER' | 'BOT' | 'AGENT'; text: string }) { return this.service.sendMessage(id, body.sender, body.text); }
|
||||
@Get('conversations/:id') @UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth') @ApiOperation({ summary: 'Get conversation with messages' }) getConversation(@Param('id') id: string) { return this.service.getConversation(id); }
|
||||
|
||||
// ---- FAQ (public) ------------------------------------------------------
|
||||
|
||||
@Get('faq')
|
||||
@IsPublic()
|
||||
@ApiOperation({ summary: 'Get FAQ categories' })
|
||||
getFaqCategories() {
|
||||
return this.service.getFaqCategories();
|
||||
}
|
||||
|
||||
@Get('faq/:categoryId/articles')
|
||||
@IsPublic()
|
||||
@ApiOperation({ summary: 'Get FAQ articles for a category' })
|
||||
getFaqArticles(@Param('categoryId') id: string) {
|
||||
return this.service.getFaqArticles(id);
|
||||
}
|
||||
|
||||
// ---- customer: authenticated passenger --------------------------------
|
||||
|
||||
@Post('conversations')
|
||||
@UseGuards(JwtGuard)
|
||||
@ApiBearerAuth('JWT-auth')
|
||||
@ApiOperation({ summary: 'Open a new support conversation' })
|
||||
createConversation(@Req() req: any, @Body() body: CreateConversationDto) {
|
||||
return this.service.createConversation(userId(req), body);
|
||||
}
|
||||
|
||||
@Get('conversations')
|
||||
@UseGuards(JwtGuard)
|
||||
@ApiBearerAuth('JWT-auth')
|
||||
@ApiOperation({ summary: 'List my support conversations' })
|
||||
listMine(@Req() req: any, @Query() query: ListConversationsQueryDto) {
|
||||
return this.service.listForCustomer({ iamUserId: userId(req) }, query);
|
||||
}
|
||||
|
||||
@Get('conversations/:id/messages')
|
||||
@UseGuards(JwtGuard)
|
||||
@ApiBearerAuth('JWT-auth')
|
||||
@ApiOperation({ summary: 'List messages in one of my conversations' })
|
||||
messages(@Req() req: any, @Param('id') id: string) {
|
||||
return this.service.getMessages(id, { iamUserId: userId(req) });
|
||||
}
|
||||
|
||||
@Post('conversations/:id/messages')
|
||||
@UseGuards(JwtGuard)
|
||||
@ApiBearerAuth('JWT-auth')
|
||||
@ApiOperation({ summary: 'Send a message as the customer' })
|
||||
send(@Req() req: any, @Param('id') id: string, @Body() body: SendMessageDto) {
|
||||
return this.service.sendMessage(id, 'USER', body.text, {
|
||||
iamUserId: userId(req),
|
||||
});
|
||||
}
|
||||
|
||||
@Post('conversations/:id/read')
|
||||
@UseGuards(JwtGuard)
|
||||
@ApiBearerAuth('JWT-auth')
|
||||
@ApiOperation({ summary: 'Mark a conversation read (customer side)' })
|
||||
read(@Req() req: any, @Param('id') id: string) {
|
||||
return this.service.markRead(id, 'USER', { iamUserId: userId(req) });
|
||||
}
|
||||
|
||||
@Get('unread-count')
|
||||
@UseGuards(JwtGuard)
|
||||
@ApiBearerAuth('JWT-auth')
|
||||
@ApiOperation({ summary: 'Count my unread support conversations' })
|
||||
unread(@Req() req: any) {
|
||||
return this.service.unreadCount('USER', { iamUserId: userId(req) });
|
||||
}
|
||||
|
||||
// ---- customer: guest (unauthenticated) --------------------------------
|
||||
// No JwtGuard. Access is scoped by a client-generated `guestId` (the bearer
|
||||
// of access — anyone with it sees that thread; accepted MVP trade-off).
|
||||
|
||||
@Post('guest/conversations')
|
||||
@IsPublic()
|
||||
@ApiOperation({ summary: 'Open a support conversation as a guest' })
|
||||
guestCreate(@Body() body: CreateGuestConversationDto) {
|
||||
return this.service.createGuestConversation(body);
|
||||
}
|
||||
|
||||
@Get('guest/conversations')
|
||||
@IsPublic()
|
||||
@ApiOperation({ summary: 'List a guest\'s conversations' })
|
||||
guestList(
|
||||
@Query('guestId') guestId: string,
|
||||
@Query() query: ListConversationsQueryDto,
|
||||
) {
|
||||
return this.service.listForCustomer({ guestId }, query);
|
||||
}
|
||||
|
||||
@Get('guest/conversations/:id/messages')
|
||||
@IsPublic()
|
||||
@ApiOperation({ summary: 'List messages in a guest conversation' })
|
||||
guestMessages(@Param('id') id: string, @Query('guestId') guestId: string) {
|
||||
return this.service.getMessages(id, { guestId });
|
||||
}
|
||||
|
||||
@Post('guest/conversations/:id/messages')
|
||||
@IsPublic()
|
||||
@ApiOperation({ summary: 'Send a message as a guest' })
|
||||
guestSend(@Param('id') id: string, @Body() body: GuestSendMessageDto) {
|
||||
return this.service.sendMessage(id, 'USER', body.text, {
|
||||
guestId: body.guestId,
|
||||
});
|
||||
}
|
||||
|
||||
@Post('guest/conversations/:id/read')
|
||||
@IsPublic()
|
||||
@ApiOperation({ summary: 'Mark a guest conversation read' })
|
||||
guestRead(@Param('id') id: string, @Body() body: GuestIdBodyDto) {
|
||||
return this.service.markRead(id, 'USER', { guestId: body.guestId });
|
||||
}
|
||||
|
||||
@Get('guest/unread-count')
|
||||
@IsPublic()
|
||||
@ApiOperation({ summary: 'Count a guest\'s unread conversations' })
|
||||
guestUnread(@Query('guestId') guestId: string) {
|
||||
return this.service.unreadCount('USER', { guestId });
|
||||
}
|
||||
|
||||
// ---- agent (backoffice) ------------------------------------------------
|
||||
// TODO: gate agent routes with a staff permission once passenger RBAC is wired.
|
||||
|
||||
@Get('agent/conversations')
|
||||
@UseGuards(JwtGuard)
|
||||
@ApiBearerAuth('JWT-auth')
|
||||
@ApiOperation({ summary: 'List all support conversations (shared inbox)' })
|
||||
agentList(@Query() query: ListConversationsQueryDto) {
|
||||
return this.service.listForAgents(query);
|
||||
}
|
||||
|
||||
@Get('agent/conversations/:id/messages')
|
||||
@UseGuards(JwtGuard)
|
||||
@ApiBearerAuth('JWT-auth')
|
||||
@ApiOperation({ summary: 'List messages in a conversation' })
|
||||
agentMessages(@Param('id') id: string) {
|
||||
return this.service.getMessages(id);
|
||||
}
|
||||
|
||||
@Post('agent/conversations/:id/messages')
|
||||
@UseGuards(JwtGuard)
|
||||
@ApiBearerAuth('JWT-auth')
|
||||
@ApiOperation({ summary: 'Reply as an agent' })
|
||||
agentSend(@Param('id') id: string, @Body() body: SendMessageDto) {
|
||||
return this.service.sendMessage(id, 'AGENT', body.text);
|
||||
}
|
||||
|
||||
@Patch('agent/conversations/:id/status')
|
||||
@UseGuards(JwtGuard)
|
||||
@ApiBearerAuth('JWT-auth')
|
||||
@ApiOperation({ summary: "Change a conversation's status" })
|
||||
agentStatus(@Param('id') id: string, @Body() body: UpdateStatusDto) {
|
||||
return this.service.setStatus(id, body.status);
|
||||
}
|
||||
|
||||
@Post('agent/conversations/:id/read')
|
||||
@UseGuards(JwtGuard)
|
||||
@ApiBearerAuth('JWT-auth')
|
||||
@ApiOperation({ summary: 'Mark a conversation read (agent side)' })
|
||||
agentRead(@Param('id') id: string) {
|
||||
return this.service.markRead(id, 'AGENT');
|
||||
}
|
||||
|
||||
@Get('agent/unread-count')
|
||||
@UseGuards(JwtGuard)
|
||||
@ApiBearerAuth('JWT-auth')
|
||||
@ApiOperation({ summary: 'Count unread conversations (agent side)' })
|
||||
agentUnread() {
|
||||
return this.service.unreadCount('AGENT');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user