day len server theo loi Khoi noi, toi xin tuyen bo mien tru trach nhiem
This commit is contained in:
225
package-lock.json
generated
225
package-lock.json
generated
@@ -15,6 +15,7 @@
|
||||
"@graphql-codegen/typescript": "^4.0.9",
|
||||
"@graphql-codegen/typescript-operations": "^4.2.3",
|
||||
"@graphql-codegen/typescript-resolvers": "^4.2.1",
|
||||
"@livekit/rtc-node": "^0.11.0",
|
||||
"@nestjs-modules/mailer": "^2.0.2",
|
||||
"@nestjs/apollo": "^12.2.0",
|
||||
"@nestjs/axios": "^3.1.1",
|
||||
@@ -53,6 +54,7 @@
|
||||
"graphql-ws": "^5.16.0",
|
||||
"ioredis": "^5.4.1",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"livekit-server-sdk": "^2.7.3",
|
||||
"luxon": "^3.5.0",
|
||||
"minio": "^8.0.1",
|
||||
"nestjs-minio": "^2.6.2",
|
||||
@@ -88,6 +90,7 @@
|
||||
"@types/nodemailer": "^6.4.16",
|
||||
"@types/passport-jwt": "^4.0.1",
|
||||
"@types/supertest": "^6.0.0",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"@types/ws": "^8.5.12",
|
||||
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
||||
"@typescript-eslint/parser": "^8.0.0",
|
||||
@@ -1913,6 +1916,12 @@
|
||||
"node": ">=14.21.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@bufbuild/protobuf": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.2.2.tgz",
|
||||
"integrity": "sha512-UNtPCbrwrenpmrXuRwn9jYpPoweNXj8X5sMvYgsqYyaH8jQ6LfUJSk3dJLnBK+6sfYPrF4iAIo5sd5HQ+tg75A==",
|
||||
"license": "(Apache-2.0 AND BSD-3-Clause)"
|
||||
},
|
||||
"node_modules/@clerk/backend": {
|
||||
"version": "1.15.2",
|
||||
"resolved": "https://registry.npmjs.org/@clerk/backend/-/backend-1.15.2.tgz",
|
||||
@@ -4139,6 +4148,134 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@livekit/mutex": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@livekit/mutex/-/mutex-1.0.0.tgz",
|
||||
"integrity": "sha512-aiUhoThBNF9UyGTxEURFzJLhhPLIVTnQiEVMjRhPnfHNKLfo2JY9xovHKIus7B78UD5hsP6DlgpmAsjrz4U0Iw==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@livekit/protocol": {
|
||||
"version": "1.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@livekit/protocol/-/protocol-1.27.1.tgz",
|
||||
"integrity": "sha512-ISEp7uWdV82mtCR1eyHFTzdRZTVbe2+ZztjmjiMPzR/KPrI1Ma/u5kLh87NNuY3Rn8wv1VlEvGHHsFjQ+dKVUw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@bufbuild/protobuf": "^1.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@livekit/protocol/node_modules/@bufbuild/protobuf": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-1.10.0.tgz",
|
||||
"integrity": "sha512-QDdVFLoN93Zjg36NoQPZfsVH9tZew7wKDKyV5qRdj8ntT4wQCOradQjRaTdwMhWUYsgKsvCINKKm87FdEk96Ag==",
|
||||
"license": "(Apache-2.0 AND BSD-3-Clause)"
|
||||
},
|
||||
"node_modules/@livekit/rtc-node": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@livekit/rtc-node/-/rtc-node-0.11.0.tgz",
|
||||
"integrity": "sha512-HRnoGsNJC+okhzV9IlljaWskOYIGi1xLD9NzwJwRkzYRdPFwEMf5QU5bmELNCfwDw8bMOpMQB4M8wIkVhCrcxg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@bufbuild/protobuf": "^2.2.0",
|
||||
"@livekit/mutex": "^1.0.0",
|
||||
"@livekit/typed-emitter": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@livekit/rtc-node-darwin-arm64": "0.11.0",
|
||||
"@livekit/rtc-node-darwin-x64": "0.11.0",
|
||||
"@livekit/rtc-node-linux-arm64-gnu": "0.11.0",
|
||||
"@livekit/rtc-node-linux-x64-gnu": "0.11.0",
|
||||
"@livekit/rtc-node-win32-x64-msvc": "0.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@livekit/rtc-node-darwin-arm64": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@livekit/rtc-node-darwin-arm64/-/rtc-node-darwin-arm64-0.11.0.tgz",
|
||||
"integrity": "sha512-ZepFYFO984NnkM6h29KvGagGpQ7/Q/7WdtVr68rhque8YyP/SLPfW1AA73jSn7+FwIVQiaUUERtrOlSlEWe8hg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@livekit/rtc-node-darwin-x64": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@livekit/rtc-node-darwin-x64/-/rtc-node-darwin-x64-0.11.0.tgz",
|
||||
"integrity": "sha512-JsqwV6XVDFYFUV67QZ3CFxD3OeK6+eZZzWuDjR7ELam5cDyqYjqi2bWEVDPnv+4zA+w6Xx9g7IPRDzkMdA60kg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@livekit/rtc-node-linux-arm64-gnu": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@livekit/rtc-node-linux-arm64-gnu/-/rtc-node-linux-arm64-gnu-0.11.0.tgz",
|
||||
"integrity": "sha512-3d1WtyMKhQwLbiZQuw6MnpazPq2nOSChFiePoIuZhcrDyZrN5Tte/QBkIL8G+dCXwyD1jfknTL4D0FeI+GRMww==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@livekit/rtc-node-linux-x64-gnu": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@livekit/rtc-node-linux-x64-gnu/-/rtc-node-linux-x64-gnu-0.11.0.tgz",
|
||||
"integrity": "sha512-iwi1FpnJgI0JNq1pNe6jgqIXa16bujBQPhEF0ul47uyp7bOnSpVzVAnbAdkrACnaalB106YWZ6cc1L37gACYPg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@livekit/rtc-node-win32-x64-msvc": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@livekit/rtc-node-win32-x64-msvc/-/rtc-node-win32-x64-msvc-0.11.0.tgz",
|
||||
"integrity": "sha512-tLrdolxU+0PBxssIrSFNJie5XaCi1X/GN6GIbp/ZnSOZ+nwaNxFBmYolGjxRuW7ks1HGR7z/zIIpJey5CnFkWg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@livekit/typed-emitter": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@livekit/typed-emitter/-/typed-emitter-3.0.0.tgz",
|
||||
"integrity": "sha512-9bl0k4MgBPZu3Qu3R3xy12rmbW17e3bE9yf4YY85gJIQ3ezLEj/uzpKHWBsLaDoL5Mozz8QCgggwIBudYQWeQg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@ljharb/through": {
|
||||
"version": "2.3.13",
|
||||
"resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.13.tgz",
|
||||
@@ -5759,6 +5896,13 @@
|
||||
"@types/superagent": "^8.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/uuid": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz",
|
||||
"integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/validator": {
|
||||
"version": "13.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.12.2.tgz",
|
||||
@@ -7686,6 +7830,60 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/camelcase-keys": {
|
||||
"version": "9.1.3",
|
||||
"resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-9.1.3.tgz",
|
||||
"integrity": "sha512-Rircqi9ch8AnZscQcsA1C47NFdaO3wukpmIRzYcDOrmvgt78hM/sj5pZhZNec2NM12uk5vTwRHZ4anGcrC4ZTg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"camelcase": "^8.0.0",
|
||||
"map-obj": "5.0.0",
|
||||
"quick-lru": "^6.1.1",
|
||||
"type-fest": "^4.3.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/camelcase-keys/node_modules/camelcase": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz",
|
||||
"integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/camelcase-keys/node_modules/map-obj": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/map-obj/-/map-obj-5.0.0.tgz",
|
||||
"integrity": "sha512-2L3MIgJynYrZ3TYMriLDLWocz15okFakV6J12HXvMXDHui2x/zgChzg1u9mFFGbbGWE+GsLpQByt4POb9Or+uA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/camelcase-keys/node_modules/type-fest": {
|
||||
"version": "4.26.1",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.1.tgz",
|
||||
"integrity": "sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==",
|
||||
"license": "(MIT OR CC0-1.0)",
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001676",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001676.tgz",
|
||||
@@ -12237,7 +12435,6 @@
|
||||
"version": "5.9.6",
|
||||
"resolved": "https://registry.npmjs.org/jose/-/jose-5.9.6.tgz",
|
||||
"integrity": "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/panva"
|
||||
@@ -12759,6 +12956,20 @@
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/livekit-server-sdk": {
|
||||
"version": "2.7.3",
|
||||
"resolved": "https://registry.npmjs.org/livekit-server-sdk/-/livekit-server-sdk-2.7.3.tgz",
|
||||
"integrity": "sha512-dBiyMJ2o3Adw7aBVuFxVOlYHmiZtGGS9zVksMuv/wiEVHY+6XSDzo0X67pZVkyGlq1moF4YZAReVY2Dbxve8NQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@livekit/protocol": "^1.27.0",
|
||||
"camelcase-keys": "^9.0.0",
|
||||
"jose": "^5.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=19"
|
||||
}
|
||||
},
|
||||
"node_modules/loader-runner": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
|
||||
@@ -15241,6 +15452,18 @@
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/quick-lru": {
|
||||
"version": "6.1.2",
|
||||
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-6.1.2.tgz",
|
||||
"integrity": "sha512-AAFUA5O1d83pIHEhJwWCq/RQcRukCkn/NSm2QsTEMle5f2hP0ChI2+3Xb051PZCkLryI/Ir1MVKviT2FIloaTQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/randombytes": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
"@graphql-codegen/typescript": "^4.0.9",
|
||||
"@graphql-codegen/typescript-operations": "^4.2.3",
|
||||
"@graphql-codegen/typescript-resolvers": "^4.2.1",
|
||||
"@livekit/rtc-node": "^0.11.0",
|
||||
"@nestjs-modules/mailer": "^2.0.2",
|
||||
"@nestjs/apollo": "^12.2.0",
|
||||
"@nestjs/axios": "^3.1.1",
|
||||
@@ -75,6 +76,7 @@
|
||||
"graphql-ws": "^5.16.0",
|
||||
"ioredis": "^5.4.1",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"livekit-server-sdk": "^2.7.3",
|
||||
"luxon": "^3.5.0",
|
||||
"minio": "^8.0.1",
|
||||
"nestjs-minio": "^2.6.2",
|
||||
@@ -110,6 +112,7 @@
|
||||
"@types/nodemailer": "^6.4.16",
|
||||
"@types/passport-jwt": "^4.0.1",
|
||||
"@types/supertest": "^6.0.0",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"@types/ws": "^8.5.12",
|
||||
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
||||
"@typescript-eslint/parser": "^8.0.0",
|
||||
|
||||
@@ -142,7 +142,7 @@ export class CenterMentorSchema extends PothosSchema {
|
||||
throw new Error('Not allowed')
|
||||
}
|
||||
// get centerId by user id from context
|
||||
const userId = ctx.http.me.id
|
||||
const userId = ctx.http.me?.id
|
||||
if (!userId) {
|
||||
throw new Error('User ID is required')
|
||||
}
|
||||
@@ -266,7 +266,7 @@ export class CenterMentorSchema extends PothosSchema {
|
||||
data: {
|
||||
content: args.adminNote ?? '',
|
||||
mentorId: mentor.id,
|
||||
notedByUserId: ctx.http.me.id,
|
||||
notedByUserId: ctx.http.me?.id ?? '',
|
||||
},
|
||||
})
|
||||
// update user role
|
||||
@@ -312,7 +312,7 @@ export class CenterMentorSchema extends PothosSchema {
|
||||
adminNote: {
|
||||
create: {
|
||||
content: args.adminNote ?? '',
|
||||
notedByUserId: ctx.http.me.id,
|
||||
notedByUserId: ctx.http.me?.id ?? '',
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
},
|
||||
|
||||
@@ -43,7 +43,7 @@ export type SchemaContext =
|
||||
http: {
|
||||
req: Request
|
||||
res: Response
|
||||
me: User
|
||||
me: User | null
|
||||
pubSub: PubSub
|
||||
invalidateCache: () => Promise<void>
|
||||
generator: PrismaCrudGenerator<BuilderTypes>
|
||||
|
||||
@@ -30,6 +30,10 @@ export class GraphqlService {
|
||||
if (disableAuth) {
|
||||
return null
|
||||
}
|
||||
// check if the sessionId is valid
|
||||
if (!sessionId) {
|
||||
return null
|
||||
}
|
||||
// redis context cache
|
||||
const cachedUser = await this.redis.getUser(sessionId)
|
||||
if (cachedUser) {
|
||||
|
||||
7
src/LiveKit/livekit.module.ts
Normal file
7
src/LiveKit/livekit.module.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
// import { Module } from '@nestjs/common'
|
||||
// import { LiveKitService } from './livekit.service'
|
||||
// @Module({
|
||||
// providers: [LiveKitService],
|
||||
// exports: [LiveKitService],
|
||||
// })
|
||||
// export class LiveKitModule {}
|
||||
24
src/LiveKit/livekit.participant.service.ts
Normal file
24
src/LiveKit/livekit.participant.service.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
// @ts-ignore
|
||||
import { AccessToken } from 'livekit-server-sdk'
|
||||
|
||||
@Injectable()
|
||||
export class LiveKitParticipantService {
|
||||
async createAccessToken(participantId: string) {
|
||||
return new AccessToken(
|
||||
process.env.LIVEKIT_API_KEY,
|
||||
process.env.LIVEKIT_API_SECRET,
|
||||
{
|
||||
identity: participantId,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
async grantRoomJoinPermission(token: AccessToken, roomName: string) {
|
||||
token.addGrant({ roomJoin: true, room: roomName })
|
||||
}
|
||||
|
||||
async toJWT(token: AccessToken) {
|
||||
return token.toJwt()
|
||||
}
|
||||
}
|
||||
36
src/LiveKit/livekit.room.service.ts
Normal file
36
src/LiveKit/livekit.room.service.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
// import {
|
||||
// Room,
|
||||
// RoomServiceClient,
|
||||
// RoomCompositeOptions,
|
||||
// // @ts-ignore
|
||||
// } from 'livekit-server-sdk'
|
||||
// import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
// export class LiveKitRoomService {
|
||||
// private roomServiceClient: RoomServiceClient
|
||||
|
||||
// constructor() {
|
||||
// this.roomServiceClient = new RoomServiceClient(
|
||||
// process.env.LIVEKIT_URL as string,
|
||||
// process.env.LIVEKIT_API_KEY as string,
|
||||
// process.env.LIVEKIT_API_SECRET as string,
|
||||
// )
|
||||
// }
|
||||
|
||||
// async createServiceMeetingRoom(chattingRoomId: string) {
|
||||
// const room = await this.roomServiceClient.createRoom({
|
||||
// maxParticipants: 3,
|
||||
// name: chattingRoomId,
|
||||
// })
|
||||
|
||||
// return room
|
||||
// }
|
||||
|
||||
// async createWorkshopMeetingRoom(workshopId: string, maxParticipants: number) {
|
||||
// const room = await this.roomServiceClient.createRoom({
|
||||
// maxParticipants: maxParticipants,
|
||||
// name: workshopId,
|
||||
// })
|
||||
// return room
|
||||
// }
|
||||
// }
|
||||
11
src/LiveKit/livekit.service.ts
Normal file
11
src/LiveKit/livekit.service.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
// import { Injectable, OnModuleInit } from '@nestjs/common'
|
||||
// import { LiveKitRoomService } from './livekit.room.service'
|
||||
|
||||
// @Injectable()
|
||||
// export class LiveKitService implements OnModuleInit {
|
||||
// private liveKitRoomService: LiveKitRoomService
|
||||
// async onModuleInit() {
|
||||
// // init livekit room service
|
||||
// this.liveKitRoomService = new LiveKitRoomService()
|
||||
// }
|
||||
// }
|
||||
5
src/LiveKit/livekit.track.service.ts
Normal file
5
src/LiveKit/livekit.track.service.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
// @ts-ignore
|
||||
import { TrackInfo } from 'livekit-server-sdk'
|
||||
|
||||
export class LiveKitTrackService {
|
||||
}
|
||||
@@ -57,7 +57,7 @@ export class MessageSchema extends PothosSchema {
|
||||
type: this.message(),
|
||||
description: 'Retrieve a single message by its unique identifier.',
|
||||
args: this.builder.generator.findUniqueArgs('Message'),
|
||||
resolve: async (query, root, args) => {
|
||||
resolve: async (query, _root, args) => {
|
||||
return await this.prisma.message.findUnique({
|
||||
...query,
|
||||
where: args.where,
|
||||
@@ -69,7 +69,7 @@ export class MessageSchema extends PothosSchema {
|
||||
description:
|
||||
'Retrieve a list of messages with optional filtering, ordering, and pagination.',
|
||||
args: this.builder.generator.findManyArgs('Message'),
|
||||
resolve: async (query, root, args) => {
|
||||
resolve: async (query, _root, args) => {
|
||||
return await this.prisma.message.findMany({
|
||||
...query,
|
||||
skip: args.skip ?? undefined,
|
||||
@@ -83,7 +83,7 @@ export class MessageSchema extends PothosSchema {
|
||||
type: [this.message()],
|
||||
description: 'Retrieve a list of messages by chat room ID.',
|
||||
args: this.builder.generator.findManyArgs('Message'),
|
||||
resolve: async (query, root, args) => {
|
||||
resolve: async (query, _root, args) => {
|
||||
return await this.prisma.message.findMany({
|
||||
...query,
|
||||
where: args.filter ?? undefined,
|
||||
@@ -94,6 +94,19 @@ export class MessageSchema extends PothosSchema {
|
||||
|
||||
// mutations
|
||||
this.builder.mutationFields((t) => ({
|
||||
testSendMessage: t.field({
|
||||
type: 'String',
|
||||
description: 'Test sending a message.',
|
||||
resolve: async (_, __, ctx) => {
|
||||
if (ctx.isSubscription) {
|
||||
throw new Error('Not allowed')
|
||||
}
|
||||
ctx.http.pubSub.publish('MESSAGE_SENT', {
|
||||
message: 'Hello, world!',
|
||||
})
|
||||
return 'Message sent'
|
||||
},
|
||||
}),
|
||||
sendMessage: t.prismaField({
|
||||
type: this.message(),
|
||||
description: 'Send a message to a chat room.',
|
||||
@@ -104,7 +117,7 @@ export class MessageSchema extends PothosSchema {
|
||||
required: true,
|
||||
}),
|
||||
},
|
||||
resolve: async (query, root, args, ctx, info) => {
|
||||
resolve: async (query, _root, args, ctx, _info) => {
|
||||
const message = await this.prisma.message.create({
|
||||
...query,
|
||||
data: args.input,
|
||||
|
||||
@@ -8,8 +8,8 @@ import {
|
||||
import { Builder } from '../Graphql/graphql.builder'
|
||||
import { PrismaService } from '../Prisma/prisma.service'
|
||||
import { OrderStatus } from '@prisma/client'
|
||||
import { DateTimeUtils } from 'src/common/utils/datetime.utils'
|
||||
import { PayosService } from 'src/Payos/payos.service'
|
||||
import { DateTimeUtils } from '../common/utils/datetime.utils'
|
||||
import { PayosService } from '../Payos/payos.service'
|
||||
@Injectable()
|
||||
export class OrderSchema extends PothosSchema {
|
||||
constructor(
|
||||
@@ -45,6 +45,9 @@ export class OrderSchema extends PothosSchema {
|
||||
scheduleId: t.exposeID('scheduleId', {
|
||||
description: 'The ID of the schedule.',
|
||||
}),
|
||||
schedule: t.relation('schedule', {
|
||||
description: 'The schedule of the order.',
|
||||
}),
|
||||
createdAt: t.expose('createdAt', {
|
||||
type: 'DateTime',
|
||||
description: 'The date and time the order was created.',
|
||||
@@ -140,7 +143,7 @@ export class OrderSchema extends PothosSchema {
|
||||
}
|
||||
// check if input schedule has order id then throw error
|
||||
const schedule = await this.prisma.schedule.findUnique({
|
||||
where: { id: args.data.scheduleId },
|
||||
where: { id: args.data.schedule.connect?.id ?? '' },
|
||||
})
|
||||
if (schedule?.orderId) {
|
||||
// check if order status is PAID OR PENDING
|
||||
@@ -159,9 +162,9 @@ export class OrderSchema extends PothosSchema {
|
||||
data: {
|
||||
status: OrderStatus.PENDING,
|
||||
total: service.price,
|
||||
userId: ctx.http.me.id,
|
||||
userId: ctx.http.me?.id ?? '',
|
||||
serviceId: service.id,
|
||||
scheduleId: args.data.scheduleId,
|
||||
scheduleId: args.data.schedule.connect?.id ?? '',
|
||||
},
|
||||
})
|
||||
// check if service is valid
|
||||
@@ -173,7 +176,7 @@ export class OrderSchema extends PothosSchema {
|
||||
if (order.total === 0) {
|
||||
// assign schedule
|
||||
await this.prisma.schedule.update({
|
||||
where: { id: args.data.scheduleId },
|
||||
where: { id: args.data.schedule.connect?.id ?? '' },
|
||||
data: {
|
||||
orderId: order.id,
|
||||
},
|
||||
@@ -197,8 +200,8 @@ export class OrderSchema extends PothosSchema {
|
||||
orderCode: paymentCode,
|
||||
amount: service.price,
|
||||
description: service.name,
|
||||
buyerName: ctx.http.me.name,
|
||||
buyerEmail: ctx.http.me.email,
|
||||
buyerName: ctx.http.me?.name ?? '',
|
||||
buyerEmail: ctx.http.me?.email ?? '',
|
||||
returnUrl: `${process.env.PAYOS_RETURN_URL}`.replace(
|
||||
'<serviceId>',
|
||||
service.id,
|
||||
|
||||
@@ -10,7 +10,12 @@ import type {
|
||||
CancelPaymentLinkRequestType,
|
||||
DataType,
|
||||
} from '@payos/node/lib/type'
|
||||
import { OrderStatus, PaymentStatus, ScheduleStatus } from '@prisma/client'
|
||||
import {
|
||||
ChatRoomType,
|
||||
OrderStatus,
|
||||
PaymentStatus,
|
||||
ScheduleStatus,
|
||||
} from '@prisma/client'
|
||||
export type CreatePaymentBody = CheckoutRequestType
|
||||
export type CreatePaymentResponse = CheckoutResponseDataType
|
||||
@Injectable()
|
||||
@@ -52,7 +57,7 @@ export class PayosService {
|
||||
status: orderStatus,
|
||||
},
|
||||
})
|
||||
const order = await this.prisma.order.findUnique({
|
||||
const order = await this.prisma.order.findUniqueOrThrow({
|
||||
where: { id: payment.orderId },
|
||||
})
|
||||
const schedule = await this.prisma.schedule.findUnique({
|
||||
@@ -62,16 +67,36 @@ export class PayosService {
|
||||
await this.prisma.schedule.update({
|
||||
where: { id: schedule?.id },
|
||||
data: {
|
||||
customerId: order?.userId,
|
||||
orderId: order?.id,
|
||||
status: ScheduleStatus.IN_PROGRESS,
|
||||
},
|
||||
})
|
||||
// get mentor id from managed service
|
||||
const managedService = await this.prisma.managedService.findUniqueOrThrow({
|
||||
where: { id: schedule?.managedServiceId },
|
||||
})
|
||||
const mentorId = managedService.mentorId
|
||||
// get center id from order service
|
||||
const orderService = await this.prisma.service.findUniqueOrThrow({
|
||||
where: { id: order?.serviceId },
|
||||
})
|
||||
const centerId = orderService.centerId
|
||||
// create chatroom for service meeting room
|
||||
await this.prisma.chatRoom.create({
|
||||
data: {
|
||||
type: ChatRoomType.SUPPORT,
|
||||
customerId: order.userId,
|
||||
centerId: centerId,
|
||||
mentorId: mentorId,
|
||||
},
|
||||
})
|
||||
return {
|
||||
message: 'Payment received',
|
||||
}
|
||||
}
|
||||
|
||||
async createPaymentURL(body: any) {
|
||||
async createPaymentURL(body: CheckoutRequestType) {
|
||||
return await this.payos.createPaymentLink(body)
|
||||
}
|
||||
|
||||
@@ -90,6 +115,7 @@ export class PayosService {
|
||||
return await this.payos.cancelPaymentLink(orderId, cancellationReason)
|
||||
}
|
||||
|
||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||
async refundPayment(body: any) {
|
||||
return body
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ export class ResumeSchema extends PothosSchema {
|
||||
const resumes = await this.prisma.resume.findMany({
|
||||
...query,
|
||||
where: {
|
||||
userId: ctx.http.me.id,
|
||||
userId: ctx.http.me?.id ?? '',
|
||||
status: args.status ?? undefined,
|
||||
},
|
||||
})
|
||||
@@ -274,7 +274,7 @@ export class ResumeSchema extends PothosSchema {
|
||||
if (ctx.isSubscription) {
|
||||
throw new Error('Not allowed')
|
||||
}
|
||||
if (ctx.http.me.role !== Role.MODERATOR) {
|
||||
if (ctx.http.me?.role !== Role.MODERATOR) {
|
||||
throw new Error('Not allowed')
|
||||
}
|
||||
const { resumeId, status, adminNote } = args
|
||||
@@ -315,7 +315,7 @@ export class ResumeSchema extends PothosSchema {
|
||||
_adminNote = await tx.adminNote.create({
|
||||
data: {
|
||||
content: adminNote,
|
||||
notedByUserId: ctx.http.me.id,
|
||||
notedByUserId: ctx.http.me?.id ?? '',
|
||||
resumeId,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -11,6 +11,7 @@ import { ScheduleStatus } from '@prisma/client'
|
||||
import { ScheduleService } from './schedule.service'
|
||||
import { AppConfigService } from '../AppConfig/appconfig.service'
|
||||
import { ScheduleConfigType } from './schedule'
|
||||
import { DateTimeUtils } from 'src/common/utils/datetime.utils'
|
||||
|
||||
@Injectable()
|
||||
export class ScheduleSchema extends PothosSchema {
|
||||
@@ -290,36 +291,40 @@ d72a864e-2f41-45ab-9c9b-bf0512a31883,e9be51fd-2382-4e43-9988-74e76fde4b56,2024-1
|
||||
required: true,
|
||||
}),
|
||||
},
|
||||
resolve: async (query, _root, args, _ctx, _info) => {
|
||||
resolve: async (query, _root, args, ctx, _info) => {
|
||||
if (ctx.isSubscription) {
|
||||
throw new Error('Cannot create schedule in subscription')
|
||||
}
|
||||
Logger.log('args.schedule', args.schedule)
|
||||
// check if there is any overlapping schedule
|
||||
const overlappingSchedules = await this.prisma.schedule.findMany({
|
||||
where: {
|
||||
OR: [
|
||||
{
|
||||
scheduleStart: {
|
||||
gte: args.schedule.scheduleStart,
|
||||
},
|
||||
scheduleEnd: {
|
||||
lte: args.schedule.scheduleEnd,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
// generate preview and check if there is any overlapping with other schedules date in same service
|
||||
const previewSchedule =
|
||||
await this.scheduleService.createSchedulePreviewForCenter({
|
||||
startDate: args.schedule.scheduleStart as string,
|
||||
endDate: args.schedule.scheduleEnd as string,
|
||||
slots: args.schedule.slots as number[],
|
||||
days: args.schedule.daysOfWeek as number[],
|
||||
})
|
||||
// check if is same managedServiceId
|
||||
if (
|
||||
overlappingSchedules.some(
|
||||
(schedule) =>
|
||||
schedule.managedServiceId ===
|
||||
args.schedule.managedService.connect?.id,
|
||||
const existingScheduleDates = await this.prisma.scheduleDate.findMany(
|
||||
{
|
||||
where: {
|
||||
serviceId: args.schedule.managedService.connect?.id,
|
||||
},
|
||||
},
|
||||
)
|
||||
) {
|
||||
throw new Error(
|
||||
`Overlapping schedule with ${JSON.stringify(
|
||||
overlappingSchedules.map((schedule) => schedule.id),
|
||||
)}`,
|
||||
// check if there is any overlapping with existing schedule dates in same service using DateTimeUtils
|
||||
const isOverlapping = DateTimeUtils.isOverlaps(
|
||||
previewSchedule.slots.map((slot) => ({
|
||||
start: DateTimeUtils.fromIsoString(slot.start),
|
||||
end: DateTimeUtils.fromIsoString(slot.end),
|
||||
})),
|
||||
existingScheduleDates.map((date) => ({
|
||||
start: DateTimeUtils.fromDate(date.start),
|
||||
end: DateTimeUtils.fromDate(date.end),
|
||||
})),
|
||||
)
|
||||
if (isOverlapping) {
|
||||
Logger.error('Overlapping schedule', 'ScheduleSchema')
|
||||
throw new Error('Overlapping schedule')
|
||||
}
|
||||
const schedule = await this.prisma.schedule.create({
|
||||
...query,
|
||||
|
||||
@@ -102,7 +102,6 @@ export class ScheduleService {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const scheduleDatesCreated =
|
||||
await this.prisma.scheduleDate.createManyAndReturn({
|
||||
data: scheduleDates,
|
||||
|
||||
@@ -159,14 +159,14 @@ export class ServiceSchema extends PothosSchema {
|
||||
throw new Error('Not allowed')
|
||||
}
|
||||
// check role if user is mentor or center owner
|
||||
const role = ctx.http.me.role
|
||||
const role = ctx.http.me?.role
|
||||
if (role !== Role.CENTER_MENTOR && role !== Role.CENTER_OWNER) {
|
||||
throw new Error('Not allowed')
|
||||
}
|
||||
if (role === Role.CENTER_MENTOR) {
|
||||
// load only service belong to center of current user
|
||||
const managedServices = await this.prisma.managedService.findMany({
|
||||
where: { mentorId: ctx.http.me.id },
|
||||
where: { mentorId: ctx.http.me?.id ?? '' },
|
||||
})
|
||||
if (!managedServices) {
|
||||
throw new Error('Managed services not found')
|
||||
@@ -179,7 +179,7 @@ export class ServiceSchema extends PothosSchema {
|
||||
// if role is center owner, load all services belong to center of current user
|
||||
if (role === Role.CENTER_OWNER) {
|
||||
const center = await this.prisma.center.findUnique({
|
||||
where: { centerOwnerId: ctx.http.me.id },
|
||||
where: { centerOwnerId: ctx.http.me?.id ?? '' },
|
||||
})
|
||||
if (!center) {
|
||||
throw new Error('Center not found')
|
||||
@@ -234,7 +234,7 @@ export class ServiceSchema extends PothosSchema {
|
||||
throw new Error('Not allowed')
|
||||
}
|
||||
// replace userId with current user id
|
||||
args.input.user = { connect: { id: ctx.http.me.id } }
|
||||
args.input.user = { connect: { id: ctx.http.me?.id ?? '' } }
|
||||
return await this.prisma.service.create({
|
||||
...query,
|
||||
data: args.input,
|
||||
@@ -321,7 +321,7 @@ export class ServiceSchema extends PothosSchema {
|
||||
adminNote: {
|
||||
create: {
|
||||
content: args.adminNote ?? '',
|
||||
notedByUserId: ctx.http.me.id,
|
||||
notedByUserId: ctx.http.me?.id ?? '',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -7,12 +7,15 @@ import {
|
||||
} from '@smatch-corp/nestjs-pothos'
|
||||
import { Builder } from '../Graphql/graphql.builder'
|
||||
import { PrismaService } from '../Prisma/prisma.service'
|
||||
// import { LiveKitRoomService } from 'src/LiveKit/livekit.room.service'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
@Injectable()
|
||||
export class ServiceMeetingRoomSchema extends PothosSchema {
|
||||
constructor(
|
||||
@Inject(SchemaBuilderToken) private readonly builder: Builder,
|
||||
private readonly prisma: PrismaService,
|
||||
// private readonly liveKitRoomService: LiveKitRoomService,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
@@ -66,5 +69,33 @@ export class ServiceMeetingRoomSchema extends PothosSchema {
|
||||
},
|
||||
}),
|
||||
}))
|
||||
|
||||
this.builder.mutationFields((t) => ({
|
||||
createServiceMeetingRoom: t.prismaField({
|
||||
type: this.serviceMeetingRoom(),
|
||||
args: {
|
||||
input: t.arg({
|
||||
type: this.builder.generator.getCreateInput('ServiceMeetingRoom'),
|
||||
required: true,
|
||||
}),
|
||||
},
|
||||
description: 'Create a new service meeting room.',
|
||||
resolve: async (query, _root, args, _ctx, _info) => {
|
||||
// for test only !!!
|
||||
if (args.input.chattingRoom.create) {
|
||||
args.input.chattingRoom.create.id = uuidv4()
|
||||
}
|
||||
|
||||
// call livekit room service to create room
|
||||
// this.liveKitRoomService.createServiceMeetingRoom(
|
||||
// args.input.chattingRoom.create?.id ?? '',
|
||||
// )
|
||||
return await this.prisma.serviceMeetingRoom.create({
|
||||
...query,
|
||||
data: args.input,
|
||||
})
|
||||
},
|
||||
}),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,7 +262,10 @@ export class UserSchema extends PothosSchema {
|
||||
if (ctx.isSubscription) {
|
||||
throw new Error('Not allowed')
|
||||
}
|
||||
const id = ctx.http.me.id
|
||||
const id = ctx.http.me?.id
|
||||
if (!id) {
|
||||
throw new Error('User not found')
|
||||
}
|
||||
if (args.imageBlob) {
|
||||
const { mimetype, createReadStream } = await args.imageBlob
|
||||
if (mimetype && createReadStream) {
|
||||
@@ -333,7 +336,7 @@ export class UserSchema extends PothosSchema {
|
||||
throw new Error('Not allowed')
|
||||
}
|
||||
// check context is admin
|
||||
if (ctx.http.me.role !== 'ADMIN') {
|
||||
if (ctx.http.me?.role !== 'ADMIN') {
|
||||
throw new UnauthorizedException(`Only admin can invite moderator`)
|
||||
}
|
||||
let user
|
||||
|
||||
@@ -4,6 +4,7 @@ import { GraphqlModule } from './Graphql/graphql.module'
|
||||
import { MailModule } from './Mail/mail.module'
|
||||
import { Module } from '@nestjs/common'
|
||||
import { RestfulModule } from './Restful/restful.module'
|
||||
// import { LiveKitModule } from './LiveKit/livekit.module'
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -14,6 +15,7 @@ import { RestfulModule } from './Restful/restful.module'
|
||||
MailModule,
|
||||
GraphqlModule,
|
||||
RestfulModule,
|
||||
// LiveKitModule,
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
@@ -60,6 +60,20 @@ export class DateTimeUtils {
|
||||
)
|
||||
}
|
||||
|
||||
static isOverlaps(
|
||||
listA: { start: DateTime; end: DateTime }[],
|
||||
listB: { start: DateTime; end: DateTime }[],
|
||||
): boolean {
|
||||
for (const a of listA) {
|
||||
for (const b of listB) {
|
||||
if (this.isOverlap(a.start, a.end, b.start, b.end)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
static fromIsoString(isoString: string): DateTime {
|
||||
const dateTime = DateTime.fromISO(isoString)
|
||||
if (!dateTime.isValid) {
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user