diff --git a/scripts/data/swagger-spec.json b/scripts/data/swagger-spec.json index 65e6554c..f9cef501 100644 --- a/scripts/data/swagger-spec.json +++ b/scripts/data/swagger-spec.json @@ -10,28 +10,42 @@ "requestBody": { "required": true, "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/CreateArticleDto" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateArticleDto" + } + } } }, "responses": { "201": { "description": "", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ArticleEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArticleEntity" + } + } } }, "422": { "description": "Validation Error - One or more fields did not pass validation", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorValidationEntity" } + "schema": { + "$ref": "#/components/schemas/ErrorValidationEntity" + } } } }, "500": { "description": "Internal server error", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorServerEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorServerEntity" + } + } } } }, @@ -42,8 +56,22 @@ "summary": "Find All Articles", "description": "Retrieves a list of all articles with pagination support.", "parameters": [ - { "name": "page", "required": true, "in": "query", "schema": { "type": "number" } }, - { "name": "pageSize", "required": true, "in": "query", "schema": { "type": "number" } } + { + "name": "page", + "required": true, + "in": "query", + "schema": { + "type": "number" + } + }, + { + "name": "pageSize", + "required": true, + "in": "query", + "schema": { + "type": "number" + } + } ], "responses": { "200": { @@ -52,7 +80,9 @@ "application/json": { "schema": { "type": "array", - "items": { "$ref": "#/components/schemas/ArticleEntity" } + "items": { + "$ref": "#/components/schemas/ArticleEntity" + } } } } @@ -60,7 +90,11 @@ "500": { "description": "Internal server error", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorServerEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorServerEntity" + } + } } } }, @@ -73,8 +107,22 @@ "summary": "Find Draft Articles", "description": "Retrieves a list of draft articles with pagination support.", "parameters": [ - { "name": "page", "required": true, "in": "query", "schema": { "type": "number" } }, - { "name": "pageSize", "required": true, "in": "query", "schema": { "type": "number" } } + { + "name": "page", + "required": true, + "in": "query", + "schema": { + "type": "number" + } + }, + { + "name": "pageSize", + "required": true, + "in": "query", + "schema": { + "type": "number" + } + } ], "responses": { "200": { @@ -83,7 +131,9 @@ "application/json": { "schema": { "type": "array", - "items": { "$ref": "#/components/schemas/ArticleEntity" } + "items": { + "$ref": "#/components/schemas/ArticleEntity" + } } } } @@ -91,7 +141,11 @@ "500": { "description": "Internal server error", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorServerEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorServerEntity" + } + } } } }, @@ -104,19 +158,34 @@ "summary": "Find Article by ID", "description": "Retrieves an article by its unique identifier.", "parameters": [ - { "name": "id", "required": true, "in": "path", "schema": { "type": "number" } } + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + } ], "responses": { "200": { "description": "", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ArticleEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArticleEntity" + } + } } }, "500": { "description": "Internal server error", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorServerEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorServerEntity" + } + } } } }, @@ -127,33 +196,54 @@ "summary": "Update Article", "description": "Updates an article with the provided details.", "parameters": [ - { "name": "id", "required": true, "in": "path", "schema": { "type": "number" } } + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + } ], "requestBody": { "required": true, "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/UpdateArticleDto" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateArticleDto" + } + } } }, "responses": { "201": { "description": "", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ArticleEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArticleEntity" + } + } } }, "422": { "description": "Validation Error - One or more fields did not pass validation", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorValidationEntity" } + "schema": { + "$ref": "#/components/schemas/ErrorValidationEntity" + } } } }, "500": { "description": "Internal server error", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorServerEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorServerEntity" + } + } } } }, @@ -164,14 +254,27 @@ "summary": "Remove Article", "description": "Deletes an article by its unique identifier.", "parameters": [ - { "name": "id", "required": true, "in": "path", "schema": { "type": "number" } } + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + } ], "responses": { - "204": { "description": "" }, + "204": { + "description": "" + }, "500": { "description": "Internal server error", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorServerEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorServerEntity" + } + } } } }, @@ -187,55 +290,93 @@ "requestBody": { "required": true, "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/CreateUserDto" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateUserDto" + } + } } }, "responses": { "201": { "description": "", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/UserEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserEntity" + } + } } }, "401": { "description": "Unauthorized", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorUnauthorizedEntity" } + "schema": { + "$ref": "#/components/schemas/ErrorUnauthorizedEntity" + } } } }, "403": { "description": "Forbidden", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorEntity" + } + } } }, "422": { "description": "Validation Error - One or more fields did not pass validation", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorValidationEntity" } + "schema": { + "$ref": "#/components/schemas/ErrorValidationEntity" + } } } }, "500": { "description": "Internal server error", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorServerEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorServerEntity" + } + } } } }, "tags": ["users"], - "security": [{ "bearer": [] }] + "security": [ + { + "bearer": [] + } + ] }, "get": { "operationId": "UsersController_findAll", "summary": "Find All Users", "description": "Retrieves a list of all users.", "parameters": [ - { "name": "page", "required": true, "in": "query", "schema": { "type": "number" } }, - { "name": "pageSize", "required": true, "in": "query", "schema": { "type": "number" } } + { + "name": "page", + "required": true, + "in": "query", + "schema": { + "type": "number" + } + }, + { + "name": "pageSize", + "required": true, + "in": "query", + "schema": { + "type": "number" + } + } ], "responses": { "200": { @@ -244,7 +385,9 @@ "application/json": { "schema": { "type": "array", - "items": { "$ref": "#/components/schemas/UserEntity" } + "items": { + "$ref": "#/components/schemas/UserEntity" + } } } } @@ -253,25 +396,39 @@ "description": "Unauthorized", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorUnauthorizedEntity" } + "schema": { + "$ref": "#/components/schemas/ErrorUnauthorizedEntity" + } } } }, "403": { "description": "Forbidden", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorEntity" + } + } } }, "500": { "description": "Internal server error", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorServerEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorServerEntity" + } + } } } }, "tags": ["users"], - "security": [{ "bearer": [] }] + "security": [ + { + "bearer": [] + } + ] } }, "/api/v1/users/{id}": { @@ -280,120 +437,199 @@ "summary": "Find User by ID", "description": "Retrieves a user by its ID.", "parameters": [ - { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } ], "responses": { "200": { "description": "", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/UserEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserEntity" + } + } } }, "401": { "description": "Unauthorized", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorUnauthorizedEntity" } + "schema": { + "$ref": "#/components/schemas/ErrorUnauthorizedEntity" + } } } }, "403": { "description": "Forbidden", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorEntity" + } + } } }, "500": { "description": "Internal server error", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorServerEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorServerEntity" + } + } } } }, "tags": ["users"], - "security": [{ "bearer": [] }] + "security": [ + { + "bearer": [] + } + ] }, "patch": { "operationId": "UsersController_update", "summary": "Update User", "description": "Updates a user with the provided details.", "parameters": [ - { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } ], "requestBody": { "required": true, "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/UpdateUserDto" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateUserDto" + } + } } }, "responses": { "201": { "description": "", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/UserEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserEntity" + } + } } }, "401": { "description": "Unauthorized", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorUnauthorizedEntity" } + "schema": { + "$ref": "#/components/schemas/ErrorUnauthorizedEntity" + } } } }, "403": { "description": "Forbidden", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorEntity" + } + } } }, "500": { "description": "Internal server error", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorServerEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorServerEntity" + } + } } } }, "tags": ["users"], - "security": [{ "bearer": [] }] + "security": [ + { + "bearer": [] + } + ] }, "delete": { "operationId": "UsersController_remove", "summary": "Remove User", "description": "Deletes a user by their unique identifier.", "parameters": [ - { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } ], "responses": { "200": { "description": "", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/UserEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserEntity" + } + } } }, "401": { "description": "Unauthorized", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorUnauthorizedEntity" } + "schema": { + "$ref": "#/components/schemas/ErrorUnauthorizedEntity" + } } } }, "403": { "description": "Forbidden", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorEntity" + } + } } }, "500": { "description": "Internal server error", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorServerEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorServerEntity" + } + } } } }, "tags": ["users"], - "security": [{ "bearer": [] }] + "security": [ + { + "bearer": [] + } + ] } }, "/api/v1/auth/email/login": { @@ -405,35 +641,51 @@ "requestBody": { "required": true, "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/AuthEmailLoginDto" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthEmailLoginDto" + } + } } }, "responses": { "201": { "description": "", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/AuthEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthEntity" + } + } } }, "401": { "description": "Unauthorized", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorUnauthorizedEntity" } + "schema": { + "$ref": "#/components/schemas/ErrorUnauthorizedEntity" + } } } }, "403": { "description": "Please activate your account before proceeding", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorEntity" + } + } } }, "422": { "description": "Validation Error - One or more fields did not pass validation", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorValidationEntity" } + "schema": { + "$ref": "#/components/schemas/ErrorValidationEntity" + } } } }, @@ -441,14 +693,20 @@ "description": "ThrottlerException: Too Many Requests", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorTooManyRequestsEntity" } + "schema": { + "$ref": "#/components/schemas/ErrorTooManyRequestsEntity" + } } } }, "500": { "description": "Internal server error", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorServerEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorServerEntity" + } + } } } }, @@ -466,14 +724,18 @@ "in": "header", "description": "Specifies the preferred language for API responses. Supported values are: en (English), de (German), pl (Polish)", "required": false, - "schema": { "type": "string" } + "schema": { + "type": "string" + } } ], "requestBody": { "required": true, "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/AuthRegisterLoginDto" } + "schema": { + "$ref": "#/components/schemas/AuthRegisterLoginDto" + } } } }, @@ -481,15 +743,23 @@ "201": { "description": "", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/UserEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserEntity" + } + } } }, - "400": { "description": "Bad Request - Missing or invalid registration details" }, + "400": { + "description": "Bad Request - Missing or invalid registration details" + }, "422": { "description": "Validation Error - One or more fields did not pass validation", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorValidationEntity" } + "schema": { + "$ref": "#/components/schemas/ErrorValidationEntity" + } } } }, @@ -497,14 +767,20 @@ "description": "ThrottlerException: Too Many Requests", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorTooManyRequestsEntity" } + "schema": { + "$ref": "#/components/schemas/ErrorTooManyRequestsEntity" + } } } }, "500": { "description": "Internal server error", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorServerEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorServerEntity" + } + } } } }, @@ -522,22 +798,32 @@ "in": "header", "description": "Specifies the preferred language for API responses. Supported values are: en (English), de (German), pl (Polish)", "required": false, - "schema": { "type": "string" } + "schema": { + "type": "string" + } } ], "requestBody": { "required": true, "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/AuthConfirmEmailDto" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthConfirmEmailDto" + } + } } }, "responses": { - "400": { "description": "Bad Request - Invalid or expired hash code" }, + "400": { + "description": "Bad Request - Invalid or expired hash code" + }, "422": { "description": "Validation Error - One or more fields did not pass validation", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorValidationEntity" } + "schema": { + "$ref": "#/components/schemas/ErrorValidationEntity" + } } } }, @@ -545,14 +831,20 @@ "description": "ThrottlerException: Too Many Requests", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorTooManyRequestsEntity" } + "schema": { + "$ref": "#/components/schemas/ErrorTooManyRequestsEntity" + } } } }, "500": { "description": "Internal server error", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorServerEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorServerEntity" + } + } } } }, @@ -570,14 +862,18 @@ "in": "header", "description": "Specifies the preferred language for API responses. Supported values are: en (English), de (German), pl (Polish)", "required": false, - "schema": { "type": "string" } + "schema": { + "type": "string" + } } ], "requestBody": { "required": true, "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/AuthResendVerificationEmailDto" } + "schema": { + "$ref": "#/components/schemas/AuthResendVerificationEmailDto" + } } } }, @@ -585,14 +881,20 @@ "400": { "description": "Bad Request - Invalid email format", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorEntity" + } + } } }, "422": { "description": "Validation Error - One or more fields did not pass validation", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorValidationEntity" } + "schema": { + "$ref": "#/components/schemas/ErrorValidationEntity" + } } } }, @@ -600,14 +902,20 @@ "description": "ThrottlerException: Too Many Requests", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorTooManyRequestsEntity" } + "schema": { + "$ref": "#/components/schemas/ErrorTooManyRequestsEntity" + } } } }, "500": { "description": "Internal server error", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorServerEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorServerEntity" + } + } } } }, @@ -625,14 +933,18 @@ "in": "header", "description": "Specifies the preferred language for API responses. Supported values are: en (English), de (German), pl (Polish)", "required": false, - "schema": { "type": "string" } + "schema": { + "type": "string" + } } ], "requestBody": { "required": true, "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/AuthForgotPasswordDto" } + "schema": { + "$ref": "#/components/schemas/AuthForgotPasswordDto" + } } } }, @@ -640,14 +952,20 @@ "400": { "description": "Bad Request - Invalid email format", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorEntity" + } + } } }, "422": { "description": "Validation Error - One or more fields did not pass validation", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorValidationEntity" } + "schema": { + "$ref": "#/components/schemas/ErrorValidationEntity" + } } } }, @@ -655,14 +973,20 @@ "description": "ThrottlerException: Too Many Requests", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorTooManyRequestsEntity" } + "schema": { + "$ref": "#/components/schemas/ErrorTooManyRequestsEntity" + } } } }, "500": { "description": "Internal server error", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorServerEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorServerEntity" + } + } } } }, @@ -679,7 +1003,9 @@ "required": true, "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/AuthResetPasswordDto" } + "schema": { + "$ref": "#/components/schemas/AuthResetPasswordDto" + } } } }, @@ -687,14 +1013,20 @@ "400": { "description": "Bad Request - Invalid or expired hash code", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorEntity" + } + } } }, "422": { "description": "Validation Error - One or more fields did not pass validation", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorValidationEntity" } + "schema": { + "$ref": "#/components/schemas/ErrorValidationEntity" + } } } }, @@ -702,14 +1034,20 @@ "description": "ThrottlerException: Too Many Requests", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorTooManyRequestsEntity" } + "schema": { + "$ref": "#/components/schemas/ErrorTooManyRequestsEntity" + } } } }, "500": { "description": "Internal server error", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorServerEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorServerEntity" + } + } } } }, @@ -727,27 +1065,39 @@ "in": "header", "description": "Specifies the preferred language for API responses. Supported values are: en (English), de (German), pl (Polish)", "required": false, - "schema": { "type": "string" } + "schema": { + "type": "string" + } } ], "requestBody": { "required": true, "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/AuthEmailChangeDto" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthEmailChangeDto" + } + } } }, "responses": { "400": { "description": "Bad Request - Invalid or expired hash code", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorEntity" + } + } } }, "422": { "description": "Validation Error - One or more fields did not pass validation", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorValidationEntity" } + "schema": { + "$ref": "#/components/schemas/ErrorValidationEntity" + } } } }, @@ -755,19 +1105,29 @@ "description": "ThrottlerException: Too Many Requests", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorTooManyRequestsEntity" } + "schema": { + "$ref": "#/components/schemas/ErrorTooManyRequestsEntity" + } } } }, "500": { "description": "Internal server error", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorServerEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorServerEntity" + } + } } } }, "tags": ["auth"], - "security": [{ "bearer": [] }] + "security": [ + { + "bearer": [] + } + ] } }, "/api/v1/auth/email/change-confirm": { @@ -781,14 +1141,18 @@ "in": "header", "description": "Specifies the preferred language for API responses. Supported values are: en (English), de (German), pl (Polish)", "required": false, - "schema": { "type": "string" } + "schema": { + "type": "string" + } } ], "requestBody": { "required": true, "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/AuthConfirmEmailChangeDto" } + "schema": { + "$ref": "#/components/schemas/AuthConfirmEmailChangeDto" + } } } }, @@ -796,26 +1160,40 @@ "400": { "description": "Bad Request - Invalid or expired hash code", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorEntity" + } + } } }, "429": { "description": "ThrottlerException: Too Many Requests", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorTooManyRequestsEntity" } + "schema": { + "$ref": "#/components/schemas/ErrorTooManyRequestsEntity" + } } } }, "500": { "description": "Internal server error", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorServerEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorServerEntity" + } + } } } }, "tags": ["auth"], - "security": [{ "bearer": [] }] + "security": [ + { + "bearer": [] + } + ] } }, "/api/v1/auth/me": { @@ -828,26 +1206,40 @@ "201": { "description": "", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/UserEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserEntity" + } + } } }, "401": { "description": "Unauthorized", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorUnauthorizedEntity" } + "schema": { + "$ref": "#/components/schemas/ErrorUnauthorizedEntity" + } } } }, "500": { "description": "Internal server error", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorServerEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorServerEntity" + } + } } } }, "tags": ["auth"], - "security": [{ "bearer": [] }] + "security": [ + { + "bearer": [] + } + ] }, "patch": { "operationId": "AuthController_update", @@ -857,7 +1249,11 @@ "requestBody": { "required": true, "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/AuthUpdateDto" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthUpdateDto" + } + } } }, "responses": { @@ -865,26 +1261,42 @@ "description": "Unauthorized", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorUnauthorizedEntity" } + "schema": { + "$ref": "#/components/schemas/ErrorUnauthorizedEntity" + } } } }, - "404": { "description": "User not found" }, + "404": { + "description": "User not found" + }, "422": { "description": "Missing old password / Incorrect old password / Missing password", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorEntity" + } + } } }, "500": { "description": "Internal server error", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorServerEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorServerEntity" + } + } } } }, "tags": ["auth"], - "security": [{ "bearer": [] }] + "security": [ + { + "bearer": [] + } + ] }, "delete": { "operationId": "AuthController_delete", @@ -896,19 +1308,29 @@ "description": "Unauthorized", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorUnauthorizedEntity" } + "schema": { + "$ref": "#/components/schemas/ErrorUnauthorizedEntity" + } } } }, "500": { "description": "Internal server error", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorServerEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorServerEntity" + } + } } } }, "tags": ["auth"], - "security": [{ "bearer": [] }] + "security": [ + { + "bearer": [] + } + ] } }, "/api/v1/auth/refresh": { @@ -921,26 +1343,40 @@ "201": { "description": "", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/RefreshEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/RefreshEntity" + } + } } }, "401": { "description": "Unauthorized", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorUnauthorizedEntity" } + "schema": { + "$ref": "#/components/schemas/ErrorUnauthorizedEntity" + } } } }, "500": { "description": "Internal server error", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorServerEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorServerEntity" + } + } } } }, "tags": ["auth"], - "security": [{ "bearer": [] }] + "security": [ + { + "bearer": [] + } + ] } }, "/api/v1/auth/logout": { @@ -954,19 +1390,29 @@ "description": "Unauthorized", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorUnauthorizedEntity" } + "schema": { + "$ref": "#/components/schemas/ErrorUnauthorizedEntity" + } } } }, "500": { "description": "Internal server error", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorServerEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorServerEntity" + } + } } } }, "tags": ["auth"], - "security": [{ "bearer": [] }] + "security": [ + { + "bearer": [] + } + ] } }, "/api/v1/auth/logout/all": { @@ -980,19 +1426,29 @@ "description": "Unauthorized", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorUnauthorizedEntity" } + "schema": { + "$ref": "#/components/schemas/ErrorUnauthorizedEntity" + } } } }, "500": { "description": "Internal server error", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorServerEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorServerEntity" + } + } } } }, "tags": ["auth"], - "security": [{ "bearer": [] }] + "security": [ + { + "bearer": [] + } + ] } }, "/api/v1/files/upload": { @@ -1008,8 +1464,13 @@ "schema": { "type": "object", "properties": { - "file": { "type": "string", "format": "binary" }, - "category": { "type": "string" } + "file": { + "type": "string", + "format": "binary" + }, + "category": { + "type": "string" + } } } } @@ -1019,14 +1480,26 @@ "201": { "description": "", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/FileEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/FileEntity" + } + } } }, - "422": { "description": "Unprocessable Entity - No file selected or missing user ID." }, - "500": { "description": "Internal server error" } + "422": { + "description": "Unprocessable Entity - No file selected or missing user ID." + }, + "500": { + "description": "Internal server error" + } }, "tags": ["files"], - "security": [{ "bearer": [] }] + "security": [ + { + "bearer": [] + } + ] } }, "/api/v1/files/{fileName}": { @@ -1035,32 +1508,58 @@ "summary": "Download File", "description": "Downloads the specified file.", "parameters": [ - { "name": "fileName", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "fileName", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } ], "responses": { "403": { "description": "Forbidden - You do not have permission to view this file or file does not exist." }, - "500": { "description": "Internal server error" } + "500": { + "description": "Internal server error" + } }, "tags": ["files"], - "security": [{ "bearer": [] }] + "security": [ + { + "bearer": [] + } + ] }, "delete": { "operationId": "FilesController_deleteFile", "summary": "Delete File", "description": "Deletes the specified file.", "parameters": [ - { "name": "fileName", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "fileName", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } ], "responses": { "403": { "description": "Forbidden - You do not have permission to delete this file or file does not exist." }, - "500": { "description": "Internal server error" } + "500": { + "description": "Internal server error" + } }, "tags": ["files"], - "security": [{ "bearer": [] }] + "security": [ + { + "bearer": [] + } + ] } }, "/api/v1/auth/google/login": { @@ -1072,32 +1571,52 @@ "requestBody": { "required": true, "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/AuthGoogleLoginDto" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthGoogleLoginDto" + } + } } }, "responses": { "201": { "description": "Successfully logged in with Google", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/AuthEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthEntity" + } + } } }, "400": { "description": "Bad Request - Error during Google token verification or processing", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorEntity" + } + } } }, "422": { "description": "Unprocessable Entity - Invalid or expired Google token", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorEntity" + } + } } }, "500": { "description": "Internal server error", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorServerEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorServerEntity" + } + } } } }, @@ -1114,7 +1633,9 @@ "required": true, "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/AuthFacebookLoginDto" } + "schema": { + "$ref": "#/components/schemas/AuthFacebookLoginDto" + } } } }, @@ -1122,25 +1643,41 @@ "201": { "description": "Successfully logged in with Facebook", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/AuthEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthEntity" + } + } } }, "400": { "description": "Bad Request - Error during Facebook token verification or processing", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorEntity" + } + } } }, "422": { "description": "Unprocessable Entity - Invalid or expired Facebook token", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorEntity" + } + } } }, "500": { "description": "Internal server error", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorServerEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorServerEntity" + } + } } } }, @@ -1156,32 +1693,52 @@ "requestBody": { "required": true, "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/AuthAppleLoginDto" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthAppleLoginDto" + } + } } }, "responses": { "201": { "description": "Successfully logged in with Apple", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/AuthEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthEntity" + } + } } }, "400": { "description": "Bad Request - Error during Apple token verification or processing", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorEntity" + } + } } }, "422": { "description": "Unprocessable Entity - Invalid or expired Apple token", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorEntity" + } + } } }, "500": { "description": "Internal server error", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorServerEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorServerEntity" + } + } } } }, @@ -1198,16 +1755,26 @@ "200": { "description": "The health check result.", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/HealthEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/HealthEntity" + } + } } }, "500": { "description": "Internal server error", "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEntity" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorEntity" + } + } } }, - "503": { "description": "One or more health checks failed" } + "503": { + "description": "One or more health checks failed" + } }, "tags": ["health"] } @@ -1219,10 +1786,58 @@ "requestBody": { "required": true, "content": { - "application/json": { "schema": { "$ref": "#/components/schemas/CheckUpdateDto" } } + "application/json": { + "schema": { + "$ref": "#/components/schemas/CheckUpdateDto" + } + } + } + }, + "responses": { + "201": { + "description": "Details about application from AppStore or PlayStore based on provided os", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AppVersionStatusEntity" + } + } + } + }, + "400": { + "description": "Bad Request - Missing or invalid app details" + }, + "422": { + "description": "Validation Error - One or more fields did not pass validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorValidationEntity" + } + } + } + }, + "429": { + "description": "ThrottlerException: Too Many Requests", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorTooManyRequestsEntity" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorServerEntity" + } + } + } } }, - "responses": { "201": { "description": "" } }, "tags": ["system"] } } @@ -1240,12 +1855,22 @@ "tags": [], "servers": [], "components": { - "securitySchemes": { "bearer": { "scheme": "bearer", "bearerFormat": "JWT", "type": "http" } }, + "securitySchemes": { + "bearer": { + "scheme": "bearer", + "bearerFormat": "JWT", + "type": "http" + } + }, "schemas": { "ErrorServerEntity": { "type": "object", "properties": { - "error": { "type": "string", "description": "Type of error", "example": "Server Error" }, + "error": { + "type": "string", + "description": "Type of error", + "example": "Server Error" + }, "statusCode": { "type": "number", "description": "HTTP status code indicating the error", @@ -1257,35 +1882,75 @@ "CreateArticleDto": { "type": "object", "properties": { - "title": { "type": "string" }, - "description": { "type": "string" }, - "body": { "type": "string" }, - "published": { "type": "boolean", "default": false } + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "body": { + "type": "string" + }, + "published": { + "type": "boolean", + "default": false + } }, "required": ["title", "body"] }, "AuthorPublicDto": { "type": "object", "properties": { - "email": { "type": "string" }, - "firstName": { "type": "string" }, - "lastName": { "type": "string" } + "email": { + "type": "string" + }, + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + } }, "required": ["email", "firstName", "lastName"] }, "ArticleEntity": { "type": "object", "properties": { - "id": { "type": "number" }, - "title": { "type": "string" }, - "description": { "type": "string", "nullable": true }, - "body": { "type": "string" }, - "published": { "type": "boolean" }, - "createdAt": { "format": "date-time", "type": "string" }, - "updatedAt": { "format": "date-time", "type": "string" }, - "deletedAt": { "format": "date-time", "type": "string" }, - "authorId": { "type": "string", "nullable": true }, - "author": { "$ref": "#/components/schemas/AuthorPublicDto" } + "id": { + "type": "number" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string", + "nullable": true + }, + "body": { + "type": "string" + }, + "published": { + "type": "boolean" + }, + "createdAt": { + "format": "date-time", + "type": "string" + }, + "updatedAt": { + "format": "date-time", + "type": "string" + }, + "deletedAt": { + "format": "date-time", + "type": "string" + }, + "authorId": { + "type": "string", + "nullable": true + }, + "author": { + "$ref": "#/components/schemas/AuthorPublicDto" + } }, "required": [ "id", @@ -1309,8 +1974,12 @@ "errors": { "type": "object", "description": "Object containing field-specific validation errors", - "example": { "email": "Invalid email format." }, - "additionalProperties": { "type": "string" } + "example": { + "email": "Invalid email format." + }, + "additionalProperties": { + "type": "string" + } } }, "required": ["statusCode", "errors"] @@ -1318,16 +1987,29 @@ "UpdateArticleDto": { "type": "object", "properties": { - "title": { "type": "string" }, - "description": { "type": "string" }, - "body": { "type": "string" }, - "published": { "type": "boolean", "default": false } + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "body": { + "type": "string" + }, + "published": { + "type": "boolean", + "default": false + } } }, "ErrorUnauthorizedEntity": { "type": "object", "properties": { - "error": { "type": "string", "description": "Type of error", "example": "Unauthorized" }, + "error": { + "type": "string", + "description": "Type of error", + "example": "Unauthorized" + }, "statusCode": { "type": "number", "description": "HTTP status code indicating the error", @@ -1344,7 +2026,11 @@ "description": "Message describing the error", "example": "An error occurred while processing your request" }, - "error": { "type": "string", "description": "Type of error", "example": "Forbidden" }, + "error": { + "type": "string", + "description": "Type of error", + "example": "Forbidden" + }, "statusCode": { "type": "number", "description": "HTTP status code indicating the error", @@ -1405,11 +2091,19 @@ }, "role": { "description": "The role assigned to the user.", - "allOf": [{ "$ref": "#/components/schemas/RoleDto" }] + "allOf": [ + { + "$ref": "#/components/schemas/RoleDto" + } + ] }, "status": { "description": "The status of the user account.", - "allOf": [{ "$ref": "#/components/schemas/StatusDto" }] + "allOf": [ + { + "$ref": "#/components/schemas/StatusDto" + } + ] } }, "required": ["email", "password", "firstName", "lastName", "locale", "role", "status"] @@ -1496,7 +2190,10 @@ "UserEntity": { "type": "object", "properties": { - "id": { "type": "string", "example": "a1b2c3d4-e5f6-7890-g1h2-i3j4k5l6m7n8" }, + "id": { + "type": "string", + "example": "a1b2c3d4-e5f6-7890-g1h2-i3j4k5l6m7n8" + }, "createdAt": { "format": "date-time", "type": "string", @@ -1507,22 +2204,60 @@ "type": "string", "example": "2022-02-01T00:00:00.000Z" }, - "deletedAt": { "format": "date-time", "type": "string", "example": null }, - "email": { "type": "string", "example": "user@example.com" }, - "firstName": { "type": "string", "example": "John" }, - "lastName": { "type": "string", "example": "Doe" }, - "provider": { "type": "string", "example": "google" }, - "socialId": { "type": "string", "example": "102209777480561953757" }, - "locale": { "type": "string", "example": "en-US" }, + "deletedAt": { + "format": "date-time", + "type": "string", + "example": null + }, + "email": { + "type": "string", + "example": "user@example.com" + }, + "firstName": { + "type": "string", + "example": "John" + }, + "lastName": { + "type": "string", + "example": "Doe" + }, + "provider": { + "type": "string", + "example": "google" + }, + "socialId": { + "type": "string", + "example": "102209777480561953757" + }, + "locale": { + "type": "string", + "example": "en-US" + }, "role": { - "example": { "id": 2, "name": "USER" }, - "allOf": [{ "$ref": "#/components/schemas/Role" }] + "example": { + "id": 2, + "name": "USER" + }, + "allOf": [ + { + "$ref": "#/components/schemas/Role" + } + ] }, "status": { - "example": { "id": 1, "name": "ACTIVE" }, - "allOf": [{ "$ref": "#/components/schemas/Status" }] + "example": { + "id": 1, + "name": "ACTIVE" + }, + "allOf": [ + { + "$ref": "#/components/schemas/Status" + } + ] }, - "consent": { "$ref": "#/components/schemas/LastConsentEntity" } + "consent": { + "$ref": "#/components/schemas/LastConsentEntity" + } }, "required": [ "id", @@ -1569,11 +2304,19 @@ }, "role": { "description": "The updated role assigned to the user.", - "allOf": [{ "$ref": "#/components/schemas/RoleDto" }] + "allOf": [ + { + "$ref": "#/components/schemas/RoleDto" + } + ] }, "status": { "description": "The updated status of the user account.", - "allOf": [{ "$ref": "#/components/schemas/StatusDto" }] + "allOf": [ + { + "$ref": "#/components/schemas/StatusDto" + } + ] }, "provider": { "type": "string", @@ -1622,7 +2365,11 @@ }, "user": { "description": "The user entity associated with the authentication.", - "allOf": [{ "$ref": "#/components/schemas/UserEntity" }] + "allOf": [ + { + "$ref": "#/components/schemas/UserEntity" + } + ] } }, "required": ["accessToken", "refreshToken", "tokenExpires", "user"] @@ -1646,17 +2393,35 @@ "AuthRegisterLoginDto": { "type": "object", "properties": { - "email": { "type": "string", "example": "example@binarapps.com" }, - "password": { "type": "string", "example": "NewSecurePassword123!" }, - "firstName": { "type": "string", "example": "Jon" }, - "lastName": { "type": "string", "example": "Doe" }, + "email": { + "type": "string", + "example": "example@binarapps.com" + }, + "password": { + "type": "string", + "example": "NewSecurePassword123!" + }, + "firstName": { + "type": "string", + "example": "Jon" + }, + "lastName": { + "type": "string", + "example": "Doe" + }, "locale": { "type": "string", "example": "en-US", "description": "IETF language tags (e.g., en-US)." }, - "termsAccepted": { "type": "boolean", "example": true }, - "privacyPolicyAccepted": { "type": "boolean", "example": true } + "termsAccepted": { + "type": "boolean", + "example": true + }, + "privacyPolicyAccepted": { + "type": "boolean", + "example": true + } }, "required": [ "email", @@ -1897,15 +2662,27 @@ "properties": { "db": { "description": "The status of the database connection", - "allOf": [{ "$ref": "#/components/schemas/HealthCheckStatusDto" }] + "allOf": [ + { + "$ref": "#/components/schemas/HealthCheckStatusDto" + } + ] }, "domain": { "description": "The status of the domain", - "allOf": [{ "$ref": "#/components/schemas/HealthCheckStatusDto" }] + "allOf": [ + { + "$ref": "#/components/schemas/HealthCheckStatusDto" + } + ] }, "cache": { "description": "The status of the cache", - "allOf": [{ "$ref": "#/components/schemas/HealthCheckStatusDto" }] + "allOf": [ + { + "$ref": "#/components/schemas/HealthCheckStatusDto" + } + ] } }, "required": ["db", "domain", "cache"] @@ -1913,10 +2690,18 @@ "HealthEntity": { "type": "object", "properties": { - "status": { "type": "string", "description": "Overall health status", "example": "ok" }, + "status": { + "type": "string", + "description": "Overall health status", + "example": "ok" + }, "info": { "description": "Detailed info about each component's health", - "allOf": [{ "$ref": "#/components/schemas/HealthCheckInfoDto" }] + "allOf": [ + { + "$ref": "#/components/schemas/HealthCheckInfoDto" + } + ] }, "error": { "type": "object", @@ -1925,7 +2710,11 @@ }, "details": { "description": "Detailed health check results for each component", - "allOf": [{ "$ref": "#/components/schemas/HealthCheckInfoDto" }] + "allOf": [ + { + "$ref": "#/components/schemas/HealthCheckInfoDto" + } + ] } }, "required": ["status", "info", "error", "details"] @@ -1946,8 +2735,49 @@ } }, "required": ["os", "currentVersion"] + }, + "AppVersionStatusEntity": { + "type": "object", + "properties": { + "latestVersion": { + "type": "string", + "example": "1.5.0", + "description": "The latest version of the app available in the app store." + }, + "minimumVersion": { + "type": "string", + "example": "1.0.0", + "description": "The minimum version of the app that still functions correctly without mandatory updates." + }, + "updateRequired": { + "type": "boolean", + "example": true, + "description": "Indicates whether an update is required to continue using the app." + }, + "appId": { + "type": "string", + "example": "com.example.com", + "description": "AppId of application in AppStore(AppStore Bundle ID) or PlayStore(Google Play Store app ID)" + }, + "currentVersionReleaseDate": { + "format": "date-time", + "type": "string", + "example": "2.0.0", + "description": "The current version of the app." + } + }, + "required": [ + "latestVersion", + "minimumVersion", + "updateRequired", + "appId", + "currentVersionReleaseDate" + ] } } }, - "externalDocs": { "description": "JSON", "url": "/docs-json" } + "externalDocs": { + "description": "JSON", + "url": "/docs-json" + } } diff --git a/src/api/query/system/system.msw.ts b/src/api/query/system/system.msw.ts index fff0efcc..ccddfb95 100644 --- a/src/api/query/system/system.msw.ts +++ b/src/api/query/system/system.msw.ts @@ -6,17 +6,38 @@ * API documentation for the starter-kit project in NestJS by BinarApps. The API allows management of users, sessions and offers various functions for logged in users. Contains examples of authentication, authorization, and CRUD for selected resources. * OpenAPI spec version: 1.0 */ +import { faker } from '@faker-js/faker' import { HttpResponse, delay, http } from 'msw' -export const getSystemControllerCheckForAppUpdateMockHandler = () => { +import type { AppVersionStatusEntity } from '../../types' + +export const getSystemControllerCheckForAppUpdateResponseMock = ( + overrideResponse: any = {} +): AppVersionStatusEntity => ({ + appId: faker.word.sample(), + currentVersionReleaseDate: `${faker.date.past().toISOString().split('.')[0]}Z`, + latestVersion: faker.word.sample(), + minimumVersion: faker.word.sample(), + updateRequired: faker.datatype.boolean(), + ...overrideResponse, +}) + +export const getSystemControllerCheckForAppUpdateMockHandler = ( + overrideResponse?: AppVersionStatusEntity +) => { return http.post('*/api/v1/system/app-updates/check', async () => { await delay(1000) - return new HttpResponse(null, { - status: 200, - headers: { - 'Content-Type': 'application/json', - }, - }) + return new HttpResponse( + JSON.stringify( + overrideResponse ? overrideResponse : getSystemControllerCheckForAppUpdateResponseMock() + ), + { + status: 200, + headers: { + 'Content-Type': 'application/json', + }, + } + ) }) } export const getSystemMock = () => [getSystemControllerCheckForAppUpdateMockHandler()] diff --git a/src/api/query/system/system.ts b/src/api/query/system/system.ts index e52669b0..76c5752d 100644 --- a/src/api/query/system/system.ts +++ b/src/api/query/system/system.ts @@ -11,7 +11,13 @@ import type { MutationFunction, UseMutationOptions } from '@tanstack/react-query import { customInstance } from '../../axios/custom-instance' import type { ErrorType, BodyType } from '../../axios/custom-instance' -import type { CheckUpdateDto } from '../../types' +import type { + AppVersionStatusEntity, + CheckUpdateDto, + ErrorServerEntity, + ErrorTooManyRequestsEntity, + ErrorValidationEntity, +} from '../../types' type SecondParameter any> = Parameters[1] @@ -19,7 +25,7 @@ export const systemControllerCheckForAppUpdate = ( checkUpdateDto: BodyType, options?: SecondParameter ) => { - return customInstance( + return customInstance( { url: `/api/v1/system/app-updates/check`, method: 'POST', @@ -31,7 +37,7 @@ export const systemControllerCheckForAppUpdate = ( } export const getSystemControllerCheckForAppUpdateMutationOptions = < - TError = ErrorType, + TError = ErrorType, TContext = unknown >(options?: { mutation?: UseMutationOptions< @@ -65,10 +71,12 @@ export type SystemControllerCheckForAppUpdateMutationResult = NonNullable< Awaited> > export type SystemControllerCheckForAppUpdateMutationBody = BodyType -export type SystemControllerCheckForAppUpdateMutationError = ErrorType +export type SystemControllerCheckForAppUpdateMutationError = ErrorType< + void | ErrorValidationEntity | ErrorTooManyRequestsEntity | ErrorServerEntity +> export const useSystemControllerCheckForAppUpdate = < - TError = ErrorType, + TError = ErrorType, TContext = unknown >(options?: { mutation?: UseMutationOptions< diff --git a/src/api/types/appVersionStatusEntity.ts b/src/api/types/appVersionStatusEntity.ts new file mode 100644 index 00000000..92511021 --- /dev/null +++ b/src/api/types/appVersionStatusEntity.ts @@ -0,0 +1,21 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/** + * Generated by orval 🍺 + * Do not edit manually. + * API + * API documentation for the starter-kit project in NestJS by BinarApps. The API allows management of users, sessions and offers various functions for logged in users. Contains examples of authentication, authorization, and CRUD for selected resources. + * OpenAPI spec version: 1.0 + */ + +export interface AppVersionStatusEntity { + /** AppId of application in AppStore(AppStore Bundle ID) or PlayStore(Google Play Store app ID) */ + appId: string + /** The current version of the app. */ + currentVersionReleaseDate: string + /** The latest version of the app available in the app store. */ + latestVersion: string + /** The minimum version of the app that still functions correctly without mandatory updates. */ + minimumVersion: string + /** Indicates whether an update is required to continue using the app. */ + updateRequired: boolean +} diff --git a/src/api/types/index.ts b/src/api/types/index.ts index 30320c2a..770cb32e 100644 --- a/src/api/types/index.ts +++ b/src/api/types/index.ts @@ -7,6 +7,7 @@ * OpenAPI spec version: 1.0 */ +export * from './appVersionStatusEntity' export * from './articleEntity' export * from './articlesControllerFindAllParams' export * from './articlesControllerFindDraftsParams' diff --git a/src/components/AppLoading.tsx b/src/components/AppLoading.tsx index e781891d..33526997 100644 --- a/src/components/AppLoading.tsx +++ b/src/components/AppLoading.tsx @@ -1,5 +1,6 @@ import { Loader, Center } from '@baca/design-system/components' import { useCachedResources, useNavigationTheme } from '@baca/hooks' +import { useCheckForAppUpdate } from '@baca/hooks/useCheckForAppUpdate' import { isSignedInAtom } from '@baca/store/auth' import * as SplashScreen from 'expo-splash-screen' import { useAtomValue } from 'jotai' @@ -11,19 +12,18 @@ SplashScreen.preventAutoHideAsync() export const AppLoading: FC = ({ children }) => { const { navigationTheme } = useNavigationTheme() - const isLoadingComplete = useCachedResources() - const isSignedIn = useAtomValue(isSignedInAtom) - const [isLayoutReady, setIsLayoutReady] = useState(false) + const [isLayoutReady, setIsLayoutReady] = useState(false) const [isSplashHidden, setIsSplashHidden] = useState(false) + const isUpdateLoading = useCheckForAppUpdate() const onLayout = useCallback(() => { setIsLayoutReady(true) }, []) - const isLoading = !isLoadingComplete || isSignedIn === null || !isLayoutReady + const isLoading = !isLoadingComplete || isSignedIn === null || !isLayoutReady || isUpdateLoading useEffect(() => { const hideSplashScreen = () => { diff --git a/src/constants/links.ts b/src/constants/links.ts new file mode 100644 index 00000000..9a2c87f7 --- /dev/null +++ b/src/constants/links.ts @@ -0,0 +1,2 @@ +export const APP_STORE_URL = `https://apps.apple.com/app/id` +export const PLAY_STORE_URL = `https://play.google.com/store/apps/details?id=` diff --git a/src/hooks/useCheckForAppUpdate.ts b/src/hooks/useCheckForAppUpdate.ts new file mode 100644 index 00000000..743d72b1 --- /dev/null +++ b/src/hooks/useCheckForAppUpdate.ts @@ -0,0 +1,51 @@ +import { useSystemControllerCheckForAppUpdate } from '@baca/api/query/system/system' +import { APP_STORE_URL, PLAY_STORE_URL } from '@baca/constants/links' +import { useTranslation } from '@baca/hooks' +import * as Application from 'expo-application' +import { useEffect, useState } from 'react' +import { Platform, Alert, Linking } from 'react-native' + +const currentVersion = Application.nativeApplicationVersion || 'unknown' +const { OS } = Platform + +export const useCheckForAppUpdate = () => { + const { t } = useTranslation() + const [isUpdateLoading, setIsUpdateLoading] = useState(true) + + const { mutate: checkForUpdate } = useSystemControllerCheckForAppUpdate() + + useEffect(() => { + if (OS === 'ios' || OS === 'android') { + checkForUpdate( + { + data: { + currentVersion, + os: OS, + }, + }, + { + onSuccess: ({ updateRequired, appId }) => { + if (updateRequired) { + const storeUrl = + OS === 'ios' ? `${APP_STORE_URL}${appId}` : `${PLAY_STORE_URL}${appId}` + Alert.alert(t('update.alert_title'), t('update.alert_message'), [ + { text: t('update.update_now'), onPress: () => Linking.openURL(storeUrl) }, + ]) + } + }, + onError: (error) => { + console.error('Error checking for updates:', error) + // CONFIG: Add log to your analytics service, for example - sentry o crashlytics + }, + onSettled: () => { + setIsUpdateLoading(false) + }, + } + ) + } else { + setIsUpdateLoading(false) + } + }, [checkForUpdate, t]) + + return isUpdateLoading +} diff --git a/src/i18n/translations/en.json b/src/i18n/translations/en.json index 01ddb9e1..e2e0dbf9 100644 --- a/src/i18n/translations/en.json +++ b/src/i18n/translations/en.json @@ -122,9 +122,9 @@ "update": { "alert_message": "You will have to restart the application to have better experiences while using it", "alert_title": "New version available", - "restart": "Restart" + "restart": "Restart", + "update_now": "Update now" }, - "application_info_screen": { "navigation_info": "When you will try to go back it will double ask if you really want to leave \n" }, @@ -149,7 +149,6 @@ "tertiary_gray": "Button tertiary gray", "with_icons": "Button with icons" }, - "loader_variants": { "bricks": "Bricks loader", "bubbles": "Bubbles loader", diff --git a/src/i18n/translations/pl.json b/src/i18n/translations/pl.json index 6ae9f1c5..9317e13b 100644 --- a/src/i18n/translations/pl.json +++ b/src/i18n/translations/pl.json @@ -120,11 +120,11 @@ "warning": {} }, "update": { - "alert_message": "Będziesz musiał zrestartować aplikację, żeby mieć lepsze doświadczenia podczas jej używania", + "alert_message": "Musisz zrestartować aplikację, żeby mieć lepsze doświadczenia podczas jej używania", "alert_title": "Aktualizacja", - "restart": "Zrestartuj" + "restart": "Zrestartuj", + "update_now": "Aktualizuj teraz" }, - "application_info_screen": { "navigation_info": "Kiedy będziesz próbował cofnąć to zostaniesz podwójnie zapytany, czy na pewno tego chcesz \n" },