update manyyyyyyyyy

This commit is contained in:
2024-11-22 15:06:49 +07:00
parent 8eec1bed55
commit d8a3894aba
10 changed files with 272 additions and 101 deletions

View File

@@ -16,7 +16,7 @@
"indentStyle": "space", "indentStyle": "space",
"indentWidth": 2, "indentWidth": 2,
"lineEnding": "lf", "lineEnding": "lf",
"lineWidth": 240, "lineWidth": 80,
"attributePosition": "auto", "attributePosition": "auto",
"bracketSpacing": true "bracketSpacing": true
}, },

View File

@@ -1,4 +1,4 @@
import { Global, Module } from '@nestjs/common' import { Global, Logger, Module } from '@nestjs/common'
import { AdminNoteModule } from '../AdminNote/adminnote.module' import { AdminNoteModule } from '../AdminNote/adminnote.module'
import { ApolloDriverConfig } from '@nestjs/apollo' import { ApolloDriverConfig } from '@nestjs/apollo'
@@ -93,6 +93,12 @@ import { FinanceModule } from 'src/Finance/finance.module'
debug: process.env.NODE_ENV === 'development' || false, debug: process.env.NODE_ENV === 'development' || false,
playground: process.env.NODE_ENV === 'development' || false, playground: process.env.NODE_ENV === 'development' || false,
introspection: process.env.NODE_ENV === 'development' || false, introspection: process.env.NODE_ENV === 'development' || false,
logger: {
debug: (...args) => Logger.debug(...args),
info: (...args) => Logger.log(...args),
warn: (...args) => Logger.warn(...args),
error: (...args) => Logger.error(...args),
},
installSubscriptionHandlers: true, installSubscriptionHandlers: true,
subscriptions: { subscriptions: {
'graphql-ws': { 'graphql-ws': {
@@ -104,7 +110,8 @@ import { FinanceModule } from 'src/Finance/finance.module'
throw new Error('No extra provided') throw new Error('No extra provided')
} }
// @ts-expect-error: TODO // @ts-expect-error: TODO
ctx.extra.request.headers['x-session-id'] = ctx.connectionParams['x-session-id'] ctx.extra.request.headers['x-session-id'] =
ctx.connectionParams['x-session-id']
}, },
}, },
}, },
@@ -128,8 +135,10 @@ import { FinanceModule } from 'src/Finance/finance.module'
websocket: { websocket: {
req: extra?.request, req: extra?.request,
pubSub: pubsub, pubSub: pubsub,
me: await graphqlService.acquireContextFromSessionId(
// @ts-expect-error: TODO // @ts-expect-error: TODO
me: await graphqlService.acquireContextFromSessionId(extra.request.headers['x-session-id']), extra.request.headers['x-session-id'],
),
}, },
} }
} }
@@ -139,7 +148,10 @@ import { FinanceModule } from 'src/Finance/finance.module'
req, req,
me: req ? await graphqlService.acquireContext(req) : null, me: req ? await graphqlService.acquireContext(req) : null,
pubSub: pubsub, pubSub: pubsub,
invalidateCache: () => graphqlService.invalidateCache(req?.headers['x-session-id'] as string), invalidateCache: () =>
graphqlService.invalidateCache(
req?.headers['x-session-id'] as string,
),
}, },
} }
}, },
@@ -150,7 +162,8 @@ import { FinanceModule } from 'src/Finance/finance.module'
RedisService, RedisService,
{ {
provide: GraphqlService, provide: GraphqlService,
useFactory: (prisma: PrismaService, redis: RedisService) => new GraphqlService(prisma, redis), useFactory: (prisma: PrismaService, redis: RedisService) =>
new GraphqlService(prisma, redis),
inject: [PrismaService, 'REDIS_CLIENT'], inject: [PrismaService, 'REDIS_CLIENT'],
}, },
{ {
@@ -168,6 +181,12 @@ import { FinanceModule } from 'src/Finance/finance.module'
useFactory: () => new PubSub(), useFactory: () => new PubSub(),
}, },
], ],
exports: [Builder, PrismaCrudGenerator, GraphqlService, RedisService, 'PUB_SUB'], exports: [
Builder,
PrismaCrudGenerator,
GraphqlService,
RedisService,
'PUB_SUB',
],
}) })
export class GraphqlModule {} export class GraphqlModule {}

View File

@@ -43,6 +43,7 @@ export class GraphqlService {
// redis context cache // redis context cache
const cachedUser = await this.redis.getUser(sessionId) const cachedUser = await this.redis.getUser(sessionId)
if (cachedUser) { if (cachedUser) {
// Logger.log(`Found cached user for sessionId: ${sessionId}`)
return cachedUser return cachedUser
} }
// check if the token is valid // check if the token is valid

View File

@@ -1,8 +1,18 @@
import { Inject, Injectable, Logger } from '@nestjs/common' import { Inject, Injectable, Logger } from '@nestjs/common'
import { Pothos, PothosRef, PothosSchema, SchemaBuilderToken } from '@smatch-corp/nestjs-pothos' import {
Pothos,
PothosRef,
PothosSchema,
SchemaBuilderToken,
} from '@smatch-corp/nestjs-pothos'
import { Builder, SchemaContext } from '../Graphql/graphql.builder' import { Builder, SchemaContext } from '../Graphql/graphql.builder'
import { PrismaService } from '../Prisma/prisma.service' import { PrismaService } from '../Prisma/prisma.service'
import { ChatRoomType, Message, MessageContextType, MessageType } from '@prisma/client' import {
ChatRoomType,
Message,
MessageContextType,
MessageType,
} from '@prisma/client'
import { DateTimeUtils } from '../common/utils/datetime.utils' import { DateTimeUtils } from '../common/utils/datetime.utils'
import { PubSubEvent } from '../common/pubsub/pubsub-event' import { PubSubEvent } from '../common/pubsub/pubsub-event'
@@ -75,7 +85,8 @@ export class MessageSchema extends PothosSchema {
}), }),
messages: t.prismaField({ messages: t.prismaField({
type: [this.message()], type: [this.message()],
description: 'Retrieve a list of messages with optional filtering, ordering, and pagination.', description:
'Retrieve a list of messages with optional filtering, ordering, and pagination.',
args: this.builder.generator.findManyArgs('Message'), args: this.builder.generator.findManyArgs('Message'),
resolve: async (query, _root, args) => { resolve: async (query, _root, args) => {
return await this.prisma.message.findMany({ return await this.prisma.message.findMany({
@@ -107,7 +118,15 @@ export class MessageSchema extends PothosSchema {
description: 'Send a message to a chat room.', description: 'Send a message to a chat room.',
args: { args: {
input: t.arg({ input: t.arg({
type: this.builder.generator.getCreateInput('Message', ['id', 'senderId', 'sender', 'sentAt', 'context', 'recipient', 'recipientId']), type: this.builder.generator.getCreateInput('Message', [
'id',
'senderId',
'sender',
'sentAt',
'context',
'recipient',
'recipientId',
]),
description: 'The message to send.', description: 'The message to send.',
required: true, required: true,
}), }),
@@ -149,8 +168,6 @@ export class MessageSchema extends PothosSchema {
if (!args.input.content || args.input.content.trim() === '') { if (!args.input.content || args.input.content.trim() === '') {
throw new Error('Content cannot be empty') throw new Error('Content cannot be empty')
} }
args.input.context = messageContext
args.input.context = messageContext
const lastActivity = DateTimeUtils.now() const lastActivity = DateTimeUtils.now()
const message = await this.prisma.$transaction(async (tx) => { const message = await this.prisma.$transaction(async (tx) => {
const message = await tx.message.create({ const message = await tx.message.create({
@@ -167,10 +184,16 @@ export class MessageSchema extends PothosSchema {
}) })
return message return message
}) })
ctx.http.pubSub.publish(`${PubSubEvent.MESSAGE_SENT}.${message.chatRoomId}`, message) ctx.http.pubSub.publish(
`${PubSubEvent.MESSAGE_SENT}.${message.chatRoomId}`,
message,
)
// publish to new message subscribers // publish to new message subscribers
userIds.forEach((userId: string) => { userIds.forEach((userId: string) => {
ctx.http.pubSub.publish(`${PubSubEvent.NEW_MESSAGE}.${userId}`, message) ctx.http.pubSub.publish(
`${PubSubEvent.NEW_MESSAGE}.${userId}`,
message,
)
}) })
return message return message
}, },
@@ -192,7 +215,9 @@ export class MessageSchema extends PothosSchema {
const { const {
websocket: { pubSub }, websocket: { pubSub },
} = ctx } = ctx
return pubSub.asyncIterator([`${PubSubEvent.MESSAGE_SENT}.${args.chatRoomId}`]) as unknown as AsyncIterable<Message> return pubSub.asyncIterator([
`${PubSubEvent.MESSAGE_SENT}.${args.chatRoomId}`,
]) as unknown as AsyncIterable<Message>
}, },
resolve: (payload: Message) => payload, resolve: (payload: Message) => payload,
}), }),

View File

@@ -1,5 +1,10 @@
import { Inject, Injectable } from '@nestjs/common' import { Inject, Injectable } from '@nestjs/common'
import { Pothos, PothosRef, PothosSchema, SchemaBuilderToken } from '@smatch-corp/nestjs-pothos' import {
Pothos,
PothosRef,
PothosSchema,
SchemaBuilderToken,
} from '@smatch-corp/nestjs-pothos'
import { Builder } from '../Graphql/graphql.builder' import { Builder } from '../Graphql/graphql.builder'
import { PrismaService } from '../Prisma/prisma.service' import { PrismaService } from '../Prisma/prisma.service'
import { OrderStatus } from '@prisma/client' import { OrderStatus } from '@prisma/client'
@@ -43,6 +48,12 @@ export class OrderSchema extends PothosSchema {
schedule: t.relation('schedule', { schedule: t.relation('schedule', {
description: 'The schedule of the order.', description: 'The schedule of the order.',
}), }),
chatRoomId: t.exposeID('chatRoomId', {
description: 'The ID of the chat room.',
}),
chatRoom: t.relation('chatRoom', {
description: 'The chat room of the order.',
}),
createdAt: t.expose('createdAt', { createdAt: t.expose('createdAt', {
type: 'DateTime', type: 'DateTime',
description: 'The date and time the order was created.', description: 'The date and time the order was created.',
@@ -79,7 +90,8 @@ export class OrderSchema extends PothosSchema {
this.builder.queryFields((t) => ({ this.builder.queryFields((t) => ({
orders: t.prismaField({ orders: t.prismaField({
type: [this.order()], type: [this.order()],
description: 'Retrieve a list of orders with optional filtering, ordering, and pagination.', description:
'Retrieve a list of orders with optional filtering, ordering, and pagination.',
args: this.builder.generator.findManyArgs('Order'), args: this.builder.generator.findManyArgs('Order'),
resolve: async (query, _root, args, _ctx, _info) => { resolve: async (query, _root, args, _ctx, _info) => {
return await this.prisma.order.findMany({ return await this.prisma.order.findMany({
@@ -110,7 +122,18 @@ export class OrderSchema extends PothosSchema {
description: 'Create a new order.', description: 'Create a new order.',
args: { args: {
data: t.arg({ data: t.arg({
type: this.builder.generator.getCreateInput('Order', ['id', 'user', 'paymentId', 'payment', 'refundTicket', 'status', 'total', 'createdAt', 'updatedAt', 'commission']), type: this.builder.generator.getCreateInput('Order', [
'id',
'user',
'paymentId',
'payment',
'refundTicket',
'status',
'total',
'createdAt',
'updatedAt',
'commission',
]),
required: true, required: true,
}), }),
}, },
@@ -137,7 +160,10 @@ export class OrderSchema extends PothosSchema {
const order = await this.prisma.order.findUnique({ const order = await this.prisma.order.findUnique({
where: { id: schedule.orderId }, where: { id: schedule.orderId },
}) })
if (order?.status === OrderStatus.PAID || order?.status === OrderStatus.PENDING) { if (
order?.status === OrderStatus.PAID ||
order?.status === OrderStatus.PENDING
) {
throw new Error('Schedule already has an order') throw new Error('Schedule already has an order')
} }
} }
@@ -187,9 +213,17 @@ export class OrderSchema extends PothosSchema {
description: service.name, description: service.name,
buyerName: ctx.http.me?.name ?? '', buyerName: ctx.http.me?.name ?? '',
buyerEmail: ctx.http.me?.email ?? '', buyerEmail: ctx.http.me?.email ?? '',
returnUrl: `${process.env.PAYOS_RETURN_URL}`.replace('<serviceId>', service.id), returnUrl: `${process.env.PAYOS_RETURN_URL}`.replace(
cancelUrl: `${process.env.PAYOS_RETURN_URL}`.replace('<serviceId>', service.id), '<serviceId>',
expiredAt: DateTimeUtils.now().plus({ minutes: 15 }).toUnixInteger(), service.id,
),
cancelUrl: `${process.env.PAYOS_RETURN_URL}`.replace(
'<serviceId>',
service.id,
),
expiredAt: DateTimeUtils.now()
.plus({ minutes: 15 })
.toUnixInteger(),
}) })
// update order payment id // update order payment id
await this.prisma.order.update({ await this.prisma.order.update({
@@ -205,15 +239,6 @@ export class OrderSchema extends PothosSchema {
paymentCode: paymentData.paymentLinkId, paymentCode: paymentData.paymentLinkId,
}, },
}) })
// update orderId for schedule dates
// await this.prisma.scheduleDate.updateMany({
// where: { scheduleId: args.data.schedule.connect?.id ?? '' },
// data: {
// orderId: order.id,
// },
// })
// refetch order // refetch order
return await this.prisma.order.findUnique({ return await this.prisma.order.findUnique({
where: { id: order.id }, where: { id: order.id },
@@ -244,7 +269,10 @@ export class OrderSchema extends PothosSchema {
description: 'Update an existing order.', description: 'Update an existing order.',
args: { args: {
data: t.arg({ data: t.arg({
type: this.builder.generator.getUpdateInput('Order', ['status', 'total']), type: this.builder.generator.getUpdateInput('Order', [
'status',
'total',
]),
required: true, required: true,
}), }),
where: t.arg({ where: t.arg({

View File

@@ -2,8 +2,20 @@ import { Inject, Injectable, Logger } from '@nestjs/common'
import { PrismaService } from '../Prisma/prisma.service' import { PrismaService } from '../Prisma/prisma.service'
import PayOS from '@payos/node' import PayOS from '@payos/node'
import type { CheckoutRequestType, CheckoutResponseDataType, WebhookType, WebhookDataType, CancelPaymentLinkRequestType, DataType } from '@payos/node/lib/type' import type {
import { ChatRoomType, OrderStatus, PaymentStatus, ScheduleStatus } from '@prisma/client' CheckoutRequestType,
CheckoutResponseDataType,
WebhookType,
WebhookDataType,
CancelPaymentLinkRequestType,
DataType,
} from '@payos/node/lib/type'
import {
ChatRoomType,
OrderStatus,
PaymentStatus,
ScheduleStatus,
} from '@prisma/client'
export type CreatePaymentBody = CheckoutRequestType export type CreatePaymentBody = CheckoutRequestType
export type CreatePaymentResponse = CheckoutResponseDataType export type CreatePaymentResponse = CheckoutResponseDataType
@Injectable() @Injectable()
@@ -33,7 +45,8 @@ export class PayosService {
Logger.error(`Invalid checksum: ${JSON.stringify(data)}`) Logger.error(`Invalid checksum: ${JSON.stringify(data)}`)
throw new Error('Invalid checksum') throw new Error('Invalid checksum')
} }
const paymentStatus = paymentData.code === '00' ? PaymentStatus.PAID : PaymentStatus.CANCELLED const paymentStatus =
paymentData.code === '00' ? PaymentStatus.PAID : PaymentStatus.CANCELLED
/* ---------------------------- begin transaction --------------------------- */ /* ---------------------------- begin transaction --------------------------- */
try { try {
await this.prisma.$transaction(async (tx) => { await this.prisma.$transaction(async (tx) => {
@@ -44,7 +57,10 @@ export class PayosService {
status: paymentStatus, status: paymentStatus,
}, },
}) })
const orderStatus = paymentStatus === PaymentStatus.PAID ? OrderStatus.PAID : OrderStatus.FAILED const orderStatus =
paymentStatus === PaymentStatus.PAID
? OrderStatus.PAID
: OrderStatus.FAILED
// update order status // update order status
await tx.order.update({ await tx.order.update({
where: { id: payment.orderId }, where: { id: payment.orderId },
@@ -78,7 +94,7 @@ export class PayosService {
}) })
const centerId = orderService.centerId const centerId = orderService.centerId
// create chatroom for support // create chatroom for support
await tx.chatRoom.create({ const chatRoom = await tx.chatRoom.create({
data: { data: {
type: ChatRoomType.SUPPORT, type: ChatRoomType.SUPPORT,
customerId: order.userId, customerId: order.userId,
@@ -86,6 +102,13 @@ export class PayosService {
mentorId: mentorId, mentorId: mentorId,
}, },
}) })
// update order chatRoomId
await tx.order.update({
where: { id: order.id },
data: {
chatRoomId: chatRoom.id,
},
})
// update orderId for schedule dates // update orderId for schedule dates
await tx.scheduleDate.updateMany({ await tx.scheduleDate.updateMany({
where: { scheduleId: schedule?.id }, where: { scheduleId: schedule?.id },
@@ -115,7 +138,10 @@ export class PayosService {
return await this.payos.getPaymentLinkInformation(orderId) return await this.payos.getPaymentLinkInformation(orderId)
} }
async cancelPaymentURL(orderId: string | number, cancellationReason?: string) { async cancelPaymentURL(
orderId: string | number,
cancellationReason?: string,
) {
return await this.payos.cancelPaymentLink(orderId, cancellationReason) return await this.payos.cancelPaymentLink(orderId, cancellationReason)
} }

View File

@@ -1,9 +1,9 @@
import { Module } from '@nestjs/common' import { Module } from '@nestjs/common'
import { UserSchema } from './user.schema' import { UserSchema } from './user.schema'
import { MessageModule } from '../Message/message.module' import { MessageModule } from '../Message/message.module'
import { ChatroomModule } from '../ChatRoom/chatroom.module'
@Module({ @Module({
imports: [MessageModule], imports: [MessageModule, ChatroomModule],
providers: [UserSchema], providers: [UserSchema],
exports: [UserSchema], exports: [UserSchema],
}) })

View File

@@ -1,33 +1,26 @@
import { Inject, Injectable, Logger } from '@nestjs/common' import { Inject, Injectable, Logger } from '@nestjs/common'
import { Pothos, PothosRef, PothosSchema, SchemaBuilderToken } from '@smatch-corp/nestjs-pothos' import {
Pothos,
PothosRef,
PothosSchema,
SchemaBuilderToken,
} from '@smatch-corp/nestjs-pothos'
import { Builder, SchemaContext } from '../Graphql/graphql.builder' import { Builder, SchemaContext } from '../Graphql/graphql.builder'
import { PrismaService } from '../Prisma/prisma.service' import { PrismaService } from '../Prisma/prisma.service'
import { clerkClient } from '@clerk/express' import { clerkClient } from '@clerk/express'
import { UnauthorizedException } from '@nestjs/common' import { UnauthorizedException } from '@nestjs/common'
import { MailService } from '../Mail/mail.service' import { MailService } from '../Mail/mail.service'
import { MessageSchema } from 'src/Message/message.schema' import { MessageSchema } from 'src/Message/message.schema'
import { ChatRoom, Message, MessageContextType, MessageType, Role } from '@prisma/client' import {
ChatRoom,
Message,
MessageContextType,
MessageType,
Role,
} from '@prisma/client'
import { PubSubEvent } from 'src/common/pubsub/pubsub-event' import { PubSubEvent } from 'src/common/pubsub/pubsub-event'
import { DateTimeUtils } from 'src/common/utils/datetime.utils' import { DateTimeUtils } from 'src/common/utils/datetime.utils'
import { JsonValue } from '@prisma/client/runtime/library' import { ChatroomSchema } from '../ChatRoom/chatroom.schema'
import { z } from 'zod'
type UserWithChatRooms = {
name: string
id: string
email: string
phoneNumber: string | null
bankBin: string | null
bankAccountNumber: string | null
packageValue: number
role: Role
avatarUrl: string | null
createdAt: Date
updatedAt: Date
customerChatRoom?: ChatRoom[]
mentorChatRoom?: ChatRoom[]
}
@Injectable() @Injectable()
export class UserSchema extends PothosSchema { export class UserSchema extends PothosSchema {
constructor( constructor(
@@ -35,6 +28,7 @@ export class UserSchema extends PothosSchema {
private readonly prisma: PrismaService, private readonly prisma: PrismaService,
private readonly mailService: MailService, private readonly mailService: MailService,
private readonly messageSchema: MessageSchema, private readonly messageSchema: MessageSchema,
private readonly chatRoomSchema: ChatroomSchema,
) { ) {
super() super()
} }
@@ -128,6 +122,30 @@ export class UserSchema extends PothosSchema {
}) })
} }
@PothosRef()
recentChatActivity() {
return this.builder.simpleObject('RecentChatActivity', {
fields: (t) => ({
chatRoom: t.field({
type: this.chatRoomSchema.chatRoom(),
description: 'The chat room.',
}),
lastActivity: t.field({
type: 'DateTime',
description: 'The last activity of the chat room.',
}),
sender: t.field({
type: this.user(),
description: 'The sender of the message.',
}),
message: t.field({
type: this.messageSchema.message(),
description: 'The last message of the chat room.',
}),
}),
})
}
// Query section // Query section
@Pothos() @Pothos()
init(): void { init(): void {
@@ -160,40 +178,73 @@ export class UserSchema extends PothosSchema {
}), }),
me: t.prismaField({ me: t.prismaField({
description: 'Retrieve the current user in context.', description: 'Retrieve the current user in context.',
type: this.user() || 'Json', type: this.user(),
args: {
includeChatRoom: t.arg({ type: 'Boolean', required: false }),
},
resolve: async (_query, _root, _args, ctx) => { resolve: async (_query, _root, _args, ctx) => {
if (ctx.isSubscription) { if (ctx.isSubscription) {
throw new Error('Not allowed') throw new Error('Not allowed')
} }
let user = ctx.http.me as UserWithChatRooms return ctx.http.me
},
if (!user?.name) { }),
throw new Error('User not found')
} recentChatActivity: t.field({
if (_args.includeChatRoom) { description: 'Retrieve the recent chat activity of the current user.',
const customerChatRoom = await this.prisma.chatRoom.findMany({ type: [this.recentChatActivity()],
where: { args: {
OR: [{ customerId: ctx.http.me?.id }, { mentorId: ctx.http.me?.id }], take: t.arg({ type: 'Int', required: false }),
},
resolve: async (_parent, args, ctx) => {
if (ctx.isSubscription) throw new Error('Not allowed')
const me = ctx.http.me
if (!me) throw new Error('User not found')
// get chat rooms that the user is a part of
const chatRooms = await this.prisma.chatRoom.findMany({
where: {
OR: [{ customerId: me.id }, { mentorId: me.id }],
}, },
distinct: ['id'],
orderBy: { orderBy: {
lastActivity: 'desc', lastActivity: 'desc',
}, },
take: args.take ?? 10,
}) })
user = {
...user, // get the last message for each chat room
customerChatRoom, const lastMessages = await Promise.all(
chatRooms.map(async (chatRoom) => {
const lastMessage = await this.prisma.message.findFirst({
where: {
chatRoomId: chatRoom.id,
},
orderBy: {
sentAt: 'desc',
},
})
if (!lastMessage) return null
const sender = lastMessage.senderId
? await this.prisma.user.findUnique({
where: { id: lastMessage.senderId },
})
: undefined
return {
chatRoom: chatRoom,
lastActivity: lastMessage.sentAt,
sender: sender,
message: lastMessage,
} }
} }),
return user )
return lastMessages.filter((msg) => msg !== null)
}, },
}), }),
users: t.prismaField({ users: t.prismaField({
description: 'Retrieve a list of users with optional filtering, ordering, and pagination.', description:
'Retrieve a list of users with optional filtering, ordering, and pagination.',
type: [this.user()], type: [this.user()],
args: this.builder.generator.findManyArgs('User'), args: this.builder.generator.findManyArgs('User'),
resolve: async (query, _root, args) => { resolve: async (query, _root, args) => {
@@ -325,7 +376,8 @@ export class UserSchema extends PothosSchema {
} }
const buffer = Buffer.concat(chunks) const buffer = Buffer.concat(chunks)
const { id: userId, imageUrl } = await clerkClient.users.updateUserProfileImage(id, { const { id: userId, imageUrl } =
await clerkClient.users.updateUserProfileImage(id, {
file: new Blob([buffer]), file: new Blob([buffer]),
}) })
await this.prisma.user.update({ await this.prisma.user.update({
@@ -405,9 +457,14 @@ export class UserSchema extends PothosSchema {
throw new Error(`User ${args.email} not found`) throw new Error(`User ${args.email} not found`)
} }
// send email // send email
await this.mailService.sendTemplateEmail([args.email], 'Thông báo chọn lựa quản trị viên cho người điều hành', 'ModeratorInvitation', { await this.mailService.sendTemplateEmail(
[args.email],
'Thông báo chọn lựa quản trị viên cho người điều hành',
'ModeratorInvitation',
{
USER_NAME: user.name, USER_NAME: user.name,
}) },
)
return 'Invited' return 'Invited'
}) })
}, },
@@ -444,7 +501,10 @@ export class UserSchema extends PothosSchema {
}, },
}) })
// publish message // publish message
await ctx.http.pubSub.publish(`${PubSubEvent.NEW_MESSAGE}.${message.recipientId}`, message) await ctx.http.pubSub.publish(
`${PubSubEvent.NEW_MESSAGE}.${message.recipientId}`,
message,
)
return message return message
}, },
}), }),
@@ -459,7 +519,9 @@ export class UserSchema extends PothosSchema {
const { const {
websocket: { pubSub }, websocket: { pubSub },
} = ctx } = ctx
return pubSub.asyncIterator([`${PubSubEvent.NEW_MESSAGE}.${ctx.websocket.me?.id}`]) as unknown as AsyncIterable<Message> return pubSub.asyncIterator([
`${PubSubEvent.NEW_MESSAGE}.${ctx.websocket.me?.id}`,
]) as unknown as AsyncIterable<Message>
}, },
resolve: async (payload: Message) => payload, resolve: async (payload: Message) => payload,
}), }),

File diff suppressed because one or more lines are too long