feat: переделал все клиентские запросы на другие endpoint для безопастности
Backend CI / build-and-test (push) Successful in 48s
Frontend CI / build-and-check (push) Failing after 5m13s
🚀 Create and publish a Docker image / Detect changes in backend and frontend (push) Successful in 15s
🚀 Create and publish a Docker image / Build & publish backend image (push) Successful in 1m9s
🚀 Create and publish a Docker image / Build & publish frontend image (push) Successful in 26s
🚀 Create and publish a Docker image / Update stack on Portainer (push) Successful in 14s

This commit is contained in:
2026-05-18 03:27:16 +03:00
parent 6eeacd80cc
commit 19ea303782
19 changed files with 660 additions and 96 deletions
@@ -26,73 +26,163 @@ public class UsersController : ControllerBase
private int CurrentUserId => int.Parse(User.FindFirstValue(ClaimTypes.NameIdentifier) ?? User.FindFirstValue("sub") ?? "0");
private static CurrentUserDto ToCurrentUserDto(UserDto user) => new(
user.Email,
user.DisplayName,
user.AvatarUrl,
user.Roles,
user.Xp,
user.Coins,
user.Level,
user.CreatedAt);
/// <summary>Получить профиль текущего пользователя.</summary>
/// <response code="200">Данные текущего пользователя.</response>
/// <response code="401">Требуется аутентификация.</response>
/// <response code="404">Пользователь не найден.</response>
[HttpGet("me")]
[ProducesResponseType(typeof(CurrentUserDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<CurrentUserDto>> GetMe() =>
Ok(ToCurrentUserDto(await _users.GetByIdAsync(CurrentUserId)));
/// <summary>Обновить профиль текущего пользователя (displayName, avatarUrl).</summary>
/// <param name="req">Обновляемые поля профиля.</param>
/// <response code="200">Обновлённые данные текущего пользователя.</response>
/// <response code="401">Требуется аутентификация.</response>
/// <response code="404">Пользователь не найден.</response>
[HttpPut("me")]
[ProducesResponseType(typeof(CurrentUserDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<CurrentUserDto>> UpdateMe([FromBody] UpdateUserRequest req) =>
Ok(ToCurrentUserDto(await _users.UpdateProfileAsync(CurrentUserId, req)));
/// <summary>Получить статистику текущего пользователя.</summary>
/// <response code="200">Статистика текущего пользователя.</response>
/// <response code="401">Требуется аутентификация.</response>
/// <response code="404">Пользователь не найден.</response>
[HttpGet("me/stats")]
[ProducesResponseType(typeof(UserStatsDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<UserStatsDto>> MyStats() =>
Ok(await _users.GetStatsAsync(CurrentUserId));
/// <summary>Получить список записей текущего пользователя на лекции.</summary>
/// <param name="pagination">Параметры пагинации.</param>
/// <response code="200">Список записей (пагинированный).</response>
/// <response code="401">Требуется аутентификация.</response>
/// <response code="404">Пользователь не найден.</response>
[HttpGet("me/enrollments")]
[ProducesResponseType(typeof(PagedResult<UniVerse.Application.DTOs.Lectures.LectureDto>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult> MyEnrollments([FromQuery] PaginationRequest pagination) =>
Ok(await _users.GetEnrollmentsAsync(CurrentUserId, pagination));
/// <summary>Получить отзывы текущего пользователя.</summary>
/// <param name="pagination">Параметры пагинации.</param>
/// <response code="200">Список отзывов (пагинированный).</response>
/// <response code="401">Требуется аутентификация.</response>
[HttpGet("me/reviews")]
[ProducesResponseType(typeof(PagedResult<UniVerse.Application.DTOs.Reviews.ReviewDto>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
public async Task<ActionResult> MyReviews([FromQuery] PaginationRequest pagination) =>
Ok(await _reviews.GetByUserAsync(CurrentUserId, pagination));
/// <summary>Получить достижения текущего пользователя.</summary>
/// <response code="200">Список полученных достижений.</response>
/// <response code="401">Требуется аутентификация.</response>
[HttpGet("me/achievements")]
[ProducesResponseType(typeof(List<UniVerse.Application.DTOs.Achievements.UserAchievementDto>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
public async Task<ActionResult> MyAchievements() =>
Ok(await _gamification.GetUserAchievementsAsync(CurrentUserId));
/// <summary>Получить историю транзакций монет текущего пользователя.</summary>
/// <param name="pagination">Параметры пагинации.</param>
/// <response code="200">История транзакций (пагинированная).</response>
/// <response code="401">Требуется аутентификация.</response>
[HttpGet("me/transactions")]
[ProducesResponseType(typeof(PagedResult<UniVerse.Application.DTOs.Gamification.CoinTransactionDto>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
public async Task<ActionResult> MyTransactions([FromQuery] PaginationRequest pagination) =>
Ok(await _gamification.GetTransactionsAsync(CurrentUserId, pagination));
/// <summary>Получить профиль пользователя по ID.</summary>
/// <remarks>Только Admin. Для текущего пользователя используйте GET /api/v1/users/me.</remarks>
/// <param name="id">ID пользователя.</param>
/// <response code="200">Данные пользователя.</response>
/// <response code="401">Требуется аутентификация.</response>
/// <response code="403">Требуется роль Admin.</response>
/// <response code="404">Пользователь не найден.</response>
[Authorize(Roles = "Admin")]
[HttpGet("{id:int}")]
[ProducesResponseType(typeof(UserDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<UserDto>> Get(int id) => Ok(await _users.GetByIdAsync(id));
/// <summary>Обновить профиль пользователя (displayName, avatarUrl).</summary>
/// <remarks>Разрешено только самому пользователю или Admin.</remarks>
/// <remarks>Только Admin. Для текущего пользователя используйте PUT /api/v1/users/me.</remarks>
/// <param name="id">ID пользователя.</param>
/// <param name="req">Обновляемые поля профиля.</param>
/// <response code="200">Обновлённые данные пользователя.</response>
/// <response code="401">Требуется аутентификация.</response>
/// <response code="403">Нет прав — только владелец профиля или Admin.</response>
/// <response code="403">Требуется роль Admin.</response>
/// <response code="404">Пользователь не найден.</response>
[Authorize(Roles = "Admin")]
[HttpPut("{id:int}")]
[ProducesResponseType(typeof(UserDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<UserDto>> Update(int id, [FromBody] UpdateUserRequest req)
{
if (CurrentUserId != id && !User.IsInRole("Admin")) return Forbid();
return Ok(await _users.UpdateProfileAsync(id, req));
}
public async Task<ActionResult<UserDto>> Update(int id, [FromBody] UpdateUserRequest req) =>
Ok(await _users.UpdateProfileAsync(id, req));
/// <summary>Получить статистику пользователя (XP, монеты, уровень, посещения).</summary>
/// <remarks>Только Admin. Для текущего пользователя используйте GET /api/v1/users/me/stats.</remarks>
/// <param name="id">ID пользователя.</param>
/// <response code="200">Статистика пользователя.</response>
/// <response code="401">Требуется аутентификация.</response>
/// <response code="403">Требуется роль Admin.</response>
/// <response code="404">Пользователь не найден.</response>
[Authorize(Roles = "Admin")]
[HttpGet("{id:int}/stats")]
[ProducesResponseType(typeof(UserStatsDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<UserStatsDto>> Stats(int id) => Ok(await _users.GetStatsAsync(id));
/// <summary>Получить список записей пользователя на лекции.</summary>
/// <remarks>Разрешено только самому пользователю или Admin.</remarks>
/// <remarks>Только Admin. Для текущего пользователя используйте GET /api/v1/users/me/enrollments.</remarks>
/// <param name="id">ID пользователя.</param>
/// <param name="pagination">Параметры пагинации.</param>
/// <response code="200">Список записей (пагинированный).</response>
/// <response code="401">Требуется аутентификация.</response>
/// <response code="403">Нет прав — только владелец или Admin.</response>
/// <response code="403">Требуется роль Admin.</response>
/// <response code="404">Пользователь не найден.</response>
[Authorize(Roles = "Admin")]
[HttpGet("{id:int}/enrollments")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(typeof(PagedResult<UniVerse.Application.DTOs.Lectures.LectureDto>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
public async Task<ActionResult> Enrollments(int id, [FromQuery] PaginationRequest pagination)
{
if (CurrentUserId != id && !User.IsInRole("Admin")) return Forbid();
// Delegate to lecture service would be more proper, but returning reviews for now
return Ok();
}
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult> Enrollments(int id, [FromQuery] PaginationRequest pagination) =>
Ok(await _users.GetEnrollmentsAsync(id, pagination));
/// <summary>Получить отзывы пользователя.</summary>
/// <remarks>Только Admin или Teacher.</remarks>
/// <remarks>Только Admin. Для текущего пользователя используйте GET /api/v1/users/me/reviews.</remarks>
/// <param name="id">ID пользователя.</param>
/// <param name="pagination">Параметры пагинации.</param>
/// <response code="200">Список отзывов (пагинированный).</response>
/// <response code="401">Требуется аутентификация.</response>
/// <response code="403">Требуется роль Admin или Teacher.</response>
[Authorize(Roles = "Admin,Teacher")]
/// <response code="403">Требуется роль Admin.</response>
[Authorize(Roles = "Admin")]
[HttpGet("{id:int}/reviews")]
[ProducesResponseType(typeof(PagedResult<UniVerse.Application.DTOs.Reviews.ReviewDto>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
@@ -101,31 +191,33 @@ public class UsersController : ControllerBase
Ok(await _reviews.GetByUserAsync(id, pagination));
/// <summary>Получить достижения пользователя.</summary>
/// <remarks>Только Admin. Для текущего пользователя используйте GET /api/v1/users/me/achievements.</remarks>
/// <param name="id">ID пользователя.</param>
/// <response code="200">Список полученных достижений.</response>
/// <response code="401">Требуется аутентификация.</response>
/// <response code="403">Требуется роль Admin.</response>
[Authorize(Roles = "Admin")]
[HttpGet("{id:int}/achievements")]
[ProducesResponseType(typeof(List<UniVerse.Application.DTOs.Achievements.UserAchievementDto>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
public async Task<ActionResult> Achievements(int id) =>
Ok(await _gamification.GetUserAchievementsAsync(id));
/// <summary>Получить историю транзакций монет пользователя.</summary>
/// <remarks>Разрешено только самому пользователю или Admin.</remarks>
/// <remarks>Только Admin. Для текущего пользователя используйте GET /api/v1/users/me/transactions.</remarks>
/// <param name="id">ID пользователя.</param>
/// <param name="pagination">Параметры пагинации.</param>
/// <response code="200">История транзакций (пагинированная).</response>
/// <response code="401">Требуется аутентификация.</response>
/// <response code="403">Нет прав — только владелец или Admin.</response>
/// <response code="403">Требуется роль Admin.</response>
[Authorize(Roles = "Admin")]
[HttpGet("{id:int}/transactions")]
[ProducesResponseType(typeof(PagedResult<UniVerse.Application.DTOs.Gamification.CoinTransactionDto>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
public async Task<ActionResult> Transactions(int id, [FromQuery] PaginationRequest pagination)
{
if (CurrentUserId != id && !User.IsInRole("Admin")) return Forbid();
return Ok(await _gamification.GetTransactionsAsync(id, pagination));
}
public async Task<ActionResult> Transactions(int id, [FromQuery] PaginationRequest pagination) =>
Ok(await _gamification.GetTransactionsAsync(id, pagination));
/// <summary>Получить список всех пользователей с фильтрацией и пагинацией.</summary>
/// <remarks>Только Admin.</remarks>
+441 -35
View File
@@ -3506,32 +3506,20 @@
]
}
},
"/api/v1/users/{id}": {
"/api/v1/users/me": {
"get": {
"tags": [
"Users"
],
"summary": "Получить профиль пользователя по ID.",
"summary": "Получить профиль текущего пользователя.",
"description": "**Required:** any authenticated user",
"parameters": [
{
"name": "id",
"in": "path",
"description": "ID пользователя.",
"required": true,
"schema": {
"type": "integer",
"format": "int32"
}
}
],
"responses": {
"200": {
"description": "Данные пользователя.",
"description": "Данные текущего пользователя.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UserDto"
"$ref": "#/components/schemas/CurrentUserDto"
}
}
}
@@ -3563,12 +3551,401 @@
}
]
},
"put": {
"tags": [
"Users"
],
"summary": "Обновить профиль текущего пользователя (displayName, avatarUrl).",
"description": "**Required:** any authenticated user",
"requestBody": {
"description": "Обновляемые поля профиля.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UpdateUserRequest"
}
},
"text/json": {
"schema": {
"$ref": "#/components/schemas/UpdateUserRequest"
}
},
"application/*+json": {
"schema": {
"$ref": "#/components/schemas/UpdateUserRequest"
}
}
}
},
"responses": {
"200": {
"description": "Обновлённые данные текущего пользователя.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CurrentUserDto"
}
}
}
},
"401": {
"description": "Требуется аутентификация.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ProblemDetails"
}
}
}
},
"404": {
"description": "Пользователь не найден.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ProblemDetails"
}
}
}
}
},
"security": [
{
"Bearer": [ ]
}
]
}
},
"/api/v1/users/me/stats": {
"get": {
"tags": [
"Users"
],
"summary": "Получить статистику текущего пользователя.",
"description": "**Required:** any authenticated user",
"responses": {
"200": {
"description": "Статистика текущего пользователя.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UserStatsDto"
}
}
}
},
"401": {
"description": "Требуется аутентификация.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ProblemDetails"
}
}
}
},
"404": {
"description": "Пользователь не найден.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ProblemDetails"
}
}
}
}
},
"security": [
{
"Bearer": [ ]
}
]
}
},
"/api/v1/users/me/enrollments": {
"get": {
"tags": [
"Users"
],
"summary": "Получить список записей текущего пользователя на лекции.",
"description": "**Required:** any authenticated user",
"parameters": [
{
"name": "Page",
"in": "query",
"schema": {
"type": "integer",
"format": "int32"
}
},
{
"name": "PageSize",
"in": "query",
"schema": {
"type": "integer",
"format": "int32"
}
}
],
"responses": {
"200": {
"description": "Список записей (пагинированный).",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/LectureDtoPagedResult"
}
}
}
},
"401": {
"description": "Требуется аутентификация.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ProblemDetails"
}
}
}
},
"404": {
"description": "Пользователь не найден.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ProblemDetails"
}
}
}
}
},
"security": [
{
"Bearer": [ ]
}
]
}
},
"/api/v1/users/me/reviews": {
"get": {
"tags": [
"Users"
],
"summary": "Получить отзывы текущего пользователя.",
"description": "**Required:** any authenticated user",
"parameters": [
{
"name": "Page",
"in": "query",
"schema": {
"type": "integer",
"format": "int32"
}
},
{
"name": "PageSize",
"in": "query",
"schema": {
"type": "integer",
"format": "int32"
}
}
],
"responses": {
"200": {
"description": "Список отзывов (пагинированный).",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ReviewDtoPagedResult"
}
}
}
},
"401": {
"description": "Требуется аутентификация.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ProblemDetails"
}
}
}
}
},
"security": [
{
"Bearer": [ ]
}
]
}
},
"/api/v1/users/me/achievements": {
"get": {
"tags": [
"Users"
],
"summary": "Получить достижения текущего пользователя.",
"description": "**Required:** any authenticated user",
"responses": {
"200": {
"description": "Список полученных достижений.",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/UserAchievementDto"
}
}
}
}
},
"401": {
"description": "Требуется аутентификация.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ProblemDetails"
}
}
}
}
},
"security": [
{
"Bearer": [ ]
}
]
}
},
"/api/v1/users/me/transactions": {
"get": {
"tags": [
"Users"
],
"summary": "Получить историю транзакций монет текущего пользователя.",
"description": "**Required:** any authenticated user",
"parameters": [
{
"name": "Page",
"in": "query",
"schema": {
"type": "integer",
"format": "int32"
}
},
{
"name": "PageSize",
"in": "query",
"schema": {
"type": "integer",
"format": "int32"
}
}
],
"responses": {
"200": {
"description": "История транзакций (пагинированная).",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CoinTransactionDtoPagedResult"
}
}
}
},
"401": {
"description": "Требуется аутентификация.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ProblemDetails"
}
}
}
}
},
"security": [
{
"Bearer": [ ]
}
]
}
},
"/api/v1/users/{id}": {
"get": {
"tags": [
"Users"
],
"summary": "Получить профиль пользователя по ID.",
"description": "Только Admin. Для текущего пользователя используйте GET /api/v1/users/me.\n\n**Required roles:** Admin",
"parameters": [
{
"name": "id",
"in": "path",
"description": "ID пользователя.",
"required": true,
"schema": {
"type": "integer",
"format": "int32"
}
}
],
"responses": {
"200": {
"description": "Данные пользователя.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UserDto"
}
}
}
},
"401": {
"description": "Требуется аутентификация.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ProblemDetails"
}
}
}
},
"403": {
"description": "Требуется роль Admin.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ProblemDetails"
}
}
}
},
"404": {
"description": "Пользователь не найден.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ProblemDetails"
}
}
}
}
},
"security": [
{
"Bearer": [ ]
}
]
},
"put": {
"tags": [
"Users"
],
"summary": "Обновить профиль пользователя (displayName, avatarUrl).",
"description": "Разрешено только самому пользователю или Admin.\n\n**Required:** any authenticated user",
"description": "Только Admin. Для текущего пользователя используйте PUT /api/v1/users/me.\n\n**Required roles:** Admin",
"parameters": [
{
"name": "id",
@@ -3623,7 +4000,7 @@
}
},
"403": {
"description": "Нет прав — только владелец профиля или Admin.",
"description": "Требуется роль Admin.",
"content": {
"application/json": {
"schema": {
@@ -3656,7 +4033,7 @@
"Users"
],
"summary": "Получить статистику пользователя (XP, монеты, уровень, посещения).",
"description": "**Required:** any authenticated user",
"description": "Только Admin. Для текущего пользователя используйте GET /api/v1/users/me/stats.\n\n**Required roles:** Admin",
"parameters": [
{
"name": "id",
@@ -3690,6 +4067,16 @@
}
}
},
"403": {
"description": "Требуется роль Admin.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ProblemDetails"
}
}
}
},
"404": {
"description": "Пользователь не найден.",
"content": {
@@ -3714,7 +4101,7 @@
"Users"
],
"summary": "Получить список записей пользователя на лекции.",
"description": "Разрешено только самому пользователю или Admin.\n\n**Required:** any authenticated user",
"description": "Только Admin. Для текущего пользователя используйте GET /api/v1/users/me/enrollments.\n\n**Required roles:** Admin",
"parameters": [
{
"name": "id",
@@ -3745,7 +4132,14 @@
],
"responses": {
"200": {
"description": "Список записей (пагинированный)."
"description": "Список записей (пагинированный).",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/LectureDtoPagedResult"
}
}
}
},
"401": {
"description": "Требуется аутентификация.",
@@ -3758,7 +4152,17 @@
}
},
"403": {
"description": "Нет прав — только владелец или Admin.",
"description": "Требуется роль Admin.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ProblemDetails"
}
}
}
},
"404": {
"description": "Пользователь не найден.",
"content": {
"application/json": {
"schema": {
@@ -3781,7 +4185,7 @@
"Users"
],
"summary": "Получить отзывы пользователя.",
"description": "Только Admin или Teacher.\n\n**Required roles:** Admin, Teacher",
"description": "Только Admin. Для текущего пользователя используйте GET /api/v1/users/me/reviews.\n\n**Required roles:** Admin",
"parameters": [
{
"name": "id",
@@ -3832,7 +4236,7 @@
}
},
"403": {
"description": "Требуется роль Admin или Teacher.",
"description": "Требуется роль Admin.",
"content": {
"application/json": {
"schema": {
@@ -3855,7 +4259,7 @@
"Users"
],
"summary": "Получить достижения пользователя.",
"description": "**Required:** any authenticated user",
"description": "Только Admin. Для текущего пользователя используйте GET /api/v1/users/me/achievements.\n\n**Required roles:** Admin",
"parameters": [
{
"name": "id",
@@ -3891,6 +4295,16 @@
}
}
}
},
"403": {
"description": "Требуется роль Admin.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ProblemDetails"
}
}
}
}
},
"security": [
@@ -3906,7 +4320,7 @@
"Users"
],
"summary": "Получить историю транзакций монет пользователя.",
"description": "Разрешено только самому пользователю или Admin.\n\n**Required:** any authenticated user",
"description": "Только Admin. Для текущего пользователя используйте GET /api/v1/users/me/transactions.\n\n**Required roles:** Admin",
"parameters": [
{
"name": "id",
@@ -3957,7 +4371,7 @@
}
},
"403": {
"description": "Нет прав — только владелец или Admin.",
"description": "Требуется роль Admin.",
"content": {
"application/json": {
"schema": {
@@ -4586,10 +5000,6 @@
"CurrentUserDto": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int32"
},
"email": {
"type": "string",
"nullable": true
@@ -5494,10 +5904,6 @@
"UserAuthDto": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int32"
},
"email": {
"type": "string",
"nullable": true