mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-27 17:50:54 +00:00
115 lines
3.3 KiB
TypeScript
115 lines
3.3 KiB
TypeScript
import { CurrentUser } from "@edr/api-common";
|
|
import {
|
|
SUPPORT_ATTACHMENT_MAX_PER_MESSAGE,
|
|
SupportAuthorRole,
|
|
} from "@edr/types";
|
|
import {
|
|
Body,
|
|
Controller,
|
|
Get,
|
|
Post,
|
|
Query,
|
|
UploadedFiles,
|
|
UseInterceptors,
|
|
} from "@nestjs/common";
|
|
import { FilesInterceptor } from "@nestjs/platform-express";
|
|
import { ApiBody, ApiConsumes, ApiOperation, ApiTags } from "@nestjs/swagger";
|
|
|
|
import {
|
|
AuthUserPayload,
|
|
resolveAuthUserId,
|
|
} from "../../common/resolve-auth-user-id";
|
|
import {
|
|
SUPPORT_ATTACHMENT_FIELD,
|
|
supportAttachmentMulterOptions,
|
|
} from "./attachment-upload.options";
|
|
import { ListMessagesQueryDto } from "./dto/list-messages-query.dto";
|
|
import { SendMessageDto } from "./dto/send-message.dto";
|
|
import { SupportChatService } from "./support-chat.service";
|
|
|
|
/**
|
|
* Portal (customer) support-chat endpoints. The caller's company has exactly one
|
|
* thread, so these are addressed as a singleton — no conversation id on the wire,
|
|
* and nothing for a portal user to pick between.
|
|
*/
|
|
@ApiTags("support-chat")
|
|
@Controller("support")
|
|
export class SupportChatController {
|
|
constructor(private readonly service: SupportChatService) {}
|
|
|
|
@Get("conversation")
|
|
@ApiOperation({
|
|
summary: "My company's support thread (null until someone speaks)",
|
|
})
|
|
conversation(@CurrentUser() user: AuthUserPayload) {
|
|
return this.service.getCustomerConversation(resolveAuthUserId(user));
|
|
}
|
|
|
|
@Get("conversation/messages")
|
|
@ApiOperation({
|
|
summary: "Messages in my company's support thread (newest page first)",
|
|
description:
|
|
"Keyset-paginated backwards from the newest message. Omit `before` for " +
|
|
"the newest page, then pass the previous response's `nextCursor` to walk " +
|
|
"back through history. `nextCursor: null` means the thread's start.",
|
|
})
|
|
messages(
|
|
@CurrentUser() user: AuthUserPayload,
|
|
@Query() query: ListMessagesQueryDto,
|
|
) {
|
|
return this.service.getCustomerMessages(resolveAuthUserId(user), query);
|
|
}
|
|
|
|
@Post("conversation/messages")
|
|
@UseInterceptors(
|
|
FilesInterceptor(
|
|
SUPPORT_ATTACHMENT_FIELD,
|
|
SUPPORT_ATTACHMENT_MAX_PER_MESSAGE,
|
|
supportAttachmentMulterOptions,
|
|
),
|
|
)
|
|
@ApiConsumes("multipart/form-data", "application/json")
|
|
@ApiBody({
|
|
schema: {
|
|
type: "object",
|
|
properties: {
|
|
body: { type: "string" },
|
|
attachments: {
|
|
type: "array",
|
|
items: { type: "string", format: "binary" },
|
|
},
|
|
},
|
|
},
|
|
})
|
|
@ApiOperation({
|
|
summary:
|
|
"Send a message as the customer (optionally with attachments), opening the thread if needed",
|
|
})
|
|
send(
|
|
@CurrentUser() user: AuthUserPayload,
|
|
@Body() body: SendMessageDto,
|
|
@UploadedFiles() attachments?: Express.Multer.File[],
|
|
) {
|
|
return this.service.sendAsCustomer(
|
|
resolveAuthUserId(user),
|
|
body.body,
|
|
attachments ?? [],
|
|
);
|
|
}
|
|
|
|
@Post("conversation/read")
|
|
@ApiOperation({ summary: "Mark my company's thread read (customer side)" })
|
|
read(@CurrentUser() user: AuthUserPayload) {
|
|
return this.service.markCustomerRead(resolveAuthUserId(user));
|
|
}
|
|
|
|
@Get("unread-count")
|
|
@ApiOperation({ summary: "Count my unread support messages" })
|
|
unread(@CurrentUser() user: AuthUserPayload) {
|
|
return this.service.unreadCount(
|
|
SupportAuthorRole.CUSTOMER,
|
|
resolveAuthUserId(user),
|
|
);
|
|
}
|
|
}
|