{
"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": "
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 } } } } } }