{ "openapi": "3.0.0", "paths": { "/v1/users": { "get": { "operationId": "UsersController_findAll", "parameters": [], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object" } } } } }, "tags": ["Admin"] }, "post": { "operationId": "UsersController_create", "parameters": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateUserDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "type": "object" } } } } }, "tags": ["Admin"] }, "delete": { "operationId": "UsersController_removeAll", "parameters": [], "responses": { "200": { "description": "" } }, "tags": ["Admin"] } }, "/v1/users/{id}": { "delete": { "operationId": "UsersController_remove", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "boolean" } } } } }, "tags": ["Admin"] } }, "/v1/auth/me": { "get": { "operationId": "AuthController_getUserData", "summary": "Current user data", "description": "Retrieve current user data.", "parameters": [], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserEntity" } } } }, "400": { "description": "User not found" }, "401": { "description": "Not logged in" } }, "tags": ["Auth"] }, "patch": { "operationId": "AuthController_updateUserData", "summary": "Update user data", "description": "Update current user data.", "parameters": [], "requestBody": { "required": true, "content": { "multipart/form-data": { "schema": { "$ref": "#/components/schemas/UpdateUserDataDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserEntity" } } } }, "400": { "description": "User not found" }, "401": { "description": "Not logged in" } }, "tags": ["Auth"] } }, "/v1/auth/login": { "post": { "operationId": "AuthController_login", "summary": "Sign in using email and password. Must have an account to do so.", "description": "Sign in using email and password. Must have an account to do so.", "parameters": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LoginDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LoginResponse" } } } }, "401": { "description": "Invalid credentials" } }, "tags": ["Auth"] } }, "/v1/auth/sign-up": { "post": { "operationId": "AuthController_registration", "summary": "Create a new user account", "description": "Create a new user account", "parameters": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RegistrationDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserEntity" } } } }, "400": { "description": "Email already exists" } }, "tags": ["Auth"] } }, "/v1/auth/verify-email": { "post": { "operationId": "AuthController_confirmRegistration", "summary": "Verify user email", "description": "Verify user email", "parameters": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EmailVerificationDto" } } } }, "responses": { "204": { "description": "Email verified successfully" }, "400": { "description": "Email has already been verified" }, "404": { "description": "User not found" } }, "tags": ["Auth"] } }, "/v1/auth/resend-verification-email": { "post": { "operationId": "AuthController_resendVerificationEmail", "summary": "Send verification email again", "description": "Send verification email again", "parameters": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ResendVerificationEmailDto" } } } }, "responses": { "204": { "description": "Verification email sent successfully" }, "400": { "description": "Email has already been verified" }, "404": { "description": "User not found" } }, "tags": ["Auth"] } }, "/v1/auth/logout": { "post": { "operationId": "AuthController_logout", "summary": "Sign current user out", "description": "Sign current user out", "parameters": [], "responses": { "204": { "description": "Logged out successfully" }, "401": { "description": "Not logged in" } }, "tags": ["Auth"] } }, "/v1/auth/refresh-token": { "post": { "operationId": "AuthController_refreshToken", "summary": "Get new access token using refresh token", "description": "Get new access token using refresh token", "parameters": [], "responses": { "204": { "description": "New tokens generated successfully" }, "401": { "description": "Invalid or missing refreshToken" } }, "tags": ["Auth"] } }, "/v1/auth/recover-password": { "post": { "operationId": "AuthController_recoverPassword", "summary": "Send password recovery email", "description": "Send password recovery email", "parameters": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecoverPasswordDto" } } } }, "responses": { "204": { "description": "Password recovery email sent successfully" }, "400": { "description": "Email has already been verified" }, "404": { "description": "User not found" } }, "tags": ["Auth"] } }, "/v1/auth/reset-password/{token}": { "post": { "operationId": "AuthController_resetPassword", "summary": "Reset password", "description": "Reset password", "parameters": [ { "name": "token", "required": true, "in": "path", "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ResetPasswordDto" } } } }, "responses": { "204": { "description": "Password reset successfully" }, "400": { "description": "Password is required" }, "404": { "description": "Incorrect or expired password reset token" } }, "tags": ["Auth"] } }, "/v1/decks": { "get": { "operationId": "DecksController_findAll", "summary": "Paginated decks list", "description": "Retrieve paginated decks list.", "parameters": [ { "name": "minCardsCount", "required": false, "in": "query", "schema": { "type": "number" } }, { "name": "maxCardsCount", "required": false, "in": "query", "schema": { "type": "number" } }, { "name": "name", "required": false, "in": "query", "description": "Search by deck name", "schema": { "type": "string" } }, { "name": "authorId", "required": false, "in": "query", "description": "Filter by deck authorId", "schema": { "type": "string" } }, { "name": "orderBy", "required": false, "in": "query", "description": "A string that represents the name of the field to order by and the order direction.\nThe format is: \"field_name-order_direction\".\nAvailable directions: \"asc\" and \"desc\".", "example": "name-desc", "schema": { "nullable": true, "type": "string" } }, { "name": "currentPage", "required": false, "in": "query", "schema": { "type": "number" } }, { "name": "itemsPerPage", "required": false, "in": "query", "schema": { "type": "number" } } ], "responses": { "206": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedDecks" } } } }, "401": { "description": "Unauthorized" } }, "tags": ["Decks"] }, "post": { "operationId": "DecksController_create", "summary": "Create a deck", "description": "Create a deck", "parameters": [], "requestBody": { "required": true, "content": { "multipart/form-data": { "schema": { "$ref": "#/components/schemas/CreateDeckDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DeckWithAuthor" } } } }, "401": { "description": "Unauthorized" } }, "tags": ["Decks"] } }, "/v1/decks/{id}": { "get": { "operationId": "DecksController_findOne", "summary": "Retrieve a deck by id", "description": "Retrieve a deck by id", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DeckWithAuthor" } } } }, "401": { "description": "Unauthorized" } }, "tags": ["Decks"] }, "patch": { "operationId": "DecksController_update", "summary": "Update a deck", "description": "Update a deck", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "multipart/form-data": { "schema": { "$ref": "#/components/schemas/UpdateDeckDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DeckWithAuthor" } } } }, "401": { "description": "Unauthorized" }, "404": { "description": "Deck not found" } }, "tags": ["Decks"] }, "delete": { "operationId": "DecksController_remove", "summary": "Delete a deck", "description": "Delete a deck", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "Deck deleted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Deck" } } } }, "401": { "description": "Unauthorized" }, "404": { "description": "Deck not found" } }, "tags": ["Decks"] } }, "/v1/decks/{id}/cards": { "get": { "operationId": "DecksController_findCardsInDeck", "summary": "Retrieve cards in a deck", "description": "Retrieve paginated cards in a deck", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } }, { "name": "question", "required": false, "in": "query", "schema": { "minLength": 1, "maxLength": 30, "type": "string" } }, { "name": "answer", "required": false, "in": "query", "schema": { "minLength": 1, "maxLength": 30, "type": "string" } }, { "name": "orderBy", "required": false, "in": "query", "schema": { "nullable": true, "type": "string" } }, { "name": "currentPage", "required": false, "in": "query", "schema": { "type": "number" } }, { "name": "itemsPerPage", "required": false, "in": "query", "schema": { "type": "number" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedCards" } } } } }, "tags": ["Decks"] }, "post": { "operationId": "DecksController_createCardInDeck", "summary": "Create a card", "description": "Create card in a deck", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "multipart/form-data": { "schema": { "$ref": "#/components/schemas/CreateCardDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Card" } } } }, "401": { "description": "Unauthorized" }, "404": { "description": "Deck not found" } }, "tags": ["Decks"] } }, "/v1/decks/{id}/learn": { "get": { "operationId": "DecksController_findRandomCardInDeck", "summary": "Retrieve a random card", "description": "Retrieve a random card in a deck. The cards priority is based on the grade", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } }, { "name": "previousCardId", "required": false, "in": "query", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Card" } } } }, "401": { "description": "Unauthorized" } }, "tags": ["Decks"] }, "post": { "operationId": "DecksController_saveGrade", "summary": "Save the grade of a card", "description": "Save the grade of a card", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SaveGradeDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object" } } } }, "204": { "description": "Grade saved" }, "401": { "description": "Unauthorized" }, "404": { "description": "Card not found" } }, "tags": ["Decks"] } }, "/v1/cards/{id}": { "get": { "operationId": "CardsController_findOne", "summary": "Get card by id", "description": "Get card by id", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Card" } } } }, "401": { "description": "Unauthorized" }, "404": { "description": "Card not found" } }, "tags": ["Cards"] }, "patch": { "operationId": "CardsController_update", "summary": "Update card", "description": "Update partial card data", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "multipart/form-data": { "schema": { "$ref": "#/components/schemas/UpdateCardDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Card" } } } }, "401": { "description": "Unauthorized" }, "404": { "description": "Card not found" } }, "tags": ["Cards"] }, "delete": { "operationId": "CardsController_remove", "summary": "Delete card by id", "description": "Delete card by id", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } } ], "responses": { "204": { "description": "New tokens generated successfully" }, "401": { "description": "Unauthorized" }, "404": { "description": "Card not found" } }, "tags": ["Cards"] } } }, "info": { "title": "Flashcards", "description": "Flashcards API", "version": "1.0", "contact": {} }, "tags": [ { "name": "Auth", "description": "" }, { "name": "Decks", "description": "" }, { "name": "Cards", "description": "" }, { "name": "Admin", "description": "" } ], "servers": [ { "url": "https://api.flashcards.andrii.es" } ], "components": { "schemas": { "CreateUserDto": { "type": "object", "properties": { "name": { "type": "string", "minLength": 3, "maxLength": 10 }, "password": { "type": "string", "minLength": 6, "maxLength": 20 }, "email": { "type": "string", "description": "User's email address", "pattern": "/^[\\w-.]+@([\\w-]+\\.)+[\\w-]{2,4}$/" } }, "required": ["name", "password", "email"] }, "UserEntity": { "type": "object", "properties": { "avatar": { "type": "string", "format": "binary" }, "id": { "type": "string" }, "email": { "type": "string" }, "isEmailVerified": { "type": "boolean" }, "name": { "type": "string" }, "created": { "format": "date-time", "type": "string" }, "updated": { "format": "date-time", "type": "string" } }, "required": ["avatar", "id", "email", "isEmailVerified", "name", "created", "updated"] }, "UpdateUserDataDto": { "type": "object", "properties": { "avatar": { "type": "string", "format": "binary" }, "name": { "type": "string" }, "email": { "type": "string" } }, "required": ["avatar", "name", "email"] }, "LoginDto": { "type": "object", "properties": { "password": { "type": "string", "minLength": 3, "maxLength": 30 }, "email": { "type": "string" }, "rememberMe": { "type": "boolean" } }, "required": ["password", "email", "rememberMe"] }, "LoginResponse": { "type": "object", "properties": { "accessToken": { "type": "string" } }, "required": ["accessToken"] }, "RegistrationDto": { "type": "object", "properties": { "html": { "type": "string", "description": "HTML template to be sent in the email;\n ##name## will be replaced with the user's name; \n ##token## will be replaced with the password recovery token", "example": "Hello, ##name##!
Please confirm your email by clicking on the link below:
Confirm email. If it doesn't work, copy and paste the following link in your browser:
http://localhost:3000/confirm-email/##token##" }, "name": { "type": "string", "minLength": 3, "maxLength": 30 }, "password": { "type": "string", "minLength": 3, "maxLength": 30 }, "email": { "type": "string" }, "subject": { "type": "string", "description": "Email subject" }, "sendConfirmationEmail": { "type": "boolean", "description": "Whether to send a confirmation email or not.\nDefaults to false", "example": false } }, "required": ["password", "email"] }, "EmailVerificationDto": { "type": "object", "properties": { "code": { "type": "string" } }, "required": ["code"] }, "ResendVerificationEmailDto": { "type": "object", "properties": { "html": { "type": "string", "description": "HTML template to be sent in the email;\n ##name## will be replaced with the user's name; \n ##token## will be replaced with the password recovery token", "example": "Hello, ##name##!
Please confirm your email by clicking on the link below:
Confirm email. If it doesn't work, copy and paste the following link in your browser:
http://localhost:3000/confirm-email/##token##" }, "userId": { "type": "string" }, "subject": { "type": "string", "description": "Email subject" } }, "required": ["userId"] }, "RecoverPasswordDto": { "type": "object", "properties": { "html": { "type": "string", "description": "HTML template to be sent in the email;\n ##name## will be replaced with the user's name; \n ##token## will be replaced with the password recovery token", "example": "

Hi, ##name##

Click here to recover your password

" }, "email": { "type": "string", "description": "User's email address" }, "subject": { "type": "string", "description": "Email subject" } }, "required": ["email"] }, "ResetPasswordDto": { "type": "object", "properties": { "password": { "type": "string", "minLength": 3, "maxLength": 30 } }, "required": ["password"] }, "DeckAuthor": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" } }, "required": ["id", "name"] }, "DeckWithAuthor": { "type": "object", "properties": { "author": { "$ref": "#/components/schemas/DeckAuthor" }, "id": { "type": "string" }, "userId": { "type": "string" }, "name": { "type": "string" }, "isPrivate": { "type": "boolean" }, "shots": { "type": "number" }, "cover": { "type": "string", "nullable": true }, "rating": { "type": "number" }, "created": { "format": "date-time", "type": "string" }, "updated": { "format": "date-time", "type": "string" }, "cardsCount": { "type": "number" } }, "required": [ "author", "id", "userId", "name", "isPrivate", "shots", "cover", "rating", "created", "updated", "cardsCount" ] }, "Pagination": { "type": "object", "properties": { "currentPage": { "type": "number" }, "itemsPerPage": { "type": "number" }, "totalPages": { "type": "number" }, "totalItems": { "type": "number" } }, "required": ["currentPage", "itemsPerPage", "totalPages", "totalItems"] }, "PaginatedDecks": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/DeckWithAuthor" } }, "pagination": { "$ref": "#/components/schemas/Pagination" }, "maxCardsCount": { "type": "number" } }, "required": ["items", "pagination", "maxCardsCount"] }, "CreateDeckDto": { "type": "object", "properties": { "cover": { "type": "string", "description": "Cover image (binary)", "format": "binary" }, "name": { "type": "string", "minLength": 3, "maxLength": 30 }, "isPrivate": { "type": "boolean", "description": "Private decks are not visible to other users" } }, "required": ["name"] }, "UpdateDeckDto": { "type": "object", "properties": { "cover": { "type": "string", "format": "binary" }, "name": { "type": "string" }, "isPrivate": { "type": "boolean" } } }, "Deck": { "type": "object", "properties": { "id": { "type": "string" }, "userId": { "type": "string" }, "name": { "type": "string" }, "isPrivate": { "type": "boolean" }, "shots": { "type": "number" }, "cover": { "type": "string", "nullable": true }, "rating": { "type": "number" }, "created": { "format": "date-time", "type": "string" }, "updated": { "format": "date-time", "type": "string" }, "cardsCount": { "type": "number" } }, "required": [ "id", "userId", "name", "isPrivate", "shots", "cover", "rating", "created", "updated", "cardsCount" ] }, "CardWithoutRating": { "type": "object", "properties": { "id": { "type": "string" }, "deckId": { "type": "string" }, "userId": { "type": "string" }, "question": { "type": "string" }, "answer": { "type": "string" }, "shots": { "type": "number" }, "answerImg": { "type": "string" }, "questionImg": { "type": "string" }, "questionVideo": { "type": "string" }, "answerVideo": { "type": "string" }, "created": { "format": "date-time", "type": "string" }, "updated": { "format": "date-time", "type": "string" } }, "required": [ "id", "deckId", "userId", "question", "answer", "shots", "answerImg", "questionImg", "questionVideo", "answerVideo", "created", "updated" ] }, "PaginatedCards": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/CardWithoutRating" } }, "pagination": { "$ref": "#/components/schemas/Pagination" } }, "required": ["items", "pagination"] }, "CreateCardDto": { "type": "object", "properties": { "question": { "type": "string", "minLength": 3, "maxLength": 500 }, "answer": { "type": "string", "minLength": 3, "maxLength": 500 }, "questionImg": { "type": "string", "minLength": 0, "maxLength": 0 }, "answerImg": { "type": "string", "minLength": 0, "maxLength": 0 }, "questionVideo": { "type": "string", "minLength": 3, "maxLength": 500 }, "answerVideo": { "type": "string", "minLength": 3, "maxLength": 500 } }, "required": ["question", "answer"] }, "Card": { "type": "object", "properties": { "id": { "type": "string" }, "deckId": { "type": "string" }, "userId": { "type": "string" }, "question": { "type": "string" }, "answer": { "type": "string" }, "shots": { "type": "number" }, "answerImg": { "type": "string" }, "questionImg": { "type": "string" }, "questionVideo": { "type": "string" }, "answerVideo": { "type": "string" }, "rating": { "type": "number" }, "created": { "format": "date-time", "type": "string" }, "updated": { "format": "date-time", "type": "string" } }, "required": [ "id", "deckId", "userId", "question", "answer", "shots", "answerImg", "questionImg", "questionVideo", "answerVideo", "rating", "created", "updated" ] }, "SaveGradeDto": { "type": "object", "properties": { "cardId": { "type": "string" }, "grade": { "type": "number", "minimum": 1, "maximum": 5 } }, "required": ["cardId", "grade"] }, "UpdateCardDto": { "type": "object", "properties": { "questionImg": { "type": "string", "minLength": 0, "maxLength": 0, "format": "binary" }, "answerImg": { "type": "string", "minLength": 0, "maxLength": 0, "format": "binary" }, "question": { "type": "string", "minLength": 3, "maxLength": 500 }, "answer": { "type": "string", "minLength": 3, "maxLength": 500 }, "questionVideo": { "type": "string", "minLength": 3, "maxLength": 500 }, "answerVideo": { "type": "string", "minLength": 3, "maxLength": 500 } } } } } }