From 194b7fb876ac48a004c78bf9eb327d11fe177aca Mon Sep 17 00:00:00 2001 From: Carlos Santos <4a.santos@gmail.com> Date: Mon, 31 Mar 2025 18:15:08 +0200 Subject: [PATCH] backend: split OpenAPI specification into public and internal files --- backend/nodemon.json | 4 +- backend/openapi/openvidu-meet-api.yaml | 229 --------- .../openapi/openvidu-meet-internal-api.yaml | 462 ++++++++++++++++++ backend/package.json | 5 +- backend/src/server.ts | 5 +- backend/src/utils/path-utils.ts | 11 +- 6 files changed, 475 insertions(+), 241 deletions(-) create mode 100644 backend/openapi/openvidu-meet-internal-api.yaml diff --git a/backend/nodemon.json b/backend/nodemon.json index 477fcf1..b2a1a6a 100644 --- a/backend/nodemon.json +++ b/backend/nodemon.json @@ -2,11 +2,11 @@ "env": { "NODE_ENV": "development" }, - "watch": ["openapi/openvidu-meet-api.yaml", "src", "../typings/src"], + "watch": ["openapi/openvidu-meet-api.yaml", "openapi/openvidu-meet-internal-api.yaml", "src", "../typings/src"], "ext": "js,json,ts", "ignore": ["node_modules", "dist", "src/typings", "public"], "exec": "node --experimental-specifier-resolution=node --loader ts-node/esm ./src/server.ts", "events": { - "restart": "npm run typings:sync && npm run generate:openapi-doc" + "restart": "npm run typings:sync && npm run doc:api && npm run doc:internal-api" } } diff --git a/backend/openapi/openvidu-meet-api.yaml b/backend/openapi/openvidu-meet-api.yaml index f25df1c..b0687dc 100644 --- a/backend/openapi/openvidu-meet-api.yaml +++ b/backend/openapi/openvidu-meet-api.yaml @@ -20,14 +20,6 @@ tags: description: Operations related to managing OpenVidu Meet rooms - name: OpenVidu Meet - Recordings description: Operations related to managing OpenVidu Meet recordings - # - name: OpenVidu Meet - Participant - # description: Operations related to managing participants in OpenVidu Meet rooms - # - name: Internal API - Room - # description: Operations related to managing OpenVidu Meet rooms - - name: Internal API - Participant - description: Operations related to managing participants in OpenVidu Meet rooms - - name: Internal API - Recordings - description: Operations related to managing OpenVidu Meet recordings paths: /rooms: @@ -855,227 +847,6 @@ paths: example: name: 'Unexpected Error' message: 'Something went wrong' - /recordings/{recordingId}/stream: - get: - operationId: getRecordingStream - summary: Stream an OpenVidu Meet recording - description: > - Streams the OpenVidu Meet recording with the specified recording ID. - This endpoint supports range requests, allowing partial content retrieval - for video playback without downloading the entire file. - tags: - - Internal API - Recordings - security: - - apiKeyInHeader: [] - parameters: - - name: recordingId - in: path - required: true - description: The ID of the recording to stream - schema: - type: string - - name: Range - in: header - required: false - description: > - Byte range for partial content retrieval. - Example: `bytes=0-1023` to request the first 1024 bytes of the file. - schema: - type: string - responses: - '200': - description: Successfully streaming the full recording - headers: - Accept-Ranges: - description: Indicates that byte-range requests are supported - schema: - type: string - Content-Length: - description: The total file size in bytes - schema: - type: integer - content: - video/mp4: - schema: - type: string - format: binary - '206': - description: Partial content streaming based on byte range - headers: - Accept-Ranges: - description: Indicates that byte-range requests are supported - schema: - type: string - Content-Range: - description: Specifies the range of bytes being sent - schema: - type: string - Content-Length: - description: The length of the partial content in bytes - schema: - type: integer - content: - video/mp4: - schema: - type: string - format: binary - '400': - description: Bad Request — Invalid range header format - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - example: - code: 400 - message: 'Invalid Range header' - '401': - description: Unauthorized — The API key is missing or invalid - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - example: - message: 'Unauthorized' - '404': - description: Recording not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - example: - code: 404 - message: 'Recording not found' - '416': - description: Requested range not satisfiable - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - example: - code: 416 - message: 'Requested range not satisfiable' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - example: - code: 500 - message: 'Internal server error' - - /participants/token: - post: - operationId: generateParticipantToken - summary: Generate a token for a participant - description: > - Generates a token for a participant to join an OpenVidu Meet room. - tags: - - Internal API - Participant - requestBody: - description: Participant details - content: - application/json: - schema: - type: object - required: - - roomName - - participantName - - secret - properties: - roomName: - type: string - example: 'OpenVidu-123456' - description: > - The name of the room to join. - participantName: - type: string - example: 'Alice' - description: > - The name of the participant. - secret: - type: string - example: 'abc123456' - description: > - The secret token from the room Url - responses: - '200': - description: Successfully generated the participant token - content: - application/json: - schema: - type: object - properties: - token: - type: string - example: 'token_123456' - description: > - The token to authenticate the participant. - '404': - description: Room not found - content: - application/json: - schema: - type: object - properties: - name: - type: string - example: 'Room not found' - message: - type: string - example: 'The room does not exist' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - example: - message: 'Internal server error' - /participants/{participantName}: - delete: - operationId: disconnectParticipant - summary: Delete a participant from a room - description: > - Deletes a participant from an OpenVidu Meet room. - tags: - - Internal API - Participant - security: - - apiKeyInHeader: [] - parameters: - - name: participantName - in: path - required: true - description: The name of the participant to delete - schema: - type: string - - name: roomName - in: query - required: true - description: The name of the room from which to delete the participant - schema: - type: string - responses: - '204': - description: Successfully disconnect the participant - '404': - description: Participant not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - example: - code: 404 - message: 'Participant not found' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - example: - code: 500 - message: 'Internal server error' components: securitySchemes: apiKeyInHeader: diff --git a/backend/openapi/openvidu-meet-internal-api.yaml b/backend/openapi/openvidu-meet-internal-api.yaml new file mode 100644 index 0000000..5a3bb1d --- /dev/null +++ b/backend/openapi/openvidu-meet-internal-api.yaml @@ -0,0 +1,462 @@ +openapi: 3.0.1 +info: + version: v1 + title: OpenVidu Meet Internal REST API + description: > + The OpenVidu Meet Internal REST API allows seamless integration of OpenVidu Meet rooms into your application. + This REST API provides endpoints to manage rooms and recordings in OpenVidu Meet. + termsOfService: https://openvidu.io/conditions/terms-of-service/ + contact: + name: OpenVidu + email: commercial@openvidu.io + url: https://openvidu.io/support/ + +servers: + - url: http://localhost:6080/meet/api/v1 + description: Development server +tags: + - name: Internal API - Participant + description: Operations related to managing participants in OpenVidu Meet rooms + - name: Internal API - Recordings + description: Operations related to managing OpenVidu Meet recordings +paths: + /recordings/{recordingId}/stream: + get: + operationId: getRecordingStream + summary: Stream an OpenVidu Meet recording + description: > + Streams the OpenVidu Meet recording with the specified recording ID. + This endpoint supports range requests, allowing partial content retrieval + for video playback without downloading the entire file. + tags: + - Internal API - Recordings + security: + - apiKeyInHeader: [] + parameters: + - name: recordingId + in: path + required: true + description: The ID of the recording to stream + schema: + type: string + - name: Range + in: header + required: false + description: > + Byte range for partial content retrieval. + Example: `bytes=0-1023` to request the first 1024 bytes of the file. + schema: + type: string + responses: + '200': + description: Successfully streaming the full recording + headers: + Accept-Ranges: + description: Indicates that byte-range requests are supported + schema: + type: string + Content-Length: + description: The total file size in bytes + schema: + type: integer + content: + video/mp4: + schema: + type: string + format: binary + '206': + description: Partial content streaming based on byte range + headers: + Accept-Ranges: + description: Indicates that byte-range requests are supported + schema: + type: string + Content-Range: + description: Specifies the range of bytes being sent + schema: + type: string + Content-Length: + description: The length of the partial content in bytes + schema: + type: integer + content: + video/mp4: + schema: + type: string + format: binary + '400': + description: Bad Request — Invalid range header format + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + code: 400 + message: 'Invalid Range header' + '401': + description: Unauthorized — The API key is missing or invalid + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + message: 'Unauthorized' + '404': + description: Recording not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + code: 404 + message: 'Recording not found' + '416': + description: Requested range not satisfiable + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + code: 416 + message: 'Requested range not satisfiable' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + code: 500 + message: 'Internal server error' + /participants/token: + post: + operationId: generateParticipantToken + summary: Generate a token for a participant + description: > + Generates a token for a participant to join an OpenVidu Meet room. + tags: + - Internal API - Participant + requestBody: + description: Participant details + content: + application/json: + schema: + type: object + required: + - roomName + - participantName + - secret + properties: + roomName: + type: string + example: 'OpenVidu-123456' + description: > + The name of the room to join. + participantName: + type: string + example: 'Alice' + description: > + The name of the participant. + secret: + type: string + example: 'abc123456' + description: > + The secret token from the room Url + responses: + '200': + description: Successfully generated the participant token + content: + application/json: + schema: + type: object + properties: + token: + type: string + example: 'token_123456' + description: > + The token to authenticate the participant. + '404': + description: Room not found + content: + application/json: + schema: + type: object + properties: + name: + type: string + example: 'Room not found' + message: + type: string + example: 'The room does not exist' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + message: 'Internal server error' + /participants/{participantName}: + delete: + operationId: disconnectParticipant + summary: Delete a participant from a room + description: > + Deletes a participant from an OpenVidu Meet room. + tags: + - Internal API - Participant + security: + - apiKeyInHeader: [] + parameters: + - name: participantName + in: path + required: true + description: The name of the participant to delete + schema: + type: string + - name: roomName + in: query + required: true + description: The name of the room from which to delete the participant + schema: + type: string + responses: + '204': + description: Successfully disconnect the participant + '404': + description: Participant not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + code: 404 + message: 'Participant not found' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + code: 500 + message: 'Internal server error' + +components: + securitySchemes: + apiKeyInHeader: + type: apiKey + name: X-API-KEY + in: header + description: > + The API key to authenticate the request. This key is required to access the OpenVidu Meet API. + jwtInCookie: + type: apiKey + name: OvMeetAccessToken + in: cookie + description: > + The JWT token to authenticate the request in case of consuming the API from the OpenVidu Meet admin console. + schemas: + OpenViduMeetRoomOptions: + type: object + required: + - expirationDate + properties: + expirationDate: + type: number + example: 1620000000000 + description: > + The expiration date of the room in milliseconds since the Unix epoch. + After this date, the room will be closed and no new participants will be allowed to join. + roomNamePrefix: + type: string + example: 'OpenVidu' + description: > + A prefix to be used for the room name. The room name will be generated by appending a random + alphanumeric string to this prefix. + maxParticipants: + type: integer + example: 10 + description: > + The maximum number of participants allowed in the room. If the number of participants exceeds + this limit, new participants will not be allowed to join. + preferences: + $ref: '#/components/schemas/RoomPreferences' + description: > + The preferences for the room. + RoomPreferences: + type: object + properties: + chatPreferences: + $ref: '#/components/schemas/ChatPreferences' + description: > + Preferences for the chat feature in the room. + + recordingPreferences: + $ref: '#/components/schemas/RecordingPreferences' + description: > + Preferences for recording the room. + + virtualBackgroundPreferences: + $ref: '#/components/schemas/VirtualBackgroundPreferences' + description: > + Preferences for virtual background in the room. + ChatPreferences: + type: object + properties: + enabled: + type: boolean + default: true + example: true + description: > + If true, the room will be allowed to send and receive chat messages. + RecordingPreferences: + type: object + properties: + enabled: + type: boolean + default: true + example: true + description: > + If true, the room will be allowed to record the video of the participants. + VirtualBackgroundPreferences: + type: object + properties: + enabled: + type: boolean + default: true + example: true + description: > + If true, the room will be allowed to use virtual background. + OpenViduMeetRoom: + type: object + properties: + roomName: + type: string + example: 'OpenVidu-123456' + description: > + The name of the room. This name is generated by appending a random alphanumeric string to the + room name prefix specified in the request. + creationDate: + type: number + example: 1620000000000 + description: > + The creation date of the room in milliseconds since the Unix epoch. + expirationDate: + type: number + example: 1620000000000 + description: > + The expiration date of the room in milliseconds since the Unix epoch. + After this date, the room will be closed and no new participants will be allowed to join. + roomNamePrefix: + type: string + example: 'OpenVidu' + description: > + The prefix used for the room name. The room name is generated by appending a random alphanumeric + string to this prefix. + preferences: + $ref: '#/components/schemas/RoomPreferences' + description: > + The preferences for the room. + maxParticipants: + type: integer + example: 10 + description: > + The maximum number of participants allowed in the room. If the number of participants exceeds + this limit, new participants will not be allowed to join. + moderatorURL: + type: string + example: 'http://localhost:6080/meet/OpenVidu-123456/?secret=tok_123456' + description: > + The URL for the moderator to join the room. The moderator has special permissions to manage the + room and participants. + publisherURL: + type: string + example: 'http://localhost:6080/meet/OpenVidu-123456/?secret=tok_123456' + description: > + The URL for the publisher to join the room. The publisher has permissions to publish audio and + video streams to the room. + viewerURL: + type: string + example: 'http://localhost:6080/meet/OpenVidu-123456/?secret=tok_123456' + description: > + The URL for the viewer to join the room. The viewer has read-only permissions to watch the room + and participants. + MeetRecordingBase: + type: object + properties: + recordingId: + type: string + example: 'room-123--EG_XYZ--XX445' + description: The unique identifier of the recording. + roomId: + type: string + example: 'room-123' + description: The ID of the room where the recording was made. + # outputMode: + # type: string + # example: 'COMPOSED' + # description: > + # The output mode of the recording. Possible value: "COMPOSED". + status: + type: string + example: 'ACTIVE' + description: > + The status of the recording. + Possible values: + - STARTING + - ACTIVE + - ENDING + - COMPLETE + - FAILED + - ABORTED + - LIMITED_REACHED + filename: + type: string + example: 'room-123--XX445.mp4' + description: The name of the recording file. + startDate: + type: number + example: 1620000000000 + description: The date when the recording was started (milliseconds since the Unix epoch). + + MeetRecording: + allOf: + - $ref: '#/components/schemas/MeetRecordingBase' + - type: object + properties: + endDate: + type: number + example: 1620000000000 + description: The date when the recording was stopped (milliseconds since the Unix epoch). + duration: + type: number + example: 3600 + description: The duration of the recording in seconds. + size: + type: number + example: 1024 + description: The size of the recording file in bytes. + errorCode: + type: number + example: 100 + description: The error code of the recording. + error: + type: string + description: The error message of the recording. + nullable: true + details: + type: string + example: 'Stopped using API' + description: Additional details about the recording. + + MeetRecordingStart: + $ref: '#/components/schemas/MeetRecordingBase' + Error: + type: object + required: + - message + properties: + name: + type: string + message: + type: string diff --git a/backend/package.json b/backend/package.json index 78bde32..866d57b 100644 --- a/backend/package.json +++ b/backend/package.json @@ -28,8 +28,9 @@ ], "scripts": { "build:prod": "tsc", - "postbuild:prod": "npm run generate:openapi-doc", - "generate:openapi-doc": "mkdir -p public/openapi && npx openapi-generate-html -i openapi/openvidu-meet-api.yaml --ui=stoplight --theme=light --title 'OpenVidu Meet REST API' --description 'OpenVidu Meet REST API' -o public/openapi/index.html", + "postbuild:prod": "npm run doc:api && npm run doc:internal-api", + "doc:api": "mkdir -p public/openapi && npx openapi-generate-html -i openapi/openvidu-meet-api.yaml --ui=stoplight --theme=light --title 'OpenVidu Meet REST API' --description 'OpenVidu Meet REST API' -o public/openapi/public.html", + "doc:internal-api": "mkdir -p public/openapi && npx openapi-generate-html -i openapi/openvidu-meet-internal-api.yaml --ui=stoplight --theme=dark --title 'OpenVidu Meet Internal REST API' --description 'OpenVidu Meet Internal REST API' -o public/openapi/internal.html", "start:prod": "node dist/src/server.js", "start:dev": "nodemon", "package:build": "npm run build:prod && npm pack", diff --git a/backend/src/server.ts b/backend/src/server.ts index a68ed93..0252a20 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -9,7 +9,7 @@ import { MEET_API_BASE_PATH_V1, MEET_INTERNAL_API_BASE_PATH_V1 } from './environment.js'; -import { openapiHtmlPath, indexHtmlPath, publicFilesPath, webcomponentBundlePath } from './utils/path-utils.js'; +import { publicApiHtmlFilePath, indexHtmlPath, publicFilesPath, webcomponentBundlePath, internalApiHtmlFilePath } from './utils/path-utils.js'; import { authRouter, internalRecordingRouter, @@ -41,7 +41,8 @@ const createApp = () => { app.use(express.json()); app.use(cookieParser()); - app.use(`${MEET_API_BASE_PATH_V1}/docs`, (_req: Request, res: Response) => res.sendFile(openapiHtmlPath)); + app.use(`${MEET_API_BASE_PATH_V1}/docs`, (_req: Request, res: Response) => res.sendFile(publicApiHtmlFilePath)); + app.use(`${MEET_INTERNAL_API_BASE_PATH_V1}/docs`, (_req: Request, res: Response) => res.sendFile(internalApiHtmlFilePath)); app.use(`${MEET_API_BASE_PATH_V1}/rooms`, /*mediaTypeValidatorMiddleware,*/ roomRouter); app.use(`${MEET_API_BASE_PATH_V1}/recordings`, /*mediaTypeValidatorMiddleware,*/ recordingRouter); app.use(`${MEET_API_BASE_PATH_V1}/auth`, /*mediaTypeValidatorMiddleware,*/ authRouter); diff --git a/backend/src/utils/path-utils.ts b/backend/src/utils/path-utils.ts index d4597dc..f46ec24 100644 --- a/backend/src/utils/path-utils.ts +++ b/backend/src/utils/path-utils.ts @@ -7,9 +7,8 @@ const __dirname = path.dirname(__filename); // Path to the source code const srcPath = path.resolve(__dirname, '..'); -const publicFilesPath = path.join(srcPath, '../public'); -const webcomponentBundlePath = path.join(srcPath, '../public/webcomponent/openvidu-meet.bundle.min.js'); -const indexHtmlPath = path.join(publicFilesPath, 'index.html'); -const openapiHtmlPath = path.join(publicFilesPath, 'openapi', 'index.html'); - -export { srcPath, publicFilesPath, indexHtmlPath, webcomponentBundlePath, openapiHtmlPath }; +export const publicFilesPath = path.join(srcPath, '../public'); +export const webcomponentBundlePath = path.join(srcPath, '../public/webcomponent/openvidu-meet.bundle.min.js'); +export const indexHtmlPath = path.join(publicFilesPath, 'index.html'); +export const publicApiHtmlFilePath = path.join(publicFilesPath, 'openapi', 'public.html'); +export const internalApiHtmlFilePath = path.join(publicFilesPath, 'openapi', 'internal.html');