From 483659f7d252f6f1510aad42e914885f0f412550 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Thu, 7 Mar 2024 12:18:03 +0100 Subject: [PATCH] feat(federation): Add endpoint to get the proxied avatar of other users Signed-off-by: Joas Schilling --- appinfo/routes/routesAvatarController.php | 9 + docs/avatar.md | 36 + lib/Controller/AvatarController.php | 109 +- .../TalkV1/Controller/AvatarController.php | 35 + lib/Federation/Proxy/TalkV1/ProxyRequest.php | 21 +- lib/Service/AvatarService.php | 5 + openapi-federation.json | 187 ++ openapi-full.json | 1523 +++++++++-------- src/types/openapi/openapi-federation.ts | 64 + src/types/openapi/openapi-full.ts | 472 ++--- 10 files changed, 1580 insertions(+), 881 deletions(-) diff --git a/appinfo/routes/routesAvatarController.php b/appinfo/routes/routesAvatarController.php index db4b61a2769..688dc1a280b 100644 --- a/appinfo/routes/routesAvatarController.php +++ b/appinfo/routes/routesAvatarController.php @@ -27,6 +27,11 @@ 'apiVersion' => '(v1)', 'token' => '^[a-z0-9]{4,30}$', ]; +$requirementsWithSize = [ + 'apiVersion' => '(v1)', + 'token' => '^[a-z0-9]{4,30}$', + 'size' => '(64|512)', +]; return [ 'ocs' => [ @@ -40,5 +45,9 @@ ['name' => 'Avatar#getAvatarDark', 'url' => '/api/{apiVersion}/room/{token}/avatar/dark', 'verb' => 'GET', 'requirements' => $requirements], /** @see \OCA\Talk\Controller\AvatarController::deleteAvatar() */ ['name' => 'Avatar#deleteAvatar', 'url' => '/api/{apiVersion}/room/{token}/avatar', 'verb' => 'DELETE', 'requirements' => $requirements], + /** @see \OCA\Talk\Controller\AvatarController::getUserProxyAvatar() */ + ['name' => 'Avatar#getUserProxyAvatar', 'url' => '/api/{apiVersion}/proxy/{token}/user-avatar/{size}', 'verb' => 'GET', 'requirements' => $requirementsWithSize], + /** @see \OCA\Talk\Controller\AvatarController::getUserProxyAvatarDark() */ + ['name' => 'Avatar#getUserProxyAvatarDark', 'url' => '/api/{apiVersion}/proxy/{token}/user-avatar/{size}/dark', 'verb' => 'GET', 'requirements' => $requirementsWithSize], ], ]; diff --git a/docs/avatar.md b/docs/avatar.md index 7fc908c4414..c1594892478 100644 --- a/docs/avatar.md +++ b/docs/avatar.md @@ -92,3 +92,39 @@ + `200 OK` + `404 Not Found` When the conversation could not be found for the participant - Body: the image file + +## Get federated user avatar (binary) + +* Required capability: `federation-v1` +* Method: `GET` +* Endpoint: `/proxy/{token}/user-avatar/{size}` +* Data: + +| field | type | Description | +|-----------|--------|------------------------------------------| +| `size` | int | Only 64 and 512 are supported | +| `cloudId` | string | Federation CloudID to get the avatar for | + +* Response: + - Status code: + + `200 OK` + + `404 Not Found` When the conversation could not be found for the participant + - Body: the image file + +## Get dark mode federated user avatar (binary) + +* Required capability: `federation-v1` +* Method: `GET` +* Endpoint: `/proxy/{token}/user-avatar/{size}/dark` +* Data: + +| field | type | Description | +|-----------|--------|------------------------------------------| +| `size` | int | Only 64 and 512 are supported | +| `cloudId` | string | Federation CloudID to get the avatar for | + +* Response: + - Status code: + + `200 OK` + + `404 Not Found` When the conversation could not be found for the participant + - Body: the image file diff --git a/lib/Controller/AvatarController.php b/lib/Controller/AvatarController.php index ef3116c1705..68fc4ca920f 100644 --- a/lib/Controller/AvatarController.php +++ b/lib/Controller/AvatarController.php @@ -30,16 +30,21 @@ use InvalidArgumentException; use OCA\Talk\Exceptions\CannotReachRemoteException; use OCA\Talk\Middleware\Attribute\FederationSupported; +use OCA\Talk\Middleware\Attribute\RequireLoggedInParticipant; use OCA\Talk\Middleware\Attribute\RequireModeratorParticipant; use OCA\Talk\Middleware\Attribute\RequireParticipantOrLoggedInAndListedConversation; use OCA\Talk\ResponseDefinitions; use OCA\Talk\Service\AvatarService; use OCA\Talk\Service\RoomFormatter; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\BruteForceProtection; use OCP\AppFramework\Http\Attribute\NoCSRFRequired; +use OCP\AppFramework\Http\Attribute\OpenAPI; use OCP\AppFramework\Http\Attribute\PublicPage; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\FileDisplayResponse; +use OCP\Federation\ICloudIdManager; +use OCP\IAvatarManager; use OCP\IL10N; use OCP\IRequest; use OCP\IUserSession; @@ -57,6 +62,8 @@ public function __construct( protected IUserSession $userSession, protected IL10N $l, protected LoggerInterface $logger, + protected ICloudIdManager $cloudIdManager, + protected IAvatarManager $avatarManager, ) { parent::__construct($appName, $request); } @@ -138,7 +145,7 @@ public function emojiAvatar(string $emoji, ?string $color): DataResponse { #[NoCSRFRequired] #[RequireParticipantOrLoggedInAndListedConversation] public function getAvatar(bool $darkTheme = false): FileDisplayResponse { - if ($this->room->getRemoteServer()) { + if ($this->room->getRemoteServer() !== '') { /** @var \OCA\Talk\Federation\Proxy\TalkV1\Controller\AvatarController $proxy */ $proxy = \OCP\Server::get(\OCA\Talk\Federation\Proxy\TalkV1\Controller\AvatarController::class); try { @@ -170,6 +177,106 @@ public function getAvatarDark(): FileDisplayResponse { return $this->getAvatar(true); } + /** + * Get the avatar of a cloudId user + * + * @param int $size Avatar size + * @psalm-param 64|512 $size + * @param string $cloudId Federation CloudID to get the avatar for + * @param bool $darkTheme Theme used for background + * @return FileDisplayResponse + * + * 200: User avatar returned + */ + #[FederationSupported] + #[BruteForceProtection(action: 'talkRoomToken')] + #[OpenAPI(scope: OpenAPI::SCOPE_FEDERATION)] + #[PublicPage] + #[NoCSRFRequired] + #[RequireLoggedInParticipant] + public function getUserProxyAvatar(int $size, string $cloudId, bool $darkTheme = false): FileDisplayResponse { + try { + $resolvedCloudId = $this->cloudIdManager->resolveCloudId($cloudId); + } catch (\InvalidArgumentException) { + return $this->getPlaceholderResponse($darkTheme); + } + + $ownId = $this->cloudIdManager->getCloudId($this->userSession->getUser()->getCloudId(), null); + + /** + * Reach out to the remote server to get the avatar + */ + if ($ownId->getRemote() !== $resolvedCloudId->getRemote()) { + /** @var \OCA\Talk\Federation\Proxy\TalkV1\Controller\AvatarController $proxy */ + $proxy = \OCP\Server::get(\OCA\Talk\Federation\Proxy\TalkV1\Controller\AvatarController::class); + try { + return $proxy->getUserProxyAvatar($resolvedCloudId->getRemote(), $resolvedCloudId->getUser(), $size, $darkTheme); + } catch (CannotReachRemoteException) { + // Falling back to a local "user" avatar + return $this->getPlaceholderResponse($darkTheme); + } + } + + /** + * We are the server that hosts the user, so getting it from the avatar manager + */ + try { + $avatar = $this->avatarManager->getAvatar($resolvedCloudId->getUser()); + $avatarFile = $avatar->getFile($size, $darkTheme); + } catch (\Exception) { + return $this->getPlaceholderResponse($darkTheme); + } + + $response = new FileDisplayResponse( + $avatarFile, + Http::STATUS_OK, + ['Content-Type' => $avatarFile->getMimeType()], + ); + // Cache for 1 day + $response->cacheFor(60 * 60 * 24, false, true); + return $response; + } + + /** + * Get the dark mode avatar of a cloudId user + * + * @param int $size Avatar size + * @psalm-param 64|512 $size + * @param string $cloudId Federation CloudID to get the avatar for + * @return FileDisplayResponse + * + * 200: User avatar returned + */ + #[FederationSupported] + #[BruteForceProtection(action: 'talkRoomToken')] + #[OpenAPI(scope: OpenAPI::SCOPE_FEDERATION)] + #[PublicPage] + #[NoCSRFRequired] + #[RequireLoggedInParticipant] + public function getUserProxyAvatarDark(int $size, string $cloudId): FileDisplayResponse { + return $this->getUserProxyAvatar($size, $cloudId, true); + } + + /** + * Get the placeholder avatar + * + * @param bool $darkTheme Theme used for background + * @return FileDisplayResponse + * + * 200: User avatar returned + */ + protected function getPlaceholderResponse(bool $darkTheme): FileDisplayResponse { + $file = $this->avatarService->getPersonPlaceholder($darkTheme); + $response = new FileDisplayResponse( + $file, + Http::STATUS_OK, + ['Content-Type' => $file->getMimeType()], + ); + $response->cacheFor(60 * 60 * 24, false, true); + return $response; + + } + /** * Delete the avatar of a room * diff --git a/lib/Federation/Proxy/TalkV1/Controller/AvatarController.php b/lib/Federation/Proxy/TalkV1/Controller/AvatarController.php index d9996463763..43592df6d20 100644 --- a/lib/Federation/Proxy/TalkV1/Controller/AvatarController.php +++ b/lib/Federation/Proxy/TalkV1/Controller/AvatarController.php @@ -77,4 +77,39 @@ public function getAvatar(Room $room, Participant $participant, bool $darkTheme) $response->cacheFor(60 * 60 * 24, false, true); return $response; } + + /** + * @see \OCA\Talk\Controller\AvatarController::getUserProxyAvatar() + * + * @return FileDisplayResponse + * @throws CannotReachRemoteException + * + * 200: User avatar returned + */ + public function getUserProxyAvatar(string $remoteServer, string $user, int $size, bool $darkTheme): FileDisplayResponse { + $proxy = $this->proxy->get( + null, + null, + $remoteServer . '/index.php/avatar/' . $user . '/' . $size . ($darkTheme ? '/dark' : ''), + ); + + if ($proxy->getStatusCode() !== Http::STATUS_OK) { + if ($proxy->getStatusCode() !== Http::STATUS_NOT_FOUND) { + $this->proxy->logUnexpectedStatusCode(__METHOD__, $proxy->getStatusCode(), (string) $proxy->getBody()); + } + throw new CannotReachRemoteException('Avatar request had unexpected status code'); + } + + $content = $proxy->getBody(); + if ($content === '') { + throw new CannotReachRemoteException('No avatar content received'); + } + + $file = new InMemoryFile($user, $content); + + $response = new FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => $file->getMimeType()]); + // Cache for 1 day + $response->cacheFor(60 * 60 * 24, false, true); + return $response; + } } diff --git a/lib/Federation/Proxy/TalkV1/ProxyRequest.php b/lib/Federation/Proxy/TalkV1/ProxyRequest.php index 224a952d8c4..548553867cb 100644 --- a/lib/Federation/Proxy/TalkV1/ProxyRequest.php +++ b/lib/Federation/Proxy/TalkV1/ProxyRequest.php @@ -59,11 +59,11 @@ public function logUnexpectedStatusCode(string $method, int $statusCode, string } protected function generateDefaultRequestOptions( - string $cloudId, + ?string $cloudId, #[SensitiveParameter] - string $accessToken, + ?string $accessToken, ): array { - return [ + $options = [ 'verify' => !$this->config->getSystemValueBool('sharing.federation.allowSelfSignedCertificates'), 'nextcloud' => [ 'allow_local_address' => $this->config->getSystemValueBool('allow_local_remote_servers'), @@ -74,8 +74,13 @@ protected function generateDefaultRequestOptions( 'OCS-APIRequest' => 'true', ], 'timeout' => 5, - 'auth' => [urlencode($cloudId), $accessToken], ]; + + if ($cloudId !== null && $accessToken !== null) { + $options['auth'] = [urlencode($cloudId), $accessToken]; + } + + return $options; } protected function prependProtocolIfNotAvailable(string $url): string { @@ -91,9 +96,9 @@ protected function prependProtocolIfNotAvailable(string $url): string { */ protected function request( string $verb, - string $cloudId, + ?string $cloudId, #[SensitiveParameter] - string $accessToken, + ?string $accessToken, string $url, array $parameters, ): IResponse { @@ -134,9 +139,9 @@ protected function request( * @throws CannotReachRemoteException */ public function get( - string $cloudId, + ?string $cloudId, #[SensitiveParameter] - string $accessToken, + ?string $accessToken, string $url, array $parameters = [], ): IResponse { diff --git a/lib/Service/AvatarService.php b/lib/Service/AvatarService.php index 82b701eedd6..70757074a98 100644 --- a/lib/Service/AvatarService.php +++ b/lib/Service/AvatarService.php @@ -237,6 +237,11 @@ public function getAvatar(Room $room, ?IUser $user, bool $darkTheme = false): IS return new InMemoryFile($token, file_get_contents($this->getAvatarPath($room, $darkTheme))); } + public function getPersonPlaceholder(bool $darkTheme = false): ISimpleFile { + $colorTone = $darkTheme ? 'dark' : 'bright'; + return new InMemoryFile('fallback', file_get_contents(__DIR__ . '/../../img/icon-conversation-user-' . $colorTone . '.svg')); + } + protected function getEmojiAvatar(string $emoji, string $fillColor): string { return str_replace([ '{letter}', diff --git a/openapi-federation.json b/openapi-federation.json index 6445bbfeb70..10bd1368e72 100644 --- a/openapi-federation.json +++ b/openapi-federation.json @@ -731,6 +731,193 @@ } }, "paths": { + "/ocs/v2.php/apps/spreed/api/{apiVersion}/proxy/{token}/user-avatar/{size}": { + "get": { + "operationId": "avatar-get-user-proxy-avatar", + "summary": "Get the avatar of a cloudId user", + "tags": [ + "avatar" + ], + "security": [ + {}, + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "parameters": [ + { + "name": "cloudId", + "in": "query", + "description": "Federation CloudID to get the avatar for", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "darkTheme", + "in": "query", + "description": "Theme used for background", + "schema": { + "type": "integer", + "default": 0, + "enum": [ + 0, + 1 + ] + } + }, + { + "name": "apiVersion", + "in": "path", + "required": true, + "schema": { + "type": "string", + "enum": [ + "v1" + ], + "default": "v1" + } + }, + { + "name": "token", + "in": "path", + "required": true, + "schema": { + "type": "string", + "pattern": "^[a-z0-9]{4,30}$" + } + }, + { + "name": "size", + "in": "path", + "description": "Avatar size", + "required": true, + "schema": { + "type": "integer", + "format": "int64", + "enum": [ + 64, + 512 + ] + } + }, + { + "name": "OCS-APIRequest", + "in": "header", + "description": "Required to be true for the API request to pass", + "required": true, + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "User avatar returned", + "content": { + "*/*": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + } + } + } + }, + "/ocs/v2.php/apps/spreed/api/{apiVersion}/proxy/{token}/user-avatar/{size}/dark": { + "get": { + "operationId": "avatar-get-user-proxy-avatar-dark", + "summary": "Get the dark mode avatar of a cloudId user", + "tags": [ + "avatar" + ], + "security": [ + {}, + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "parameters": [ + { + "name": "cloudId", + "in": "query", + "description": "Federation CloudID to get the avatar for", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "apiVersion", + "in": "path", + "required": true, + "schema": { + "type": "string", + "enum": [ + "v1" + ], + "default": "v1" + } + }, + { + "name": "token", + "in": "path", + "required": true, + "schema": { + "type": "string", + "pattern": "^[a-z0-9]{4,30}$" + } + }, + { + "name": "size", + "in": "path", + "description": "Avatar size", + "required": true, + "schema": { + "type": "integer", + "format": "int64", + "enum": [ + 64, + 512 + ] + } + }, + { + "name": "OCS-APIRequest", + "in": "header", + "description": "Required to be true for the API request to pass", + "required": true, + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "User avatar returned", + "content": { + "*/*": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + } + } + } + }, "/ocs/v2.php/apps/spreed/api/{apiVersion}/federation/invitation/{id}": { "post": { "operationId": "federation-accept-share", diff --git a/openapi-full.json b/openapi-full.json index ba5cef35da5..e4ea605dfe6 100644 --- a/openapi-full.json +++ b/openapi-full.json @@ -16141,13 +16141,12 @@ } } }, - "/ocs/v2.php/apps/spreed/api/{apiVersion}/bot/{token}/message": { - "post": { - "operationId": "bot-send-message", - "summary": "Sends a new chat message to the given room", - "description": "The author and timestamp are automatically set to the current user/guest and time.", + "/ocs/v2.php/apps/spreed/api/{apiVersion}/proxy/{token}/user-avatar/{size}": { + "get": { + "operationId": "avatar-get-user-proxy-avatar", + "summary": "Get the avatar of a cloudId user", "tags": [ - "bot" + "avatar" ], "security": [ {}, @@ -16160,37 +16159,18 @@ ], "parameters": [ { - "name": "message", + "name": "cloudId", "in": "query", - "description": "The message to send", + "description": "Federation CloudID to get the avatar for", "required": true, "schema": { "type": "string" } }, { - "name": "referenceId", - "in": "query", - "description": "For the message to be able to later identify it again", - "schema": { - "type": "string", - "default": "" - } - }, - { - "name": "replyTo", - "in": "query", - "description": "Parent id which this message is a reply to", - "schema": { - "type": "integer", - "format": "int64", - "default": 0 - } - }, - { - "name": "silent", + "name": "darkTheme", "in": "query", - "description": "If sent silent the chat message will not create any notifications", + "description": "Theme used for background", "schema": { "type": "integer", "default": 0, @@ -16215,13 +16195,26 @@ { "name": "token", "in": "path", - "description": "Conversation token", "required": true, "schema": { "type": "string", "pattern": "^[a-z0-9]{4,30}$" } }, + { + "name": "size", + "in": "path", + "description": "Avatar size", + "required": true, + "schema": { + "type": "integer", + "format": "int64", + "enum": [ + 64, + 512 + ] + } + }, { "name": "OCS-APIRequest", "in": "header", @@ -16234,114 +16227,13 @@ } ], "responses": { - "201": { - "description": "Message sent successfully", - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "ocs" - ], - "properties": { - "ocs": { - "type": "object", - "required": [ - "meta", - "data" - ], - "properties": { - "meta": { - "$ref": "#/components/schemas/OCSMeta" - }, - "data": {} - } - } - } - } - } - } - }, - "400": { - "description": "When the replyTo is invalid or message is empty", - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "ocs" - ], - "properties": { - "ocs": { - "type": "object", - "required": [ - "meta", - "data" - ], - "properties": { - "meta": { - "$ref": "#/components/schemas/OCSMeta" - }, - "data": {} - } - } - } - } - } - } - }, - "401": { - "description": "Sending message is not allowed", - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "ocs" - ], - "properties": { - "ocs": { - "type": "object", - "required": [ - "meta", - "data" - ], - "properties": { - "meta": { - "$ref": "#/components/schemas/OCSMeta" - }, - "data": {} - } - } - } - } - } - } - }, - "413": { - "description": "Message too long", + "200": { + "description": "User avatar returned", "content": { - "application/json": { + "*/*": { "schema": { - "type": "object", - "required": [ - "ocs" - ], - "properties": { - "ocs": { - "type": "object", - "required": [ - "meta", - "data" - ], - "properties": { - "meta": { - "$ref": "#/components/schemas/OCSMeta" - }, - "data": {} - } - } - } + "type": "string", + "format": "binary" } } } @@ -16349,12 +16241,12 @@ } } }, - "/ocs/v2.php/apps/spreed/api/{apiVersion}/bot/{token}/reaction/{messageId}": { - "post": { - "operationId": "bot-react", - "summary": "Adds a reaction to a chat message", + "/ocs/v2.php/apps/spreed/api/{apiVersion}/proxy/{token}/user-avatar/{size}/dark": { + "get": { + "operationId": "avatar-get-user-proxy-avatar-dark", + "summary": "Get the dark mode avatar of a cloudId user", "tags": [ - "bot" + "avatar" ], "security": [ {}, @@ -16367,9 +16259,9 @@ ], "parameters": [ { - "name": "reaction", + "name": "cloudId", "in": "query", - "description": "Reaction to add", + "description": "Federation CloudID to get the avatar for", "required": true, "schema": { "type": "string" @@ -16390,7 +16282,6 @@ { "name": "token", "in": "path", - "description": "Conversation token", "required": true, "schema": { "type": "string", @@ -16398,13 +16289,17 @@ } }, { - "name": "messageId", + "name": "size", "in": "path", - "description": "ID of the message", + "description": "Avatar size", "required": true, "schema": { "type": "integer", - "format": "int64" + "format": "int64", + "enum": [ + 64, + 512 + ] } }, { @@ -16420,35 +16315,73 @@ ], "responses": { "200": { - "description": "Reaction already exists", + "description": "User avatar returned", "content": { - "application/json": { + "*/*": { "schema": { - "type": "object", - "required": [ - "ocs" - ], - "properties": { - "ocs": { - "type": "object", - "required": [ - "meta", - "data" - ], - "properties": { - "meta": { - "$ref": "#/components/schemas/OCSMeta" - }, - "data": {} - } - } - } + "type": "string", + "format": "binary" } } } + } + } + } + }, + "/ocs/v2.php/apps/spreed/api/{apiVersion}/federation/invitation/{id}": { + "post": { + "operationId": "federation-accept-share", + "summary": "Accept a federation invites", + "description": "🚧 Draft: Still work in progress", + "tags": [ + "federation" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "parameters": [ + { + "name": "apiVersion", + "in": "path", + "required": true, + "schema": { + "type": "string", + "enum": [ + "v1" + ], + "default": "v1" + } }, - "201": { - "description": "Reacted successfully", + { + "name": "id", + "in": "path", + "description": "ID of the share", + "required": true, + "schema": { + "type": "integer", + "format": "int64", + "minimum": 0 + } + }, + { + "name": "OCS-APIRequest", + "in": "header", + "description": "Required to be true for the API request to pass", + "required": true, + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "Invite accepted successfully", "content": { "application/json": { "schema": { @@ -16467,7 +16400,9 @@ "meta": { "$ref": "#/components/schemas/OCSMeta" }, - "data": {} + "data": { + "$ref": "#/components/schemas/Room" + } } } } @@ -16476,7 +16411,7 @@ } }, "400": { - "description": "Reacting is not possible", + "description": "Invite can not be accepted (maybe it was accepted already)", "content": { "application/json": { "schema": { @@ -16495,7 +16430,17 @@ "meta": { "$ref": "#/components/schemas/OCSMeta" }, - "data": {} + "data": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "string" + } + } + } } } } @@ -16503,8 +16448,8 @@ } } }, - "401": { - "description": "Reacting is not allowed", + "410": { + "description": "Remote server could not be reached to notify about the acceptance", "content": { "application/json": { "schema": { @@ -16523,7 +16468,17 @@ "meta": { "$ref": "#/components/schemas/OCSMeta" }, - "data": {} + "data": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "string" + } + } + } } } } @@ -16532,7 +16487,7 @@ } }, "404": { - "description": "Reaction not found", + "description": "Invite can not be found", "content": { "application/json": { "schema": { @@ -16551,7 +16506,14 @@ "meta": { "$ref": "#/components/schemas/OCSMeta" }, - "data": {} + "data": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + } + } } } } @@ -16562,13 +16524,13 @@ } }, "delete": { - "operationId": "bot-delete-reaction", - "summary": "Deletes a reaction from a chat message", + "operationId": "federation-reject-share", + "summary": "Decline a federation invites", + "description": "🚧 Draft: Still work in progress", "tags": [ - "bot" + "federation" ], "security": [ - {}, { "bearer_auth": [] }, @@ -16577,15 +16539,6 @@ } ], "parameters": [ - { - "name": "reaction", - "in": "query", - "description": "Reaction to delete", - "required": true, - "schema": { - "type": "string" - } - }, { "name": "apiVersion", "in": "path", @@ -16599,23 +16552,14 @@ } }, { - "name": "token", - "in": "path", - "description": "Conversation token", - "required": true, - "schema": { - "type": "string", - "pattern": "^[a-z0-9]{4,30}$" - } - }, - { - "name": "messageId", + "name": "id", "in": "path", - "description": "ID of the message", + "description": "ID of the share", "required": true, "schema": { "type": "integer", - "format": "int64" + "format": "int64", + "minimum": 0 } }, { @@ -16631,35 +16575,7 @@ ], "responses": { "200": { - "description": "Reaction deleted successfully", - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "ocs" - ], - "properties": { - "ocs": { - "type": "object", - "required": [ - "meta", - "data" - ], - "properties": { - "meta": { - "$ref": "#/components/schemas/OCSMeta" - }, - "data": {} - } - } - } - } - } - } - }, - "400": { - "description": "Reacting is not possible", + "description": "Invite declined successfully", "content": { "application/json": { "schema": { @@ -16687,35 +16603,7 @@ } }, "404": { - "description": "Reaction not found", - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "ocs" - ], - "properties": { - "ocs": { - "type": "object", - "required": [ - "meta", - "data" - ], - "properties": { - "meta": { - "$ref": "#/components/schemas/OCSMeta" - }, - "data": {} - } - } - } - } - } - } - }, - "401": { - "description": "Reacting is not allowed", + "description": "Invite can not be found", "content": { "application/json": { "schema": { @@ -16734,7 +16622,14 @@ "meta": { "$ref": "#/components/schemas/OCSMeta" }, - "data": {} + "data": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + } + } } } } @@ -16745,13 +16640,13 @@ } } }, - "/ocs/v2.php/apps/spreed/api/{apiVersion}/bot/admin": { + "/ocs/v2.php/apps/spreed/api/{apiVersion}/federation/invitation": { "get": { - "operationId": "settings-admin-list-bots", - "summary": "List admin bots", - "description": "This endpoint requires admin access", + "operationId": "federation-get-shares", + "summary": "Get a list of federation invites", + "description": "🚧 Draft: Still work in progress", "tags": [ - "settings" + "federation" ], "security": [ { @@ -16787,7 +16682,7 @@ ], "responses": { "200": { - "description": "Bot list returned", + "description": "Get list of received federation invites successfully", "content": { "application/json": { "schema": { @@ -16809,7 +16704,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/BotWithDetails" + "$ref": "#/components/schemas/FederationInvite" } } } @@ -16822,15 +16717,15 @@ } } }, - "/ocs/v2.php/apps/spreed/api/{apiVersion}/certificate/expiration": { - "get": { - "operationId": "settings-get-certificate-expiration", - "summary": "Get the certificate expiration for a host", - "description": "This endpoint requires admin access", + "/ocs/v2.php/apps/spreed/api/{apiVersion}/room/{token}/federation/active": { + "post": { + "operationId": "room-join-federated-room", + "summary": "Fake join a room on the host server to verify the federated user is still part of it", "tags": [ - "settings" + "room" ], "security": [ + {}, { "bearer_auth": [] }, @@ -16840,24 +16735,25 @@ ], "parameters": [ { - "name": "host", - "in": "query", - "description": "Host to check", + "name": "apiVersion", + "in": "path", "required": true, "schema": { - "type": "string" + "type": "string", + "enum": [ + "v4" + ], + "default": "v4" } }, { - "name": "apiVersion", + "name": "token", "in": "path", + "description": "Token of the room", "required": true, "schema": { "type": "string", - "enum": [ - "v1" - ], - "default": "v1" + "pattern": "^[a-z0-9]{4,30}$" } }, { @@ -16873,7 +16769,14 @@ ], "responses": { "200": { - "description": "Certificate expiration returned", + "description": "Federated user is still part of the room", + "headers": { + "X-Nextcloud-Talk-Hash": { + "schema": { + "type": "string" + } + } + }, "content": { "application/json": { "schema": { @@ -16881,30 +16784,18 @@ "required": [ "ocs" ], - "properties": { - "ocs": { - "type": "object", - "required": [ - "meta", - "data" - ], - "properties": { - "meta": { - "$ref": "#/components/schemas/OCSMeta" - }, - "data": { - "type": "object", - "required": [ - "expiration_in_days" - ], - "properties": { - "expiration_in_days": { - "type": "integer", - "format": "int64", - "nullable": true - } - } - } + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": {} } } } @@ -16912,8 +16803,8 @@ } } }, - "400": { - "description": "Getting certificate expiration is not possible", + "404": { + "description": "Room not found", "content": { "application/json": { "schema": { @@ -16933,15 +16824,7 @@ "$ref": "#/components/schemas/OCSMeta" }, "data": { - "type": "object", - "required": [ - "message" - ], - "properties": { - "message": { - "type": "string" - } - } + "nullable": true } } } @@ -16953,15 +16836,16 @@ } } }, - "/ocs/v2.php/apps/spreed/api/{apiVersion}/hostedsignalingserver/requesttrial": { + "/ocs/v2.php/apps/spreed/api/{apiVersion}/bot/{token}/message": { "post": { - "operationId": "hosted_signaling_server-request-trial", - "summary": "Request a trial account", - "description": "This endpoint requires admin access", + "operationId": "bot-send-message", + "summary": "Sends a new chat message to the given room", + "description": "The author and timestamp are automatically set to the current user/guest and time.", "tags": [ - "hosted_signaling_server" + "bot" ], "security": [ + {}, { "bearer_auth": [] }, @@ -16971,48 +16855,44 @@ ], "parameters": [ { - "name": "url", - "in": "query", - "description": "Server URL", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "name", + "name": "message", "in": "query", - "description": "Display name of the user", + "description": "The message to send", "required": true, "schema": { "type": "string" } }, { - "name": "email", + "name": "referenceId", "in": "query", - "description": "Email of the user", - "required": true, + "description": "For the message to be able to later identify it again", "schema": { - "type": "string" + "type": "string", + "default": "" } }, { - "name": "language", + "name": "replyTo", "in": "query", - "description": "Language of the user", - "required": true, + "description": "Parent id which this message is a reply to", "schema": { - "type": "string" + "type": "integer", + "format": "int64", + "default": 0 } }, { - "name": "country", + "name": "silent", "in": "query", - "description": "Country of the user", - "required": true, + "description": "If sent silent the chat message will not create any notifications", "schema": { - "type": "string" + "type": "integer", + "default": 0, + "enum": [ + 0, + 1 + ] } }, { @@ -17027,6 +16907,16 @@ "default": "v1" } }, + { + "name": "token", + "in": "path", + "description": "Conversation token", + "required": true, + "schema": { + "type": "string", + "pattern": "^[a-z0-9]{4,30}$" + } + }, { "name": "OCS-APIRequest", "in": "header", @@ -17039,8 +16929,8 @@ } ], "responses": { - "200": { - "description": "Trial requested successfully", + "201": { + "description": "Message sent successfully", "content": { "application/json": { "schema": { @@ -17059,12 +16949,7 @@ "meta": { "$ref": "#/components/schemas/OCSMeta" }, - "data": { - "type": "object", - "additionalProperties": { - "type": "object" - } - } + "data": {} } } } @@ -17073,7 +16958,7 @@ } }, "400": { - "description": "Requesting trial is not possible", + "description": "When the replyTo is invalid or message is empty", "content": { "application/json": { "schema": { @@ -17092,17 +16977,7 @@ "meta": { "$ref": "#/components/schemas/OCSMeta" }, - "data": { - "type": "object", - "required": [ - "message" - ], - "properties": { - "message": { - "type": "string" - } - } - } + "data": {} } } } @@ -17110,8 +16985,8 @@ } } }, - "500": { - "description": "", + "401": { + "description": "Sending message is not allowed", "content": { "application/json": { "schema": { @@ -17130,17 +17005,35 @@ "meta": { "$ref": "#/components/schemas/OCSMeta" }, - "data": { - "type": "object", - "required": [ - "message" - ], - "properties": { - "message": { - "type": "string" - } - } - } + "data": {} + } + } + } + } + } + } + }, + "413": { + "description": "Message too long", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": {} } } } @@ -17151,15 +17044,15 @@ } } }, - "/ocs/v2.php/apps/spreed/api/{apiVersion}/hostedsignalingserver/delete": { - "delete": { - "operationId": "hosted_signaling_server-delete-account", - "summary": "Delete the account", - "description": "This endpoint requires admin access", + "/ocs/v2.php/apps/spreed/api/{apiVersion}/bot/{token}/reaction/{messageId}": { + "post": { + "operationId": "bot-react", + "summary": "Adds a reaction to a chat message", "tags": [ - "hosted_signaling_server" + "bot" ], "security": [ + {}, { "bearer_auth": [] }, @@ -17168,6 +17061,15 @@ } ], "parameters": [ + { + "name": "reaction", + "in": "query", + "description": "Reaction to add", + "required": true, + "schema": { + "type": "string" + } + }, { "name": "apiVersion", "in": "path", @@ -17180,6 +17082,26 @@ "default": "v1" } }, + { + "name": "token", + "in": "path", + "description": "Conversation token", + "required": true, + "schema": { + "type": "string", + "pattern": "^[a-z0-9]{4,30}$" + } + }, + { + "name": "messageId", + "in": "path", + "description": "ID of the message", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, { "name": "OCS-APIRequest", "in": "header", @@ -17192,8 +17114,64 @@ } ], "responses": { - "204": { - "description": "Account deleted successfully", + "200": { + "description": "Reaction already exists", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": {} + } + } + } + } + } + } + }, + "201": { + "description": "Reacted successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": {} + } + } + } + } + } + } + }, + "400": { + "description": "Reacting is not possible", "content": { "application/json": { "schema": { @@ -17220,8 +17198,8 @@ } } }, - "400": { - "description": "Deleting account is not possible", + "401": { + "description": "Reacting is not allowed", "content": { "application/json": { "schema": { @@ -17240,17 +17218,7 @@ "meta": { "$ref": "#/components/schemas/OCSMeta" }, - "data": { - "type": "object", - "required": [ - "message" - ], - "properties": { - "message": { - "type": "string" - } - } - } + "data": {} } } } @@ -17258,8 +17226,8 @@ } } }, - "500": { - "description": "", + "404": { + "description": "Reaction not found", "content": { "application/json": { "schema": { @@ -17278,17 +17246,7 @@ "meta": { "$ref": "#/components/schemas/OCSMeta" }, - "data": { - "type": "object", - "required": [ - "message" - ], - "properties": { - "message": { - "type": "string" - } - } - } + "data": {} } } } @@ -17297,17 +17255,15 @@ } } } - } - }, - "/ocs/v2.php/apps/spreed/api/{apiVersion}/bridge": { + }, "delete": { - "operationId": "matterbridge-stop-all-bridges", - "summary": "Stop all bridges", - "description": "This endpoint requires admin access", + "operationId": "bot-delete-reaction", + "summary": "Deletes a reaction from a chat message", "tags": [ - "matterbridge" + "bot" ], "security": [ + {}, { "bearer_auth": [] }, @@ -17316,6 +17272,15 @@ } ], "parameters": [ + { + "name": "reaction", + "in": "query", + "description": "Reaction to delete", + "required": true, + "schema": { + "type": "string" + } + }, { "name": "apiVersion", "in": "path", @@ -17328,6 +17293,26 @@ "default": "v1" } }, + { + "name": "token", + "in": "path", + "description": "Conversation token", + "required": true, + "schema": { + "type": "string", + "pattern": "^[a-z0-9]{4,30}$" + } + }, + { + "name": "messageId", + "in": "path", + "description": "ID of the message", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, { "name": "OCS-APIRequest", "in": "header", @@ -17341,7 +17326,7 @@ ], "responses": { "200": { - "description": "All bridges stopped successfully", + "description": "Reaction deleted successfully", "content": { "application/json": { "schema": { @@ -17360,9 +17345,7 @@ "meta": { "$ref": "#/components/schemas/OCSMeta" }, - "data": { - "type": "boolean" - } + "data": {} } } } @@ -17370,8 +17353,8 @@ } } }, - "406": { - "description": "Stopping all bridges is not possible", + "400": { + "description": "Reacting is not possible", "content": { "application/json": { "schema": { @@ -17390,70 +17373,16 @@ "meta": { "$ref": "#/components/schemas/OCSMeta" }, - "data": { - "type": "object", - "required": [ - "error" - ], - "properties": { - "error": { - "type": "string" - } - } - } + "data": {} } } } } } } - } - } - } - }, - "/ocs/v2.php/apps/spreed/api/{apiVersion}/bridge/version": { - "get": { - "operationId": "matterbridge-get-matterbridge-version", - "summary": "Get Matterbridge version", - "description": "This endpoint requires admin access", - "tags": [ - "matterbridge" - ], - "security": [ - { - "bearer_auth": [] - }, - { - "basic_auth": [] - } - ], - "parameters": [ - { - "name": "apiVersion", - "in": "path", - "required": true, - "schema": { - "type": "string", - "enum": [ - "v1" - ], - "default": "v1" - } }, - { - "name": "OCS-APIRequest", - "in": "header", - "description": "Required to be true for the API request to pass", - "required": true, - "schema": { - "type": "boolean", - "default": true - } - } - ], - "responses": { - "200": { - "description": "Bridge version returned", + "404": { + "description": "Reaction not found", "content": { "application/json": { "schema": { @@ -17472,17 +17401,7 @@ "meta": { "$ref": "#/components/schemas/OCSMeta" }, - "data": { - "type": "object", - "required": [ - "version" - ], - "properties": { - "version": { - "type": "string" - } - } - } + "data": {} } } } @@ -17490,8 +17409,8 @@ } } }, - "400": { - "description": "Getting bridge version is not possible", + "401": { + "description": "Reacting is not allowed", "content": { "application/json": { "schema": { @@ -17510,17 +17429,7 @@ "meta": { "$ref": "#/components/schemas/OCSMeta" }, - "data": { - "type": "object", - "required": [ - "error" - ], - "properties": { - "error": { - "type": "string" - } - } - } + "data": {} } } } @@ -17531,10 +17440,10 @@ } } }, - "/ocs/v2.php/apps/spreed/api/{apiVersion}/recording/welcome/{serverId}": { + "/ocs/v2.php/apps/spreed/api/{apiVersion}/bot/admin": { "get": { - "operationId": "settings-get-welcome-message", - "summary": "Get the welcome message of a recording server", + "operationId": "settings-admin-list-bots", + "summary": "List admin bots", "description": "This endpoint requires admin access", "tags": [ "settings" @@ -17560,17 +17469,6 @@ "default": "v1" } }, - { - "name": "serverId", - "in": "path", - "description": "ID of the server", - "required": true, - "schema": { - "type": "integer", - "format": "int64", - "minimum": 0 - } - }, { "name": "OCS-APIRequest", "in": "header", @@ -17584,7 +17482,7 @@ ], "responses": { "200": { - "description": "Welcome message returned", + "description": "Bot list returned", "content": { "application/json": { "schema": { @@ -17604,15 +17502,9 @@ "$ref": "#/components/schemas/OCSMeta" }, "data": { - "type": "object", - "required": [ - "version" - ], - "properties": { - "version": { - "type": "number", - "format": "float" - } + "type": "array", + "items": { + "$ref": "#/components/schemas/BotWithDetails" } } } @@ -17621,9 +17513,62 @@ } } } + } + } + } + }, + "/ocs/v2.php/apps/spreed/api/{apiVersion}/certificate/expiration": { + "get": { + "operationId": "settings-get-certificate-expiration", + "summary": "Get the certificate expiration for a host", + "description": "This endpoint requires admin access", + "tags": [ + "settings" + ], + "security": [ + { + "bearer_auth": [] }, - "404": { - "description": "Recording server not found or not configured", + { + "basic_auth": [] + } + ], + "parameters": [ + { + "name": "host", + "in": "query", + "description": "Host to check", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "apiVersion", + "in": "path", + "required": true, + "schema": { + "type": "string", + "enum": [ + "v1" + ], + "default": "v1" + } + }, + { + "name": "OCS-APIRequest", + "in": "header", + "description": "Required to be true for the API request to pass", + "required": true, + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "Certificate expiration returned", "content": { "application/json": { "schema": { @@ -17642,7 +17587,19 @@ "meta": { "$ref": "#/components/schemas/OCSMeta" }, - "data": {} + "data": { + "type": "object", + "required": [ + "expiration_in_days" + ], + "properties": { + "expiration_in_days": { + "type": "integer", + "format": "int64", + "nullable": true + } + } + } } } } @@ -17650,8 +17607,8 @@ } } }, - "500": { - "description": "", + "400": { + "description": "Getting certificate expiration is not possible", "content": { "application/json": { "schema": { @@ -17673,10 +17630,10 @@ "data": { "type": "object", "required": [ - "error" + "message" ], "properties": { - "error": { + "message": { "type": "string" } } @@ -17691,13 +17648,13 @@ } } }, - "/ocs/v2.php/apps/spreed/api/{apiVersion}/settings/sip": { + "/ocs/v2.php/apps/spreed/api/{apiVersion}/hostedsignalingserver/requesttrial": { "post": { - "operationId": "settings-setsip-settings", - "summary": "Update SIP bridge settings", + "operationId": "hosted_signaling_server-request-trial", + "summary": "Request a trial account", "description": "This endpoint requires admin access", "tags": [ - "settings" + "hosted_signaling_server" ], "security": [ { @@ -17709,33 +17666,48 @@ ], "parameters": [ { - "name": "sipGroups[]", + "name": "url", "in": "query", - "description": "New SIP groups", + "description": "Server URL", + "required": true, "schema": { - "type": "array", - "default": [], - "items": { - "type": "string" - } + "type": "string" } }, { - "name": "dialInInfo", + "name": "name", "in": "query", - "description": "New dial info", + "description": "Display name of the user", + "required": true, "schema": { - "type": "string", - "default": "" + "type": "string" } }, { - "name": "sharedSecret", + "name": "email", "in": "query", - "description": "New shared secret", + "description": "Email of the user", + "required": true, "schema": { - "type": "string", - "default": "" + "type": "string" + } + }, + { + "name": "language", + "in": "query", + "description": "Language of the user", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "country", + "in": "query", + "description": "Country of the user", + "required": true, + "schema": { + "type": "string" } }, { @@ -17763,7 +17735,7 @@ ], "responses": { "200": { - "description": "Successfully set new SIP settings", + "description": "Trial requested successfully", "content": { "application/json": { "schema": { @@ -17782,7 +17754,88 @@ "meta": { "$ref": "#/components/schemas/OCSMeta" }, - "data": {} + "data": { + "type": "object", + "additionalProperties": { + "type": "object" + } + } + } + } + } + } + } + } + }, + "400": { + "description": "Requesting trial is not possible", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "type": "object", + "required": [ + "message" + ], + "properties": { + "message": { + "type": "string" + } + } + } + } + } + } + } + } + } + }, + "500": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "type": "object", + "required": [ + "message" + ], + "properties": { + "message": { + "type": "string" + } + } + } } } } @@ -17793,13 +17846,13 @@ } } }, - "/ocs/v2.php/apps/spreed/api/{apiVersion}/signaling/welcome/{serverId}": { - "get": { - "operationId": "settings-get-welcome-message", - "summary": "Get the welcome message from a signaling server", - "description": "Only available for logged-in users because guests can not use the apps right now.\nThis endpoint requires admin access", + "/ocs/v2.php/apps/spreed/api/{apiVersion}/hostedsignalingserver/delete": { + "delete": { + "operationId": "hosted_signaling_server-delete-account", + "summary": "Delete the account", + "description": "This endpoint requires admin access", "tags": [ - "settings" + "hosted_signaling_server" ], "security": [ { @@ -17817,20 +17870,9 @@ "schema": { "type": "string", "enum": [ - "v3" + "v1" ], - "default": "v3" - } - }, - { - "name": "serverId", - "in": "path", - "description": "ID of the signaling server", - "required": true, - "schema": { - "type": "integer", - "format": "int64", - "minimum": 0 + "default": "v1" } }, { @@ -17845,8 +17887,8 @@ } ], "responses": { - "200": { - "description": "Welcome message returned", + "204": { + "description": "Account deleted successfully", "content": { "application/json": { "schema": { @@ -17865,12 +17907,7 @@ "meta": { "$ref": "#/components/schemas/OCSMeta" }, - "data": { - "type": "object", - "additionalProperties": { - "type": "object" - } - } + "data": {} } } } @@ -17878,8 +17915,8 @@ } } }, - "404": { - "description": "Signaling server not found", + "400": { + "description": "Deleting account is not possible", "content": { "application/json": { "schema": { @@ -17898,7 +17935,17 @@ "meta": { "$ref": "#/components/schemas/OCSMeta" }, - "data": {} + "data": { + "type": "object", + "required": [ + "message" + ], + "properties": { + "message": { + "type": "string" + } + } + } } } } @@ -17929,13 +17976,10 @@ "data": { "type": "object", "required": [ - "error" + "message" ], "properties": { - "error": { - "type": "string" - }, - "version": { + "message": { "type": "string" } } @@ -17950,13 +17994,13 @@ } } }, - "/ocs/v2.php/apps/spreed/api/{apiVersion}/federation/invitation/{id}": { - "post": { - "operationId": "federation-accept-share", - "summary": "Accept a federation invites", - "description": "🚧 Draft: Still work in progress", + "/ocs/v2.php/apps/spreed/api/{apiVersion}/bridge": { + "delete": { + "operationId": "matterbridge-stop-all-bridges", + "summary": "Stop all bridges", + "description": "This endpoint requires admin access", "tags": [ - "federation" + "matterbridge" ], "security": [ { @@ -17979,17 +18023,6 @@ "default": "v1" } }, - { - "name": "id", - "in": "path", - "description": "ID of the share", - "required": true, - "schema": { - "type": "integer", - "format": "int64", - "minimum": 0 - } - }, { "name": "OCS-APIRequest", "in": "header", @@ -18003,7 +18036,7 @@ ], "responses": { "200": { - "description": "Invite accepted successfully", + "description": "All bridges stopped successfully", "content": { "application/json": { "schema": { @@ -18023,7 +18056,7 @@ "$ref": "#/components/schemas/OCSMeta" }, "data": { - "$ref": "#/components/schemas/Room" + "type": "boolean" } } } @@ -18032,8 +18065,8 @@ } } }, - "400": { - "description": "Invite can not be accepted (maybe it was accepted already)", + "406": { + "description": "Stopping all bridges is not possible", "content": { "application/json": { "schema": { @@ -18069,9 +18102,53 @@ } } } + } + } + } + }, + "/ocs/v2.php/apps/spreed/api/{apiVersion}/bridge/version": { + "get": { + "operationId": "matterbridge-get-matterbridge-version", + "summary": "Get Matterbridge version", + "description": "This endpoint requires admin access", + "tags": [ + "matterbridge" + ], + "security": [ + { + "bearer_auth": [] }, - "410": { - "description": "Remote server could not be reached to notify about the acceptance", + { + "basic_auth": [] + } + ], + "parameters": [ + { + "name": "apiVersion", + "in": "path", + "required": true, + "schema": { + "type": "string", + "enum": [ + "v1" + ], + "default": "v1" + } + }, + { + "name": "OCS-APIRequest", + "in": "header", + "description": "Required to be true for the API request to pass", + "required": true, + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "Bridge version returned", "content": { "application/json": { "schema": { @@ -18093,10 +18170,10 @@ "data": { "type": "object", "required": [ - "error" + "version" ], "properties": { - "error": { + "version": { "type": "string" } } @@ -18108,8 +18185,8 @@ } } }, - "404": { - "description": "Invite can not be found", + "400": { + "description": "Getting bridge version is not possible", "content": { "application/json": { "schema": { @@ -18130,6 +18207,9 @@ }, "data": { "type": "object", + "required": [ + "error" + ], "properties": { "error": { "type": "string" @@ -18144,13 +18224,15 @@ } } } - }, - "delete": { - "operationId": "federation-reject-share", - "summary": "Decline a federation invites", - "description": "🚧 Draft: Still work in progress", + } + }, + "/ocs/v2.php/apps/spreed/api/{apiVersion}/recording/welcome/{serverId}": { + "get": { + "operationId": "settings-get-welcome-message", + "summary": "Get the welcome message of a recording server", + "description": "This endpoint requires admin access", "tags": [ - "federation" + "settings" ], "security": [ { @@ -18174,9 +18256,9 @@ } }, { - "name": "id", + "name": "serverId", "in": "path", - "description": "ID of the share", + "description": "ID of the server", "required": true, "schema": { "type": "integer", @@ -18197,7 +18279,7 @@ ], "responses": { "200": { - "description": "Invite declined successfully", + "description": "Welcome message returned", "content": { "application/json": { "schema": { @@ -18216,7 +18298,18 @@ "meta": { "$ref": "#/components/schemas/OCSMeta" }, - "data": {} + "data": { + "type": "object", + "required": [ + "version" + ], + "properties": { + "version": { + "type": "number", + "format": "float" + } + } + } } } } @@ -18225,7 +18318,35 @@ } }, "404": { - "description": "Invite can not be found", + "description": "Recording server not found or not configured", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": {} + } + } + } + } + } + } + }, + "500": { + "description": "", "content": { "application/json": { "schema": { @@ -18246,6 +18367,9 @@ }, "data": { "type": "object", + "required": [ + "error" + ], "properties": { "error": { "type": "string" @@ -18262,13 +18386,13 @@ } } }, - "/ocs/v2.php/apps/spreed/api/{apiVersion}/federation/invitation": { - "get": { - "operationId": "federation-get-shares", - "summary": "Get a list of federation invites", - "description": "🚧 Draft: Still work in progress", + "/ocs/v2.php/apps/spreed/api/{apiVersion}/settings/sip": { + "post": { + "operationId": "settings-setsip-settings", + "summary": "Update SIP bridge settings", + "description": "This endpoint requires admin access", "tags": [ - "federation" + "settings" ], "security": [ { @@ -18279,6 +18403,36 @@ } ], "parameters": [ + { + "name": "sipGroups[]", + "in": "query", + "description": "New SIP groups", + "schema": { + "type": "array", + "default": [], + "items": { + "type": "string" + } + } + }, + { + "name": "dialInInfo", + "in": "query", + "description": "New dial info", + "schema": { + "type": "string", + "default": "" + } + }, + { + "name": "sharedSecret", + "in": "query", + "description": "New shared secret", + "schema": { + "type": "string", + "default": "" + } + }, { "name": "apiVersion", "in": "path", @@ -18304,7 +18458,7 @@ ], "responses": { "200": { - "description": "Get list of received federation invites successfully", + "description": "Successfully set new SIP settings", "content": { "application/json": { "schema": { @@ -18323,12 +18477,7 @@ "meta": { "$ref": "#/components/schemas/OCSMeta" }, - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/FederationInvite" - } - } + "data": {} } } } @@ -18339,15 +18488,15 @@ } } }, - "/ocs/v2.php/apps/spreed/api/{apiVersion}/room/{token}/federation/active": { - "post": { - "operationId": "room-join-federated-room", - "summary": "Fake join a room on the host server to verify the federated user is still part of it", + "/ocs/v2.php/apps/spreed/api/{apiVersion}/signaling/welcome/{serverId}": { + "get": { + "operationId": "settings-get-welcome-message", + "summary": "Get the welcome message from a signaling server", + "description": "Only available for logged-in users because guests can not use the apps right now.\nThis endpoint requires admin access", "tags": [ - "room" + "settings" ], "security": [ - {}, { "bearer_auth": [] }, @@ -18363,19 +18512,20 @@ "schema": { "type": "string", "enum": [ - "v4" + "v3" ], - "default": "v4" + "default": "v3" } }, { - "name": "token", + "name": "serverId", "in": "path", - "description": "Token of the room", + "description": "ID of the signaling server", "required": true, "schema": { - "type": "string", - "pattern": "^[a-z0-9]{4,30}$" + "type": "integer", + "format": "int64", + "minimum": 0 } }, { @@ -18391,14 +18541,40 @@ ], "responses": { "200": { - "description": "Federated user is still part of the room", - "headers": { - "X-Nextcloud-Talk-Hash": { + "description": "Welcome message returned", + "content": { + "application/json": { "schema": { - "type": "string" + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "type": "object", + "additionalProperties": { + "type": "object" + } + } + } + } + } } } - }, + } + }, + "404": { + "description": "Signaling server not found", "content": { "application/json": { "schema": { @@ -18425,8 +18601,8 @@ } } }, - "404": { - "description": "Room not found", + "500": { + "description": "", "content": { "application/json": { "schema": { @@ -18446,7 +18622,18 @@ "$ref": "#/components/schemas/OCSMeta" }, "data": { - "nullable": true + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "string" + }, + "version": { + "type": "string" + } + } } } } diff --git a/src/types/openapi/openapi-federation.ts b/src/types/openapi/openapi-federation.ts index 9995752ed30..216e7caea18 100644 --- a/src/types/openapi/openapi-federation.ts +++ b/src/types/openapi/openapi-federation.ts @@ -10,6 +10,14 @@ type XOR = (T | U) extends object ? (Without & U) | (Without & type OneOf = T extends [infer Only] ? Only : T extends [infer A, infer B, ...infer Rest] ? OneOf<[XOR, ...Rest]> : never; export type paths = { + "/ocs/v2.php/apps/spreed/api/{apiVersion}/proxy/{token}/user-avatar/{size}": { + /** Get the avatar of a cloudId user */ + get: operations["avatar-get-user-proxy-avatar"]; + }; + "/ocs/v2.php/apps/spreed/api/{apiVersion}/proxy/{token}/user-avatar/{size}/dark": { + /** Get the dark mode avatar of a cloudId user */ + get: operations["avatar-get-user-proxy-avatar-dark"]; + }; "/ocs/v2.php/apps/spreed/api/{apiVersion}/federation/invitation/{id}": { /** * Accept a federation invites @@ -266,6 +274,62 @@ export type external = Record; export type operations = { + /** Get the avatar of a cloudId user */ + "avatar-get-user-proxy-avatar": { + parameters: { + query: { + /** @description Federation CloudID to get the avatar for */ + cloudId: string; + /** @description Theme used for background */ + darkTheme?: 0 | 1; + }; + header: { + /** @description Required to be true for the API request to pass */ + "OCS-APIRequest": boolean; + }; + path: { + apiVersion: "v1"; + token: string; + /** @description Avatar size */ + size: 64 | 512; + }; + }; + responses: { + /** @description User avatar returned */ + 200: { + content: { + "*/*": string; + }; + }; + }; + }; + /** Get the dark mode avatar of a cloudId user */ + "avatar-get-user-proxy-avatar-dark": { + parameters: { + query: { + /** @description Federation CloudID to get the avatar for */ + cloudId: string; + }; + header: { + /** @description Required to be true for the API request to pass */ + "OCS-APIRequest": boolean; + }; + path: { + apiVersion: "v1"; + token: string; + /** @description Avatar size */ + size: 64 | 512; + }; + }; + responses: { + /** @description User avatar returned */ + 200: { + content: { + "*/*": string; + }; + }; + }; + }; /** * Accept a federation invites * @description 🚧 Draft: Still work in progress diff --git a/src/types/openapi/openapi-full.ts b/src/types/openapi/openapi-full.ts index ca65decb1bb..d0ff3c7ac3f 100644 --- a/src/types/openapi/openapi-full.ts +++ b/src/types/openapi/openapi-full.ts @@ -372,6 +372,37 @@ export type paths = { /** Delete your avatar as a user */ delete: operations["user_avatar-delete-avatar"]; }; + "/ocs/v2.php/apps/spreed/api/{apiVersion}/proxy/{token}/user-avatar/{size}": { + /** Get the avatar of a cloudId user */ + get: operations["avatar-get-user-proxy-avatar"]; + }; + "/ocs/v2.php/apps/spreed/api/{apiVersion}/proxy/{token}/user-avatar/{size}/dark": { + /** Get the dark mode avatar of a cloudId user */ + get: operations["avatar-get-user-proxy-avatar-dark"]; + }; + "/ocs/v2.php/apps/spreed/api/{apiVersion}/federation/invitation/{id}": { + /** + * Accept a federation invites + * @description 🚧 Draft: Still work in progress + */ + post: operations["federation-accept-share"]; + /** + * Decline a federation invites + * @description 🚧 Draft: Still work in progress + */ + delete: operations["federation-reject-share"]; + }; + "/ocs/v2.php/apps/spreed/api/{apiVersion}/federation/invitation": { + /** + * Get a list of federation invites + * @description 🚧 Draft: Still work in progress + */ + get: operations["federation-get-shares"]; + }; + "/ocs/v2.php/apps/spreed/api/{apiVersion}/room/{token}/federation/active": { + /** Fake join a room on the host server to verify the federated user is still part of it */ + post: operations["room-join-federated-room"]; + }; "/ocs/v2.php/apps/spreed/api/{apiVersion}/bot/{token}/message": { /** * Sends a new chat message to the given room @@ -449,29 +480,6 @@ export type paths = { */ get: operations["settings-get-welcome-message"]; }; - "/ocs/v2.php/apps/spreed/api/{apiVersion}/federation/invitation/{id}": { - /** - * Accept a federation invites - * @description 🚧 Draft: Still work in progress - */ - post: operations["federation-accept-share"]; - /** - * Decline a federation invites - * @description 🚧 Draft: Still work in progress - */ - delete: operations["federation-reject-share"]; - }; - "/ocs/v2.php/apps/spreed/api/{apiVersion}/federation/invitation": { - /** - * Get a list of federation invites - * @description 🚧 Draft: Still work in progress - */ - get: operations["federation-get-shares"]; - }; - "/ocs/v2.php/apps/spreed/api/{apiVersion}/room/{token}/federation/active": { - /** Fake join a room on the host server to verify the federated user is still part of it */ - post: operations["room-join-federated-room"]; - }; "/ocs/v2.php/apps/spreed/api/{apiVersion}/recording/backend": { /** Update the recording status as a backend */ post: operations["recording-backend"]; @@ -5506,6 +5514,243 @@ export type operations = { }; }; }; + /** Get the avatar of a cloudId user */ + "avatar-get-user-proxy-avatar": { + parameters: { + query: { + /** @description Federation CloudID to get the avatar for */ + cloudId: string; + /** @description Theme used for background */ + darkTheme?: 0 | 1; + }; + header: { + /** @description Required to be true for the API request to pass */ + "OCS-APIRequest": boolean; + }; + path: { + apiVersion: "v1"; + token: string; + /** @description Avatar size */ + size: 64 | 512; + }; + }; + responses: { + /** @description User avatar returned */ + 200: { + content: { + "*/*": string; + }; + }; + }; + }; + /** Get the dark mode avatar of a cloudId user */ + "avatar-get-user-proxy-avatar-dark": { + parameters: { + query: { + /** @description Federation CloudID to get the avatar for */ + cloudId: string; + }; + header: { + /** @description Required to be true for the API request to pass */ + "OCS-APIRequest": boolean; + }; + path: { + apiVersion: "v1"; + token: string; + /** @description Avatar size */ + size: 64 | 512; + }; + }; + responses: { + /** @description User avatar returned */ + 200: { + content: { + "*/*": string; + }; + }; + }; + }; + /** + * Accept a federation invites + * @description 🚧 Draft: Still work in progress + */ + "federation-accept-share": { + parameters: { + header: { + /** @description Required to be true for the API request to pass */ + "OCS-APIRequest": boolean; + }; + path: { + apiVersion: "v1"; + /** @description ID of the share */ + id: number; + }; + }; + responses: { + /** @description Invite accepted successfully */ + 200: { + content: { + "application/json": { + ocs: { + meta: components["schemas"]["OCSMeta"]; + data: components["schemas"]["Room"]; + }; + }; + }; + }; + /** @description Invite can not be accepted (maybe it was accepted already) */ + 400: { + content: { + "application/json": { + ocs: { + meta: components["schemas"]["OCSMeta"]; + data: { + error: string; + }; + }; + }; + }; + }; + /** @description Invite can not be found */ + 404: { + content: { + "application/json": { + ocs: { + meta: components["schemas"]["OCSMeta"]; + data: { + error?: string; + }; + }; + }; + }; + }; + /** @description Remote server could not be reached to notify about the acceptance */ + 410: { + content: { + "application/json": { + ocs: { + meta: components["schemas"]["OCSMeta"]; + data: { + error: string; + }; + }; + }; + }; + }; + }; + }; + /** + * Decline a federation invites + * @description 🚧 Draft: Still work in progress + */ + "federation-reject-share": { + parameters: { + header: { + /** @description Required to be true for the API request to pass */ + "OCS-APIRequest": boolean; + }; + path: { + apiVersion: "v1"; + /** @description ID of the share */ + id: number; + }; + }; + responses: { + /** @description Invite declined successfully */ + 200: { + content: { + "application/json": { + ocs: { + meta: components["schemas"]["OCSMeta"]; + data: unknown; + }; + }; + }; + }; + /** @description Invite can not be found */ + 404: { + content: { + "application/json": { + ocs: { + meta: components["schemas"]["OCSMeta"]; + data: { + error?: string; + }; + }; + }; + }; + }; + }; + }; + /** + * Get a list of federation invites + * @description 🚧 Draft: Still work in progress + */ + "federation-get-shares": { + parameters: { + header: { + /** @description Required to be true for the API request to pass */ + "OCS-APIRequest": boolean; + }; + path: { + apiVersion: "v1"; + }; + }; + responses: { + /** @description Get list of received federation invites successfully */ + 200: { + content: { + "application/json": { + ocs: { + meta: components["schemas"]["OCSMeta"]; + data: components["schemas"]["FederationInvite"][]; + }; + }; + }; + }; + }; + }; + /** Fake join a room on the host server to verify the federated user is still part of it */ + "room-join-federated-room": { + parameters: { + header: { + /** @description Required to be true for the API request to pass */ + "OCS-APIRequest": boolean; + }; + path: { + apiVersion: "v4"; + /** @description Token of the room */ + token: string; + }; + }; + responses: { + /** @description Federated user is still part of the room */ + 200: { + headers: { + "X-Nextcloud-Talk-Hash"?: string; + }; + content: { + "application/json": { + ocs: { + meta: components["schemas"]["OCSMeta"]; + data: unknown; + }; + }; + }; + }; + /** @description Room not found */ + 404: { + content: { + "application/json": { + ocs: { + meta: components["schemas"]["OCSMeta"]; + data: unknown; + }; + }; + }; + }; + }; + }; /** * Sends a new chat message to the given room * @description The author and timestamp are automatically set to the current user/guest and time. @@ -6095,187 +6340,6 @@ export type operations = { }; }; }; - /** - * Accept a federation invites - * @description 🚧 Draft: Still work in progress - */ - "federation-accept-share": { - parameters: { - header: { - /** @description Required to be true for the API request to pass */ - "OCS-APIRequest": boolean; - }; - path: { - apiVersion: "v1"; - /** @description ID of the share */ - id: number; - }; - }; - responses: { - /** @description Invite accepted successfully */ - 200: { - content: { - "application/json": { - ocs: { - meta: components["schemas"]["OCSMeta"]; - data: components["schemas"]["Room"]; - }; - }; - }; - }; - /** @description Invite can not be accepted (maybe it was accepted already) */ - 400: { - content: { - "application/json": { - ocs: { - meta: components["schemas"]["OCSMeta"]; - data: { - error: string; - }; - }; - }; - }; - }; - /** @description Invite can not be found */ - 404: { - content: { - "application/json": { - ocs: { - meta: components["schemas"]["OCSMeta"]; - data: { - error?: string; - }; - }; - }; - }; - }; - /** @description Remote server could not be reached to notify about the acceptance */ - 410: { - content: { - "application/json": { - ocs: { - meta: components["schemas"]["OCSMeta"]; - data: { - error: string; - }; - }; - }; - }; - }; - }; - }; - /** - * Decline a federation invites - * @description 🚧 Draft: Still work in progress - */ - "federation-reject-share": { - parameters: { - header: { - /** @description Required to be true for the API request to pass */ - "OCS-APIRequest": boolean; - }; - path: { - apiVersion: "v1"; - /** @description ID of the share */ - id: number; - }; - }; - responses: { - /** @description Invite declined successfully */ - 200: { - content: { - "application/json": { - ocs: { - meta: components["schemas"]["OCSMeta"]; - data: unknown; - }; - }; - }; - }; - /** @description Invite can not be found */ - 404: { - content: { - "application/json": { - ocs: { - meta: components["schemas"]["OCSMeta"]; - data: { - error?: string; - }; - }; - }; - }; - }; - }; - }; - /** - * Get a list of federation invites - * @description 🚧 Draft: Still work in progress - */ - "federation-get-shares": { - parameters: { - header: { - /** @description Required to be true for the API request to pass */ - "OCS-APIRequest": boolean; - }; - path: { - apiVersion: "v1"; - }; - }; - responses: { - /** @description Get list of received federation invites successfully */ - 200: { - content: { - "application/json": { - ocs: { - meta: components["schemas"]["OCSMeta"]; - data: components["schemas"]["FederationInvite"][]; - }; - }; - }; - }; - }; - }; - /** Fake join a room on the host server to verify the federated user is still part of it */ - "room-join-federated-room": { - parameters: { - header: { - /** @description Required to be true for the API request to pass */ - "OCS-APIRequest": boolean; - }; - path: { - apiVersion: "v4"; - /** @description Token of the room */ - token: string; - }; - }; - responses: { - /** @description Federated user is still part of the room */ - 200: { - headers: { - "X-Nextcloud-Talk-Hash"?: string; - }; - content: { - "application/json": { - ocs: { - meta: components["schemas"]["OCSMeta"]; - data: unknown; - }; - }; - }; - }; - /** @description Room not found */ - 404: { - content: { - "application/json": { - ocs: { - meta: components["schemas"]["OCSMeta"]; - data: unknown; - }; - }; - }; - }; - }; - }; /** Update the recording status as a backend */ "recording-backend": { parameters: {