backend: split OpenAPI specification into public and internal files

This commit is contained in:
Carlos Santos 2025-03-31 18:15:08 +02:00
parent 669c5b29a4
commit 194b7fb876
6 changed files with 475 additions and 241 deletions

View File

@ -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"
}
}

View File

@ -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:

View File

@ -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

View File

@ -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",

View File

@ -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);

View File

@ -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');