feat: добавил поддержку подписки на календарь и экспорт расписания лекций в формате .ics
Backend CI / build-and-test (push) Successful in 57s
Frontend CI / build-and-check (push) Failing after 26s
🚀 Create and publish a Docker image / Detect changes in backend and frontend (push) Successful in 11s
🚀 Create and publish a Docker image / Build & publish backend image (push) Successful in 2m33s
🚀 Create and publish a Docker image / Build & publish frontend image (push) Successful in 33s
🚀 Create and publish a Docker image / Update stack on Portainer (push) Successful in 8s

This commit is contained in:
2026-06-02 21:26:48 +03:00
parent 7050851bd4
commit 136bcce7db
16 changed files with 639 additions and 8 deletions
@@ -5,6 +5,7 @@ using UniVerse.Application.DTOs.Users;
using UniVerse.Application.Interfaces;
using UniVerse.Domain.Enums;
using System.Security.Claims;
using System.Text;
namespace UniVerse.Api.Controllers;
@@ -83,6 +84,52 @@ public class UsersController : ControllerBase
public async Task<ActionResult> MyEnrollments([FromQuery] PaginationRequest pagination) =>
Ok(await _users.GetEnrollmentsAsync(CurrentUserId, pagination));
[HttpGet("me/enrollments/calendar-subscription")]
[ProducesResponseType(typeof(CalendarSubscriptionDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
public async Task<ActionResult<CalendarSubscriptionDto>> CalendarSubscription()
{
var token = await _users.GetCalendarSubscriptionTokenAsync(CurrentUserId);
var feedUrl = Url.Action(
nameof(CalendarEnrollmentsIcs),
null,
new { token },
Request.Scheme)
?? $"{Request.Scheme}://{Request.Host}/api/v1/users/calendar/enrollments/{token}.ics";
return Ok(new CalendarSubscriptionDto(feedUrl));
}
[AllowAnonymous]
[HttpGet("calendar/enrollments/{token}.ics")]
[Produces("text/calendar")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
public async Task<FileContentResult> CalendarEnrollmentsIcs(string token)
{
var ics = await _users.GetEnrollmentsIcsBySubscriptionTokenAsync(token);
return File(Encoding.UTF8.GetBytes(ics), "text/calendar; charset=utf-8", "my-lectures.ics");
}
[HttpGet("me/enrollments.ics")]
[Produces("text/calendar")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<FileContentResult> MyEnrollmentsIcs()
{
var ics = await _users.GetMyEnrollmentsIcsAsync(CurrentUserId);
return File(Encoding.UTF8.GetBytes(ics), "text/calendar; charset=utf-8", "my-lectures.ics");
}
[HttpGet("me/enrollments/{lectureId:int}.ics")]
[Produces("text/calendar")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<FileContentResult> EnrollmentIcs(int lectureId)
{
var ics = await _users.GetEnrollmentIcsAsync(CurrentUserId, lectureId);
return File(Encoding.UTF8.GetBytes(ics), "text/calendar; charset=utf-8", $"lecture-{lectureId}.ics");
}
/// <summary>Получить отзывы текущего пользователя.</summary>
/// <param name="pagination">Параметры пагинации.</param>
/// <response code="200">Список отзывов (пагинированный).</response>