update manyyyyyyyyy
This commit is contained in:
@@ -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
|
||||||
},
|
},
|
||||||
|
|||||||
Submodule epess-database updated: 43ddf9328e...252c8d6924
@@ -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 {}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -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({
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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
Reference in New Issue
Block a user