From a2c3fa9e0900641c502c322a6a9bf776e76c1c59 Mon Sep 17 00:00:00 2001 From: raymond Date: Sun, 19 Mar 2023 13:58:24 +0900 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=9A=A7chore=20:=20OPENVIDU=20?= =?UTF-8?q?=ED=99=98=EA=B2=BD=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/server/package.json | 1 + apps/server/src/app.module.ts | 7 +++++ apps/server/src/openvidu/openvidu.module.ts | 29 +++++++++++++++++ apps/server/src/openvidu/openvidu.resolver.ts | 4 +++ apps/server/src/openvidu/openvidu.service.ts | 4 +++ apps/sfu/src/common/common.constant.ts | 1 + docker/db/docker-compose.yaml | 8 ++++- yarn.lock | 31 ++++++++++++++++++- 8 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 apps/server/src/openvidu/openvidu.module.ts create mode 100644 apps/server/src/openvidu/openvidu.resolver.ts create mode 100644 apps/server/src/openvidu/openvidu.service.ts diff --git a/apps/server/package.json b/apps/server/package.json index aa8f11c..f9a37dc 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -50,6 +50,7 @@ "class-validator": "^0.14.0", "graphql": "^16.6.0", "jsonwebtoken": "^9.0.0", + "openvidu-node-client": "^2.26.2", "redis": "^4.6.4", "reflect-metadata": "^0.1.13", "rxjs": "^7.2.0", diff --git a/apps/server/src/app.module.ts b/apps/server/src/app.module.ts index bcf7f24..9857471 100644 --- a/apps/server/src/app.module.ts +++ b/apps/server/src/app.module.ts @@ -19,6 +19,7 @@ import { UsersModule } from './users/users.module' import { AuthModule } from './auth/auth.module' import { SocketsModule } from './sockets/sockets.module' import { GrpcModule } from 'grpc/grpc.module' +import { OpenviduModule } from './openvidu/openvidu.module' @Module({ imports: [ @@ -36,6 +37,8 @@ import { GrpcModule } from 'grpc/grpc.module' REDIS_PASSWORD: joi.string().required(), JWT_PRIVATE_KEY: joi.string().required(), JWT_PUBLIC_KEY: joi.string().required(), + OEPNVIDU_URL: joi.string().required(), + OPENVIDU_SECRET: joi.string().required(), }), }), PrismaModule, @@ -66,6 +69,10 @@ import { GrpcModule } from 'grpc/grpc.module' AuthModule, SocketsModule, GrpcModule.forRoot(), + OpenviduModule.forRoot({ + OPENVIDU_URL: process.env.OPENVIDU_URL, + OPENVIDU_SECRET: process.env.OPENVIDU_SECRET, + }), ], controllers: [], providers: [], diff --git a/apps/server/src/openvidu/openvidu.module.ts b/apps/server/src/openvidu/openvidu.module.ts new file mode 100644 index 0000000..1b30a4a --- /dev/null +++ b/apps/server/src/openvidu/openvidu.module.ts @@ -0,0 +1,29 @@ +import { OPENVIDU_MODULE } from './../../../sfu/src/common/common.constant' +import { DynamicModule, Module } from '@nestjs/common' +// import { OpenVidu } from 'openvidu-node-client' +import { OpenviduService } from './openvidu.service' +import { OpenviduResolver } from './openvidu.resolver' + +class OpenViduConfig { + OPENVIDU_URL: string + OPENVIDU_SECRET: string +} + +@Module({}) +export class OpenviduModule { + static forRoot(option: OpenViduConfig): DynamicModule { + // const vidu = new OpenVidu(option.OPENVIDU_URL, option.OPENVIDU_SECRET) + return { + module: OpenviduModule, + providers: [ + OpenviduResolver, + { + provide: OPENVIDU_MODULE, + useValue: { test: 123 }, + }, + OpenviduService, + ], + exports: [OpenviduService], + } + } +} diff --git a/apps/server/src/openvidu/openvidu.resolver.ts b/apps/server/src/openvidu/openvidu.resolver.ts new file mode 100644 index 0000000..7198153 --- /dev/null +++ b/apps/server/src/openvidu/openvidu.resolver.ts @@ -0,0 +1,4 @@ +import { Resolver } from '@nestjs/graphql' + +@Resolver() +export class OpenviduResolver {} diff --git a/apps/server/src/openvidu/openvidu.service.ts b/apps/server/src/openvidu/openvidu.service.ts new file mode 100644 index 0000000..97d890c --- /dev/null +++ b/apps/server/src/openvidu/openvidu.service.ts @@ -0,0 +1,4 @@ +import { Injectable } from '@nestjs/common' + +@Injectable() +export class OpenviduService {} diff --git a/apps/sfu/src/common/common.constant.ts b/apps/sfu/src/common/common.constant.ts index 3d291ff..914df3c 100644 --- a/apps/sfu/src/common/common.constant.ts +++ b/apps/sfu/src/common/common.constant.ts @@ -1 +1,2 @@ export const PUBSUB_MODULE = 'PUBSUB_MODULE' +export const OPENVIDU_MODULE = 'OPENVIDU_MODULE' diff --git a/docker/db/docker-compose.yaml b/docker/db/docker-compose.yaml index e50fd20..1f3b60f 100644 --- a/docker/db/docker-compose.yaml +++ b/docker/db/docker-compose.yaml @@ -18,4 +18,10 @@ services: environment: POSTGRES_USER: PLUG POSTGRES_PASSWORD: 1234 - POSTGRES_DB: PLUG \ No newline at end of file + POSTGRES_DB: PLUG + openVidu: + image: openvidu/openvidu-dev:2.26.0 + ports: + - "4443:4443" + environment: + OPENVIDU_SECRET: MY_SECRET \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 8538e99..1dae20f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3952,6 +3952,14 @@ autoprefixer@^10.4.13: picocolors "^1.0.0" postcss-value-parser "^4.2.0" +axios@0.27.2: + version "0.27.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" + integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ== + dependencies: + follow-redirects "^1.14.9" + form-data "^4.0.0" + babel-jest@^29.4.3: version "29.4.3" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.4.3.tgz#478b84d430972b277ad67dd631be94abea676792" @@ -4172,6 +4180,14 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== +buffer@6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + buffer@^5.5.0: version "5.7.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" @@ -5645,6 +5661,11 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== +follow-redirects@^1.14.9: + version "1.15.2" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" + integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== + foreach@^2.0.4: version "2.0.6" resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.6.tgz#87bcc8a1a0e74000ff2bf9802110708cfb02eb6e" @@ -6094,7 +6115,7 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4: dependencies: safer-buffer ">= 2.1.2 < 3" -ieee754@^1.1.13: +ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -7782,6 +7803,14 @@ open@7.4.2: is-docker "^2.0.0" is-wsl "^2.1.1" +openvidu-node-client@^2.26.2: + version "2.26.2" + resolved "https://registry.yarnpkg.com/openvidu-node-client/-/openvidu-node-client-2.26.2.tgz#11b0d4b8f1645c5ba0394a1993d07d97b710b5c0" + integrity sha512-3xUgQhgIsUHUxgpQCHJnv6hfYg61z+iEyrWKqtaCCF0G/Q1VjIZQ4aOgF4eGOMotno4siTYUtSaVopp7x31Tlg== + dependencies: + axios "0.27.2" + buffer "6.0.3" + optimism@^0.16.1: version "0.16.2" resolved "https://registry.yarnpkg.com/optimism/-/optimism-0.16.2.tgz#519b0c78b3b30954baed0defe5143de7776bf081" From e66574bfe3e6d742f423ee1a0685a8cc54907b89 Mon Sep 17 00:00:00 2001 From: raymond Date: Sun, 19 Mar 2023 23:38:13 +0900 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=8E=A8feat=20:=20openvidu=20=EC=A0=84?= =?UTF-8?q?=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - openvidu session 생성 코드 작성 --- apps/client/codegen.ts | 11 +- apps/client/package.json | 5 +- apps/client/src/__api__/types.ts | 74 ---- apps/client/src/gql/fragment-masking.ts | 48 +++ apps/client/src/gql/gql.ts | 62 +++ apps/client/src/gql/graphql.ts | 152 +++++++ apps/client/src/gql/index.ts | 2 + apps/client/src/hooks/useGetToken.ts | 50 +++ apps/client/src/hooks/useOv.ts | 154 +++++++ apps/client/src/layout/SocketLayout.tsx | 14 +- apps/client/src/model/UserModel.ts | 100 +++++ apps/client/src/screen/Login.tsx | 1 - apps/client/src/screen/Room.tsx | 33 +- apps/client/vite.config.ts | 8 + apps/gateway/.mesh/index.ts | 83 +++- apps/gateway/.mesh/schema.graphql | 25 +- .../.mesh/sources/Server/schema.graphql | 72 ++++ apps/gateway/.mesh/sources/Server/types.ts | 133 ++++++ .../.mesh/sources/Users/schema.graphql | 24 +- apps/gateway/.mesh/sources/Users/types.ts | 38 +- apps/gateway/.meshrc.yaml | 2 +- apps/server/src/app.module.ts | 9 +- apps/server/src/common/common.constants.ts | 1 + .../src/common/scalar/timestamp.scalar.ts | 31 ++ apps/server/src/events/events.gateway.ts | 19 - .../src/openvidu/dtos/viduoutput.dto.ts | 13 + .../src/openvidu/entities/vidu.entitiy.ts | 320 ++++++++++++++ apps/server/src/openvidu/openvidu.module.ts | 8 +- apps/server/src/openvidu/openvidu.resolver.ts | 49 ++- apps/server/src/openvidu/openvidu.service.ts | 34 +- apps/server/src/users/users.resolver.ts | 6 +- apps/server/src/users/users.service.ts | 1 - .../src/workspaces/workspaces.gateway.ts | 219 +++++----- apps/sfu/src/common/common.constant.ts | 1 - package.json | 6 +- yarn.lock | 398 ++++++++++++++++-- 36 files changed, 1904 insertions(+), 302 deletions(-) delete mode 100644 apps/client/src/__api__/types.ts create mode 100644 apps/client/src/gql/fragment-masking.ts create mode 100644 apps/client/src/gql/gql.ts create mode 100644 apps/client/src/gql/graphql.ts create mode 100644 apps/client/src/gql/index.ts create mode 100644 apps/client/src/hooks/useGetToken.ts create mode 100644 apps/client/src/hooks/useOv.ts create mode 100644 apps/client/src/model/UserModel.ts create mode 100644 apps/gateway/.mesh/sources/Server/schema.graphql create mode 100644 apps/gateway/.mesh/sources/Server/types.ts create mode 100644 apps/server/src/common/scalar/timestamp.scalar.ts create mode 100644 apps/server/src/openvidu/dtos/viduoutput.dto.ts create mode 100644 apps/server/src/openvidu/entities/vidu.entitiy.ts diff --git a/apps/client/codegen.ts b/apps/client/codegen.ts index 5f0ea9f..c530a8a 100644 --- a/apps/client/codegen.ts +++ b/apps/client/codegen.ts @@ -2,14 +2,19 @@ import type { CodegenConfig } from '@graphql-codegen/cli' const config: CodegenConfig = { overwrite: true, - schema: 'http://localhost:4337/graphql', - documents: ['src/**/*.tsx'], + schema: 'http://192.168.0.137:4337/graphql', + documents: ['src/**/*.{tsx,ts}'], ignoreNoDocuments: true, generates: { - './src/__api__/types.ts': { + // './src/__api__/types.ts': { + // plugins: ['typescript', 'typescript-operations'], + // }, + './src/gql/': { plugins: ['typescript', 'typescript-operations'], + preset: 'client', }, }, } export default config +// https://github.com/OpenVidu/openvidu-call-react/blob/dea85b34254e8540c0c9ca6874bb90f60603101c/openvidu-call-react/src/components/VideoRoomComponent.js diff --git a/apps/client/package.json b/apps/client/package.json index ff9ed52..e66f944 100644 --- a/apps/client/package.json +++ b/apps/client/package.json @@ -7,13 +7,16 @@ "dev": "vite", "build": "tsc && vite build", "preview": "vite preview", - "codegen": "graphql-codegen --config codegen.ts" + "codegen": "graphql-codegen --config codegen.ts", + "codegen:watch": "graphql-codegen --config codegen.ts --watch" }, "dependencies": { "@apollo/client": "^3.7.7", "@types/js-cookie": "^3.0.2", "graphql": "^16.6.0", "js-cookie": "^3.0.1", + "openvidu-browser": "^2.26.0", + "openvidu-react": "^2.26.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-hook-form": "^7.43.0", diff --git a/apps/client/src/__api__/types.ts b/apps/client/src/__api__/types.ts deleted file mode 100644 index a075ece..0000000 --- a/apps/client/src/__api__/types.ts +++ /dev/null @@ -1,74 +0,0 @@ -export type Maybe = T | null; -export type InputMaybe = Maybe; -export type Exact = { [K in keyof T]: T[K] }; -export type MakeOptional = Omit & { [SubKey in K]?: Maybe }; -export type MakeMaybe = Omit & { [SubKey in K]: Maybe }; -/** All built-in and custom scalars, mapped to their actual values */ -export type Scalars = { - ID: string; - String: string; - Boolean: boolean; - Int: number; - Float: number; - DateTime: any; -}; - -export type LoginInput = { - nickname: Scalars['String']; -}; - -export type LoginOutput = { - __typename?: 'LoginOutput'; - error?: Maybe; - ok: Scalars['Boolean']; - token?: Maybe; - user?: Maybe; -}; - -export type Mutation = { - __typename?: 'Mutation'; - login: LoginOutput; -}; - - -export type MutationLoginArgs = { - input: LoginInput; -}; - -export type Query = { - __typename?: 'Query'; - getMe: UserProfileOutput; -}; - -export type User = { - __typename?: 'User'; - createdAt: Scalars['DateTime']; - id: Scalars['Float']; - nickname: Scalars['String']; - role?: Maybe; - updatedAt: Scalars['DateTime']; -}; - -export type UserProfileOutput = { - __typename?: 'UserProfileOutput'; - error?: Maybe; - ok: Scalars['Boolean']; - user?: Maybe; -}; - -export enum UserRole { - Admin = 'Admin', - Client = 'Client' -} - -export type GetMeQueryVariables = Exact<{ [key: string]: never; }>; - - -export type GetMeQuery = { __typename?: 'Query', getMe: { __typename?: 'UserProfileOutput', ok: boolean, error?: string | null, user?: { __typename?: 'User', id: number, nickname: string, createdAt: any, updatedAt: any, role?: UserRole | null } | null } }; - -export type LoginMutationVariables = Exact<{ - LoginInput: LoginInput; -}>; - - -export type LoginMutation = { __typename?: 'Mutation', login: { __typename?: 'LoginOutput', ok: boolean, token?: string | null, error?: string | null, user?: { __typename?: 'User', id: number, nickname: string, updatedAt: any, createdAt: any, role?: UserRole | null } | null } }; diff --git a/apps/client/src/gql/fragment-masking.ts b/apps/client/src/gql/fragment-masking.ts new file mode 100644 index 0000000..097ba25 --- /dev/null +++ b/apps/client/src/gql/fragment-masking.ts @@ -0,0 +1,48 @@ +import { ResultOf, TypedDocumentNode as DocumentNode, } from '@graphql-typed-document-node/core'; + + +export type FragmentType> = TDocumentType extends DocumentNode< + infer TType, + any +> + ? TType extends { ' $fragmentName'?: infer TKey } + ? TKey extends string + ? { ' $fragmentRefs'?: { [key in TKey]: TType } } + : never + : never + : never; + +// return non-nullable if `fragmentType` is non-nullable +export function useFragment( + _documentNode: DocumentNode, + fragmentType: FragmentType> +): TType; +// return nullable if `fragmentType` is nullable +export function useFragment( + _documentNode: DocumentNode, + fragmentType: FragmentType> | null | undefined +): TType | null | undefined; +// return array of non-nullable if `fragmentType` is array of non-nullable +export function useFragment( + _documentNode: DocumentNode, + fragmentType: ReadonlyArray>> +): ReadonlyArray; +// return array of nullable if `fragmentType` is array of nullable +export function useFragment( + _documentNode: DocumentNode, + fragmentType: ReadonlyArray>> | null | undefined +): ReadonlyArray | null | undefined; +export function useFragment( + _documentNode: DocumentNode, + fragmentType: FragmentType> | ReadonlyArray>> | null | undefined +): TType | ReadonlyArray | null | undefined { + return fragmentType as any; +} + + +export function makeFragmentData< + F extends DocumentNode, + FT extends ResultOf +>(data: FT, _fragment: F): FragmentType { + return data as FragmentType; +} \ No newline at end of file diff --git a/apps/client/src/gql/gql.ts b/apps/client/src/gql/gql.ts new file mode 100644 index 0000000..bf27683 --- /dev/null +++ b/apps/client/src/gql/gql.ts @@ -0,0 +1,62 @@ +/* eslint-disable */ +import * as types from './graphql'; +import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; + +/** + * Map of all GraphQL operations in the project. + * + * This map has several performance disadvantages: + * 1. It is not tree-shakeable, so it will include all operations in the project. + * 2. It is not minifiable, so the string of a GraphQL query will be multiple times inside the bundle. + * 3. It does not support dead code elimination, so it will add unused operations. + * + * Therefore it is highly recommended to use the babel-plugin for production. + */ +const documents = { + "\n query getToken($sessionId: String!) {\n getToken(sessionId: $sessionId) {\n ok\n token\n }\n }\n": types.GetTokenDocument, + "\n query getSession($sessionId: String!) {\n getSession(sessionId: $sessionId) {\n ok\n session {\n sessionId\n createdAt\n }\n error\n }\n }\n": types.GetSessionDocument, + "\n query getTokenConnection($sessionId: String!) {\n getTokenConnection(sessionId: $sessionId) {\n ok\n token\n error\n }\n }\n": types.GetTokenConnectionDocument, + "\n query getMe {\n getMe {\n ok\n user {\n id\n nickname\n createdAt\n updatedAt\n role\n sessionId\n }\n error\n }\n }\n": types.GetMeDocument, + "\n mutation login($LoginInput: LoginInput!) {\n login(input: $LoginInput) {\n ok\n token\n error\n user {\n id\n nickname\n updatedAt\n createdAt\n role\n }\n }\n }\n": types.LoginDocument, +}; + +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + * + * + * @example + * ```ts + * const query = gql(`query GetUser($id: ID!) { user(id: $id) { name } }`); + * ``` + * + * The query argument is unknown! + * Please regenerate the types. + */ +export function graphql(source: string): unknown; + +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "\n query getToken($sessionId: String!) {\n getToken(sessionId: $sessionId) {\n ok\n token\n }\n }\n"): (typeof documents)["\n query getToken($sessionId: String!) {\n getToken(sessionId: $sessionId) {\n ok\n token\n }\n }\n"]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "\n query getSession($sessionId: String!) {\n getSession(sessionId: $sessionId) {\n ok\n session {\n sessionId\n createdAt\n }\n error\n }\n }\n"): (typeof documents)["\n query getSession($sessionId: String!) {\n getSession(sessionId: $sessionId) {\n ok\n session {\n sessionId\n createdAt\n }\n error\n }\n }\n"]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "\n query getTokenConnection($sessionId: String!) {\n getTokenConnection(sessionId: $sessionId) {\n ok\n token\n error\n }\n }\n"): (typeof documents)["\n query getTokenConnection($sessionId: String!) {\n getTokenConnection(sessionId: $sessionId) {\n ok\n token\n error\n }\n }\n"]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "\n query getMe {\n getMe {\n ok\n user {\n id\n nickname\n createdAt\n updatedAt\n role\n sessionId\n }\n error\n }\n }\n"): (typeof documents)["\n query getMe {\n getMe {\n ok\n user {\n id\n nickname\n createdAt\n updatedAt\n role\n sessionId\n }\n error\n }\n }\n"]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "\n mutation login($LoginInput: LoginInput!) {\n login(input: $LoginInput) {\n ok\n token\n error\n user {\n id\n nickname\n updatedAt\n createdAt\n role\n }\n }\n }\n"): (typeof documents)["\n mutation login($LoginInput: LoginInput!) {\n login(input: $LoginInput) {\n ok\n token\n error\n user {\n id\n nickname\n updatedAt\n createdAt\n role\n }\n }\n }\n"]; + +export function graphql(source: string) { + return (documents as any)[source] ?? {}; +} + +export type DocumentType> = TDocumentNode extends DocumentNode< infer TType, any> ? TType : never; \ No newline at end of file diff --git a/apps/client/src/gql/graphql.ts b/apps/client/src/gql/graphql.ts new file mode 100644 index 0000000..5fe260b --- /dev/null +++ b/apps/client/src/gql/graphql.ts @@ -0,0 +1,152 @@ +/* eslint-disable */ +import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; +export type Maybe = T | null; +export type InputMaybe = Maybe; +export type Exact = { [K in keyof T]: T[K] }; +export type MakeOptional = Omit & { [SubKey in K]?: Maybe }; +export type MakeMaybe = Omit & { [SubKey in K]: Maybe }; +/** All built-in and custom scalars, mapped to their actual values */ +export type Scalars = { + ID: string; + String: string; + Boolean: boolean; + Int: number; + Float: number; + /** A date-time string at UTC, such as 2019-12-03T09:54:33Z, compliant with the date-time format. */ + DateTime: any; + /** TimeStamp Test Scalar */ + TimeStamp: any; +}; + +export type LoginInput = { + nickname: Scalars['String']; +}; + +export type LoginOutput = { + __typename?: 'LoginOutput'; + error?: Maybe; + ok: Scalars['Boolean']; + token?: Maybe; + user?: Maybe; +}; + +export type Mutation = { + __typename?: 'Mutation'; + login: LoginOutput; +}; + + +export type MutationLoginArgs = { + input: LoginInput; +}; + +export type Query = { + __typename?: 'Query'; + getMe: UserProfileOutput; + getSession: ViduSessionOutput; + getToken: ViduTokenOutput; + getTokenConnection: ViduTokenOutput; + getUser: UserProfileOutput; +}; + + +export type QueryGetSessionArgs = { + sessionId: Scalars['String']; +}; + + +export type QueryGetTokenArgs = { + sessionId: Scalars['String']; +}; + + +export type QueryGetTokenConnectionArgs = { + sessionId: Scalars['String']; +}; + + +export type QueryGetUserArgs = { + id: Scalars['Float']; +}; + +export type User = { + __typename?: 'User'; + createdAt: Scalars['DateTime']; + id: Scalars['ID']; + nickname: Scalars['String']; + role?: Maybe; + sessionId?: Maybe; + updatedAt: Scalars['DateTime']; +}; + +export type UserProfileOutput = { + __typename?: 'UserProfileOutput'; + error?: Maybe; + ok: Scalars['Boolean']; + user?: Maybe; +}; + +export enum UserRole { + Admin = 'Admin', + Client = 'Client' +} + +export type ViduSession = { + __typename?: 'ViduSession'; + createdAt: Scalars['TimeStamp']; + sessionId: Scalars['String']; +}; + +export type ViduSessionOutput = { + __typename?: 'ViduSessionOutput'; + error?: Maybe; + ok: Scalars['Boolean']; + session?: Maybe; +}; + +export type ViduTokenOutput = { + __typename?: 'ViduTokenOutput'; + error?: Maybe; + ok: Scalars['Boolean']; + token?: Maybe; +}; + +export type GetTokenQueryVariables = Exact<{ + sessionId: Scalars['String']; +}>; + + +export type GetTokenQuery = { __typename?: 'Query', getToken: { __typename?: 'ViduTokenOutput', ok: boolean, token?: string | null } }; + +export type GetSessionQueryVariables = Exact<{ + sessionId: Scalars['String']; +}>; + + +export type GetSessionQuery = { __typename?: 'Query', getSession: { __typename?: 'ViduSessionOutput', ok: boolean, error?: string | null, session?: { __typename?: 'ViduSession', sessionId: string, createdAt: any } | null } }; + +export type GetTokenConnectionQueryVariables = Exact<{ + sessionId: Scalars['String']; +}>; + + +export type GetTokenConnectionQuery = { __typename?: 'Query', getTokenConnection: { __typename?: 'ViduTokenOutput', ok: boolean, token?: string | null, error?: string | null } }; + +export type GetMeQueryVariables = Exact<{ [key: string]: never; }>; + + +export type GetMeQuery = { __typename?: 'Query', getMe: { __typename?: 'UserProfileOutput', ok: boolean, error?: string | null, user?: { __typename?: 'User', id: string, nickname: string, createdAt: any, updatedAt: any, role?: UserRole | null, sessionId?: string | null } | null } }; + +export type LoginMutationVariables = Exact<{ + LoginInput: LoginInput; +}>; + + +export type LoginMutation = { __typename?: 'Mutation', login: { __typename?: 'LoginOutput', ok: boolean, token?: string | null, error?: string | null, user?: { __typename?: 'User', id: string, nickname: string, updatedAt: any, createdAt: any, role?: UserRole | null } | null } }; + + +export const GetTokenDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"getToken"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"sessionId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getToken"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"sessionId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"sessionId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ok"}},{"kind":"Field","name":{"kind":"Name","value":"token"}}]}}]}}]} as unknown as DocumentNode; +export const GetSessionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"getSession"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"sessionId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getSession"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"sessionId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"sessionId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ok"}},{"kind":"Field","name":{"kind":"Name","value":"session"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"sessionId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}},{"kind":"Field","name":{"kind":"Name","value":"error"}}]}}]}}]} as unknown as DocumentNode; +export const GetTokenConnectionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"getTokenConnection"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"sessionId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getTokenConnection"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"sessionId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"sessionId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ok"}},{"kind":"Field","name":{"kind":"Name","value":"token"}},{"kind":"Field","name":{"kind":"Name","value":"error"}}]}}]}}]} as unknown as DocumentNode; +export const GetMeDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"getMe"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getMe"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ok"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"nickname"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"sessionId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"error"}}]}}]}}]} as unknown as DocumentNode; +export const LoginDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"login"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"LoginInput"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"LoginInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"login"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"LoginInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ok"}},{"kind":"Field","name":{"kind":"Name","value":"token"}},{"kind":"Field","name":{"kind":"Name","value":"error"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"nickname"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}}]}}]}}]} as unknown as DocumentNode; \ No newline at end of file diff --git a/apps/client/src/gql/index.ts b/apps/client/src/gql/index.ts new file mode 100644 index 0000000..f515991 --- /dev/null +++ b/apps/client/src/gql/index.ts @@ -0,0 +1,2 @@ +export * from "./fragment-masking"; +export * from "./gql"; \ No newline at end of file diff --git a/apps/client/src/hooks/useGetToken.ts b/apps/client/src/hooks/useGetToken.ts new file mode 100644 index 0000000..9686667 --- /dev/null +++ b/apps/client/src/hooks/useGetToken.ts @@ -0,0 +1,50 @@ +import { useCallback } from 'react' +import { gql, useLazyQuery, useQuery } from '@apollo/client' +import { graphql } from 'gql' + +const GET_VIDU_TOKEN = graphql(/** GraphQl */ ` + query getToken($sessionId: String!) { + getToken(sessionId: $sessionId) { + ok + token + } + } +`) + +const GET_VIDU_SESSION = graphql(/** GraphQl */ ` + query getSession($sessionId: String!) { + getSession(sessionId: $sessionId) { + ok + session { + sessionId + createdAt + } + error + } + } +`) + +const GET_VIDU_TOKEN_CONNECTION = graphql(` + query getTokenConnection($sessionId: String!) { + getTokenConnection(sessionId: $sessionId) { + ok + token + error + } + } +`) + +const useGetToken = () => { + const [getToken, { data, loading, error }] = useLazyQuery(GET_VIDU_TOKEN_CONNECTION, { + onCompleted(data) { + console.log(data, '< { + const [session, setSession] = useState() + const [subscribers, setSubscriber] = useState([]) + const [localUserAccessAllowed, setLocalUserAccessAllowed] = useState(false) + const localUser = useRef(new UserModel()).current + const { getToken, token } = useGetToken() + const ov = useRef(new OpenVidu()).current + + const joinSession = useCallback(() => { + setSession(() => ov.initSession()) + }, []) + + const checkSomeoneShareScreen = useCallback(() => { + let isScreenShared = + subscribers.some((user) => user.isScreenShareActive()) || localUser.isScreenShareActive() + }, [subscribers, localUser]) + + const sendSignalUserChanged = useCallback( + (data: SignalOptions) => { + if (session) { + const signalOptions = { + data: JSON.stringify(data), + type: 'userChanged', + } + session?.signal(signalOptions) + } + }, + [session], + ) + + const updateSubscribers = useCallback(() => { + if (localUser) { + } + }, []) + + const subscribeToStreamCreated = useCallback(() => { + if (session) { + session.on('streamCreated', (event) => { + const subscriber = session.subscribe(event.stream, undefined) + subscriber.on('streamPlaying', (event) => { + checkSomeoneShareScreen() + // 커스텀 클래스 추가 + // subscriber.videos[0].video.parentElement + }) + const newUser = new UserModel() + newUser.setStreamManager(subscriber) + newUser.setConnectionId(event.stream.connection.connectionId) + newUser.setType('remote') + const nickname = event.stream.connection.data.split('%')[0] + newUser.setNickname(JSON.parse(nickname).clientData) + setSubscriber((prev) => [...prev, newUser]) + if (localUserAccessAllowed) { + updateSubscribers() + } + }) + // const sub = session?.subscribe() + } + }, [session]) + + const connectWebCam = useCallback(async () => { + if (session) { + await ov.getUserMedia({ audioSource: undefined, videoSource: undefined }) + var devices = await ov.getDevices() + var videoDevices = devices.filter((device) => device.kind === 'videoinput') + + let publisher = ov.initPublisher(undefined, { + audioSource: undefined, + videoSource: videoDevices[0].deviceId, + publishAudio: localUser.isAudioActive(), + publishVideo: localUser.isVideoActive(), + resolution: '640x480', + frameRate: 30, + insertMode: 'APPEND', + }) + + if (session?.capabilities.publish) { + publisher.on('accessAllowed', () => { + session.publish(publisher).then(() => { + updateSubscribers() + setLocalUserAccessAllowed(true) + // if (this.props.joinSession) { + // this.props.joinSession() + // } + }) + }) + } + // localUser.setNickname(this.state.myUserName) + localUser.setConnectionId(session.connection.connectionId) + localUser.setScreenShareActive(false) + localUser.setStreamManager(publisher) + // this.subscribeToUserChanged() + // this.subscribeToStreamDestroyed() + // this.sendSignalUserChanged({ isScreenShareActive: localUser.isScreenShareActive() }) + + // this.setState({ currentVideoDevice: videoDevices[0], localUser: localUser }, () => { + // this.state.localUser.getStreamManager().on('streamPlaying', (e) => { + // this.updateLayout() + // publisher.videos[0].video.parentElement.classList.remove('custom-class') + // }) + // }) + } + }, []) + const connect = useCallback((token: string) => { + if (session) { + session + .connect(token, { clientData: localUser.getNickname() }) + .then(() => { + connectWebCam() + }) + .catch((error: any) => { + console.log('There was an error connecting to the session:', error.code, error.message) + }) + } + }, []) + + const connectToSession = useCallback(async () => { + if (session) { + if (token) { + connect(token) + } else { + try { + console.log('START!!', roomName) + getToken({ + variables: { + sessionId: roomName, + }, + }) + } catch (error: any) { + console.error('There was an error getting the token:', error.code, error.message) + } + } + } + }, [token, session]) + + useEffect(() => { + joinSession() + }, []) + + useEffect(() => { + if (session) { + subscribeToStreamCreated() + connectToSession() + } + }, [session]) + + return +} + +export default useOv diff --git a/apps/client/src/layout/SocketLayout.tsx b/apps/client/src/layout/SocketLayout.tsx index 0730887..3fb561b 100644 --- a/apps/client/src/layout/SocketLayout.tsx +++ b/apps/client/src/layout/SocketLayout.tsx @@ -1,10 +1,11 @@ -import { gql, useQuery } from '@apollo/client' +import { useQuery } from '@apollo/client' +import { graphql } from 'gql' +import useGetToken from 'hooks/useGetToken' import { Outlet, useNavigate } from 'react-router-dom' import { useClearUser } from 'store/action' import store from 'store/index' -import { GetMeQuery } from '__api__/types' -const GET_ME = gql` +const GET_ME = graphql(/** GraphQl */ ` query getMe { getMe { ok @@ -14,21 +15,21 @@ const GET_ME = gql` createdAt updatedAt role + sessionId } error } } -` +`) const SocketLayout = () => { const key = import.meta.env.VITE_AUTH_KEY const token = localStorage.getItem(key) || '' - const { setUser, clear } = store((state) => ({ setUser: state.setUser, clear: state.clear, })) - const { data, loading, client } = useQuery(GET_ME, { + const { data, loading, client } = useQuery(GET_ME, { context: { headers: { [key]: token, @@ -36,6 +37,7 @@ const SocketLayout = () => { }, onCompleted({ getMe: { ok, error, user } }) { if (ok && user) { + console.log(user) setUser({ user, token }) } else { clear() diff --git a/apps/client/src/model/UserModel.ts b/apps/client/src/model/UserModel.ts new file mode 100644 index 0000000..e689d3b --- /dev/null +++ b/apps/client/src/model/UserModel.ts @@ -0,0 +1,100 @@ +import { Publisher, Subscriber } from 'openvidu-browser' + +interface UserModelOption { + connectionId: string + audioActive: boolean + videoActive: boolean + screenShareActive: boolean + nickname: string + streamManager: Subscriber | Publisher | null + type: 'remote' | 'local' +} + +class UserModel { + // static instance: UserModel + private connectionId: string = '' + private audioActive: boolean = true + private videoActive: boolean = true + private screenShareActive: boolean = false + private nickname: string = '' + private streamManager: Subscriber | Publisher | null = null + private type: 'remote' | 'local' = 'local' + + // constructor(option: Partial = {}) { + // if (UserModel.instance) return UserModel.instance + // this.connectionId = option.connectionId ?? '' + // this.audioActive = option.audioActive ?? true + // this.videoActive = option.videoActive ?? true + // this.screenShareActive = option.screenShareActive ?? false + // this.nickname = option.nickname ?? '' + // this.streamManager = option.streamManager ?? null + // this.type = option.type ?? 'local' + // } + constructor(option: Partial = {}) { + // if (UserModel.instance) return UserModel.instance + this.connectionId = option.connectionId ?? '' + this.audioActive = option.audioActive ?? true + this.videoActive = option.videoActive ?? true + this.screenShareActive = option.screenShareActive ?? false + this.nickname = option.nickname ?? '' + this.streamManager = option.streamManager ?? null + this.type = option.type ?? 'local' + } + + isAudioActive() { + return this.audioActive + } + + isVideoActive() { + return this.videoActive + } + + isScreenShareActive() { + return this.screenShareActive + } + + getConnectionId() { + return this.connectionId + } + + getNickname() { + return this.nickname + } + + getStreamManager() { + return this.streamManager + } + + isLocal() { + return this.type === 'local' + } + isRemote() { + return !this.isLocal() + } + setAudioActive(isAudioActive: boolean) { + this.audioActive = isAudioActive + } + setVideoActive(isVideoActive: boolean) { + this.videoActive = isVideoActive + } + setScreenShareActive(isScreenShareActive: boolean) { + this.screenShareActive = isScreenShareActive + } + setStreamManager(streamManager: Subscriber | Publisher) { + this.streamManager = streamManager + } + + setConnectionId(conecctionId: string) { + this.connectionId = conecctionId + } + setNickname(nickname: string) { + this.nickname = nickname + } + setType(type: 'local' | 'remote') { + if (type === 'local' || type === 'remote') { + this.type = type + } + } +} + +export default UserModel diff --git a/apps/client/src/screen/Login.tsx b/apps/client/src/screen/Login.tsx index 5ed0274..21d4751 100644 --- a/apps/client/src/screen/Login.tsx +++ b/apps/client/src/screen/Login.tsx @@ -31,7 +31,6 @@ const Login = () => { const { register, handleSubmit } = useForm() const navigate = useNavigate() const onCompleted = (data: LoginMutation) => { - console.log(data) const { login: { error, ok, token, user }, } = data diff --git a/apps/client/src/screen/Room.tsx b/apps/client/src/screen/Room.tsx index d9fc8ba..9fa27b6 100644 --- a/apps/client/src/screen/Room.tsx +++ b/apps/client/src/screen/Room.tsx @@ -1,4 +1,3 @@ -// import useSocket from 'hooks/useSocket' import { useNavigate, useParams } from 'react-router-dom' import { Welcome } from 'types/dtos/socketResponse.dto' import useSocket from 'hooks/useSocket' @@ -6,10 +5,14 @@ import { useCallback, useEffect, useRef, useState } from 'react' import useRTCConnection from 'hooks/useRTCConnection' import { Socket } from 'socket.io-client' import { handleIce } from 'libs/fn' +import store from 'store/index' +import { OpenVidu } from 'openvidu-browser' +import useOv from 'hooks/useOv' const Room = () => { const { roomName } = useParams() const navigate = useNavigate() + const test = useOv(roomName!) const [joinedUsers, setJoindUsers] = useState<[] | string[]>([]) const [lastJoinedUser, setLastJoined] = useState(null) const myVideo = useRef(null) @@ -107,7 +110,6 @@ const Room = () => { fromSessionId: payload.sessionId, }), ) - console.log('이거여러번실행함?') socket.emit( 'answer', { @@ -136,14 +138,14 @@ const Room = () => { onConnect(socket) { socket.listen('welcome', onRefreshRoomSetting) socket.listen('leave_room', onRefreshRoomSetting) - socket.listen( - 'offer', - async (data: { sdp: string; sessionId: string; channelId: string }) => { - const payload = data + // socket.listen( + // 'offer', + // async (data: { sdp: string; sessionId: string; channelId: string }) => { + // const payload = data - const peer = await createStreamPeer(payload) - }, - ) + // const peer = await createStreamPeer(payload) + // }, + // ) }, onMounted(socket) { if (socket.connected) { @@ -155,11 +157,14 @@ const Room = () => { socket.emit('leave_room', roomName) }, }) - const { isLoading, stream } = useRTCConnection({ - async onConnect(stream) { - await createPeer(socket, stream) - }, - }) + + const me = store((state) => state.user) + console.log(me) + // const { isLoading, stream } = useRTCConnection({ + // async onConnect(stream) { + // await createPeer(socket, stream) + // }, + // }) useEffect(() => { socket.emit('join_room', roomName, onRefreshRoomSetting) }, []) diff --git a/apps/client/vite.config.ts b/apps/client/vite.config.ts index 250c90b..b201dfe 100644 --- a/apps/client/vite.config.ts +++ b/apps/client/vite.config.ts @@ -36,6 +36,14 @@ export default ({ mode }) => { find: 'store', replacement: path.resolve(__dirname, 'src/store'), }, + { + find: 'gql', + replacement: path.resolve(__dirname, 'src/gql'), + }, + { + find: 'model', + replacement: path.resolve(__dirname, 'src/model'), + }, ], }, }) diff --git a/apps/gateway/.mesh/index.ts b/apps/gateway/.mesh/index.ts index 587a36e..505c0a5 100644 --- a/apps/gateway/.mesh/index.ts +++ b/apps/gateway/.mesh/index.ts @@ -6,7 +6,7 @@ import { getMesh, ExecuteMeshFn, SubscribeMeshFn, MeshContext as BaseMeshContext import { MeshStore, FsStoreStorageAdapter } from '@graphql-mesh/store'; import { path as pathModule } from '@graphql-mesh/cross-helpers'; import { ImportFn } from '@graphql-mesh/types'; -import type { UsersTypes } from './sources/Users/types'; +import type { ServerTypes } from './sources/Server/types'; export type Maybe = T | null; export type InputMaybe = Maybe; export type Exact = { [K in keyof T]: T[K] }; @@ -24,6 +24,7 @@ export type Scalars = { Int: number; Float: number; DateTime: any; + TimeStamp: any; }; export type User = { @@ -52,9 +53,29 @@ export type UserProfileOutput = { user?: Maybe; }; +export type ViduSession = { + sessionId: Scalars['String']; + createdAt: Scalars['TimeStamp']; +}; + +export type ViduSessionOutput = { + error?: Maybe; + ok: Scalars['Boolean']; + session?: Maybe; +}; + +export type ViduTokenOutput = { + error?: Maybe; + ok: Scalars['Boolean']; + token?: Maybe; +}; + export type Query = { - getUser: User; + getUser: UserProfileOutput; getMe: UserProfileOutput; + getSession: ViduSessionOutput; + getToken: ViduTokenOutput; + getTokenConnection: ViduTokenOutput; }; @@ -62,6 +83,21 @@ export type QuerygetUserArgs = { id: Scalars['Float']; }; + +export type QuerygetSessionArgs = { + sessionId: Scalars['String']; +}; + + +export type QuerygetTokenArgs = { + sessionId: Scalars['String']; +}; + + +export type QuerygetTokenConnectionArgs = { + sessionId: Scalars['String']; +}; + export type Mutation = { login: LoginOutput; }; @@ -167,6 +203,10 @@ export type ResolversTypes = ResolversObject<{ LoginOutput: ResolverTypeWrapper; Boolean: ResolverTypeWrapper; UserProfileOutput: ResolverTypeWrapper; + ViduSession: ResolverTypeWrapper; + TimeStamp: ResolverTypeWrapper; + ViduSessionOutput: ResolverTypeWrapper; + ViduTokenOutput: ResolverTypeWrapper; Query: ResolverTypeWrapper<{}>; Float: ResolverTypeWrapper; Mutation: ResolverTypeWrapper<{}>; @@ -182,6 +222,10 @@ export type ResolversParentTypes = ResolversObject<{ LoginOutput: LoginOutput; Boolean: Scalars['Boolean']; UserProfileOutput: UserProfileOutput; + ViduSession: ViduSession; + TimeStamp: Scalars['TimeStamp']; + ViduSessionOutput: ViduSessionOutput; + ViduTokenOutput: ViduTokenOutput; Query: {}; Float: Scalars['Float']; Mutation: {}; @@ -217,9 +261,36 @@ export type UserProfileOutputResolvers; }>; +export type ViduSessionResolvers = ResolversObject<{ + sessionId?: Resolver; + createdAt?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}>; + +export interface TimeStampScalarConfig extends GraphQLScalarTypeConfig { + name: 'TimeStamp'; +} + +export type ViduSessionOutputResolvers = ResolversObject<{ + error?: Resolver, ParentType, ContextType>; + ok?: Resolver; + session?: Resolver, ParentType, ContextType>; + __isTypeOf?: IsTypeOfResolverFn; +}>; + +export type ViduTokenOutputResolvers = ResolversObject<{ + error?: Resolver, ParentType, ContextType>; + ok?: Resolver; + token?: Resolver, ParentType, ContextType>; + __isTypeOf?: IsTypeOfResolverFn; +}>; + export type QueryResolvers = ResolversObject<{ - getUser?: Resolver>; + getUser?: Resolver>; getMe?: Resolver; + getSession?: Resolver>; + getToken?: Resolver>; + getTokenConnection?: Resolver>; }>; export type MutationResolvers = ResolversObject<{ @@ -231,12 +302,16 @@ export type Resolvers = ResolversObject<{ DateTime?: GraphQLScalarType; LoginOutput?: LoginOutputResolvers; UserProfileOutput?: UserProfileOutputResolvers; + ViduSession?: ViduSessionResolvers; + TimeStamp?: GraphQLScalarType; + ViduSessionOutput?: ViduSessionOutputResolvers; + ViduTokenOutput?: ViduTokenOutputResolvers; Query?: QueryResolvers; Mutation?: MutationResolvers; }>; -export type MeshContext = UsersTypes.Context & BaseMeshContext; +export type MeshContext = ServerTypes.Context & BaseMeshContext; const baseDir = pathModule.join(typeof __dirname === 'string' ? __dirname : '/', '..'); diff --git a/apps/gateway/.mesh/schema.graphql b/apps/gateway/.mesh/schema.graphql index a835be7..83166f6 100644 --- a/apps/gateway/.mesh/schema.graphql +++ b/apps/gateway/.mesh/schema.graphql @@ -35,9 +35,32 @@ type UserProfileOutput { user: User } +type ViduSession { + sessionId: String! + createdAt: TimeStamp! +} + +"""TimeStamp Test Scalar""" +scalar TimeStamp + +type ViduSessionOutput { + error: String + ok: Boolean! + session: ViduSession +} + +type ViduTokenOutput { + error: String + ok: Boolean! + token: String +} + type Query { - getUser(id: Float!): User! + getUser(id: Float!): UserProfileOutput! getMe: UserProfileOutput! + getSession(sessionId: String!): ViduSessionOutput! + getToken(sessionId: String!): ViduTokenOutput! + getTokenConnection(sessionId: String!): ViduTokenOutput! } type Mutation { diff --git a/apps/gateway/.mesh/sources/Server/schema.graphql b/apps/gateway/.mesh/sources/Server/schema.graphql new file mode 100644 index 0000000..83166f6 --- /dev/null +++ b/apps/gateway/.mesh/sources/Server/schema.graphql @@ -0,0 +1,72 @@ +schema { + query: Query + mutation: Mutation +} + +type User { + id: ID! + createdAt: DateTime! + updatedAt: DateTime! + nickname: String! + role: UserRole + sessionId: String +} + +""" +A date-time string at UTC, such as 2019-12-03T09:54:33Z, compliant with the date-time format. +""" +scalar DateTime + +enum UserRole { + Admin + Client +} + +type LoginOutput { + error: String + ok: Boolean! + token: String + user: User +} + +type UserProfileOutput { + error: String + ok: Boolean! + user: User +} + +type ViduSession { + sessionId: String! + createdAt: TimeStamp! +} + +"""TimeStamp Test Scalar""" +scalar TimeStamp + +type ViduSessionOutput { + error: String + ok: Boolean! + session: ViduSession +} + +type ViduTokenOutput { + error: String + ok: Boolean! + token: String +} + +type Query { + getUser(id: Float!): UserProfileOutput! + getMe: UserProfileOutput! + getSession(sessionId: String!): ViduSessionOutput! + getToken(sessionId: String!): ViduTokenOutput! + getTokenConnection(sessionId: String!): ViduTokenOutput! +} + +type Mutation { + login(input: LoginInput!): LoginOutput! +} + +input LoginInput { + nickname: String! +} \ No newline at end of file diff --git a/apps/gateway/.mesh/sources/Server/types.ts b/apps/gateway/.mesh/sources/Server/types.ts new file mode 100644 index 0000000..8154593 --- /dev/null +++ b/apps/gateway/.mesh/sources/Server/types.ts @@ -0,0 +1,133 @@ +// @ts-nocheck + +import { InContextSdkMethod } from '@graphql-mesh/types'; +import { MeshContext } from '@graphql-mesh/runtime'; + +export namespace ServerTypes { + export type Maybe = T | null; +export type InputMaybe = Maybe; +export type Exact = { [K in keyof T]: T[K] }; +export type MakeOptional = Omit & { [SubKey in K]?: Maybe }; +export type MakeMaybe = Omit & { [SubKey in K]: Maybe }; +/** All built-in and custom scalars, mapped to their actual values */ +export type Scalars = { + ID: string; + String: string; + Boolean: boolean; + Int: number; + Float: number; + DateTime: any; + TimeStamp: any; +}; + +export type User = { + id: Scalars['ID']; + createdAt: Scalars['DateTime']; + updatedAt: Scalars['DateTime']; + nickname: Scalars['String']; + role?: Maybe; + sessionId?: Maybe; +}; + +export type UserRole = + | 'Admin' + | 'Client'; + +export type LoginOutput = { + error?: Maybe; + ok: Scalars['Boolean']; + token?: Maybe; + user?: Maybe; +}; + +export type UserProfileOutput = { + error?: Maybe; + ok: Scalars['Boolean']; + user?: Maybe; +}; + +export type ViduSession = { + sessionId: Scalars['String']; + createdAt: Scalars['TimeStamp']; +}; + +export type ViduSessionOutput = { + error?: Maybe; + ok: Scalars['Boolean']; + session?: Maybe; +}; + +export type ViduTokenOutput = { + error?: Maybe; + ok: Scalars['Boolean']; + token?: Maybe; +}; + +export type Query = { + getUser: UserProfileOutput; + getMe: UserProfileOutput; + getSession: ViduSessionOutput; + getToken: ViduTokenOutput; + getTokenConnection: ViduTokenOutput; +}; + + +export type QuerygetUserArgs = { + id: Scalars['Float']; +}; + + +export type QuerygetSessionArgs = { + sessionId: Scalars['String']; +}; + + +export type QuerygetTokenArgs = { + sessionId: Scalars['String']; +}; + + +export type QuerygetTokenConnectionArgs = { + sessionId: Scalars['String']; +}; + +export type Mutation = { + login: LoginOutput; +}; + + +export type MutationloginArgs = { + input: LoginInput; +}; + +export type LoginInput = { + nickname: Scalars['String']; +}; + + export type QuerySdk = { + /** null **/ + getUser: InContextSdkMethod, + /** null **/ + getMe: InContextSdkMethod, + /** null **/ + getSession: InContextSdkMethod, + /** null **/ + getToken: InContextSdkMethod, + /** null **/ + getTokenConnection: InContextSdkMethod + }; + + export type MutationSdk = { + /** null **/ + login: InContextSdkMethod + }; + + export type SubscriptionSdk = { + + }; + + export type Context = { + ["Server"]: { Query: QuerySdk, Mutation: MutationSdk, Subscription: SubscriptionSdk }, + + }; +} diff --git a/apps/gateway/.mesh/sources/Users/schema.graphql b/apps/gateway/.mesh/sources/Users/schema.graphql index a835be7..8390777 100644 --- a/apps/gateway/.mesh/sources/Users/schema.graphql +++ b/apps/gateway/.mesh/sources/Users/schema.graphql @@ -35,9 +35,31 @@ type UserProfileOutput { user: User } +type ViduSession { + sessionId: String! + createdAt: TimeStamp! +} + +"""TimeStamp Test Scalar""" +scalar TimeStamp + +type ViduSessionOutput { + error: String + ok: Boolean! + session: ViduSession +} + +type ViduTokenOutput { + error: String + ok: Boolean! + token: String +} + type Query { - getUser(id: Float!): User! + getUser(id: Float!): UserProfileOutput! getMe: UserProfileOutput! + getSession(sessionId: String!): ViduSessionOutput! + getToken(sessionId: String!): ViduTokenOutput! } type Mutation { diff --git a/apps/gateway/.mesh/sources/Users/types.ts b/apps/gateway/.mesh/sources/Users/types.ts index fc47b34..1e695d2 100644 --- a/apps/gateway/.mesh/sources/Users/types.ts +++ b/apps/gateway/.mesh/sources/Users/types.ts @@ -17,6 +17,7 @@ export type Scalars = { Int: number; Float: number; DateTime: any; + TimeStamp: any; }; export type User = { @@ -45,9 +46,28 @@ export type UserProfileOutput = { user?: Maybe; }; +export type ViduSession = { + sessionId: Scalars['String']; + createdAt: Scalars['TimeStamp']; +}; + +export type ViduSessionOutput = { + error?: Maybe; + ok: Scalars['Boolean']; + session?: Maybe; +}; + +export type ViduTokenOutput = { + error?: Maybe; + ok: Scalars['Boolean']; + token?: Maybe; +}; + export type Query = { - getUser: User; + getUser: UserProfileOutput; getMe: UserProfileOutput; + getSession: ViduSessionOutput; + getToken: ViduTokenOutput; }; @@ -55,6 +75,16 @@ export type QuerygetUserArgs = { id: Scalars['Float']; }; + +export type QuerygetSessionArgs = { + sessionId: Scalars['String']; +}; + + +export type QuerygetTokenArgs = { + sessionId: Scalars['String']; +}; + export type Mutation = { login: LoginOutput; }; @@ -72,7 +102,11 @@ export type LoginInput = { /** null **/ getUser: InContextSdkMethod, /** null **/ - getMe: InContextSdkMethod + getMe: InContextSdkMethod, + /** null **/ + getSession: InContextSdkMethod, + /** null **/ + getToken: InContextSdkMethod }; export type MutationSdk = { diff --git a/apps/gateway/.meshrc.yaml b/apps/gateway/.meshrc.yaml index dc60f69..adde954 100644 --- a/apps/gateway/.meshrc.yaml +++ b/apps/gateway/.meshrc.yaml @@ -1,5 +1,5 @@ sources: - - name: Users + - name: Server handler: graphql: endpoint: http://localhost:4000/graphql diff --git a/apps/server/src/app.module.ts b/apps/server/src/app.module.ts index 9857471..48462ca 100644 --- a/apps/server/src/app.module.ts +++ b/apps/server/src/app.module.ts @@ -18,8 +18,9 @@ import { PrismaModule } from './prisma/prisma.module' import { UsersModule } from './users/users.module' import { AuthModule } from './auth/auth.module' import { SocketsModule } from './sockets/sockets.module' -import { GrpcModule } from 'grpc/grpc.module' +// import { GrpcModule } from 'grpc/grpc.module' import { OpenviduModule } from './openvidu/openvidu.module' +import { TimeStamp } from 'common/scalar/timestamp.scalar' @Module({ imports: [ @@ -37,13 +38,14 @@ import { OpenviduModule } from './openvidu/openvidu.module' REDIS_PASSWORD: joi.string().required(), JWT_PRIVATE_KEY: joi.string().required(), JWT_PUBLIC_KEY: joi.string().required(), - OEPNVIDU_URL: joi.string().required(), + OPENVIDU_URL: joi.string().required(), OPENVIDU_SECRET: joi.string().required(), }), }), PrismaModule, GraphQLModule.forRoot({ driver: ApolloDriver, + // resolvers: { TimeStamp: TimeStamp }, autoSchemaFile: true, context: ({ req, connection }) => { return { @@ -67,8 +69,7 @@ import { OpenviduModule } from './openvidu/openvidu.module' }), UsersModule, AuthModule, - SocketsModule, - GrpcModule.forRoot(), + // GrpcModule.forRoot(), OpenviduModule.forRoot({ OPENVIDU_URL: process.env.OPENVIDU_URL, OPENVIDU_SECRET: process.env.OPENVIDU_SECRET, diff --git a/apps/server/src/common/common.constants.ts b/apps/server/src/common/common.constants.ts index 90234db..fd7ee2d 100644 --- a/apps/server/src/common/common.constants.ts +++ b/apps/server/src/common/common.constants.ts @@ -1,3 +1,4 @@ export const JWT_PROVIDER = 'jwt_provider' export const GRPC_SERVICE = 'GRPC_SERVICE' export const GRPC_CLIENT = 'GRPC_CLIENT' +export const OPENVIDU_MODULE = 'OPENVIDU_MODULE' diff --git a/apps/server/src/common/scalar/timestamp.scalar.ts b/apps/server/src/common/scalar/timestamp.scalar.ts new file mode 100644 index 0000000..a850c7d --- /dev/null +++ b/apps/server/src/common/scalar/timestamp.scalar.ts @@ -0,0 +1,31 @@ +import { GraphQLScalarType } from 'graphql' +import dayjs from 'dayjs' +function validate(time) { + if (typeof time === 'number') { + return dayjs(time).format('YYYY-MM-DD HH:mm:ss') + } else if (typeof time === 'string') { + return dayjs(time).get('milliseconds') + } +} + +export const TimeStamp = new GraphQLScalarType({ + name: 'TimeStamp', + description: 'TimeStamp Test Scalar', + serialize: (value) => { + if (typeof value === 'number') { + return validate(value) + } + throw new Error('invalid type') + }, + parseValue: (value) => { + if (typeof value === 'string') { + return validate(value) + } + throw new Error('invalid type') + }, + // TODO : 해당 매소드 기능 확인 후 정의 필요 + parseLiteral: (ast: any) => { + console.log(ast, 'parseLiteral') + return 1 + }, +}) diff --git a/apps/server/src/events/events.gateway.ts b/apps/server/src/events/events.gateway.ts index a2d1bb3..724c9a3 100644 --- a/apps/server/src/events/events.gateway.ts +++ b/apps/server/src/events/events.gateway.ts @@ -19,9 +19,6 @@ import { UsersService } from 'users/users.service' import { JwtService } from 'jwt/jwt.service' import { WSAuthMiddleware } from 'sockets/sockets.middleware' -import { Client, ClientGrpc, Transport } from '@nestjs/microservices' -import { join } from 'path' -import { PlugClient } from '@plug/proto' @UseFilters(new WsExceptionFilter()) @WebSocketGateway({ @@ -34,18 +31,6 @@ import { PlugClient } from '@plug/proto' export class EventsGateway implements OnGatewayConnection, OnGatewayDisconnect, OnGatewayInit { - @Client({ - transport: Transport.GRPC, - options: { - url: process.env.GRPC_URL, - package: process.env.GRPC_PACKAGE, - protoPath: join(__dirname, '../proto/plug.proto'), - }, - }) - private readonly client1: ClientGrpc - - private grpc: PlugClient - private readonly logger: Logger @WebSocketServer() public io: Namespace @@ -58,10 +43,6 @@ export class EventsGateway this.logger = new Logger(EventsGateway.name) } - onModuleInit() { - this.grpc = this.client1.getService('Plug') - } - @SubscribeMessage('set_nickname') async setNickname( @ConnectedSocket() client: AuthSocket, diff --git a/apps/server/src/openvidu/dtos/viduoutput.dto.ts b/apps/server/src/openvidu/dtos/viduoutput.dto.ts new file mode 100644 index 0000000..4581316 --- /dev/null +++ b/apps/server/src/openvidu/dtos/viduoutput.dto.ts @@ -0,0 +1,13 @@ +import { Field, ObjectType } from '@nestjs/graphql' +import { CoreOutput } from 'common/dtos/core.entites' +import { ViduSession } from 'openvidu/entities/vidu.entitiy' +@ObjectType() +export class ViduSessionOutput extends CoreOutput { + @Field(() => ViduSession, { nullable: true }) + session?: ViduSession +} +@ObjectType() +export class ViduTokenOutput extends CoreOutput { + @Field(() => String, { nullable: true }) + token?: string +} diff --git a/apps/server/src/openvidu/entities/vidu.entitiy.ts b/apps/server/src/openvidu/entities/vidu.entitiy.ts new file mode 100644 index 0000000..0ebacdf --- /dev/null +++ b/apps/server/src/openvidu/entities/vidu.entitiy.ts @@ -0,0 +1,320 @@ +import { + Directive, + Field, + Int, + ObjectType, + registerEnumType, +} from '@nestjs/graphql' + +import { IsArray, IsBoolean, IsEnum, IsNumber, IsString } from 'class-validator' +import { TimeStamp } from 'common/scalar/timestamp.scalar' +import { + MediaMode, + RecordingMode, + Recording, + RecordingLayout, + VideoCodec, + OpenViduRole, + ConnectionProperties, +} from 'openvidu-node-client' +registerEnumType(MediaMode, { name: 'MediaMode' }) +registerEnumType(RecordingMode, { name: 'RecordingMode' }) +registerEnumType(Recording.OutputMode, { name: 'RecordingOutputMode' }) +registerEnumType(RecordingLayout, { name: 'RecordingLayout' }) +registerEnumType(VideoCodec, { name: 'VideoCodec' }) +registerEnumType(OpenViduRole, { name: 'OpenViduRole' }) + +@ObjectType() +class MediaNode { + @Field(() => String) + @IsString() + id: string +} + +export class RecordingProperties { + @Field(() => String, { nullable: true }) + @IsString() + /** + * Name of the Recording. The video file will be named after this property. + * You can access this same value in your clients on recording events + * (`recordingStarted`, `recordingStopped`) + */ + name?: string + + @Field(() => Boolean, { nullable: true }) + @IsBoolean() + /** + * Whether or not to record audio. Cannot be set to false at the same time as {@link RecordingProperties.hasVideo} + * + * Default to true + */ + hasAudio?: boolean + + @Field(() => Boolean, { nullable: true }) + @IsBoolean() + /** + * Whether or not to record video. Cannot be set to false at the same time as {@link RecordingProperties.hasAudio} + * + * Default to true + */ + hasVideo?: boolean + + @Field(() => Recording.OutputMode, { nullable: true }) + @IsEnum(Recording.OutputMode) + /** + * The mode of recording: COMPOSED for a single archive in a grid layout or INDIVIDUAL for one archive for each stream + * + * Default to {@link Recording.OutputMode.COMPOSED} + */ + outputMode?: Recording.OutputMode + + @Field(() => RecordingLayout, { nullable: true }) + @IsEnum(RecordingLayout) + /** + * The layout to be used in the recording.
+ * Will only have effect if {@link RecordingProperties.outputMode} is set to {@link Recording.OutputMode.COMPOSED} or {@link Recording.OutputMode.COMPOSED_QUICK_START} + * + * Default to {@link RecordingLayout.BEST_FIT} + */ + recordingLayout?: RecordingLayout + + @Field(() => String, { nullable: true }) + @IsString() + /** + * Recording video file resolution. Must be a string with format "WIDTHxHEIGHT", + * being both WIDTH and HEIGHT the number of pixels between 100 and 1999.
+ * Will only have effect if {@link RecordingProperties.outputMode} is set to {@link Recording.OutputMode.COMPOSED} or {@link Recording.OutputMode.COMPOSED_QUICK_START} + * and {@link RecordingProperties.hasVideo} is set to true. For {@link Recording.OutputMode.INDIVIDUAL} all individual video files will have the native resolution of the published stream. + * + * Default to "1280x720" + */ + resolution?: string + + @Field(() => Int) + @IsNumber() + /** + * Recording video file frame rate.
+ * Will only have effect if {@link RecordingProperties.outputMode} is set to {@link Recording.OutputMode.COMPOSED} or {@link Recording.OutputMode.COMPOSED_QUICK_START} + * and {@link RecordingProperties.hasVideo} is set to true. For {@link Recording.OutputMode.INDIVIDUAL} all individual video files will have the native frame rate of the published stream. + * + * Default to 25 + */ + frameRate?: number + + @Field(() => Int) + @IsNumber() + /** + * The amount of shared memory reserved for the recording process in bytes. + * Will only have effect if {@link RecordingProperties.outputMode} is set to {@link Recording.OutputMode.COMPOSED} or {@link Recording.OutputMode.COMPOSED_QUICK_START} + * and {@link RecordingProperties.hasVideo} is set to true. Property ignored for INDIVIDUAL recordings and audio-only recordings. + * Minimum 134217728 (128MB). + * + * Default to 536870912 (512 MB) + */ + shmSize?: number + + @Field(() => String, { nullable: true }) + @IsString() + /** + * The relative path to the specific custom layout you want to use.
+ * Will only have effect if {@link RecordingProperties.outputMode} is set to {@link Recording.OutputMode.COMPOSED} or {@link Recording.OutputMode.COMPOSED_QUICK_START} + * and {@link RecordingProperties.recordingLayout} is set to {@link RecordingLayout.CUSTOM}
+ * See [Custom recording layouts](/en/stable/advanced-features/recording#custom-recording-layouts) to learn more. + */ + customLayout?: string + + @Field(() => Boolean, { nullable: true }) + @IsBoolean() + /** + * Whether to ignore failed streams or not when starting the recording. This property only applies if {@link RecordingProperties.outputMode} is set to {@link Recording.OutputMode.INDIVIDUAL}. + * For this type of recordings, when calling {@link OpenVidu.startRecording} by default all the streams available at the moment the recording process starts must be healthy + * and properly sending media. If some stream that should be sending media is broken, then the recording process fails after a 10s timeout. In this way your application is notified + * that some stream is not being recorded, so it can retry the process again. + * + * But you can disable this rollback behavior and simply ignore any failed stream, which will be susceptible to be recorded in the future if media starts flowing as expected at any point. + * The downside of this behavior is that you will have no guarantee that all streams present at the beginning of a recording are actually being recorded. + * + * Default to false + */ + ignoreFailedStreams?: boolean + + @Field(() => MediaNode, { nullable: true }) + /** + * **This feature is part of OpenVidu + * PRO + * and + * ENTERPRISE + * editions** + * + * The Media Node where to host the recording. The default option if this property is not defined is the same + * Media Node hosting the Session to record. This object defines the following properties as Media Node selector: + * - `id`: Media Node unique identifier + */ + mediaNode?: { + id: string + } +} +export class Publisher { + @Field(() => String) + @IsString() + streamId: string + + @Field(() => Int) + @IsNumber() + createdAt: number + + @Field(() => Boolean) + @IsBoolean() + hasAudio: boolean + + @Field(() => Boolean) + @IsBoolean() + hasVideo: boolean + + @Field(() => Boolean) + @IsBoolean() + audioActive: boolean + @Field(() => Boolean) + @IsBoolean() + videoActive: boolean + + @Field(() => Int) + @IsNumber() + frameRate: number + + @Field(() => String) + @IsString() + typeOfVideo: string + + @Field(() => String) + @IsString() + videoDimensions: string +} + +export class Connection { + /** + * Identifier of the Connection. You can call methods {@link Session.forceDisconnect} + * or {@link Session.updateConnection} passing this property as parameter + */ + @Field(() => String) + @IsString() + connectionId: string + /** + * Returns the status of the Connection. Can be: + * - `pending`: if the Connection is waiting for any user to use + * its internal token to connect to the session, calling method + * [Session.connect](https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/Session.html#connect) + * in OpenVidu Browser. + * - `active`: if the internal token of the Connection has already + * been used by some user to connect to the session, and it cannot be used + * again. + */ + @Field(() => String) + @IsString() + status: string + /** + * Timestamp when the Connection was created, in UTC milliseconds (ms since Jan 1, 1970, 00:00:00 UTC) + */ + @Field(() => Int) + @IsNumber() + createdAt: number + /** + * Timestamp when the Connection was taken by a user (passing from status "pending" to "active") + * in UTC milliseconds (ms since Jan 1, 1970, 00:00:00 UTC) + */ + @Field(() => Int) + @IsNumber() + activeAt: number + /** + * PRO + * Geo location of the Connection, with the following format: `"CITY, COUNTRY"` (`"unknown"` if it wasn't possible to locate it) + */ + @Field(() => String) + @IsString() + location: string + /** + * IP of the Connection, as seen by OpenVidu Server + */ + @Field(() => String) + @IsString() + ip: string + /** + * A complete description of the platform used by the participant to connect to the session + */ + @Field(() => String) + @IsString() + platform: string + /** + * Data associated to the Connection on the client-side. This value is set with second parameter of method + * [Session.connect](/en/stable/api/openvidu-browser/classes/Session.html#connect) in OpenVidu Browser + */ + @Field(() => String) + @IsString() + clientData: string + /** + * The {@link ConnectionProperties} assigned to the Connection + */ + + connectionProperties: ConnectionProperties + /** + * Token associated to the Connection. This is the value that must be sent to the client-side to be consumed in OpenVidu Browser + * method [Session.connect](https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/Session.html#connect). + */ + @Field(() => String) + @IsString() + token: string + /** + * Array of Publisher objects this particular Connection is publishing to the Session (each Publisher object has one Stream, uniquely + * identified by its `streamId`). You can call {@link Session.forceUnpublish} passing any of this values as parameter + */ + publishers: Publisher[] + /** + * Array of streams (their `streamId` properties) this particular Connection is subscribed to. Each one always corresponds to one + * Publisher of some other Connection: each string of this array must be equal to one {@link Publisher.streamId} of other Connection + */ + @Field(() => [String]) + @IsArray() + subscribers: string[] + /** + * @hidden deprecated. Inside ConnectionProperties + */ + @Field(() => OpenViduRole, { nullable: true }) + @IsEnum(OpenViduRole) + role?: OpenViduRole + /** + * @hidden deprecated. Inside ConnectionProperties + */ + @Field(() => String) + @IsString() + serverData?: string +} + +export interface SessionProperties { + mediaMode?: MediaMode + recordingMode?: RecordingMode + defaultRecordingProperties?: RecordingProperties + customSessionId?: string + mediaNode?: { + id: string + } + forcedVideoCodec?: VideoCodec + allowTranscoding?: boolean +} + +@ObjectType() +@Directive('@key(fields: "sessionId")') +export class ViduSession { + @Field(() => String) + @IsString() + sessionId: string + + @Field(() => TimeStamp) + @IsNumber() + createdAt: number +} + +@ObjectType() +export class ViduToken { + @Field(() => String) + token?: string +} diff --git a/apps/server/src/openvidu/openvidu.module.ts b/apps/server/src/openvidu/openvidu.module.ts index 1b30a4a..353d0d6 100644 --- a/apps/server/src/openvidu/openvidu.module.ts +++ b/apps/server/src/openvidu/openvidu.module.ts @@ -1,6 +1,6 @@ -import { OPENVIDU_MODULE } from './../../../sfu/src/common/common.constant' +import { OPENVIDU_MODULE } from 'common/common.constants' import { DynamicModule, Module } from '@nestjs/common' -// import { OpenVidu } from 'openvidu-node-client' +import { OpenVidu } from 'openvidu-node-client' import { OpenviduService } from './openvidu.service' import { OpenviduResolver } from './openvidu.resolver' @@ -12,14 +12,14 @@ class OpenViduConfig { @Module({}) export class OpenviduModule { static forRoot(option: OpenViduConfig): DynamicModule { - // const vidu = new OpenVidu(option.OPENVIDU_URL, option.OPENVIDU_SECRET) + const vidu = new OpenVidu(option.OPENVIDU_URL, option.OPENVIDU_SECRET) return { module: OpenviduModule, providers: [ OpenviduResolver, { provide: OPENVIDU_MODULE, - useValue: { test: 123 }, + useValue: { vidu }, }, OpenviduService, ], diff --git a/apps/server/src/openvidu/openvidu.resolver.ts b/apps/server/src/openvidu/openvidu.resolver.ts index 7198153..cf53407 100644 --- a/apps/server/src/openvidu/openvidu.resolver.ts +++ b/apps/server/src/openvidu/openvidu.resolver.ts @@ -1,4 +1,47 @@ -import { Resolver } from '@nestjs/graphql' +import { OpenviduService } from './openvidu.service' +import { Args, Query, Resolver, ResolveReference } from '@nestjs/graphql' +import { ViduSession } from './entities/vidu.entitiy' +import { Role } from 'auth/role.decorator' +import { ViduSessionOutput, ViduTokenOutput } from './dtos/viduoutput.dto' -@Resolver() -export class OpenviduResolver {} +@Resolver(() => ViduSession) +export class OpenviduResolver { + constructor(private readonly openviduService: OpenviduService) {} + @ResolveReference() + async resolveReference(reference: { + __typename: string + sessionId: string + }): Promise { + console.log('?????????????', reference) + return this.openviduService + .getSession(reference.sessionId) + .then((res) => res) + } + + /** + * + * @deprecated 통합 리졸버 테스트 후 제거예정 + */ + @Query(() => ViduSessionOutput) + @Role(['Any']) + getSession(@Args('sessionId') sessionId: string) { + return this.openviduService.getSession(sessionId) + } + + /** + * + * @deprecated 통합 리졸버 테스트 후 제거예정 + */ + @Query(() => ViduTokenOutput) + @Role(['Any']) + getToken(@Args('sessionId') sessionId: string) { + return this.openviduService.getToken(sessionId) + } + + @Query(() => ViduTokenOutput) + @Role(['Any']) + async getTokenConnection(@Args('sessionId') roomName: string) { + const session = await this.openviduService.getSession(roomName) + return this.openviduService.getToken(session.session.sessionId) + } +} diff --git a/apps/server/src/openvidu/openvidu.service.ts b/apps/server/src/openvidu/openvidu.service.ts index 97d890c..fecc3ff 100644 --- a/apps/server/src/openvidu/openvidu.service.ts +++ b/apps/server/src/openvidu/openvidu.service.ts @@ -1,4 +1,34 @@ -import { Injectable } from '@nestjs/common' +import { ViduSessionOutput, ViduTokenOutput } from './dtos/viduoutput.dto' +import { OpenVidu } from 'openvidu-node-client' +import { Injectable, Inject } from '@nestjs/common' +import { OPENVIDU_MODULE } from 'common/common.constants' @Injectable() -export class OpenviduService {} +export class OpenviduService { + constructor(@Inject(OPENVIDU_MODULE) private readonly vidu: OpenVidu) {} + + async getSession(customSessionId: string): Promise { + try { + const res = await this.vidu.createSession({ customSessionId }) + return { + ok: true, + session: { sessionId: res.sessionId, createdAt: res.createdAt }, + } + } catch (err) { + return { ok: false, error: err.message } + } + } + + async getToken(sessionId: string): Promise { + const session = this.vidu.activeSessions.find( + (session) => session.sessionId === sessionId, + ) + if (!session) { + // Todo : 커스텀 에러 핸들러 정의 필요 + return { ok: false, error: '세션이 없습니다.' } + } + + const connection = await session.createConnection() + return { ok: true, token: connection.token } + } +} diff --git a/apps/server/src/users/users.resolver.ts b/apps/server/src/users/users.resolver.ts index 29cebdc..36d2ec8 100644 --- a/apps/server/src/users/users.resolver.ts +++ b/apps/server/src/users/users.resolver.ts @@ -24,9 +24,9 @@ export class UsersResolver { return this.usersService.findById(reference.id).then((res) => res.user) } - @Query(() => User) - async getUser(@Args('id') id: number): Promise { - return this.usersService.findById(id).then((res) => res.user) + @Query(() => UserProfileOutput) + async getUser(@Args('id') id: number): Promise { + return this.usersService.findById(id) } @Query(() => UserProfileOutput) diff --git a/apps/server/src/users/users.service.ts b/apps/server/src/users/users.service.ts index 9cd1f4d..d778932 100644 --- a/apps/server/src/users/users.service.ts +++ b/apps/server/src/users/users.service.ts @@ -18,7 +18,6 @@ export class UsersService { id, }, }) - return { ok: true, user, diff --git a/apps/server/src/workspaces/workspaces.gateway.ts b/apps/server/src/workspaces/workspaces.gateway.ts index 44601fa..968e2e7 100644 --- a/apps/server/src/workspaces/workspaces.gateway.ts +++ b/apps/server/src/workspaces/workspaces.gateway.ts @@ -19,7 +19,7 @@ import { import { Namespace, Socket, AuthSocket } from 'socket.io' import { getServerRoomDto } from 'events/dtos/gateway.dto' import { WsExceptionFilter } from 'sockets/sockets-exception.filter' -import { GrpcService } from 'grpc/grpc.service' +// import { GrpcService } from 'grpc/grpc.service' import { createClient } from 'redis' import { GrpcInterceptor } from 'grpc/grpc.interceptor' import { ExceptionFilter } from 'grpc/grpc.filter' @@ -40,8 +40,7 @@ export class WorkspacesGateway private subscriber: redisClient constructor( private readonly usersService: UsersService, - private readonly jwtService: JwtService, - private readonly grpcService: GrpcService, + private readonly jwtService: JwtService, // private readonly grpcService: GrpcService, ) {} @WebSocketServer() public io: Namespace @SubscribeMessage('join_room') @@ -62,86 +61,6 @@ export class WorkspacesGateway this.serverRoomChange() return { joinedUserNickname: nickname, userList, ok: true } } - // - @SubscribeMessage('offer') - async onStream( - @ConnectedSocket() client: Socket, - @MessageBody() signal: Signal, - ) { - try { - const result = await this.grpcService.connect({ - sessionId: client.sessionId, - fromSessionId: client.sessionId, - candidate: '', - ...signal, - }) - - this.subscriber.subscribe('icecandidate', async (data) => { - const payload = JSON.parse(data) - this.io.server - .of('/workspace') - .in(payload.channelId) - .emit('icecandidate', payload) - }) - this.subscriber.subscribe('offer', (data) => { - const payload = JSON.parse(data) - console.log(payload, '<< { - const payload = JSON.parse(data) - this.io.server - .of('/workspace') - .in(payload.channelId) - .emit('client-icecandidate', payload) - }) - - return result - } catch (err) { - return err - } - } - // - @SubscribeMessage('add-ice') - async sendCandidate( - @ConnectedSocket() client: Socket, - @MessageBody() - data: { - candidate: RTCIceCandidate - type: 'icecandidate' - channelId: string - }, - ) { - return this.grpcService.addIce({ - channelId: data.channelId, - sessionId: client.sessionId, - candidate: JSON.stringify(data.candidate), - type: data.type, - }) - } - @SubscribeMessage('add-client-ice') - async sendClientCandidate( - @ConnectedSocket() client: Socket, - @MessageBody() - data: { - candidate: RTCIceCandidate - type: 'clientIce' - channelId: string - fromSessionId: string - }, - ) { - // - return this.grpcService.addIce({ - channelId: data.channelId, - sessionId: client.sessionId, - candidate: JSON.stringify(data.candidate), - type: data.type, - fromSessionId: data.fromSessionId, - }) - } @SubscribeMessage('leave_room') async leaveRoom( @@ -152,34 +71,114 @@ export class WorkspacesGateway const userList = await this.findJoinedUsers(roomName) this.logger.debug(`LEAVEROOM : ${client.sessionId}`) client.to(roomName).emit('leave_room', { userList, ok: true }) - await this.grpcService.Leave({ - channelId: client.roomName, - sessionId: client.sessionId, - }) + // await this.grpcService.Leave({ + // channelId: client.roomName, + // sessionId: client.sessionId, + // }) this.serverRoomChange() return roomName } + // + // @SubscribeMessage('offer') + // async onStream( + // @ConnectedSocket() client: Socket, + // @MessageBody() signal: Signal, + // ) { + // try { + // const result = await this.grpcService.connect({ + // sessionId: client.sessionId, + // fromSessionId: client.sessionId, + // candidate: '', + // ...signal, + // }) - @SubscribeMessage('answer') - sendRTCanswer( - @ConnectedSocket() client: Socket, - @MessageBody() signal: Signal, - ) { - return this.grpcService.answer({ - ...signal, - candidate: '', - sessionId: client.sessionId, - }) - } + // this.subscriber.subscribe('icecandidate', async (data) => { + // const payload = JSON.parse(data) + // this.io.server + // .of('/workspace') + // .in(payload.channelId) + // .emit('icecandidate', payload) + // }) + // this.subscriber.subscribe('offer', (data) => { + // const payload = JSON.parse(data) + // console.log(payload, '<< { + // const payload = JSON.parse(data) + // this.io.server + // .of('/workspace') + // .in(payload.channelId) + // .emit('client-icecandidate', payload) + // }) - @SubscribeMessage('ice') - requestRTCICECandidate( - @ConnectedSocket() client: Socket, - @MessageBody('ice') ice: any, - @MessageBody('roomName') roomName: string, - ) { - client.to(roomName).emit('ice', ice) - } + // return result + // } catch (err) { + // return err + // } + // } + // + // @SubscribeMessage('add-ice') + // async sendCandidate( + // @ConnectedSocket() client: Socket, + // @MessageBody() + // data: { + // candidate: RTCIceCandidate + // type: 'icecandidate' + // channelId: string + // }, + // ) { + // return this.grpcService.addIce({ + // channelId: data.channelId, + // sessionId: client.sessionId, + // candidate: JSON.stringify(data.candidate), + // type: data.type, + // }) + // } + // @SubscribeMessage('add-client-ice') + // async sendClientCandidate( + // @ConnectedSocket() client: Socket, + // @MessageBody() + // data: { + // candidate: RTCIceCandidate + // type: 'clientIce' + // channelId: string + // fromSessionId: string + // }, + // ) { + // // + // return this.grpcService.addIce({ + // channelId: data.channelId, + // sessionId: client.sessionId, + // candidate: JSON.stringify(data.candidate), + // type: data.type, + // fromSessionId: data.fromSessionId, + // }) + // } + + // @SubscribeMessage('answer') + // sendRTCanswer( + // @ConnectedSocket() client: Socket, + // @MessageBody() signal: Signal, + // ) { + // return this.grpcService.answer({ + // ...signal, + // candidate: '', + // sessionId: client.sessionId, + // }) + // } + + // @SubscribeMessage('ice') + // requestRTCICECandidate( + // @ConnectedSocket() client: Socket, + // @MessageBody('ice') ice: any, + // @MessageBody('roomName') roomName: string, + // ) { + // client.to(roomName).emit('ice', ice) + // } handleConnection(@ConnectedSocket() client: Socket) { this.logger.debug(`connected : ${client.sessionId}`) @@ -189,10 +188,10 @@ export class WorkspacesGateway async handleDisconnect(@ConnectedSocket() client: Socket) { this.logger.log(`disconnected : ${client.sessionId}`) - await this.grpcService.Leave({ - channelId: client.roomName, - sessionId: client.sessionId, - }) + // await this.grpcService.Leave({ + // channelId: client.roomName, + // sessionId: client.sessionId, + // }) this.serverRoomChange() } // diff --git a/apps/sfu/src/common/common.constant.ts b/apps/sfu/src/common/common.constant.ts index 914df3c..3d291ff 100644 --- a/apps/sfu/src/common/common.constant.ts +++ b/apps/sfu/src/common/common.constant.ts @@ -1,2 +1 @@ export const PUBSUB_MODULE = 'PUBSUB_MODULE' -export const OPENVIDU_MODULE = 'OPENVIDU_MODULE' diff --git a/package.json b/package.json index e48c7d1..d4639bc 100644 --- a/package.json +++ b/package.json @@ -13,12 +13,12 @@ "format": "prettier --write \"**/*.{ts,tsx,md}\"", "db:push": "dotenv -e .env.development turbo run db:push", "gateway:dev": "dotenv -e .env.development turbo run gateway:dev", - "proto:types" : "turbo run proto:types", - "proto:build" : "turbo run proto:build" + "proto:types": "turbo run proto:types", + "proto:build": "turbo run proto:build" }, "devDependencies": { "prettier": "latest", - "turbo": "^1.7.4" + "turbo": "^1.8.3" }, "engines": { "node": ">=14.0.0" diff --git a/yarn.lock b/yarn.lock index 1dae20f..00999cb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -743,6 +743,13 @@ dependencies: regenerator-runtime "^0.13.11" +"@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.7": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673" + integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw== + dependencies: + regenerator-runtime "^0.13.11" + "@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.3.3": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" @@ -950,6 +957,11 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" +"@emotion/hash@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413" + integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow== + "@envelop/core@3.0.4": version "3.0.4" resolved "https://registry.yarnpkg.com/@envelop/core/-/core-3.0.4.tgz#6801049bed24487599b4ffa0f836f70cb62714fc" @@ -2440,6 +2452,77 @@ resolved "https://registry.yarnpkg.com/@lukeed/csprng/-/csprng-1.0.1.tgz#625e93a0edb2c830e3c52ce2d67b9d53377c6a66" integrity sha512-uSvJdwQU5nK+Vdf6zxcWAY2A8r7uqe+gePwLWzJ+fsQehq18pc0I2hJKwypZ2aLM90+Er9u1xn4iLJPZ+xlL4g== +"@material-ui/core@4.11.0": + version "4.11.0" + resolved "https://registry.yarnpkg.com/@material-ui/core/-/core-4.11.0.tgz#b69b26e4553c9e53f2bfaf1053e216a0af9be15a" + integrity sha512-bYo9uIub8wGhZySHqLQ833zi4ZML+XCBE1XwJ8EuUVSpTWWG57Pm+YugQToJNFsEyiKFhPh8DPD0bgupz8n01g== + dependencies: + "@babel/runtime" "^7.4.4" + "@material-ui/styles" "^4.10.0" + "@material-ui/system" "^4.9.14" + "@material-ui/types" "^5.1.0" + "@material-ui/utils" "^4.10.2" + "@types/react-transition-group" "^4.2.0" + clsx "^1.0.4" + hoist-non-react-statics "^3.3.2" + popper.js "1.16.1-lts" + prop-types "^15.7.2" + react-is "^16.8.0" + react-transition-group "^4.4.0" + +"@material-ui/icons@4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@material-ui/icons/-/icons-4.9.1.tgz#fdeadf8cb3d89208945b33dbc50c7c616d0bd665" + integrity sha512-GBitL3oBWO0hzBhvA9KxqcowRUsA0qzwKkURyC8nppnC3fw54KPKZ+d4V1Eeg/UnDRSzDaI9nGCdel/eh9AQMg== + dependencies: + "@babel/runtime" "^7.4.4" + +"@material-ui/styles@^4.10.0": + version "4.11.5" + resolved "https://registry.yarnpkg.com/@material-ui/styles/-/styles-4.11.5.tgz#19f84457df3aafd956ac863dbe156b1d88e2bbfb" + integrity sha512-o/41ot5JJiUsIETME9wVLAJrmIWL3j0R0Bj2kCOLbSfqEkKf0fmaPt+5vtblUh5eXr2S+J/8J3DaCb10+CzPGA== + dependencies: + "@babel/runtime" "^7.4.4" + "@emotion/hash" "^0.8.0" + "@material-ui/types" "5.1.0" + "@material-ui/utils" "^4.11.3" + clsx "^1.0.4" + csstype "^2.5.2" + hoist-non-react-statics "^3.3.2" + jss "^10.5.1" + jss-plugin-camel-case "^10.5.1" + jss-plugin-default-unit "^10.5.1" + jss-plugin-global "^10.5.1" + jss-plugin-nested "^10.5.1" + jss-plugin-props-sort "^10.5.1" + jss-plugin-rule-value-function "^10.5.1" + jss-plugin-vendor-prefixer "^10.5.1" + prop-types "^15.7.2" + +"@material-ui/system@^4.9.14": + version "4.12.2" + resolved "https://registry.yarnpkg.com/@material-ui/system/-/system-4.12.2.tgz#f5c389adf3fce4146edd489bf4082d461d86aa8b" + integrity sha512-6CSKu2MtmiJgcCGf6nBQpM8fLkuB9F55EKfbdTC80NND5wpTmKzwdhLYLH3zL4cLlK0gVaaltW7/wMuyTnN0Lw== + dependencies: + "@babel/runtime" "^7.4.4" + "@material-ui/utils" "^4.11.3" + csstype "^2.5.2" + prop-types "^15.7.2" + +"@material-ui/types@5.1.0", "@material-ui/types@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@material-ui/types/-/types-5.1.0.tgz#efa1c7a0b0eaa4c7c87ac0390445f0f88b0d88f2" + integrity sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A== + +"@material-ui/utils@^4.10.2", "@material-ui/utils@^4.11.3": + version "4.11.3" + resolved "https://registry.yarnpkg.com/@material-ui/utils/-/utils-4.11.3.tgz#232bd86c4ea81dab714f21edad70b7fdf0253942" + integrity sha512-ZuQPV4rBK/V1j2dIkSSEcH5uT6AaHuKWFfotADHsC0wVL1NLd2WkFCm4ZZbX33iO4ydl6V0GPngKm8HZQ2oujg== + dependencies: + "@babel/runtime" "^7.4.4" + prop-types "^15.7.2" + react-is "^16.8.0 || ^17.0.0" + "@nestjs/apollo@^10.1.7": version "10.2.0" resolved "https://registry.yarnpkg.com/@nestjs/apollo/-/apollo-10.2.0.tgz#c2cb1c0a086d46c044bc94739caeb6e8f1a880f7" @@ -3150,6 +3233,13 @@ dependencies: "@types/react" "*" +"@types/react-transition-group@^4.2.0": + version "4.4.5" + resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.5.tgz#aae20dcf773c5aa275d5b9f7cdbca638abc5e416" + integrity sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA== + dependencies: + "@types/react" "*" + "@types/react@*", "@types/react@^18.0.26": version "18.0.28" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.28.tgz#accaeb8b86f4908057ad629a26635fe641480065" @@ -3952,6 +4042,13 @@ autoprefixer@^10.4.13: picocolors "^1.0.0" postcss-value-parser "^4.2.0" +axios@0.20.0: + version "0.20.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.20.0.tgz#057ba30f04884694993a8cd07fa394cff11c50bd" + integrity sha512-ANA4rr2BDcmmAQLOKft2fufrtuvlqR+cXNNinUmvfeSNCOF98PZL+7M/v1zIdGo7OLjEA9J2gXJL+j4zGsl0bA== + dependencies: + follow-redirects "^1.10.0" + axios@0.27.2: version "0.27.2" resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" @@ -4473,6 +4570,11 @@ clone@^1.0.2: resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== +clsx@^1.0.4: + version "1.2.1" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" + integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== + cluster-key-slot@1.1.2, cluster-key-slot@^1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz#88ddaa46906e303b5de30d3153b7d9fe0a0c19ac" @@ -4754,6 +4856,14 @@ cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" +css-vendor@^2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/css-vendor/-/css-vendor-2.0.8.tgz#e47f91d3bd3117d49180a3c935e62e3d9f7f449d" + integrity sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ== + dependencies: + "@babel/runtime" "^7.8.3" + is-in-browser "^1.0.2" + cssesc@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" @@ -4764,6 +4874,11 @@ cssfilter@0.0.10: resolved "https://registry.yarnpkg.com/cssfilter/-/cssfilter-0.0.10.tgz#c6d2672632a2e5c83e013e6864a42ce8defd20ae" integrity sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw== +csstype@^2.5.2: + version "2.6.21" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.21.tgz#2efb85b7cc55c80017c66a5ad7cbd931fda3a90e" + integrity sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w== + csstype@^3.0.2: version "3.1.1" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9" @@ -4988,6 +5103,14 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" +dom-helpers@^5.0.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902" + integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA== + dependencies: + "@babel/runtime" "^7.8.7" + csstype "^3.0.2" + domexception@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90" @@ -5337,7 +5460,7 @@ eventemitter3@^3.1.0: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== -events@^3.2.0: +events@3.3.0, events@^3.2.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== @@ -5661,7 +5784,7 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== -follow-redirects@^1.14.9: +follow-redirects@^1.10.0, follow-redirects@^1.14.9: version "1.15.2" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== @@ -5722,6 +5845,13 @@ fraction.js@^4.2.0: resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950" integrity sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA== +freeice@2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/freeice/-/freeice-2.2.2.tgz#f66b3e0f67c2fda835161f76d4884881cde83be4" + integrity sha512-XNoIxDHufqPIBSLpp4IrFPnoc+hv/0RwdOGhIoggIDC2ZKf5r6OoixbeoFJSmZOAq2aYiEUArhuQ8zVVrM5C4w== + dependencies: + normalice "^1.0.0" + fresh@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" @@ -5994,6 +6124,13 @@ hard-rejection@^2.1.0: resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== +hark@1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/hark/-/hark-1.2.3.tgz#959981400f561be5580ecd4321a9f55b16bacbd0" + integrity sha512-u68vz9SCa38ESiFJSDjqK8XbXqWzyot7Cj6Y2b6jk2NJ+II3MY2dIrLMg/kjtIAun4Y1DHF/20hfx4rq1G5GMg== + dependencies: + wildemitter "^1.2.0" + has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -6108,6 +6245,11 @@ husky@^8.0.3: resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.3.tgz#4936d7212e46d1dea28fef29bb3a108872cd9184" integrity sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg== +hyphenate-style-name@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz#691879af8e220aea5750e8827db4ef62a54e361d" + integrity sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ== + iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -6305,6 +6447,11 @@ is-glob@4.0.3, is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-in-browser@^1.0.2, is-in-browser@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835" + integrity sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g== + is-interactive@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" @@ -6853,6 +7000,11 @@ joi@^17.7.0: "@sideway/formula" "^3.0.1" "@sideway/pinpoint" "^2.0.0" +jquery@3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.5.1.tgz#d7b4d08e1bfdb86ad2f1a3d039ea17304717abb5" + integrity sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg== + js-cookie@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-3.0.1.tgz#9e39b4c6c2f56563708d7d31f6f5f21873a92414" @@ -6898,6 +7050,11 @@ jsesc@^2.5.1: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== +jsnlog@2.30.0: + version "2.30.0" + resolved "https://registry.yarnpkg.com/jsnlog/-/jsnlog-2.30.0.tgz#6feb20bf44fee693b255e31f92f739b21b4188f9" + integrity sha512-o3ROQVkhek+dkc7/9TXlB4TNtxUpYsRLOBJHZYk3Vy0B5zRBmfv9tyr56PrjcgEXuy06ARgfLTANY0+ImhzzGA== + json-bigint-patch@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/json-bigint-patch/-/json-bigint-patch-0.0.8.tgz#45d954da1f21c6d4f3ae9ef64c9ac227cd0ab0fe" @@ -6984,6 +7141,76 @@ jsonwebtoken@^9.0.0: ms "^2.1.1" semver "^7.3.8" +jss-plugin-camel-case@^10.5.1: + version "10.10.0" + resolved "https://registry.yarnpkg.com/jss-plugin-camel-case/-/jss-plugin-camel-case-10.10.0.tgz#27ea159bab67eb4837fa0260204eb7925d4daa1c" + integrity sha512-z+HETfj5IYgFxh1wJnUAU8jByI48ED+v0fuTuhKrPR+pRBYS2EDwbusU8aFOpCdYhtRc9zhN+PJ7iNE8pAWyPw== + dependencies: + "@babel/runtime" "^7.3.1" + hyphenate-style-name "^1.0.3" + jss "10.10.0" + +jss-plugin-default-unit@^10.5.1: + version "10.10.0" + resolved "https://registry.yarnpkg.com/jss-plugin-default-unit/-/jss-plugin-default-unit-10.10.0.tgz#db3925cf6a07f8e1dd459549d9c8aadff9804293" + integrity sha512-SvpajxIECi4JDUbGLefvNckmI+c2VWmP43qnEy/0eiwzRUsafg5DVSIWSzZe4d2vFX1u9nRDP46WCFV/PXVBGQ== + dependencies: + "@babel/runtime" "^7.3.1" + jss "10.10.0" + +jss-plugin-global@^10.5.1: + version "10.10.0" + resolved "https://registry.yarnpkg.com/jss-plugin-global/-/jss-plugin-global-10.10.0.tgz#1c55d3c35821fab67a538a38918292fc9c567efd" + integrity sha512-icXEYbMufiNuWfuazLeN+BNJO16Ge88OcXU5ZDC2vLqElmMybA31Wi7lZ3lf+vgufRocvPj8443irhYRgWxP+A== + dependencies: + "@babel/runtime" "^7.3.1" + jss "10.10.0" + +jss-plugin-nested@^10.5.1: + version "10.10.0" + resolved "https://registry.yarnpkg.com/jss-plugin-nested/-/jss-plugin-nested-10.10.0.tgz#db872ed8925688806e77f1fc87f6e62264513219" + integrity sha512-9R4JHxxGgiZhurDo3q7LdIiDEgtA1bTGzAbhSPyIOWb7ZubrjQe8acwhEQ6OEKydzpl8XHMtTnEwHXCARLYqYA== + dependencies: + "@babel/runtime" "^7.3.1" + jss "10.10.0" + tiny-warning "^1.0.2" + +jss-plugin-props-sort@^10.5.1: + version "10.10.0" + resolved "https://registry.yarnpkg.com/jss-plugin-props-sort/-/jss-plugin-props-sort-10.10.0.tgz#67f4dd4c70830c126f4ec49b4b37ccddb680a5d7" + integrity sha512-5VNJvQJbnq/vRfje6uZLe/FyaOpzP/IH1LP+0fr88QamVrGJa0hpRRyAa0ea4U/3LcorJfBFVyC4yN2QC73lJg== + dependencies: + "@babel/runtime" "^7.3.1" + jss "10.10.0" + +jss-plugin-rule-value-function@^10.5.1: + version "10.10.0" + resolved "https://registry.yarnpkg.com/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.10.0.tgz#7d99e3229e78a3712f78ba50ab342e881d26a24b" + integrity sha512-uEFJFgaCtkXeIPgki8ICw3Y7VMkL9GEan6SqmT9tqpwM+/t+hxfMUdU4wQ0MtOiMNWhwnckBV0IebrKcZM9C0g== + dependencies: + "@babel/runtime" "^7.3.1" + jss "10.10.0" + tiny-warning "^1.0.2" + +jss-plugin-vendor-prefixer@^10.5.1: + version "10.10.0" + resolved "https://registry.yarnpkg.com/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.10.0.tgz#c01428ef5a89f2b128ec0af87a314d0c767931c7" + integrity sha512-UY/41WumgjW8r1qMCO8l1ARg7NHnfRVWRhZ2E2m0DMYsr2DD91qIXLyNhiX83hHswR7Wm4D+oDYNC1zWCJWtqg== + dependencies: + "@babel/runtime" "^7.3.1" + css-vendor "^2.0.8" + jss "10.10.0" + +jss@10.10.0, jss@^10.5.1: + version "10.10.0" + resolved "https://registry.yarnpkg.com/jss/-/jss-10.10.0.tgz#a75cc85b0108c7ac8c7b7d296c520a3e4fbc6ccc" + integrity sha512-cqsOTS7jqPsPMjtKYDUpdFC0AbhYFLTcuGRqymgmdJIeQ8cH7+AgX7YSgQy79wXloZq2VvATYxUOUQEvS1V/Zw== + dependencies: + "@babel/runtime" "^7.3.1" + csstype "^3.0.2" + is-in-browser "^1.1.3" + tiny-warning "^1.0.2" + jwa@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" @@ -7437,6 +7664,11 @@ mime@2.6.0: resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== +mime@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-3.0.0.tgz#b374550dca3a0c18443b0c950a6a58f1931cf7a7" + integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A== + mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -7645,6 +7877,11 @@ nopt@^4.0.1: abbrev "1" osenv "^0.1.4" +normalice@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/normalice/-/normalice-1.0.1.tgz#03435c2eecd5631a6bca02da3930ec3e345a80f7" + integrity sha512-wF2/tv9q/K8S+RqCgll5yC6z/zcXNr+rEHfGIw8A6D58vjfJo+kp749MI6cAHv72LE7nwv92Qi6tZhIeMOOJpg== + normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" @@ -7803,6 +8040,22 @@ open@7.4.2: is-docker "^2.0.0" is-wsl "^2.1.1" +openvidu-browser@2.26.0, openvidu-browser@^2.26.0: + version "2.26.0" + resolved "https://registry.yarnpkg.com/openvidu-browser/-/openvidu-browser-2.26.0.tgz#0bdbc579bf01675beec76b1d92ca7a28cead05ae" + integrity sha512-Z/zFriOOYzFtYen00DoQWaLaPhMfb5YDBvxu1cSiwH6/uwsb4Qoi8ZvrABHVmFVnz4pVmB+4cA7+WeIzXJUo9Q== + dependencies: + events "3.3.0" + freeice "2.2.2" + hark "1.2.3" + inherits "2.0.4" + jsnlog "2.30.0" + mime "3.0.0" + platform "1.3.6" + semver "7.3.8" + uuid "9.0.0" + wolfy87-eventemitter "5.2.9" + openvidu-node-client@^2.26.2: version "2.26.2" resolved "https://registry.yarnpkg.com/openvidu-node-client/-/openvidu-node-client-2.26.2.tgz#11b0d4b8f1645c5ba0394a1993d07d97b710b5c0" @@ -7811,6 +8064,18 @@ openvidu-node-client@^2.26.2: axios "0.27.2" buffer "6.0.3" +openvidu-react@^2.26.0: + version "2.26.0" + resolved "https://registry.yarnpkg.com/openvidu-react/-/openvidu-react-2.26.0.tgz#63970ec3a517420d20371f495c3c0ad2f0c33b46" + integrity sha512-GI76rtfANOoyGTBxqV/y0SBoO+uxPjcaURmdbQm594oj/qJkKwyeXps+reac/3C5Qxe1pPwsOWH24adukOACLg== + dependencies: + "@material-ui/core" "4.11.0" + "@material-ui/icons" "4.9.1" + axios "0.20.0" + jquery "3.5.1" + openvidu-browser "2.26.0" + typeface-roboto "0.0.75" + optimism@^0.16.1: version "0.16.2" resolved "https://registry.yarnpkg.com/optimism/-/optimism-0.16.2.tgz#519b0c78b3b30954baed0defe5143de7776bf081" @@ -8053,11 +8318,21 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +platform@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.6.tgz#48b4ce983164b209c2d45a107adb31f473a6e7a7" + integrity sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg== + pluralize@8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== +popper.js@1.16.1-lts: + version "1.16.1-lts" + resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1-lts.tgz#cf6847b807da3799d80ee3d6d2f90df8a3f50b05" + integrity sha512-Kjw8nKRl1m+VrSFCoVGPph93W/qrSO7ZkqPpTf7F4bk/sqcfWK019dWBUpE/fBOsOQY1dks/Bmcbfn1heM/IsA== + postcss-import@^14.1.0: version "14.1.0" resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-14.1.0.tgz#a7333ffe32f0b8795303ee9e40215dac922781f0" @@ -8164,7 +8439,7 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15.7.2: +prop-types@^15.6.2, prop-types@^15.7.2: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -8319,11 +8594,16 @@ react-hook-form@^7.43.0: resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.43.1.tgz#0d0d7822f3f7fc05ffc41d5f012b49b90fcfa0f0" integrity sha512-+s3+s8LLytRMriwwuSqeLStVjRXFGxgjjx2jED7Z+wz1J/88vpxieRQGvJVvzrzVxshZ0BRuocFERb779m2kNg== -react-is@^16.13.1, react-is@^16.7.0: +react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== +"react-is@^16.8.0 || ^17.0.0": + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== + react-is@^18.0.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" @@ -8362,6 +8642,16 @@ react-router@6.8.1: dependencies: "@remix-run/router" "1.3.2" +react-transition-group@^4.4.0: + version "4.4.5" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1" + integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g== + dependencies: + "@babel/runtime" "^7.5.5" + dom-helpers "^5.0.1" + loose-envify "^1.4.0" + prop-types "^15.6.2" + react@^18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" @@ -9291,6 +9581,11 @@ tiny-lru@8.0.2: resolved "https://registry.yarnpkg.com/tiny-lru/-/tiny-lru-8.0.2.tgz#812fccbe6e622ded552e3ff8a4c3b5ff34a85e4c" integrity sha512-ApGvZ6vVvTNdsmt676grvCkUCGwzG9IqXma5Z07xJgiC5L7akUMof5U8G2JTI9Rz/ovtVhJBlY6mNhEvtjzOIg== +tiny-warning@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" + integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== + title-case@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/title-case/-/title-case-3.0.3.tgz#bc689b46f02e411f1d1e1d081f7c3deca0489982" @@ -9451,47 +9746,47 @@ tsutils@^3.21.0: dependencies: tslib "^1.8.1" -turbo-darwin-64@1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/turbo-darwin-64/-/turbo-darwin-64-1.8.1.tgz#3e3e64fe7ea7d0bcd192e2e608274a06c662d1d5" - integrity sha512-H7pxGF/vsYG7kbY+vB8h+3r8VXn2L6hhYQi0XWA+EjZ1e2zu7+TzEMRWFYmvJPx8TRo5cV5txtg0I22/Y7bxUA== - -turbo-darwin-arm64@1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/turbo-darwin-arm64/-/turbo-darwin-arm64-1.8.1.tgz#d855340af02a448c428881a65c2deef75108caff" - integrity sha512-zMcvplVGluR6v4oJXW7S1/R9QFsHdDkXMhPq8PIdvT3HwTb69ms0MNv7aKiQ0ZFy5D/eKCTyBRUFZvjorZmBqA== - -turbo-linux-64@1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/turbo-linux-64/-/turbo-linux-64-1.8.1.tgz#9f1dac8c7f40d7ba43adf407ed59dc03f52c7601" - integrity sha512-eJNx8iWDn5Lt8d0221RFd6lBjViLiPCVWNFF5JtqOohgRYplvepY3y/THa1GivMxY4px6zjTiy2oPE/VscVP4w== - -turbo-linux-arm64@1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/turbo-linux-arm64/-/turbo-linux-arm64-1.8.1.tgz#4ab5ca579b16b501ad98f0ed1ff22bcc13ffd1d7" - integrity sha512-hFZkm8tq9kLE8tdbOzD6EbNzftdzMR4JEuuoKC6AbTzx1ZsWRvXJ/BGTeSH9/dYYm/wfuIEUiOP7HeXWiZRx7g== - -turbo-windows-64@1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/turbo-windows-64/-/turbo-windows-64-1.8.1.tgz#4bc3648ff387ec4dfc781c4ec9213da320cac563" - integrity sha512-o3oDg0lTYZl5KZD3Mi753On2vQT51unIiungoUmHDCeDH5JXfWPFu+6p+GAoIyRwQkZPvaEzMLuUtRgklKcBJw== - -turbo-windows-arm64@1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/turbo-windows-arm64/-/turbo-windows-arm64-1.8.1.tgz#9b4487ec4382da8f95e2be361fbbaff9d1fb8901" - integrity sha512-NDYr2Mra21KOdl18BhMRoH2jQmlu+oqkpqRd+cGB8+c5P0B6LDVCM83cfcXQ+PNqX9S3Y1eZDRENZJx9I03sSw== - -turbo@^1.7.4: - version "1.8.1" - resolved "https://registry.yarnpkg.com/turbo/-/turbo-1.8.1.tgz#f53c0dce2d69f75dc64dfd40a1d8faee2330744b" - integrity sha512-g8RltmG5zd0nYbKpkBQwnTSXTWUiup9+yileQ1TETNzpjxI3TL5k7kt2WkgUHEqR947IPUV+ckIduZHVITJmIQ== +turbo-darwin-64@1.8.3: + version "1.8.3" + resolved "https://registry.yarnpkg.com/turbo-darwin-64/-/turbo-darwin-64-1.8.3.tgz#f220459e7636056d9a67bc9ead8dc01c495f9d55" + integrity sha512-bLM084Wr17VAAY/EvCWj7+OwYHvI9s/NdsvlqGp8iT5HEYVimcornCHespgJS/yvZDfC+mX9EQkn3V2JmYgGGw== + +turbo-darwin-arm64@1.8.3: + version "1.8.3" + resolved "https://registry.yarnpkg.com/turbo-darwin-arm64/-/turbo-darwin-arm64-1.8.3.tgz#1529f0755cd683e372140d6b9532efe4ca523b38" + integrity sha512-4oZjXtzakopMK110kue3z/hqu3WLv+eDLZOX1NGdo49gqca9BeD8GbH+sXpAp6tqyeuzpss+PIliVYuyt7LgbA== + +turbo-linux-64@1.8.3: + version "1.8.3" + resolved "https://registry.yarnpkg.com/turbo-linux-64/-/turbo-linux-64-1.8.3.tgz#1aed7f4bb4492cb4c9d8278044a66d3c6107ee5b" + integrity sha512-uvX2VKotf5PU14FCxJA5iHItPQno2JWzerMd+g3/h/Asay6dvxvtVjc39MQeGT0H5njSvzVKFkT+3/5q8lgOEg== + +turbo-linux-arm64@1.8.3: + version "1.8.3" + resolved "https://registry.yarnpkg.com/turbo-linux-arm64/-/turbo-linux-arm64-1.8.3.tgz#0269b31b2947c40833052325361a94193ca46150" + integrity sha512-E1p+oH3XKMaPS4rqWhYsL4j2Pzc0d/9P5KU7Kn1kqVLo2T3iRA7n2KVULEieUNE0nTH+aIJPXYXOpqCI5wFJaA== + +turbo-windows-64@1.8.3: + version "1.8.3" + resolved "https://registry.yarnpkg.com/turbo-windows-64/-/turbo-windows-64-1.8.3.tgz#cf94f427414eb8416c1fe22229f9a578dd1ec78b" + integrity sha512-cnzAytHtoLXd0J7aNzRpZFpL/GTjcBmkvAPlbOdf/Pl1iwS4qzGrudZQ+OM1lmLgLIfBPIavsGHBknTwTNib4A== + +turbo-windows-arm64@1.8.3: + version "1.8.3" + resolved "https://registry.yarnpkg.com/turbo-windows-arm64/-/turbo-windows-arm64-1.8.3.tgz#db5739fe1d6907d07874779f6d5fac87b3f3ca6a" + integrity sha512-ulIiItNm2w/zYJdD5/oAzjzNns1IjbpweRzpsE8tLXaWwo6+fnXXkyloUug0IUhcd2k6fJXfoiDZfygqpOVuXg== + +turbo@^1.8.3: + version "1.8.3" + resolved "https://registry.yarnpkg.com/turbo/-/turbo-1.8.3.tgz#6fe1ce749a38b54f15f0fcb24ee45baefa98e948" + integrity sha512-zGrkU1EuNFmkq6iky6LcMqD4h0OLE8XysVFxQWRIZbcTNnf0XAycbsbeEyiJpiWeqb7qtg2bVuY9EYcNoNhVuQ== optionalDependencies: - turbo-darwin-64 "1.8.1" - turbo-darwin-arm64 "1.8.1" - turbo-linux-64 "1.8.1" - turbo-linux-arm64 "1.8.1" - turbo-windows-64 "1.8.1" - turbo-windows-arm64 "1.8.1" + turbo-darwin-64 "1.8.3" + turbo-darwin-arm64 "1.8.3" + turbo-linux-64 "1.8.3" + turbo-linux-arm64 "1.8.3" + turbo-windows-64 "1.8.3" + turbo-windows-arm64 "1.8.3" type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" @@ -9543,6 +9838,11 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== +typeface-roboto@0.0.75: + version "0.0.75" + resolved "https://registry.yarnpkg.com/typeface-roboto/-/typeface-roboto-0.0.75.tgz#98d5ba35ec234bbc7172374c8297277099cc712b" + integrity sha512-VrR/IiH00Z1tFP4vDGfwZ1esNqTiDMchBEXYY9kilT6wRGgFoCAlgkEUMHb1E3mB0FsfZhv756IF0+R+SFPfdg== + typescript@4.9.5, typescript@^4.6.4, typescript@^4.7.4, typescript@^4.9.3: version "4.9.5" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" @@ -9851,6 +10151,16 @@ wide-align@^1.1.0: dependencies: string-width "^1.0.2 || 2 || 3 || 4" +wildemitter@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/wildemitter/-/wildemitter-1.2.1.tgz#9da3b5ca498e4378628d1783145493c70a10b774" + integrity sha512-UMmSUoIQSir+XbBpTxOTS53uJ8s/lVhADCkEbhfRjUGFDPme/XGOb0sBWLx5sTz7Wx/2+TlAw1eK9O5lw5PiEw== + +wolfy87-eventemitter@5.2.9: + version "5.2.9" + resolved "https://registry.yarnpkg.com/wolfy87-eventemitter/-/wolfy87-eventemitter-5.2.9.tgz#e879f770b30fbb6512a8afbb330c388591099c2a" + integrity sha512-P+6vtWyuDw+MB01X7UeF8TaHBvbCovf4HPEMF/SV7BdDc1SMTiBy13SRD71lQh4ExFTG1d/WNzDGDCyOKSMblw== + word-wrap@^1.0.3, word-wrap@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" From defae3f4a013a2fb228d7d2b9e3e3f631fb4c90f Mon Sep 17 00:00:00 2001 From: raymond Date: Mon, 20 Mar 2023 22:49:24 +0900 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=8E=A8feat=20:=20N:N=20=EC=97=B0?= =?UTF-8?q?=EA=B2=B0=EC=84=B1=EA=B3=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - openvidu 도입하여 미디어 서버 연동 - 공식문서에 권장하는 sdk가 불친절하여 커스텀 제작 중 --- README.md | 2 +- apps/client/codegen.ts | 2 +- apps/client/src/components/VideoArea.tsx | 24 ++++ apps/client/src/hooks/useGetToken.ts | 8 ++ apps/client/src/hooks/useOv.ts | 120 ++++++++++++++---- apps/client/src/screen/Room.tsx | 12 +- apps/client/src/store/index.ts | 4 +- apps/client/tsconfig.json | 3 + apps/client/vite.config.ts | 4 + apps/server/src/auth/auth.guard.ts | 2 +- apps/server/src/jwt/jwt.middleware.ts | 1 + apps/server/src/openvidu/openvidu.module.ts | 2 +- apps/server/src/openvidu/openvidu.resolver.ts | 1 - apps/server/src/openvidu/openvidu.service.ts | 1 + apps/sfu/package.json | 2 +- 15 files changed, 148 insertions(+), 40 deletions(-) create mode 100644 apps/client/src/components/VideoArea.tsx diff --git a/README.md b/README.md index e0bc718..57b4128 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ https://github.com/mooyaho-webrtc/mooyaho https://github.com/mqttjs/MQTT.js/#mqttclientstreambuilder-options - +https://github.com/OpenVidu/openvidu-call-react/blob/dea85b34254e8540c0c9ca6874bb90f60603101c/openvidu-call-react/src/components/VideoRoomComponent.js ## TODO diff --git a/apps/client/codegen.ts b/apps/client/codegen.ts index c530a8a..73ebe95 100644 --- a/apps/client/codegen.ts +++ b/apps/client/codegen.ts @@ -17,4 +17,4 @@ const config: CodegenConfig = { } export default config -// https://github.com/OpenVidu/openvidu-call-react/blob/dea85b34254e8540c0c9ca6874bb90f60603101c/openvidu-call-react/src/components/VideoRoomComponent.js + diff --git a/apps/client/src/components/VideoArea.tsx b/apps/client/src/components/VideoArea.tsx new file mode 100644 index 0000000..b325116 --- /dev/null +++ b/apps/client/src/components/VideoArea.tsx @@ -0,0 +1,24 @@ +import UserModel from 'model/UserModel' +import React, { FC, useEffect, useRef } from 'react' + +interface VideoAreaProps { + user: UserModel +} + +const VideoArea: FC = ({ user }) => { + const videoRef = useRef(null) + useEffect(() => { + if (user.getStreamManager()) { + user.getStreamManager()?.addVideoElement(videoRef.current!) + } + }, [user]) + return ( +
+ +
+ ) +} + +export default VideoArea diff --git a/apps/client/src/hooks/useGetToken.ts b/apps/client/src/hooks/useGetToken.ts index 9686667..9e17da4 100644 --- a/apps/client/src/hooks/useGetToken.ts +++ b/apps/client/src/hooks/useGetToken.ts @@ -1,6 +1,7 @@ import { useCallback } from 'react' import { gql, useLazyQuery, useQuery } from '@apollo/client' import { graphql } from 'gql' +import store from 'store/index' const GET_VIDU_TOKEN = graphql(/** GraphQl */ ` query getToken($sessionId: String!) { @@ -35,7 +36,14 @@ const GET_VIDU_TOKEN_CONNECTION = graphql(` `) const useGetToken = () => { + const oAuthToken = store((state) => state.token) + const [getToken, { data, loading, error }] = useLazyQuery(GET_VIDU_TOKEN_CONNECTION, { + context: { + headers: { + [import.meta.env.VITE_AUTH_KEY]: oAuthToken, + }, + }, onCompleted(data) { console.log(data, '< { const [session, setSession] = useState() const [subscribers, setSubscriber] = useState([]) const [localUserAccessAllowed, setLocalUserAccessAllowed] = useState(false) - const localUser = useRef(new UserModel()).current + const [localUser, setLocalUser] = useState(new UserModel()) + const currentVideoDevice = useRef() const { getToken, token } = useGetToken() const ov = useRef(new OpenVidu()).current @@ -21,7 +29,7 @@ const useOv = (roomName: string) => { }, [subscribers, localUser]) const sendSignalUserChanged = useCallback( - (data: SignalOptions) => { + (data: Partial) => { if (session) { const signalOptions = { data: JSON.stringify(data), @@ -62,6 +70,52 @@ const useOv = (roomName: string) => { } }, [session]) + const subscribeToUserChanged = useCallback(() => { + if (session) { + session.on('signal:userChanged', (event) => { + const newRemoteSubscribers = [...subscribers] + newRemoteSubscribers.forEach((user) => { + if (user.getConnectionId() === event.from?.connectionId) { + const data = JSON.parse(event?.data ?? '') + console.log('EVENTO REMOTE: ', event.data) + if (data.isAudioActive) { + user.setAudioActive(data.isAudioActive) + } + if (data.isVideoActive !== undefined) { + user.setVideoActive(data.isVideoActive) + } + if (data.nickname !== undefined) { + user.setNickname(data.nickname) + } + if (data.isScreenShareActive !== undefined) { + user.setScreenShareActive(data.isScreenShareActive) + } + } + }) + setSubscriber(() => newRemoteSubscribers) + }) + } + }, []) + const deleteSubscriber = (stream) => { + const remoteUsers = [...subscribers] + const userStream = + remoteUsers.filter((user) => user.getStreamManager()?.stream === stream)[0] || [] + let index = remoteUsers.indexOf(userStream, 0) + if (index > -1) { + remoteUsers.splice(index, 1) + setSubscriber(() => remoteUsers) + } + } + + const subscribeToStreamDestroyed = () => { + if (session) { + session.on('streamDestroyed', (event) => { + event.preventDefault() + deleteSubscriber(event.stream) + }) + } + } + const connectWebCam = useCallback(async () => { if (session) { await ov.getUserMedia({ audioSource: undefined, videoSource: undefined }) @@ -89,34 +143,37 @@ const useOv = (roomName: string) => { }) }) } + // publisher.on('streamPlaying', (e) => { + // publisher.videos[0].video.parentElement + // }) // localUser.setNickname(this.state.myUserName) localUser.setConnectionId(session.connection.connectionId) localUser.setScreenShareActive(false) localUser.setStreamManager(publisher) - // this.subscribeToUserChanged() - // this.subscribeToStreamDestroyed() - // this.sendSignalUserChanged({ isScreenShareActive: localUser.isScreenShareActive() }) - - // this.setState({ currentVideoDevice: videoDevices[0], localUser: localUser }, () => { - // this.state.localUser.getStreamManager().on('streamPlaying', (e) => { - // this.updateLayout() - // publisher.videos[0].video.parentElement.classList.remove('custom-class') - // }) - // }) - } - }, []) - const connect = useCallback((token: string) => { - if (session) { - session - .connect(token, { clientData: localUser.getNickname() }) - .then(() => { - connectWebCam() - }) - .catch((error: any) => { - console.log('There was an error connecting to the session:', error.code, error.message) - }) + console.log(localUser, 'changed') + subscribeToUserChanged() + subscribeToStreamDestroyed() + sendSignalUserChanged({ isScreenShareActive: localUser.isScreenShareActive() }) + currentVideoDevice.current = videoDevices[0] } - }, []) + }, [session]) + const connect = useCallback( + (token: string) => { + console.log('connection call') + if (session) { + session + .connect(token, { clientData: localUser.getNickname() }) + .then(() => { + console.log('connection done') + connectWebCam() + }) + .catch((error: any) => { + console.log('There was an error connecting to the session:', error.code, error.message) + }) + } + }, + [session], + ) const connectToSession = useCallback(async () => { if (session) { @@ -137,6 +194,12 @@ const useOv = (roomName: string) => { } }, [token, session]) + useEffect(() => { + if (token) { + connect(token) + } + }, [token]) + useEffect(() => { joinSession() }, []) @@ -148,7 +211,10 @@ const useOv = (roomName: string) => { } }, [session]) - return + return { + localUser, + subscribers, + } } export default useOv diff --git a/apps/client/src/screen/Room.tsx b/apps/client/src/screen/Room.tsx index 9fa27b6..418e9ec 100644 --- a/apps/client/src/screen/Room.tsx +++ b/apps/client/src/screen/Room.tsx @@ -8,11 +8,12 @@ import { handleIce } from 'libs/fn' import store from 'store/index' import { OpenVidu } from 'openvidu-browser' import useOv from 'hooks/useOv' +import VideoArea from 'components/VideoArea' const Room = () => { const { roomName } = useParams() const navigate = useNavigate() - const test = useOv(roomName!) + const [joinedUsers, setJoindUsers] = useState<[] | string[]>([]) const [lastJoinedUser, setLastJoined] = useState(null) const myVideo = useRef(null) @@ -20,6 +21,7 @@ const Room = () => { if (!roomName) { return null } + const { localUser, subscribers } = useOv(roomName!) const createPeer = useCallback(async (socket: Socket, stream?: MediaStream) => { const peer = new RTCPeerConnection({ iceServers: [ @@ -159,7 +161,7 @@ const Room = () => { }) const me = store((state) => state.user) - console.log(me) + // const { isLoading, stream } = useRTCConnection({ // async onConnect(stream) { // await createPeer(socket, stream) @@ -168,6 +170,7 @@ const Room = () => { useEffect(() => { socket.emit('join_room', roomName, onRefreshRoomSetting) }, []) + return (
{lastJoinedUser ?

Welcome {lastJoinedUser} !

: null} @@ -175,9 +178,8 @@ const Room = () => {

{item}

))} - + {localUser.getStreamManager() ? : null} + {subscribers.length ? subscribers.map((remoteUser) => ) : null}
) } diff --git a/apps/client/src/store/index.ts b/apps/client/src/store/index.ts index 664dc1a..2c65834 100644 --- a/apps/client/src/store/index.ts +++ b/apps/client/src/store/index.ts @@ -1,6 +1,6 @@ +import { User } from 'gql/graphql' import { create } from 'zustand' import { devtools, subscribeWithSelector, persist, createJSONStorage } from 'zustand/middleware' -import { User } from '__api__/types' interface IStore { user: User | null @@ -16,7 +16,7 @@ const store = create( user: null, token: null, setUser: ({ user, token }) => { - localStorage.setItem('_PLUG_AUTH_', token) + localStorage.setItem(import.meta.env.VITE_AUTH_KEY, token) set((state) => ({ ...state, user, token })) }, clear: () => { diff --git a/apps/client/tsconfig.json b/apps/client/tsconfig.json index d1f8833..37b6ae4 100644 --- a/apps/client/tsconfig.json +++ b/apps/client/tsconfig.json @@ -8,6 +8,9 @@ "layout/*" : ["./layout/*"], "libs/*" : ["./libs/*"], "store/*" : ["./store/*"], + "model/*" : ["./model/*"], + "gql/*" : ["./gql/*"], + "components/*" : ["./components/*"], }, "target": "ESNext", "useDefineForClassFields": true, diff --git a/apps/client/vite.config.ts b/apps/client/vite.config.ts index b201dfe..28740c3 100644 --- a/apps/client/vite.config.ts +++ b/apps/client/vite.config.ts @@ -44,6 +44,10 @@ export default ({ mode }) => { find: 'model', replacement: path.resolve(__dirname, 'src/model'), }, + { + find: 'components', + replacement: path.resolve(__dirname, 'src/components'), + }, ], }, }) diff --git a/apps/server/src/auth/auth.guard.ts b/apps/server/src/auth/auth.guard.ts index 7915367..307a652 100644 --- a/apps/server/src/auth/auth.guard.ts +++ b/apps/server/src/auth/auth.guard.ts @@ -28,7 +28,7 @@ export class AuthGuard implements CanActivate { } } } - + console.log(gqlContext) throw new RoleException() } } diff --git a/apps/server/src/jwt/jwt.middleware.ts b/apps/server/src/jwt/jwt.middleware.ts index e958ae4..3cd2dbe 100644 --- a/apps/server/src/jwt/jwt.middleware.ts +++ b/apps/server/src/jwt/jwt.middleware.ts @@ -23,6 +23,7 @@ export class JwtMiddleware implements NestMiddleware { req['user'] = user } } catch (e) { + console.log(e, '????') throw new RoleException() } } diff --git a/apps/server/src/openvidu/openvidu.module.ts b/apps/server/src/openvidu/openvidu.module.ts index 353d0d6..c71b1a9 100644 --- a/apps/server/src/openvidu/openvidu.module.ts +++ b/apps/server/src/openvidu/openvidu.module.ts @@ -19,7 +19,7 @@ export class OpenviduModule { OpenviduResolver, { provide: OPENVIDU_MODULE, - useValue: { vidu }, + useValue: vidu, }, OpenviduService, ], diff --git a/apps/server/src/openvidu/openvidu.resolver.ts b/apps/server/src/openvidu/openvidu.resolver.ts index cf53407..e67df60 100644 --- a/apps/server/src/openvidu/openvidu.resolver.ts +++ b/apps/server/src/openvidu/openvidu.resolver.ts @@ -12,7 +12,6 @@ export class OpenviduResolver { __typename: string sessionId: string }): Promise { - console.log('?????????????', reference) return this.openviduService .getSession(reference.sessionId) .then((res) => res) diff --git a/apps/server/src/openvidu/openvidu.service.ts b/apps/server/src/openvidu/openvidu.service.ts index fecc3ff..d09088d 100644 --- a/apps/server/src/openvidu/openvidu.service.ts +++ b/apps/server/src/openvidu/openvidu.service.ts @@ -15,6 +15,7 @@ export class OpenviduService { session: { sessionId: res.sessionId, createdAt: res.createdAt }, } } catch (err) { + console.log(err, '????') return { ok: false, error: err.message } } } diff --git a/apps/sfu/package.json b/apps/sfu/package.json index 2bf9b89..08f7d1e 100644 --- a/apps/sfu/package.json +++ b/apps/sfu/package.json @@ -10,7 +10,7 @@ "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", "start": "cross-env NODE_ENV=production nest start", "start:dev": "cross-env NODE_ENV=development nest start --watch", - "dev": "cross-env NODE_ENV=development nest start --watch", + "sfu": "cross-env NODE_ENV=development nest start --watch", "start:watch": "cross-env NODE_ENV=development nest build --webpack --webpackPath webpack-hmr.config.js --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main",