Compare commits
6 Commits
4b3036364b
...
b46e3a0715
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b46e3a0715 | ||
|
|
68887284c1 | ||
|
|
9e8bb0db75 | ||
|
|
dcb2efbedb | ||
|
|
8230951839 | ||
|
|
164cf455fd |
@@ -13,15 +13,18 @@ public class AudioController : ControllerBase
|
|||||||
{
|
{
|
||||||
private readonly UserManager<ApplicationUser> _userManager;
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
private readonly YandexMusicService _yandexService;
|
private readonly YandexMusicService _yandexService;
|
||||||
|
private readonly SharedPlaylistService _sharedService;
|
||||||
private readonly JwtService _jwtService;
|
private readonly JwtService _jwtService;
|
||||||
|
|
||||||
public AudioController(
|
public AudioController(
|
||||||
UserManager<ApplicationUser> userManager,
|
UserManager<ApplicationUser> userManager,
|
||||||
YandexMusicService yandexService,
|
YandexMusicService yandexService,
|
||||||
|
SharedPlaylistService sharedService,
|
||||||
JwtService jwtService)
|
JwtService jwtService)
|
||||||
{
|
{
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
_yandexService = yandexService;
|
_yandexService = yandexService;
|
||||||
|
_sharedService = sharedService;
|
||||||
_jwtService = jwtService;
|
_jwtService = jwtService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,17 +32,18 @@ public class AudioController : ControllerBase
|
|||||||
/// Потоковое воспроизведение трека из Яндекс.Музыки.
|
/// Потоковое воспроизведение трека из Яндекс.Музыки.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="trackId">ID трека (например, "21696942").</param>
|
/// <param name="trackId">ID трека (например, "21696942").</param>
|
||||||
|
/// <param name="access_token">gwt пользователя</param>
|
||||||
|
/// <param name="shared_id">ID расшаренного плейлиста</param>
|
||||||
[HttpGet("track/{trackId}")]
|
[HttpGet("track/{trackId}")]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
public async Task<IActionResult> StreamTrack(string trackId, [FromQuery] string? access_token = null)
|
public async Task<IActionResult> StreamTrack(string trackId, [FromQuery] string? access_token = null, [FromQuery] string? shared_id = null)
|
||||||
{
|
{
|
||||||
var user = await GetUserFromToken(access_token);
|
var user = await GetUserFromToken(access_token);
|
||||||
if (user == null)
|
if (user == null) user = await GetUserFromSharedPlaylistId(shared_id);
|
||||||
return Unauthorized();
|
if (user == null) return Unauthorized();
|
||||||
|
|
||||||
var streamUrl = await _yandexService.GetTrackFileUrlAsync(user, trackId);
|
var streamUrl = await _yandexService.GetTrackFileUrlAsync(user, trackId);
|
||||||
if (string.IsNullOrEmpty(streamUrl))
|
if (string.IsNullOrEmpty(streamUrl)) return NotFound();
|
||||||
return NotFound();
|
|
||||||
|
|
||||||
var httpClient = new HttpClient();
|
var httpClient = new HttpClient();
|
||||||
var request = new HttpRequestMessage(HttpMethod.Get, streamUrl);
|
var request = new HttpRequestMessage(HttpMethod.Get, streamUrl);
|
||||||
@@ -56,11 +60,11 @@ public class AudioController : ControllerBase
|
|||||||
Response.ContentType = response.Content.Headers.ContentType?.ToString() ?? "audio/mpeg";
|
Response.ContentType = response.Content.Headers.ContentType?.ToString() ?? "audio/mpeg";
|
||||||
|
|
||||||
if (response.Content.Headers.Contains("Content-Range"))
|
if (response.Content.Headers.Contains("Content-Range"))
|
||||||
Response.Headers.Add("Content-Range", response.Content.Headers.ContentRange?.ToString());
|
Response.Headers.Append("Content-Range", response.Content.Headers.ContentRange?.ToString());
|
||||||
if (response.Headers.Contains("Accept-Ranges"))
|
if (response.Headers.Contains("Accept-Ranges"))
|
||||||
Response.Headers.Add("Accept-Ranges", response.Headers.AcceptRanges?.ToString());
|
Response.Headers.Append("Accept-Ranges", response.Headers.AcceptRanges?.ToString());
|
||||||
if (response.Content.Headers.Contains("Content-Length"))
|
if (response.Content.Headers.Contains("Content-Length"))
|
||||||
Response.Headers.Add("Content-Length", response.Content.Headers.ContentLength?.ToString());
|
Response.Headers.Append("Content-Length", response.Content.Headers.ContentLength?.ToString());
|
||||||
|
|
||||||
await response.Content.CopyToAsync(Response.Body);
|
await response.Content.CopyToAsync(Response.Body);
|
||||||
return new EmptyResult();
|
return new EmptyResult();
|
||||||
@@ -68,17 +72,28 @@ public class AudioController : ControllerBase
|
|||||||
|
|
||||||
private async Task<ApplicationUser?> GetUserFromToken(string? token)
|
private async Task<ApplicationUser?> GetUserFromToken(string? token)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(token))
|
if (string.IsNullOrEmpty(token)) return null;
|
||||||
return null;
|
|
||||||
|
|
||||||
var principal = _jwtService.ValidateToken(token);
|
var principal = _jwtService.ValidateToken(token);
|
||||||
if (principal == null)
|
if (principal == null) return null;
|
||||||
return null;
|
|
||||||
|
|
||||||
var userId = principal.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
var userId = principal.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
||||||
if (string.IsNullOrEmpty(userId))
|
if (string.IsNullOrEmpty(userId)) return null;
|
||||||
return null;
|
|
||||||
|
|
||||||
return await _userManager.FindByIdAsync(userId);
|
return await _userManager.FindByIdAsync(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<ApplicationUser?> GetUserFromSharedPlaylistId(string? sharedId)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(sharedId)) return null;
|
||||||
|
|
||||||
|
var playlist = await _sharedService.GetEntityByTokenAsync(sharedId);
|
||||||
|
|
||||||
|
if (playlist == null) return null;
|
||||||
|
|
||||||
|
if (!await _sharedService.CanPlayEveryoneAsync(playlist)) return null;
|
||||||
|
|
||||||
|
return await _userManager.FindByIdAsync(playlist.CreatorUserId.ToString());
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
82
PlaylistShared.Api/Controllers/FavoritesController.cs
Normal file
82
PlaylistShared.Api/Controllers/FavoritesController.cs
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using PlaylistShared.Api.Entities;
|
||||||
|
using PlaylistShared.Api.Extensions;
|
||||||
|
using PlaylistShared.Api.Services;
|
||||||
|
using PlaylistShared.Shared;
|
||||||
|
using PlaylistShared.Shared.Shared;
|
||||||
|
|
||||||
|
namespace PlaylistShared.Api.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
[Authorize]
|
||||||
|
public class FavoritesController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
|
private readonly FavoritesService _favoritesService;
|
||||||
|
private readonly SharedPlaylistService _sharedPlaylistService;
|
||||||
|
|
||||||
|
public FavoritesController(
|
||||||
|
UserManager<ApplicationUser> userManager,
|
||||||
|
FavoritesService favoritesService,
|
||||||
|
SharedPlaylistService sharedPlaylistService)
|
||||||
|
{
|
||||||
|
_userManager = userManager;
|
||||||
|
_favoritesService = favoritesService;
|
||||||
|
_sharedPlaylistService = sharedPlaylistService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Получить список избранных плейлистов текущего пользователя.</summary>
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<ActionResult<ApiResponse<List<SharedPlaylistDto>>>> GetFavorites()
|
||||||
|
{
|
||||||
|
var userId = User.GetUserId();
|
||||||
|
var user = await _userManager.FindByIdAsync(userId.ToString());
|
||||||
|
if (user == null) return Unauthorized();
|
||||||
|
|
||||||
|
var favorites = await _favoritesService.GetUserFavoritesAsync(userId);
|
||||||
|
return Ok(ApiResponse<List<SharedPlaylistDto>>.Ok(favorites));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Проверить, добавлен ли плейлист в избранное.</summary>
|
||||||
|
[HttpGet("{shareToken}/check")]
|
||||||
|
public async Task<ActionResult<ApiResponse<bool>>> CheckFavorite(string shareToken)
|
||||||
|
{
|
||||||
|
var userId = User.GetUserId();
|
||||||
|
var user = await _userManager.FindByIdAsync(userId.ToString());
|
||||||
|
if (user == null) return Unauthorized();
|
||||||
|
|
||||||
|
var isFavorite = await _favoritesService.IsFavoriteAsync(userId, shareToken);
|
||||||
|
return Ok(ApiResponse<bool>.Ok(isFavorite));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Добавить плейлист в избранное.</summary>
|
||||||
|
[HttpPost("{shareToken}")]
|
||||||
|
public async Task<ActionResult<ApiResponse<object>>> AddFavorite(string shareToken)
|
||||||
|
{
|
||||||
|
var userId = User.GetUserId();
|
||||||
|
var user = await _userManager.FindByIdAsync(userId.ToString());
|
||||||
|
if (user == null) return Unauthorized();
|
||||||
|
|
||||||
|
var playlist = await _sharedPlaylistService.GetEntityByTokenAsync(shareToken);
|
||||||
|
if (playlist == null)
|
||||||
|
return NotFound(ApiResponse<object>.Fail(new ErrorResponse { StatusCode = 404, Message = "Плейлист не найден" }));
|
||||||
|
|
||||||
|
await _favoritesService.AddFavoriteAsync(userId, shareToken);
|
||||||
|
return Ok(ApiResponse<object>.Ok(new { message = "Плейлист добавлен в избранное" }));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Удалить плейлист из избранного.</summary>
|
||||||
|
[HttpDelete("{shareToken}")]
|
||||||
|
public async Task<ActionResult<ApiResponse<object>>> RemoveFavorite(string shareToken)
|
||||||
|
{
|
||||||
|
var userId = User.GetUserId();
|
||||||
|
var user = await _userManager.FindByIdAsync(userId.ToString());
|
||||||
|
if (user == null) return Unauthorized();
|
||||||
|
|
||||||
|
await _favoritesService.RemoveFavoriteAsync(userId, shareToken);
|
||||||
|
return Ok(ApiResponse<object>.Ok(new { message = "Плейлист удалён из избранного" }));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -82,13 +82,15 @@ public class PlaylistsController : ControllerBase
|
|||||||
|
|
||||||
var dto = new SharePlaylistDto
|
var dto = new SharePlaylistDto
|
||||||
{
|
{
|
||||||
|
YandexPlaylistUuid = playlist.PlaylistUuid,
|
||||||
YandexPlaylistKind = request.Kind,
|
YandexPlaylistKind = request.Kind,
|
||||||
YandexPlaylistOwnerUid = request.OwnerUid,
|
YandexPlaylistOwnerUid = request.OwnerUid,
|
||||||
Title = playlist.Title,
|
Title = playlist.Title,
|
||||||
Description = playlist.Description,
|
Description = playlist.Description,
|
||||||
ViewPermission = ViewPermission.Everyone,
|
ViewPermission = ViewPermission.Everyone,
|
||||||
|
PlayPermission = ViewPermission.Everyone,
|
||||||
AddPermission = EditPermission.AuthorizedOnly,
|
AddPermission = EditPermission.AuthorizedOnly,
|
||||||
RemovePermission = EditPermission.AddedByUserOnly
|
RemovePermission = EditPermission.AddedByUserOnly,
|
||||||
};
|
};
|
||||||
|
|
||||||
var result = await _sharedService.CreateAsync(userId, dto);
|
var result = await _sharedService.CreateAsync(userId, dto);
|
||||||
|
|||||||
@@ -1,23 +1,33 @@
|
|||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using PlaylistShared.Api.Entities;
|
using PlaylistShared.Api.Entities;
|
||||||
|
|
||||||
namespace PlaylistShared.Api.Data;
|
namespace PlaylistShared.Api.Data;
|
||||||
|
|
||||||
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, IdentityRole<Guid>, Guid>
|
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, IdentityRole<Guid>, Guid>, IDataProtectionKeyContext
|
||||||
{
|
{
|
||||||
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { }
|
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { }
|
||||||
|
|
||||||
|
public DbSet<FavoritePlaylist> FavoritePlaylists => Set<FavoritePlaylist>();
|
||||||
|
|
||||||
public DbSet<SharedPlaylist> SharedPlaylists => Set<SharedPlaylist>();
|
public DbSet<SharedPlaylist> SharedPlaylists => Set<SharedPlaylist>();
|
||||||
public DbSet<TrackAdditionLog> TrackAdditionLogs => Set<TrackAdditionLog>();
|
public DbSet<TrackAdditionLog> TrackAdditionLogs => Set<TrackAdditionLog>();
|
||||||
public DbSet<UserSession> UserSessions => Set<UserSession>();
|
|
||||||
public DbSet<TrackRemovalLog> TrackRemovalLogs => Set<TrackRemovalLog>();
|
public DbSet<TrackRemovalLog> TrackRemovalLogs => Set<TrackRemovalLog>();
|
||||||
|
public DbSet<UserSession> UserSessions => Set<UserSession>();
|
||||||
|
public DbSet<DataProtectionKey> DataProtectionKeys { get; set; }
|
||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder builder)
|
protected override void OnModelCreating(ModelBuilder builder)
|
||||||
{
|
{
|
||||||
base.OnModelCreating(builder);
|
base.OnModelCreating(builder);
|
||||||
|
|
||||||
|
builder.Entity<DataProtectionKey>(entity =>
|
||||||
|
{
|
||||||
|
entity.HasKey(e => e.Id);
|
||||||
|
entity.Property(e => e.FriendlyName).IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
builder.Entity<SharedPlaylist>(entity =>
|
builder.Entity<SharedPlaylist>(entity =>
|
||||||
{
|
{
|
||||||
entity.HasKey(e => e.Id);
|
entity.HasKey(e => e.Id);
|
||||||
@@ -26,6 +36,7 @@ public class ApplicationDbContext : IdentityDbContext<ApplicationUser, IdentityR
|
|||||||
.WithMany(u => u.OwnedPlaylists)
|
.WithMany(u => u.OwnedPlaylists)
|
||||||
.HasForeignKey(e => e.CreatorUserId)
|
.HasForeignKey(e => e.CreatorUserId)
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
entity.Property(e => e.YandexPlaylistUuid).IsRequired().HasMaxLength(50);
|
||||||
entity.Property(e => e.YandexPlaylistKind).IsRequired().HasMaxLength(50);
|
entity.Property(e => e.YandexPlaylistKind).IsRequired().HasMaxLength(50);
|
||||||
entity.Property(e => e.YandexPlaylistOwnerUid).IsRequired().HasMaxLength(50);
|
entity.Property(e => e.YandexPlaylistOwnerUid).IsRequired().HasMaxLength(50);
|
||||||
entity.Property(e => e.Title).IsRequired().HasMaxLength(255);
|
entity.Property(e => e.Title).IsRequired().HasMaxLength(255);
|
||||||
@@ -77,5 +88,19 @@ public class ApplicationDbContext : IdentityDbContext<ApplicationUser, IdentityR
|
|||||||
.HasForeignKey(e => e.SessionId)
|
.HasForeignKey(e => e.SessionId)
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
builder.Entity<FavoritePlaylist>(entity =>
|
||||||
|
{
|
||||||
|
entity.HasKey(e => new { e.UserId, e.SharedPlaylistId });
|
||||||
|
entity.HasOne(e => e.User)
|
||||||
|
.WithMany(u => u.FavoritePlaylists)
|
||||||
|
.HasForeignKey(e => e.UserId)
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
entity.HasOne(e => e.SharedPlaylist)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey(e => e.SharedPlaylistId)
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
entity.Property(e => e.AddedAtUtc).IsRequired();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
547
PlaylistShared.Api/Data/Migrations/20260414094124_AddSharedPermissions.Designer.cs
generated
Normal file
547
PlaylistShared.Api/Data/Migrations/20260414094124_AddSharedPermissions.Designer.cs
generated
Normal file
@@ -0,0 +1,547 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using PlaylistShared.Api.Data;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace PlaylistShared.Api.Data.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(ApplicationDbContext))]
|
||||||
|
[Migration("20260414094124_AddSharedPermissions")]
|
||||||
|
partial class AddSharedPermissions
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "10.0.5")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||||
|
|
||||||
|
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("RoleNameIndex")
|
||||||
|
.HasFilter("[NormalizedName] IS NOT NULL");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoles", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<Guid>("RoleId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoleClaims", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserClaims", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("LoginProvider")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderKey")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderDisplayName")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.HasKey("LoginProvider", "ProviderKey");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserLogins", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<Guid>("RoleId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "RoleId");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserRoles", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("LoginProvider")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "LoginProvider", "Name");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserTokens", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.ApplicationUser", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<int>("AccessFailedCount")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<bool>("EmailConfirmed")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<bool>("LockoutEnabled")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||||
|
.HasColumnType("datetimeoffset");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedEmail")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedUserName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<bool>("PhoneNumberConfirmed")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<string>("RefreshToken")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("RefreshTokenExpiryUtc")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<string>("SecurityStamp")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<bool>("TwoFactorEnabled")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<string>("YandexAccessToken")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("YandexId")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("YandexRefreshToken")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("YandexTokenExpiryUtc")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedEmail")
|
||||||
|
.HasDatabaseName("EmailIndex");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedUserName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("UserNameIndex")
|
||||||
|
.HasFilter("[NormalizedUserName] IS NOT NULL");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUsers", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.SharedPlaylist", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<int>("AddPermission")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("CoverUrl")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid>("CreatorUserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsDeleted")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<int>("PlayPermission")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("RemovePermission")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("ShareToken")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(255)
|
||||||
|
.HasColumnType("nvarchar(255)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<int>("ViewPermission")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("YandexPlaylistKind")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("nvarchar(50)");
|
||||||
|
|
||||||
|
b.Property<string>("YandexPlaylistOwnerUid")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("nvarchar(50)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CreatorUserId");
|
||||||
|
|
||||||
|
b.HasIndex("ShareToken")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("SharedPlaylists");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.TrackAdditionLog", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedAtUtc")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid?>("AddedByUserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("SessionId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(449)");
|
||||||
|
|
||||||
|
b.Property<Guid>("SharedPlaylistId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("TrackId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("AddedByUserId");
|
||||||
|
|
||||||
|
b.HasIndex("SessionId");
|
||||||
|
|
||||||
|
b.HasIndex("SharedPlaylistId", "TrackId");
|
||||||
|
|
||||||
|
b.ToTable("TrackAdditionLogs");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.UserSession", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("SessionId")
|
||||||
|
.HasMaxLength(449)
|
||||||
|
.HasColumnType("nvarchar(449)");
|
||||||
|
|
||||||
|
b.Property<Guid?>("AssociatedUserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("ClientIpAddress")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("FirstSeenUtc")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastSeenUtc")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<string>("UserAgent")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.HasKey("SessionId");
|
||||||
|
|
||||||
|
b.HasIndex("AssociatedUserId");
|
||||||
|
|
||||||
|
b.ToTable("UserSessions");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("TrackRemovalLog", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<DateTime>("RemovedAtUtc")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid?>("RemovedByUserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("SessionId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(449)");
|
||||||
|
|
||||||
|
b.Property<Guid>("SharedPlaylistId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("TrackId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("RemovedByUserId");
|
||||||
|
|
||||||
|
b.HasIndex("SessionId");
|
||||||
|
|
||||||
|
b.HasIndex("SharedPlaylistId", "TrackId");
|
||||||
|
|
||||||
|
b.ToTable("TrackRemovalLogs");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.SharedPlaylist", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", "Creator")
|
||||||
|
.WithMany("OwnedPlaylists")
|
||||||
|
.HasForeignKey("CreatorUserId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Creator");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.TrackAdditionLog", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", "AddedByUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("AddedByUserId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.UserSession", "Session")
|
||||||
|
.WithMany("TrackAdditionLogs")
|
||||||
|
.HasForeignKey("SessionId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.SharedPlaylist", "SharedPlaylist")
|
||||||
|
.WithMany("TrackAdditionLogs")
|
||||||
|
.HasForeignKey("SharedPlaylistId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("AddedByUser");
|
||||||
|
|
||||||
|
b.Navigation("Session");
|
||||||
|
|
||||||
|
b.Navigation("SharedPlaylist");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.UserSession", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", "User")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("AssociatedUserId")
|
||||||
|
.OnDelete(DeleteBehavior.SetNull);
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("TrackRemovalLog", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", "RemovedByUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RemovedByUserId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.UserSession", "Session")
|
||||||
|
.WithMany("TrackRemovalLogs")
|
||||||
|
.HasForeignKey("SessionId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.SharedPlaylist", "SharedPlaylist")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("SharedPlaylistId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("RemovedByUser");
|
||||||
|
|
||||||
|
b.Navigation("Session");
|
||||||
|
|
||||||
|
b.Navigation("SharedPlaylist");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.ApplicationUser", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("OwnedPlaylists");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.SharedPlaylist", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("TrackAdditionLogs");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.UserSession", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("TrackAdditionLogs");
|
||||||
|
|
||||||
|
b.Navigation("TrackRemovalLogs");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace PlaylistShared.Api.Data.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddSharedPermissions : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "PlayPermission",
|
||||||
|
table: "SharedPlaylists",
|
||||||
|
type: "int",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "PlayPermission",
|
||||||
|
table: "SharedPlaylists");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
591
PlaylistShared.Api/Data/Migrations/20260414111229_AddUserFavorites.Designer.cs
generated
Normal file
591
PlaylistShared.Api/Data/Migrations/20260414111229_AddUserFavorites.Designer.cs
generated
Normal file
@@ -0,0 +1,591 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using PlaylistShared.Api.Data;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace PlaylistShared.Api.Data.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(ApplicationDbContext))]
|
||||||
|
[Migration("20260414111229_AddUserFavorites")]
|
||||||
|
partial class AddUserFavorites
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "10.0.5")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||||
|
|
||||||
|
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("RoleNameIndex")
|
||||||
|
.HasFilter("[NormalizedName] IS NOT NULL");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoles", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<Guid>("RoleId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoleClaims", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserClaims", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("LoginProvider")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderKey")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderDisplayName")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.HasKey("LoginProvider", "ProviderKey");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserLogins", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<Guid>("RoleId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "RoleId");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserRoles", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("LoginProvider")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "LoginProvider", "Name");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserTokens", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.ApplicationUser", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<int>("AccessFailedCount")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<bool>("EmailConfirmed")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<bool>("LockoutEnabled")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||||
|
.HasColumnType("datetimeoffset");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedEmail")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedUserName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<bool>("PhoneNumberConfirmed")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<string>("RefreshToken")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("RefreshTokenExpiryUtc")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<string>("SecurityStamp")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<bool>("TwoFactorEnabled")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<string>("YandexAccessToken")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("YandexId")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("YandexRefreshToken")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("YandexTokenExpiryUtc")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedEmail")
|
||||||
|
.HasDatabaseName("EmailIndex");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedUserName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("UserNameIndex")
|
||||||
|
.HasFilter("[NormalizedUserName] IS NOT NULL");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUsers", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.FavoritePlaylist", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<Guid>("SharedPlaylistId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedAtUtc")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "SharedPlaylistId");
|
||||||
|
|
||||||
|
b.HasIndex("SharedPlaylistId");
|
||||||
|
|
||||||
|
b.ToTable("FavoritePlaylists");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.SharedPlaylist", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<int>("AddPermission")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("CoverUrl")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid>("CreatorUserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsDeleted")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<int>("PlayPermission")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("RemovePermission")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("ShareToken")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(255)
|
||||||
|
.HasColumnType("nvarchar(255)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<int>("ViewPermission")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("YandexPlaylistKind")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("nvarchar(50)");
|
||||||
|
|
||||||
|
b.Property<string>("YandexPlaylistOwnerUid")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("nvarchar(50)");
|
||||||
|
|
||||||
|
b.Property<string>("YandexPlaylistUuid")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("nvarchar(50)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CreatorUserId");
|
||||||
|
|
||||||
|
b.HasIndex("ShareToken")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("SharedPlaylists");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.TrackAdditionLog", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedAtUtc")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid?>("AddedByUserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("SessionId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(449)");
|
||||||
|
|
||||||
|
b.Property<Guid>("SharedPlaylistId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("TrackId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("AddedByUserId");
|
||||||
|
|
||||||
|
b.HasIndex("SessionId");
|
||||||
|
|
||||||
|
b.HasIndex("SharedPlaylistId", "TrackId");
|
||||||
|
|
||||||
|
b.ToTable("TrackAdditionLogs");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.UserSession", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("SessionId")
|
||||||
|
.HasMaxLength(449)
|
||||||
|
.HasColumnType("nvarchar(449)");
|
||||||
|
|
||||||
|
b.Property<Guid?>("AssociatedUserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("ClientIpAddress")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("FirstSeenUtc")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastSeenUtc")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<string>("UserAgent")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.HasKey("SessionId");
|
||||||
|
|
||||||
|
b.HasIndex("AssociatedUserId");
|
||||||
|
|
||||||
|
b.ToTable("UserSessions");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("TrackRemovalLog", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<DateTime>("RemovedAtUtc")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid?>("RemovedByUserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("SessionId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(449)");
|
||||||
|
|
||||||
|
b.Property<Guid>("SharedPlaylistId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("TrackId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("RemovedByUserId");
|
||||||
|
|
||||||
|
b.HasIndex("SessionId");
|
||||||
|
|
||||||
|
b.HasIndex("SharedPlaylistId", "TrackId");
|
||||||
|
|
||||||
|
b.ToTable("TrackRemovalLogs");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.FavoritePlaylist", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.SharedPlaylist", "SharedPlaylist")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("SharedPlaylistId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", "User")
|
||||||
|
.WithMany("FavoritePlaylists")
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("SharedPlaylist");
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.SharedPlaylist", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", "Creator")
|
||||||
|
.WithMany("OwnedPlaylists")
|
||||||
|
.HasForeignKey("CreatorUserId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Creator");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.TrackAdditionLog", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", "AddedByUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("AddedByUserId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.UserSession", "Session")
|
||||||
|
.WithMany("TrackAdditionLogs")
|
||||||
|
.HasForeignKey("SessionId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.SharedPlaylist", "SharedPlaylist")
|
||||||
|
.WithMany("TrackAdditionLogs")
|
||||||
|
.HasForeignKey("SharedPlaylistId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("AddedByUser");
|
||||||
|
|
||||||
|
b.Navigation("Session");
|
||||||
|
|
||||||
|
b.Navigation("SharedPlaylist");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.UserSession", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", "User")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("AssociatedUserId")
|
||||||
|
.OnDelete(DeleteBehavior.SetNull);
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("TrackRemovalLog", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", "RemovedByUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RemovedByUserId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.UserSession", "Session")
|
||||||
|
.WithMany("TrackRemovalLogs")
|
||||||
|
.HasForeignKey("SessionId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.SharedPlaylist", "SharedPlaylist")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("SharedPlaylistId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("RemovedByUser");
|
||||||
|
|
||||||
|
b.Navigation("Session");
|
||||||
|
|
||||||
|
b.Navigation("SharedPlaylist");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.ApplicationUser", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("FavoritePlaylists");
|
||||||
|
|
||||||
|
b.Navigation("OwnedPlaylists");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.SharedPlaylist", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("TrackAdditionLogs");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.UserSession", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("TrackAdditionLogs");
|
||||||
|
|
||||||
|
b.Navigation("TrackRemovalLogs");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace PlaylistShared.Api.Data.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddUserFavorites : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "YandexPlaylistUuid",
|
||||||
|
table: "SharedPlaylists",
|
||||||
|
type: "nvarchar(50)",
|
||||||
|
maxLength: 50,
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "FavoritePlaylists",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
UserId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
SharedPlaylistId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
AddedAtUtc = table.Column<DateTime>(type: "datetime2", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_FavoritePlaylists", x => new { x.UserId, x.SharedPlaylistId });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_FavoritePlaylists_AspNetUsers_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "AspNetUsers",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_FavoritePlaylists_SharedPlaylists_SharedPlaylistId",
|
||||||
|
column: x => x.SharedPlaylistId,
|
||||||
|
principalTable: "SharedPlaylists",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_FavoritePlaylists_SharedPlaylistId",
|
||||||
|
table: "FavoritePlaylists",
|
||||||
|
column: "SharedPlaylistId");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "FavoritePlaylists");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "YandexPlaylistUuid",
|
||||||
|
table: "SharedPlaylists");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
611
PlaylistShared.Api/Data/Migrations/20260414121754_AddDataProtectionKeys.Designer.cs
generated
Normal file
611
PlaylistShared.Api/Data/Migrations/20260414121754_AddDataProtectionKeys.Designer.cs
generated
Normal file
@@ -0,0 +1,611 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using PlaylistShared.Api.Data;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace PlaylistShared.Api.Data.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(ApplicationDbContext))]
|
||||||
|
[Migration("20260414121754_AddDataProtectionKeys")]
|
||||||
|
partial class AddDataProtectionKeys
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "10.0.5")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||||
|
|
||||||
|
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("FriendlyName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Xml")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("DataProtectionKeys");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("RoleNameIndex")
|
||||||
|
.HasFilter("[NormalizedName] IS NOT NULL");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoles", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<Guid>("RoleId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoleClaims", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserClaims", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("LoginProvider")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderKey")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderDisplayName")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.HasKey("LoginProvider", "ProviderKey");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserLogins", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<Guid>("RoleId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "RoleId");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserRoles", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("LoginProvider")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "LoginProvider", "Name");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserTokens", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.ApplicationUser", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<int>("AccessFailedCount")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<bool>("EmailConfirmed")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<bool>("LockoutEnabled")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||||
|
.HasColumnType("datetimeoffset");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedEmail")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedUserName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<bool>("PhoneNumberConfirmed")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<string>("RefreshToken")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("RefreshTokenExpiryUtc")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<string>("SecurityStamp")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<bool>("TwoFactorEnabled")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<string>("YandexAccessToken")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("YandexId")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("YandexRefreshToken")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("YandexTokenExpiryUtc")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedEmail")
|
||||||
|
.HasDatabaseName("EmailIndex");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedUserName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("UserNameIndex")
|
||||||
|
.HasFilter("[NormalizedUserName] IS NOT NULL");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUsers", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.FavoritePlaylist", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<Guid>("SharedPlaylistId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedAtUtc")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "SharedPlaylistId");
|
||||||
|
|
||||||
|
b.HasIndex("SharedPlaylistId");
|
||||||
|
|
||||||
|
b.ToTable("FavoritePlaylists");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.SharedPlaylist", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<int>("AddPermission")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("CoverUrl")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid>("CreatorUserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsDeleted")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<int>("PlayPermission")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("RemovePermission")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("ShareToken")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(255)
|
||||||
|
.HasColumnType("nvarchar(255)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<int>("ViewPermission")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("YandexPlaylistKind")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("nvarchar(50)");
|
||||||
|
|
||||||
|
b.Property<string>("YandexPlaylistOwnerUid")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("nvarchar(50)");
|
||||||
|
|
||||||
|
b.Property<string>("YandexPlaylistUuid")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("nvarchar(50)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CreatorUserId");
|
||||||
|
|
||||||
|
b.HasIndex("ShareToken")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("SharedPlaylists");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.TrackAdditionLog", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedAtUtc")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid?>("AddedByUserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("SessionId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(449)");
|
||||||
|
|
||||||
|
b.Property<Guid>("SharedPlaylistId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("TrackId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("AddedByUserId");
|
||||||
|
|
||||||
|
b.HasIndex("SessionId");
|
||||||
|
|
||||||
|
b.HasIndex("SharedPlaylistId", "TrackId");
|
||||||
|
|
||||||
|
b.ToTable("TrackAdditionLogs");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.UserSession", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("SessionId")
|
||||||
|
.HasMaxLength(449)
|
||||||
|
.HasColumnType("nvarchar(449)");
|
||||||
|
|
||||||
|
b.Property<Guid?>("AssociatedUserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("ClientIpAddress")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("FirstSeenUtc")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastSeenUtc")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<string>("UserAgent")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.HasKey("SessionId");
|
||||||
|
|
||||||
|
b.HasIndex("AssociatedUserId");
|
||||||
|
|
||||||
|
b.ToTable("UserSessions");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("TrackRemovalLog", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<DateTime>("RemovedAtUtc")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid?>("RemovedByUserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("SessionId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(449)");
|
||||||
|
|
||||||
|
b.Property<Guid>("SharedPlaylistId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("TrackId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("RemovedByUserId");
|
||||||
|
|
||||||
|
b.HasIndex("SessionId");
|
||||||
|
|
||||||
|
b.HasIndex("SharedPlaylistId", "TrackId");
|
||||||
|
|
||||||
|
b.ToTable("TrackRemovalLogs");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.FavoritePlaylist", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.SharedPlaylist", "SharedPlaylist")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("SharedPlaylistId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", "User")
|
||||||
|
.WithMany("FavoritePlaylists")
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("SharedPlaylist");
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.SharedPlaylist", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", "Creator")
|
||||||
|
.WithMany("OwnedPlaylists")
|
||||||
|
.HasForeignKey("CreatorUserId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Creator");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.TrackAdditionLog", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", "AddedByUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("AddedByUserId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.UserSession", "Session")
|
||||||
|
.WithMany("TrackAdditionLogs")
|
||||||
|
.HasForeignKey("SessionId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.SharedPlaylist", "SharedPlaylist")
|
||||||
|
.WithMany("TrackAdditionLogs")
|
||||||
|
.HasForeignKey("SharedPlaylistId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("AddedByUser");
|
||||||
|
|
||||||
|
b.Navigation("Session");
|
||||||
|
|
||||||
|
b.Navigation("SharedPlaylist");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.UserSession", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", "User")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("AssociatedUserId")
|
||||||
|
.OnDelete(DeleteBehavior.SetNull);
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("TrackRemovalLog", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", "RemovedByUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RemovedByUserId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.UserSession", "Session")
|
||||||
|
.WithMany("TrackRemovalLogs")
|
||||||
|
.HasForeignKey("SessionId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.SharedPlaylist", "SharedPlaylist")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("SharedPlaylistId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("RemovedByUser");
|
||||||
|
|
||||||
|
b.Navigation("Session");
|
||||||
|
|
||||||
|
b.Navigation("SharedPlaylist");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.ApplicationUser", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("FavoritePlaylists");
|
||||||
|
|
||||||
|
b.Navigation("OwnedPlaylists");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.SharedPlaylist", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("TrackAdditionLogs");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.UserSession", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("TrackAdditionLogs");
|
||||||
|
|
||||||
|
b.Navigation("TrackRemovalLogs");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace PlaylistShared.Api.Data.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddDataProtectionKeys : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "DataProtectionKeys",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "int", nullable: false)
|
||||||
|
.Annotation("SqlServer:Identity", "1, 1"),
|
||||||
|
FriendlyName = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||||
|
Xml = table.Column<string>(type: "nvarchar(max)", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_DataProtectionKeys", x => x.Id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "DataProtectionKeys");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,6 +22,26 @@ namespace PlaylistShared.Api.Data.Migrations
|
|||||||
|
|
||||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("FriendlyName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Xml")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("DataProtectionKeys");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", b =>
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
@@ -237,6 +257,24 @@ namespace PlaylistShared.Api.Data.Migrations
|
|||||||
b.ToTable("AspNetUsers", (string)null);
|
b.ToTable("AspNetUsers", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.FavoritePlaylist", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<Guid>("SharedPlaylistId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedAtUtc")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "SharedPlaylistId");
|
||||||
|
|
||||||
|
b.HasIndex("SharedPlaylistId");
|
||||||
|
|
||||||
|
b.ToTable("FavoritePlaylists");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("PlaylistShared.Api.Entities.SharedPlaylist", b =>
|
modelBuilder.Entity("PlaylistShared.Api.Entities.SharedPlaylist", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
@@ -261,6 +299,9 @@ namespace PlaylistShared.Api.Data.Migrations
|
|||||||
b.Property<bool>("IsDeleted")
|
b.Property<bool>("IsDeleted")
|
||||||
.HasColumnType("bit");
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<int>("PlayPermission")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<int>("RemovePermission")
|
b.Property<int>("RemovePermission")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
@@ -289,6 +330,11 @@ namespace PlaylistShared.Api.Data.Migrations
|
|||||||
.HasMaxLength(50)
|
.HasMaxLength(50)
|
||||||
.HasColumnType("nvarchar(50)");
|
.HasColumnType("nvarchar(50)");
|
||||||
|
|
||||||
|
b.Property<string>("YandexPlaylistUuid")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("nvarchar(50)");
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.HasIndex("CreatorUserId");
|
b.HasIndex("CreatorUserId");
|
||||||
@@ -446,6 +492,25 @@ namespace PlaylistShared.Api.Data.Migrations
|
|||||||
.IsRequired();
|
.IsRequired();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.FavoritePlaylist", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.SharedPlaylist", "SharedPlaylist")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("SharedPlaylistId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", "User")
|
||||||
|
.WithMany("FavoritePlaylists")
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("SharedPlaylist");
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("PlaylistShared.Api.Entities.SharedPlaylist", b =>
|
modelBuilder.Entity("PlaylistShared.Api.Entities.SharedPlaylist", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", "Creator")
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", "Creator")
|
||||||
@@ -521,6 +586,8 @@ namespace PlaylistShared.Api.Data.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("PlaylistShared.Api.Entities.ApplicationUser", b =>
|
modelBuilder.Entity("PlaylistShared.Api.Entities.ApplicationUser", b =>
|
||||||
{
|
{
|
||||||
|
b.Navigation("FavoritePlaylists");
|
||||||
|
|
||||||
b.Navigation("OwnedPlaylists");
|
b.Navigation("OwnedPlaylists");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -25,4 +25,7 @@ public class ApplicationUser : IdentityUser<Guid>
|
|||||||
|
|
||||||
/// <summary>Плейлисты, созданные пользователем.</summary>
|
/// <summary>Плейлисты, созданные пользователем.</summary>
|
||||||
public ICollection<SharedPlaylist> OwnedPlaylists { get; set; } = new List<SharedPlaylist>();
|
public ICollection<SharedPlaylist> OwnedPlaylists { get; set; } = new List<SharedPlaylist>();
|
||||||
|
|
||||||
|
/// <summary>Избранные плейлисты.</summary>
|
||||||
|
public ICollection<FavoritePlaylist> FavoritePlaylists { get; set; } = new List<FavoritePlaylist>();
|
||||||
}
|
}
|
||||||
13
PlaylistShared.Api/Entities/FavoritePlaylist.cs
Normal file
13
PlaylistShared.Api/Entities/FavoritePlaylist.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
namespace PlaylistShared.Api.Entities;
|
||||||
|
|
||||||
|
/// <summary>Избранный расшаренный плейлист пользователя.</summary>
|
||||||
|
public class FavoritePlaylist
|
||||||
|
{
|
||||||
|
public Guid UserId { get; set; }
|
||||||
|
public Guid SharedPlaylistId { get; set; }
|
||||||
|
public DateTime AddedAtUtc { get; set; }
|
||||||
|
|
||||||
|
// Навигационные свойства
|
||||||
|
public ApplicationUser User { get; set; } = null!;
|
||||||
|
public SharedPlaylist SharedPlaylist { get; set; } = null!;
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ public class SharedPlaylist
|
|||||||
{
|
{
|
||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
public Guid CreatorUserId { get; set; }
|
public Guid CreatorUserId { get; set; }
|
||||||
|
public string YandexPlaylistUuid { get; set; } = null!;
|
||||||
public string YandexPlaylistKind { get; set; } = null!;
|
public string YandexPlaylistKind { get; set; } = null!;
|
||||||
public string YandexPlaylistOwnerUid { get; set; } = null!;
|
public string YandexPlaylistOwnerUid { get; set; } = null!;
|
||||||
public string Title { get; set; } = null!;
|
public string Title { get; set; } = null!;
|
||||||
@@ -17,6 +18,7 @@ public class SharedPlaylist
|
|||||||
public bool IsDeleted { get; set; }
|
public bool IsDeleted { get; set; }
|
||||||
public string ShareToken { get; set; } = null!;
|
public string ShareToken { get; set; } = null!;
|
||||||
public ViewPermission ViewPermission { get; set; }
|
public ViewPermission ViewPermission { get; set; }
|
||||||
|
public ViewPermission PlayPermission { get; set; }
|
||||||
public EditPermission AddPermission { get; set; }
|
public EditPermission AddPermission { get; set; }
|
||||||
public EditPermission RemovePermission { get; set; }
|
public EditPermission RemovePermission { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
using AutoMapper;
|
|
||||||
using PlaylistShared.Api.Entities;
|
|
||||||
using PlaylistShared.Shared.Auth;
|
|
||||||
using PlaylistShared.Shared.Shared;
|
|
||||||
|
|
||||||
namespace PlaylistShared.Api.Mapping;
|
|
||||||
|
|
||||||
public class AppMappingProfile : Profile
|
|
||||||
{
|
|
||||||
public AppMappingProfile()
|
|
||||||
{
|
|
||||||
CreateMap<SharedPlaylist, SharedPlaylistDto>()
|
|
||||||
.ForMember(dest => dest.Creator, opt => opt.MapFrom(src => src.Creator));
|
|
||||||
CreateMap<ApplicationUser, ApplicationUserDto>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -10,9 +10,9 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AutoMapper" Version="16.1.1" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.5" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.5" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="10.0.5" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="10.0.5" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.DataProtection.EntityFrameworkCore" Version="10.0.5" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="10.0.5" />
|
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="10.0.5" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="10.0.5" />
|
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="10.0.5" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.5" />
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.5" />
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
|
|
||||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
|
using Microsoft.AspNetCore.DataProtection;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.IdentityModel.Tokens;
|
using Microsoft.IdentityModel.Tokens;
|
||||||
using PlaylistShared.Api.Data;
|
using PlaylistShared.Api.Data;
|
||||||
using PlaylistShared.Api.Entities;
|
using PlaylistShared.Api.Entities;
|
||||||
using PlaylistShared.Api.Mapping;
|
|
||||||
using PlaylistShared.Api.Services;
|
using PlaylistShared.Api.Services;
|
||||||
using System.IdentityModel.Tokens.Jwt;
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@@ -23,7 +23,8 @@ public class Program
|
|||||||
|
|
||||||
// DbContext
|
// DbContext
|
||||||
builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
||||||
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));// Identity
|
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
|
||||||
|
// Identity
|
||||||
builder.Services.AddIdentity<ApplicationUser, IdentityRole<Guid>>(options =>
|
builder.Services.AddIdentity<ApplicationUser, IdentityRole<Guid>>(options =>
|
||||||
{
|
{
|
||||||
options.User.RequireUniqueEmail = true;
|
options.User.RequireUniqueEmail = true;
|
||||||
@@ -88,30 +89,24 @@ public class Program
|
|||||||
|
|
||||||
builder.Services.AddHttpContextAccessor();
|
builder.Services.AddHttpContextAccessor();
|
||||||
builder.Services.AddAuthorization();
|
builder.Services.AddAuthorization();
|
||||||
builder.Services.AddAutoMapper(t => t.AddProfile<AppMappingProfile>());
|
|
||||||
builder.Services.AddScoped<JwtService>();
|
builder.Services.AddScoped<JwtService>();
|
||||||
builder.Services.AddScoped<UserSessionService>();
|
builder.Services.AddScoped<UserSessionService>();
|
||||||
builder.Services.AddScoped<YandexMusicService>();
|
builder.Services.AddScoped<YandexMusicService>();
|
||||||
builder.Services.AddScoped<SharedPlaylistService>();
|
builder.Services.AddScoped<SharedPlaylistService>();
|
||||||
builder.Services.AddScoped<TrackAdditionLogService>();
|
builder.Services.AddScoped<TrackAdditionLogService>();
|
||||||
builder.Services.AddScoped<TrackRemovalLogService>();
|
builder.Services.AddScoped<TrackRemovalLogService>();
|
||||||
builder.Services.AddDataProtection();
|
builder.Services.AddScoped<FavoritesService>();
|
||||||
|
builder.Services.AddDataProtection()
|
||||||
|
.PersistKeysToDbContext<ApplicationDbContext>()
|
||||||
|
.SetApplicationName("PlaylistShared.Api");
|
||||||
|
|
||||||
builder.Services.AddHttpClient();
|
builder.Services.AddHttpClient();
|
||||||
|
|
||||||
builder.Services.AddCors(options =>
|
builder.Services.AddCors(options =>
|
||||||
{
|
{
|
||||||
options.AddPolicy("Development", policy =>
|
|
||||||
{
|
|
||||||
policy.WithOrigins("http://localhost:5053", "https://localhost:7225", "http://localhost:5181", "https://api.playlistshare.frigat.duckdns.org", "https://playlistshare.frigat.duckdns.org")
|
|
||||||
.AllowAnyMethod()
|
|
||||||
.AllowAnyHeader()
|
|
||||||
.AllowCredentials();
|
|
||||||
});
|
|
||||||
|
|
||||||
options.AddPolicy("Production", policy =>
|
options.AddPolicy("Production", policy =>
|
||||||
{
|
{
|
||||||
policy.WithOrigins("https://api.playlistshare.frigat.duckdns.org", "https://playlistshare.frigat.duckdns.org")
|
policy.WithOrigins(builder.Configuration.GetSection("Cors:Origins").Get<string[]>())
|
||||||
.AllowAnyMethod()
|
.AllowAnyMethod()
|
||||||
.AllowAnyHeader()
|
.AllowAnyHeader()
|
||||||
.AllowCredentials();
|
.AllowCredentials();
|
||||||
@@ -131,17 +126,14 @@ public class Program
|
|||||||
app.UseSwagger();
|
app.UseSwagger();
|
||||||
app.UseSwaggerUI();
|
app.UseSwaggerUI();
|
||||||
|
|
||||||
if (app.Environment.IsDevelopment())
|
|
||||||
{
|
|
||||||
app.UseCors("Development");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
app.UseHttpsRedirection();
|
|
||||||
app.UseCors("Production");
|
app.UseCors("Production");
|
||||||
|
|
||||||
|
if (!app.Environment.IsDevelopment())
|
||||||
|
{
|
||||||
|
app.UseHttpsRedirection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
app.UseSession();
|
app.UseSession();
|
||||||
app.UseAuthentication();
|
app.UseAuthentication();
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|||||||
100
PlaylistShared.Api/Services/FavoritesService.cs
Normal file
100
PlaylistShared.Api/Services/FavoritesService.cs
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using PlaylistShared.Api.Data;
|
||||||
|
using PlaylistShared.Api.Entities;
|
||||||
|
using PlaylistShared.Shared.Shared;
|
||||||
|
|
||||||
|
namespace PlaylistShared.Api.Services;
|
||||||
|
|
||||||
|
public class FavoritesService
|
||||||
|
{
|
||||||
|
private readonly ApplicationDbContext _db;
|
||||||
|
private readonly SharedPlaylistService _sharedPlaylistService;
|
||||||
|
|
||||||
|
public FavoritesService(ApplicationDbContext db, SharedPlaylistService sharedPlaylistService)
|
||||||
|
{
|
||||||
|
_db = db;
|
||||||
|
_sharedPlaylistService = sharedPlaylistService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> IsFavoriteAsync(Guid userId, string shareToken)
|
||||||
|
{
|
||||||
|
var playlist = await _sharedPlaylistService.GetEntityByTokenAsync(shareToken);
|
||||||
|
if (playlist == null) return false;
|
||||||
|
return await _db.FavoritePlaylists
|
||||||
|
.AnyAsync(f => f.UserId == userId && f.SharedPlaylistId == playlist.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task AddFavoriteAsync(Guid userId, string shareToken)
|
||||||
|
{
|
||||||
|
var playlist = await _sharedPlaylistService.GetEntityByTokenAsync(shareToken);
|
||||||
|
if (playlist == null)
|
||||||
|
throw new ArgumentException("Playlist not found");
|
||||||
|
|
||||||
|
var exists = await _db.FavoritePlaylists
|
||||||
|
.AnyAsync(f => f.UserId == userId && f.SharedPlaylistId == playlist.Id);
|
||||||
|
if (exists) return;
|
||||||
|
|
||||||
|
var favorite = new FavoritePlaylist
|
||||||
|
{
|
||||||
|
UserId = userId,
|
||||||
|
SharedPlaylistId = playlist.Id,
|
||||||
|
AddedAtUtc = DateTime.UtcNow
|
||||||
|
};
|
||||||
|
_db.FavoritePlaylists.Add(favorite);
|
||||||
|
await _db.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task RemoveFavoriteAsync(Guid userId, string shareToken)
|
||||||
|
{
|
||||||
|
var playlist = await _sharedPlaylistService.GetEntityByTokenAsync(shareToken);
|
||||||
|
if (playlist == null) return;
|
||||||
|
|
||||||
|
var favorite = await _db.FavoritePlaylists
|
||||||
|
.FirstOrDefaultAsync(f => f.UserId == userId && f.SharedPlaylistId == playlist.Id);
|
||||||
|
if (favorite != null)
|
||||||
|
{
|
||||||
|
_db.FavoritePlaylists.Remove(favorite);
|
||||||
|
await _db.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<SharedPlaylistDto>> GetUserFavoritesAsync(Guid userId)
|
||||||
|
{
|
||||||
|
var favoritePlaylists = await _db.FavoritePlaylists
|
||||||
|
.Include(f => f.SharedPlaylist)
|
||||||
|
.ThenInclude(sp => sp.Creator)
|
||||||
|
.Where(f => f.UserId == userId)
|
||||||
|
.OrderByDescending(f => f.AddedAtUtc)
|
||||||
|
.Select(f => f.SharedPlaylist)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
// Маппинг в DTO (можно использовать AutoMapper, но для простоты сделаем вручную)
|
||||||
|
return favoritePlaylists.Select(sp => new SharedPlaylistDto
|
||||||
|
{
|
||||||
|
Id = sp.Id,
|
||||||
|
CreatorUserId = sp.CreatorUserId,
|
||||||
|
YandexPlaylistUuid = sp.YandexPlaylistUuid,
|
||||||
|
YandexPlaylistKind = sp.YandexPlaylistKind,
|
||||||
|
YandexPlaylistOwnerUid = sp.YandexPlaylistOwnerUid,
|
||||||
|
Title = sp.Title,
|
||||||
|
Description = sp.Description,
|
||||||
|
CoverUrl = sp.CoverUrl,
|
||||||
|
CreatedAt = sp.CreatedAt,
|
||||||
|
UpdatedAt = sp.UpdatedAt,
|
||||||
|
IsDeleted = sp.IsDeleted,
|
||||||
|
ShareToken = sp.ShareToken,
|
||||||
|
ViewPermission = sp.ViewPermission,
|
||||||
|
PlayPermission = sp.PlayPermission,
|
||||||
|
AddPermission = sp.AddPermission,
|
||||||
|
RemovePermission = sp.RemovePermission,
|
||||||
|
Creator = sp.Creator != null ? new Shared.Auth.ApplicationUserDto
|
||||||
|
{
|
||||||
|
Id = sp.Creator.Id,
|
||||||
|
UserName = sp.Creator.UserName,
|
||||||
|
Email = sp.Creator.Email,
|
||||||
|
YandexId = sp.Creator.YandexId,
|
||||||
|
DisplayName = sp.Creator.UserName
|
||||||
|
} : null
|
||||||
|
}).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using AutoMapper;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using PlaylistShared.Api.Data;
|
using PlaylistShared.Api.Data;
|
||||||
using PlaylistShared.Api.Entities;
|
using PlaylistShared.Api.Entities;
|
||||||
|
using PlaylistShared.Shared.Auth;
|
||||||
using PlaylistShared.Shared.Enums;
|
using PlaylistShared.Shared.Enums;
|
||||||
using PlaylistShared.Shared.Playlist;
|
using PlaylistShared.Shared.Playlist;
|
||||||
using PlaylistShared.Shared.Shared;
|
using PlaylistShared.Shared.Shared;
|
||||||
@@ -11,13 +11,11 @@ namespace PlaylistShared.Api.Services;
|
|||||||
public class SharedPlaylistService
|
public class SharedPlaylistService
|
||||||
{
|
{
|
||||||
private readonly ApplicationDbContext _db;
|
private readonly ApplicationDbContext _db;
|
||||||
private readonly IMapper _mapper;
|
|
||||||
private readonly TrackAdditionLogService _trackLogService;
|
private readonly TrackAdditionLogService _trackLogService;
|
||||||
|
|
||||||
public SharedPlaylistService(ApplicationDbContext db, IMapper mapper, TrackAdditionLogService trackLogService)
|
public SharedPlaylistService(ApplicationDbContext db, TrackAdditionLogService trackLogService)
|
||||||
{
|
{
|
||||||
_db = db;
|
_db = db;
|
||||||
_mapper = mapper;
|
|
||||||
_trackLogService = trackLogService;
|
_trackLogService = trackLogService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,6 +25,7 @@ public class SharedPlaylistService
|
|||||||
{
|
{
|
||||||
Id = Guid.NewGuid(),
|
Id = Guid.NewGuid(),
|
||||||
CreatorUserId = creatorUserId,
|
CreatorUserId = creatorUserId,
|
||||||
|
YandexPlaylistUuid = dto.YandexPlaylistUuid,
|
||||||
YandexPlaylistKind = dto.YandexPlaylistKind,
|
YandexPlaylistKind = dto.YandexPlaylistKind,
|
||||||
YandexPlaylistOwnerUid = dto.YandexPlaylistOwnerUid,
|
YandexPlaylistOwnerUid = dto.YandexPlaylistOwnerUid,
|
||||||
Title = dto.Title,
|
Title = dto.Title,
|
||||||
@@ -34,13 +33,14 @@ public class SharedPlaylistService
|
|||||||
CreatedAt = DateTime.UtcNow,
|
CreatedAt = DateTime.UtcNow,
|
||||||
UpdatedAt = DateTime.UtcNow,
|
UpdatedAt = DateTime.UtcNow,
|
||||||
ShareToken = GenerateToken(),
|
ShareToken = GenerateToken(),
|
||||||
|
PlayPermission = dto.PlayPermission,
|
||||||
ViewPermission = dto.ViewPermission,
|
ViewPermission = dto.ViewPermission,
|
||||||
AddPermission = dto.AddPermission,
|
AddPermission = dto.AddPermission,
|
||||||
RemovePermission = dto.RemovePermission
|
RemovePermission = dto.RemovePermission
|
||||||
};
|
};
|
||||||
_db.SharedPlaylists.Add(entity);
|
_db.SharedPlaylists.Add(entity);
|
||||||
await _db.SaveChangesAsync();
|
await _db.SaveChangesAsync();
|
||||||
return _mapper.Map<SharedPlaylistDto>(entity);
|
return MapToDto(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<SharedPlaylistDto?> GetByTokenAsync(string token)
|
public async Task<SharedPlaylistDto?> GetByTokenAsync(string token)
|
||||||
@@ -48,7 +48,7 @@ public class SharedPlaylistService
|
|||||||
var entity = await _db.SharedPlaylists
|
var entity = await _db.SharedPlaylists
|
||||||
.Include(sp => sp.Creator)
|
.Include(sp => sp.Creator)
|
||||||
.FirstOrDefaultAsync(sp => sp.ShareToken == token && !sp.IsDeleted);
|
.FirstOrDefaultAsync(sp => sp.ShareToken == token && !sp.IsDeleted);
|
||||||
return entity == null ? null : _mapper.Map<SharedPlaylistDto>(entity);
|
return entity == null ? null : MapToDto(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<SharedPlaylist?> GetEntityByTokenAsync(string token)
|
public async Task<SharedPlaylist?> GetEntityByTokenAsync(string token)
|
||||||
@@ -63,11 +63,12 @@ public class SharedPlaylistService
|
|||||||
var entity = await _db.SharedPlaylists.FindAsync(playlistId);
|
var entity = await _db.SharedPlaylists.FindAsync(playlistId);
|
||||||
if (entity == null) return null;
|
if (entity == null) return null;
|
||||||
entity.ViewPermission = dto.ViewPermission;
|
entity.ViewPermission = dto.ViewPermission;
|
||||||
|
entity.PlayPermission = dto.PlayPermission;
|
||||||
entity.AddPermission = dto.AddPermission;
|
entity.AddPermission = dto.AddPermission;
|
||||||
entity.RemovePermission = dto.RemovePermission;
|
entity.RemovePermission = dto.RemovePermission;
|
||||||
entity.UpdatedAt = DateTime.UtcNow;
|
entity.UpdatedAt = DateTime.UtcNow;
|
||||||
await _db.SaveChangesAsync();
|
await _db.SaveChangesAsync();
|
||||||
return _mapper.Map<SharedPlaylistDto>(entity);
|
return MapToDto(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> DeleteAsync(Guid playlistId)
|
public async Task<bool> DeleteAsync(Guid playlistId)
|
||||||
@@ -80,6 +81,18 @@ public class SharedPlaylistService
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<bool> CanPlayAsync(SharedPlaylist playlist, Guid? currentUserId)
|
||||||
|
{
|
||||||
|
if (currentUserId == playlist.CreatorUserId) return true;
|
||||||
|
return playlist.PlayPermission == ViewPermission.Everyone ||
|
||||||
|
(playlist.PlayPermission == ViewPermission.AuthorizedOnly && currentUserId.HasValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> CanPlayEveryoneAsync(SharedPlaylist playlist)
|
||||||
|
{
|
||||||
|
return playlist.PlayPermission == ViewPermission.Everyone;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<bool> CanViewAsync(SharedPlaylist playlist, Guid? currentUserId)
|
public async Task<bool> CanViewAsync(SharedPlaylist playlist, Guid? currentUserId)
|
||||||
{
|
{
|
||||||
if (currentUserId == playlist.CreatorUserId) return true;
|
if (currentUserId == playlist.CreatorUserId) return true;
|
||||||
@@ -129,4 +142,36 @@ public class SharedPlaylistService
|
|||||||
.Where(sp => sp.CreatorUserId == userId && !sp.IsDeleted)
|
.Where(sp => sp.CreatorUserId == userId && !sp.IsDeleted)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ручное маппинг сущности в DTO
|
||||||
|
private SharedPlaylistDto MapToDto(SharedPlaylist entity)
|
||||||
|
{
|
||||||
|
return new SharedPlaylistDto
|
||||||
|
{
|
||||||
|
Id = entity.Id,
|
||||||
|
CreatorUserId = entity.CreatorUserId,
|
||||||
|
YandexPlaylistUuid = entity.YandexPlaylistUuid,
|
||||||
|
YandexPlaylistKind = entity.YandexPlaylistKind,
|
||||||
|
YandexPlaylistOwnerUid = entity.YandexPlaylistOwnerUid,
|
||||||
|
Title = entity.Title,
|
||||||
|
Description = entity.Description,
|
||||||
|
CoverUrl = entity.CoverUrl,
|
||||||
|
CreatedAt = entity.CreatedAt,
|
||||||
|
UpdatedAt = entity.UpdatedAt,
|
||||||
|
IsDeleted = entity.IsDeleted,
|
||||||
|
ShareToken = entity.ShareToken,
|
||||||
|
ViewPermission = entity.ViewPermission,
|
||||||
|
PlayPermission = entity.PlayPermission,
|
||||||
|
AddPermission = entity.AddPermission,
|
||||||
|
RemovePermission = entity.RemovePermission,
|
||||||
|
Creator = entity.Creator != null ? new ApplicationUserDto
|
||||||
|
{
|
||||||
|
Id = entity.Creator.Id,
|
||||||
|
UserName = entity.Creator.UserName ?? string.Empty,
|
||||||
|
Email = entity.Creator.Email,
|
||||||
|
YandexId = entity.Creator.YandexId,
|
||||||
|
DisplayName = entity.Creator.UserName
|
||||||
|
} : null
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -7,6 +7,12 @@
|
|||||||
"Issuer": "PlaylistShared.Api",
|
"Issuer": "PlaylistShared.Api",
|
||||||
"Audience": "PlaylistShared.Client"
|
"Audience": "PlaylistShared.Client"
|
||||||
},
|
},
|
||||||
|
"Cors": {
|
||||||
|
"Origins": [
|
||||||
|
"https://api.playlistshare.frigat.duckdns.org",
|
||||||
|
"https://playlistshare.frigat.duckdns.org"
|
||||||
|
]
|
||||||
|
},
|
||||||
"Yandex": {
|
"Yandex": {
|
||||||
"ClientId": "0916685f8a3641ca8fc382dbccf77236",
|
"ClientId": "0916685f8a3641ca8fc382dbccf77236",
|
||||||
"ClientSecret": "f7398893cd814f8b84b85aeb2a0a6698"
|
"ClientSecret": "f7398893cd814f8b84b85aeb2a0a6698"
|
||||||
|
|||||||
@@ -59,12 +59,16 @@
|
|||||||
|
|
||||||
[Inject] protected IJSRuntime JS { get; set; } = null!;
|
[Inject] protected IJSRuntime JS { get; set; } = null!;
|
||||||
[Inject] private TokenStorage TokenStorage { get; set; } = null!;
|
[Inject] private TokenStorage TokenStorage { get; set; } = null!;
|
||||||
|
[Inject] private PlayerStorage PlayerStorage { get; set; } = null!;
|
||||||
[Inject] private AuthenticationStateProvider AuthProvider { get; set; } = null!;
|
[Inject] private AuthenticationStateProvider AuthProvider { get; set; } = null!;
|
||||||
[Inject] private ISnackbar Snackbar { get; set; } = null!;
|
[Inject] private ISnackbar Snackbar { get; set; } = null!;
|
||||||
|
|
||||||
/// <summary>Требовать ли авторизацию для воспроизведения (по умолчанию true).</summary>
|
/// <summary>Требовать ли авторизацию для воспроизведения (по умолчанию true).</summary>
|
||||||
[Parameter] public bool RequireAuth { get; set; } = true;
|
[Parameter] public bool RequireAuth { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>ID расшаренного плейлиста.</summary>
|
||||||
|
[Parameter] public string SharedPlaylistId { get; set; } = string.Empty;
|
||||||
|
|
||||||
/// <summary>Событие при завершении трека.</summary>
|
/// <summary>Событие при завершении трека.</summary>
|
||||||
[Parameter] public EventCallback OnTrackEnded { get; set; }
|
[Parameter] public EventCallback OnTrackEnded { get; set; }
|
||||||
|
|
||||||
@@ -73,6 +77,8 @@
|
|||||||
if (firstRender)
|
if (firstRender)
|
||||||
{
|
{
|
||||||
await EnsureAudioModuleAsync();
|
await EnsureAudioModuleAsync();
|
||||||
|
await ChangeVolume(await PlayerStorage.GetVolumeAsync());
|
||||||
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,16 +145,16 @@
|
|||||||
var tokens = await TokenStorage.GetTokensAsync();
|
var tokens = await TokenStorage.GetTokensAsync();
|
||||||
var accessToken = tokens.token;
|
var accessToken = tokens.token;
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(accessToken))
|
if (string.IsNullOrWhiteSpace(accessToken) && string.IsNullOrWhiteSpace(SharedPlaylistId))
|
||||||
{
|
{
|
||||||
Snackbar.Add("Токен авторизации не найден. Пожалуйста, войдите заново.", Severity.Error);
|
Snackbar.Add("Токен авторизации не найден. Пожалуйста, войдите заново.", Severity.Error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var streamUrl = new Uri(Http.BaseAddress, $"/api/audio/track/{trackId}").ToString();
|
var streamUrl = new Uri(Http.BaseAddress!, $"/api/audio/track/{trackId}").ToString();
|
||||||
|
|
||||||
await EnsureAudioModuleAsync();
|
await EnsureAudioModuleAsync();
|
||||||
await _audioElement!.InvokeVoidAsync("loadAndPlay", streamUrl, accessToken);
|
await _audioElement!.InvokeVoidAsync("loadAndPlay", streamUrl, accessToken, SharedPlaylistId);
|
||||||
_isPlaying = true;
|
_isPlaying = true;
|
||||||
StartProgressTimer();
|
StartProgressTimer();
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
@@ -223,6 +229,8 @@
|
|||||||
var volume = value / 100;
|
var volume = value / 100;
|
||||||
await _audioElement.InvokeVoidAsync("setVolume", volume);
|
await _audioElement.InvokeVoidAsync("setVolume", volume);
|
||||||
_isMuted = false;
|
_isMuted = false;
|
||||||
|
_currentVolume = value;
|
||||||
|
await PlayerStorage.SetVolumeAsync(value);
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|||||||
75
PlaylistShared.Pwa/Components/YandexTokenInstructions.razor
Normal file
75
PlaylistShared.Pwa/Components/YandexTokenInstructions.razor
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
@* Компонент с инструкцией по получению токена Яндекс.Музыки *@
|
||||||
|
|
||||||
|
<MudContainer Class="pa-4">
|
||||||
|
<MudText Typo="Typo.body2" GutterBottom>
|
||||||
|
Токен нужен для доступа к вашим плейлистам. Получите его один раз:
|
||||||
|
</MudText>
|
||||||
|
|
||||||
|
<!-- Вертикальный список шагов -->
|
||||||
|
<div class="instruction-steps">
|
||||||
|
<div class="step-item">
|
||||||
|
<div class="step-number">1</div>
|
||||||
|
<div class="step-content">
|
||||||
|
Перейдите по <MudLink Href="https://oauth.yandex.ru/authorize?response_type=token&client_id=23cabbbdc6cd418abb4b39c32c41195d" Target="_blank">ссылке</MudLink>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="step-item">
|
||||||
|
<div class="step-number">2</div>
|
||||||
|
<div class="step-content">
|
||||||
|
Авторизуйтесь в Яндексе (если ещё не вошли)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="step-item">
|
||||||
|
<div class="step-number">3</div>
|
||||||
|
<div class="step-content">
|
||||||
|
Нажмите «Разрешить»
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="step-item">
|
||||||
|
<div class="step-number">4</div>
|
||||||
|
<div class="step-content">
|
||||||
|
Скопируйте <strong>access_token</strong> из адресной строки после перенаправления
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<MudAlert Severity="Severity.Info" Class="mt-4">
|
||||||
|
Пример: <code>https://music.yandex.ru/#access_token=ВАШ_ТОКЕН&...</code>
|
||||||
|
</MudAlert>
|
||||||
|
|
||||||
|
<MudAlert Severity="Severity.Warning" Class="mt-2">
|
||||||
|
Токен даёт доступ к вашим плейлистам. Никому его не сообщайте.
|
||||||
|
</MudAlert>
|
||||||
|
|
||||||
|
<MudAlert Severity="Severity.Success" Class="mt-2" Icon="@Icons.Material.Filled.Security">
|
||||||
|
Ваш токен сохраняется в зашифрованном виде и никому не передаётся.
|
||||||
|
</MudAlert>
|
||||||
|
</MudContainer>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.instruction-steps {
|
||||||
|
margin: 16px 0;
|
||||||
|
}
|
||||||
|
.step-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
.step-number {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
background-color: var(--mud-palette-primary);
|
||||||
|
color: white;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-weight: bold;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.step-content {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -2,18 +2,23 @@
|
|||||||
|
|
||||||
<AuthorizeView>
|
<AuthorizeView>
|
||||||
<Authorized>
|
<Authorized>
|
||||||
<MudText Typo="Typo.body2" Class="d-inline mr-2">Здравствуйте, @context.User.Identity?.Name!</MudText>
|
<MudMenu Label="@context.User.Identity?.Name" Variant="Variant.Text" Color="Color.Inherit" Class="user-menu">
|
||||||
<MudButton Variant="Variant.Text" Color="Color.Inherit" OnClick="BeginLogOut">Выйти</MudButton>
|
<MudMenuItem OnClick="GoToProfile">Профиль</MudMenuItem>
|
||||||
|
<MudMenuItem OnClick="BeginLogOut">Выйти</MudMenuItem>
|
||||||
|
</MudMenu>
|
||||||
</Authorized>
|
</Authorized>
|
||||||
<NotAuthorized>
|
<NotAuthorized>
|
||||||
<MudLink Href="/login" Color="Color.Inherit" Underline="Underline.Hover" Typo="Typo.body2">Вход</MudLink>
|
<MudLink Href="/login" Color="Color.Inherit" Underline="Underline.Hover" Typo="Typo.body2">Вход</MudLink>
|
||||||
<MudText Class="d-inline mx-1">|</MudText>
|
|
||||||
<MudLink Href="/register" Color="Color.Inherit" Underline="Underline.Hover" Typo="Typo.body2">Регистрация</MudLink>
|
|
||||||
</NotAuthorized>
|
</NotAuthorized>
|
||||||
</AuthorizeView>
|
</AuthorizeView>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
public void BeginLogOut()
|
private void GoToProfile()
|
||||||
|
{
|
||||||
|
Navigation.NavigateTo("/profile");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BeginLogOut()
|
||||||
{
|
{
|
||||||
Navigation.NavigateTo("/logout");
|
Navigation.NavigateTo("/logout");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,9 @@
|
|||||||
<MudNavLink Href="/" Match="NavLinkMatch.All" Icon="@Icons.Material.Filled.Home">Главная</MudNavLink>
|
<MudNavLink Href="/" Match="NavLinkMatch.All" Icon="@Icons.Material.Filled.Home">Главная</MudNavLink>
|
||||||
<AuthorizeView>
|
<AuthorizeView>
|
||||||
<Authorized>
|
<Authorized>
|
||||||
<MudNavLink Href="/my-playlists" Icon="@Icons.Material.Filled.QueueMusic">Мои плейлисты</MudNavLink>
|
|
||||||
<MudNavLink Href="/profile" Icon="@Icons.Material.Filled.Person">Профиль</MudNavLink>
|
<MudNavLink Href="/profile" Icon="@Icons.Material.Filled.Person">Профиль</MudNavLink>
|
||||||
|
<MudNavLink Href="/my-playlists" Icon="@Icons.Material.Filled.QueueMusic">Мои плейлисты</MudNavLink>
|
||||||
|
<MudNavLink Href="/favorites" Icon="@Icons.Material.Filled.Star">Избранное</MudNavLink>
|
||||||
</Authorized>
|
</Authorized>
|
||||||
</AuthorizeView>
|
</AuthorizeView>
|
||||||
</MudNavMenu>
|
</MudNavMenu>
|
||||||
110
PlaylistShared.Pwa/Pages/Favorites.razor
Normal file
110
PlaylistShared.Pwa/Pages/Favorites.razor
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
@page "/favorites"
|
||||||
|
@using PlaylistShared.Shared.Shared
|
||||||
|
@attribute [Authorize]
|
||||||
|
@inject HttpClient Http
|
||||||
|
@inject ISnackbar Snackbar
|
||||||
|
@inject NavigationManager Navigation
|
||||||
|
|
||||||
|
<MudContainer MaxWidth="MaxWidth.Large" Class="mt-8">
|
||||||
|
<MudCard>
|
||||||
|
<MudCardHeader>
|
||||||
|
<CardHeaderContent>
|
||||||
|
<MudText Typo="Typo.h5">Избранные плейлисты</MudText>
|
||||||
|
<MudText Typo="Typo.body2">Расшаренные плейлисты, которые вы добавили в избранное</MudText>
|
||||||
|
</CardHeaderContent>
|
||||||
|
<CardHeaderActions>
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.Refresh" OnClick="LoadFavorites" />
|
||||||
|
</CardHeaderActions>
|
||||||
|
</MudCardHeader>
|
||||||
|
<MudCardContent>
|
||||||
|
@if (_loading)
|
||||||
|
{
|
||||||
|
<MudProgressCircular Indeterminate />
|
||||||
|
}
|
||||||
|
else if (_favorites == null || !_favorites.Any())
|
||||||
|
{
|
||||||
|
<MudAlert Severity="Severity.Info">
|
||||||
|
У вас пока нет избранных плейлистов. Перейдите на страницу расшаренного плейлиста и нажмите ★, чтобы добавить.
|
||||||
|
</MudAlert>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudTable Items="@_favorites">
|
||||||
|
<HeaderContent>
|
||||||
|
<MudTh>Название</MudTh>
|
||||||
|
<MudTh>Владелец</MudTh>
|
||||||
|
<MudTh></MudTh>
|
||||||
|
</HeaderContent>
|
||||||
|
<RowTemplate>
|
||||||
|
<MudTd DataLabel="Название">
|
||||||
|
<MudLink Href="@($"/shared/{context.ShareToken}")" Underline="Underline.Hover">
|
||||||
|
@context.Title
|
||||||
|
</MudLink>
|
||||||
|
</MudTd>
|
||||||
|
<MudTd DataLabel="Владелец">@context.Creator?.UserName</MudTd>
|
||||||
|
<MudTd DataLabel="">
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.Delete"
|
||||||
|
Color="Color.Error"
|
||||||
|
OnClick="() => RemoveFromFavorites(context)"
|
||||||
|
Title="Удалить из избранного" />
|
||||||
|
</MudTd>
|
||||||
|
</RowTemplate>
|
||||||
|
</MudTable>
|
||||||
|
}
|
||||||
|
</MudCardContent>
|
||||||
|
</MudCard>
|
||||||
|
</MudContainer>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private List<SharedPlaylistDto> _favorites = new();
|
||||||
|
private bool _loading = true;
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
await LoadFavorites();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task LoadFavorites()
|
||||||
|
{
|
||||||
|
_loading = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await Http.GetFromJsonAsync<ApiResponse<List<SharedPlaylistDto>>>("/api/favorites");
|
||||||
|
if (response?.Success == true)
|
||||||
|
_favorites = response.Data ?? new();
|
||||||
|
else
|
||||||
|
Snackbar.Add(response?.Error?.Message ?? "Ошибка загрузки избранного", Severity.Error);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Snackbar.Add($"Ошибка: {ex.Message}", Severity.Error);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_loading = false;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RemoveFromFavorites(SharedPlaylistDto playlist)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await Http.DeleteAsync($"/api/favorites/{playlist.ShareToken}");
|
||||||
|
if (response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
Snackbar.Add($"Плейлист \"{playlist.Title}\" удалён из избранного", Severity.Success);
|
||||||
|
await LoadFavorites();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var error = await response.Content.ReadFromJsonAsync<ApiResponse<object>>();
|
||||||
|
Snackbar.Add(error?.Error?.Message ?? "Ошибка удаления", Severity.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Snackbar.Add($"Ошибка: {ex.Message}", Severity.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,91 +1,74 @@
|
|||||||
@page "/"
|
@page "/"
|
||||||
@using PlaylistShared.Pwa.Services
|
@using PlaylistShared.Pwa.Services
|
||||||
@inject NavigationManager Navigation
|
@inject NavigationManager Navigation
|
||||||
|
@inject AuthenticationStateProvider AuthProvider
|
||||||
|
|
||||||
<MudContainer MaxWidth="MaxWidth.Large" Class="mt-8">
|
<MudContainer MaxWidth="MaxWidth.Large" Class="mt-8">
|
||||||
<MudCard>
|
<MudCard>
|
||||||
<MudCardHeader>
|
<MudCardHeader>
|
||||||
<CardHeaderContent>
|
<CardHeaderContent>
|
||||||
<MudText Typo="Typo.h4" GutterBottom>🎵 Playlist share</MudText>
|
<MudText Typo="Typo.h4" GutterBottom>🎵 Playlist share</MudText>
|
||||||
<MudText Typo="Typo.body1">Делитесь плейлистами Яндекс.Музыки с друзьями и управляйте треками вместе!</MudText>
|
<MudText Typo="Typo.body1">
|
||||||
|
Делитесь плейлистами Яндекс.Музыки с друзьями и управляйте треками вместе!
|
||||||
|
</MudText>
|
||||||
</CardHeaderContent>
|
</CardHeaderContent>
|
||||||
</MudCardHeader>
|
</MudCardHeader>
|
||||||
|
|
||||||
<MudCardContent>
|
<MudCardContent>
|
||||||
<MudText Typo="Typo.h6" GutterBottom>🚀 Как начать</MudText>
|
<MudText Typo="Typo.body2" Class="mb-4">
|
||||||
|
Playlist share — это веб-приложение, которое позволяет создавать совместные плейлисты,
|
||||||
|
предоставлять доступ к ним по ссылке и слушать музыку прямо в браузере.
|
||||||
|
Для работы требуется аккаунт Яндекс.Музыки (подписка не обязательна).
|
||||||
|
</MudText>
|
||||||
|
|
||||||
|
<!-- Блок с требованием регистрации для расшаривания -->
|
||||||
|
<MudAlert Severity="Severity.Info" Variant="Variant.Outlined" Class="my-4">
|
||||||
|
<div style="display: flex; align-items: center; gap: 12px; flex-wrap: wrap;">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.Share" Size="Size.Medium" />
|
||||||
|
<div style="flex: 1;">
|
||||||
|
<MudText Typo="Typo.body1" FontWeight="FontWeight.Bold">
|
||||||
|
Чтобы расшаривать плейлисты, необходимо зарегистрироваться
|
||||||
|
</MudText>
|
||||||
|
<MudText Typo="Typo.body2">
|
||||||
|
Создайте аккаунт или войдите в существующий — это займёт всего минуту.
|
||||||
|
</MudText>
|
||||||
|
</div>
|
||||||
|
<MudButton Variant="Variant.Filled" Color="Color.Primary" Href="/register">
|
||||||
|
Зарегистрироваться
|
||||||
|
</MudButton>
|
||||||
|
<MudButton Variant="Variant.Outlined" Color="Color.Primary" Href="/login">
|
||||||
|
Войти
|
||||||
|
</MudButton>
|
||||||
|
</div>
|
||||||
|
</MudAlert>
|
||||||
|
|
||||||
|
<!-- Краткие преимущества -->
|
||||||
<MudGrid>
|
<MudGrid>
|
||||||
<MudItem xs="12" md="6">
|
<MudItem xs="12" sm="4">
|
||||||
<MudPaper Class="pa-4" Elevation="0" Style="background-color: rgba(0,0,0,0.04); border-radius: 8px;">
|
<MudPaper Class="pa-3 text-center" Elevation="0" Style="background-color: rgba(0,0,0,0.04); border-radius: 8px;">
|
||||||
<MudText Typo="Typo.h6" GutterBottom>1️⃣ Регистрация и вход</MudText>
|
<MudIcon Icon="@Icons.Material.Filled.Link" Size="Size.Medium" Color="Color.Primary" />
|
||||||
<MudText Typo="Typo.body2" Class="mb-2">
|
<MudText Typo="Typo.body2" Class="mt-2">Создавайте ссылки-приглашения</MudText>
|
||||||
• Нажмите <MudLink Href="/register" Style="font-weight:bold;">«Регистрация»</MudLink> и создайте аккаунт.<br />
|
|
||||||
• Или <MudLink Href="/login" Style="font-weight:bold;">войдите</MudLink> в систему, если уже зарегистрированы.
|
|
||||||
</MudText>
|
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
</MudItem>
|
</MudItem>
|
||||||
|
<MudItem xs="12" sm="4">
|
||||||
<MudItem xs="12" md="6">
|
<MudPaper Class="pa-3 text-center" Elevation="0" Style="background-color: rgba(0,0,0,0.04); border-radius: 8px;">
|
||||||
<MudPaper Class="pa-4" Elevation="0" Style="background-color: rgba(0,0,0,0.04); border-radius: 8px;">
|
<MudIcon Icon="@Icons.Material.Filled.People" Size="Size.Medium" Color="Color.Primary" />
|
||||||
<MudText Typo="Typo.h6" GutterBottom>2️⃣ Получение токена Яндекс.Музыки</MudText>
|
<MudText Typo="Typo.body2" Class="mt-2">Совместное управление треками</MudText>
|
||||||
<MudText Typo="Typo.body2" Class="mb-2">
|
|
||||||
Токен нужен для доступа к вашим плейлистам. Получите его один раз:
|
|
||||||
</MudText>
|
|
||||||
<ol style="margin-left: 1.2rem;">
|
|
||||||
<li>Перейдите по <MudLink Href="https://oauth.yandex.ru/authorize?response_type=token&client_id=23cabbbdc6cd418abb4b39c32c41195d" Target="_blank">ссылке</MudLink></li>
|
|
||||||
<li>Авторизуйтесь в Яндексе (если ещё не вошли)</li>
|
|
||||||
<li>Нажмите «Разрешить»</li>
|
|
||||||
<li>Скопируйте <strong>access_token</strong> из адресной строки после перенаправления</li>
|
|
||||||
</ol>
|
|
||||||
<MudAlert Severity="Severity.Info" Class="mt-2">
|
|
||||||
Пример: <code>https://music.yandex.ru/#access_token=ВАШ_ТОКЕН&...</code>
|
|
||||||
</MudAlert>
|
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
</MudItem>
|
</MudItem>
|
||||||
|
<MudItem xs="12" sm="4">
|
||||||
<MudItem xs="12" md="6">
|
<MudPaper Class="pa-3 text-center" Elevation="0" Style="background-color: rgba(0,0,0,0.04); border-radius: 8px;">
|
||||||
<MudPaper Class="pa-4" Elevation="0" Style="background-color: rgba(0,0,0,0.04); border-radius: 8px;">
|
<MudIcon Icon="@Icons.Material.Filled.Security" Size="Size.Medium" Color="Color.Primary" />
|
||||||
<MudText Typo="Typo.h6" GutterBottom>3️⃣ Добавление токена в профиле</MudText>
|
<MudText Typo="Typo.body2" Class="mt-2">Гибкие настройки доступа</MudText>
|
||||||
<MudText Typo="Typo.body2" Class="mb-2">
|
|
||||||
• Перейдите в <MudLink Href="/profile" Style="font-weight:bold;">Профиль</MudLink><br />
|
|
||||||
• Вставьте скопированный токен в поле «Токен Яндекс.Музыки»<br />
|
|
||||||
• Нажмите «Сохранить»
|
|
||||||
</MudText>
|
|
||||||
<MudAlert Severity="Severity.Success" Class="mt-2">✅ Токен сохраняется в зашифрованном виде.</MudAlert>
|
|
||||||
</MudPaper>
|
|
||||||
</MudItem>
|
|
||||||
|
|
||||||
<MudItem xs="12" md="6">
|
|
||||||
<MudPaper Class="pa-4" Elevation="0" Style="background-color: rgba(0,0,0,0.04); border-radius: 8px;">
|
|
||||||
<MudText Typo="Typo.h6" GutterBottom>4️⃣ Расшаривание плейлиста</MudText>
|
|
||||||
<MudText Typo="Typo.body2" Class="mb-2">
|
|
||||||
• Откройте <MudLink Href="/my-playlists" Style="font-weight:bold;">Мои плейлисты</MudLink><br />
|
|
||||||
• Нажмите «Поделиться» рядом с нужным плейлистом<br />
|
|
||||||
• Скопируйте полученную ссылку и отправьте друзьям
|
|
||||||
</MudText>
|
|
||||||
<MudAlert Severity="Severity.Info" Class="mt-2">
|
|
||||||
Вы можете настроить права на добавление/удаление треков для гостей.
|
|
||||||
</MudAlert>
|
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
</MudItem>
|
</MudItem>
|
||||||
</MudGrid>
|
</MudGrid>
|
||||||
|
|
||||||
<MudDivider Class="my-6" />
|
<MudDivider Class="my-4" />
|
||||||
|
|
||||||
<MudText Typo="Typo.h6" GutterBottom>📌 Важно</MudText>
|
<MudText Typo="Typo.body2" Color="Color.Secondary">
|
||||||
<MudGrid>
|
🔐 Все данные передаются по защищённому соединению, токены хранятся в зашифрованном виде.
|
||||||
<MudItem xs="12" sm="6">
|
</MudText>
|
||||||
<MudAlert Severity="Severity.Warning" Variant="Variant.Outlined">
|
|
||||||
🔐 Токен даёт доступ к вашим плейлистам. Никому его не сообщайте.
|
|
||||||
</MudAlert>
|
|
||||||
</MudItem>
|
|
||||||
<MudItem xs="12" sm="6">
|
|
||||||
<MudAlert Severity="Severity.Info" Variant="Variant.Outlined">
|
|
||||||
🎧 Для работы с плейлистами нужна активная подписка Яндекс.Плюс?<br />
|
|
||||||
<MudText Typo="Typo.body2">Нет, достаточно обычного аккаунта.</MudText>
|
|
||||||
</MudAlert>
|
|
||||||
</MudItem>
|
|
||||||
</MudGrid>
|
|
||||||
</MudCardContent>
|
</MudCardContent>
|
||||||
</MudCard>
|
</MudCard>
|
||||||
</MudContainer>
|
</MudContainer>
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
<!-- Локальная форма входа -->
|
<!-- Локальная форма входа -->
|
||||||
<MudTextField @bind-Value="_loginModel.Username" Label="Имя пользователя" Variant="Variant.Outlined" FullWidth="true" Class="mb-3" />
|
<MudTextField @bind-Value="_loginModel.Username" Label="Имя пользователя" Variant="Variant.Outlined" FullWidth="true" Class="mb-3" />
|
||||||
<MudTextField @bind-Value="_loginModel.Password" Label="Пароль" Variant="Variant.Outlined" FullWidth="true" InputType="InputType.Password" />
|
<MudTextField @bind-Value="_loginModel.Password" Label="Пароль" Variant="Variant.Outlined" FullWidth="true" InputType="InputType.Password" @onkeypress="@(async (e) => { if (e.Key == "Enter") await LocalLogin(); })" />
|
||||||
|
|
||||||
<MudButton Variant="Variant.Outlined" Color="Color.Secondary" OnClick="LocalLogin" FullWidth="true" Class="mt-4">
|
<MudButton Variant="Variant.Outlined" Color="Color.Secondary" OnClick="LocalLogin" FullWidth="true" Class="mt-4">
|
||||||
Войти (локально)
|
Войти (локально)
|
||||||
|
|||||||
@@ -13,17 +13,45 @@
|
|||||||
</CardHeaderContent>
|
</CardHeaderContent>
|
||||||
</MudCardHeader>
|
</MudCardHeader>
|
||||||
<MudCardContent>
|
<MudCardContent>
|
||||||
<MudText Typo="Typo.body2" Class="mb-4">Здесь вы можете указать токен доступа к Яндекс.Музыке.</MudText>
|
<div style="display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 8px; margin-bottom: 16px;">
|
||||||
|
<MudText Typo="Typo.body2">
|
||||||
|
Здесь вы можете указать токен доступа к Яндекс.Музыке.
|
||||||
|
</MudText>
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.HelpOutline"
|
||||||
|
Color="Color.Info"
|
||||||
|
OnClick="() => _instructionDrawerOpen = true"
|
||||||
|
Title="Как получить токен?" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<MudTextField @bind-Value="_token" Label="Токен Яндекс.Музыки" Variant="Variant.Outlined" FullWidth="true" />
|
<MudTextField @bind-Value="_token" Label="Токен Яндекс.Музыки" Variant="Variant.Outlined" FullWidth="true" />
|
||||||
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="SaveToken" Class="mt-4" FullWidth="true">Сохранить токен</MudButton>
|
|
||||||
|
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="SaveToken" Class="mt-4" FullWidth="true">
|
||||||
|
Сохранить токен
|
||||||
|
</MudButton>
|
||||||
|
|
||||||
<MudText Class="mt-4" Typo="Typo.body2">Статус: @_statusText</MudText>
|
<MudText Class="mt-4" Typo="Typo.body2">Статус: @_statusText</MudText>
|
||||||
</MudCardContent>
|
</MudCardContent>
|
||||||
</MudCard>
|
</MudCard>
|
||||||
</MudContainer>
|
</MudContainer>
|
||||||
|
|
||||||
|
<!-- Выдвижная панель с инструкцией -->
|
||||||
|
<MudDrawer @bind-Open="_instructionDrawerOpen"
|
||||||
|
Anchor="Anchor.Right"
|
||||||
|
Variant="DrawerVariant.Temporary"
|
||||||
|
Elevation="3"
|
||||||
|
Width="500px"
|
||||||
|
MiniWidth="0px">
|
||||||
|
<MudDrawerHeader>
|
||||||
|
<MudText Typo="Typo.h6">Как получить токен Яндекс.Музыки</MudText>
|
||||||
|
</MudDrawerHeader>
|
||||||
|
<MudDivider />
|
||||||
|
<YandexTokenInstructions />
|
||||||
|
</MudDrawer>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private string _token = "";
|
private string _token = "";
|
||||||
private string _statusText = "Загрузка...";
|
private string _statusText = "Загрузка...";
|
||||||
|
private bool _instructionDrawerOpen = false;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -27,10 +27,21 @@
|
|||||||
<div style="display: flex; gap: 16px; align-items: center;">
|
<div style="display: flex; gap: 16px; align-items: center;">
|
||||||
@if (!string.IsNullOrEmpty(_playlist.CoverUrl))
|
@if (!string.IsNullOrEmpty(_playlist.CoverUrl))
|
||||||
{
|
{
|
||||||
<MudImage Src="@FormatCoverUrl(_playlist.CoverUrl)" Height="80" Width="80" Class="rounded" />
|
<MudImage Src="@FormatCoverUrl(_playlist.CoverUrl, "80x80")" Height="80" Width="80" Class="rounded" />
|
||||||
}
|
}
|
||||||
<div>
|
<div>
|
||||||
<MudText Typo="Typo.h5">@_playlist.Title</MudText>
|
<div style="display: flex; align-items: center; gap: 8px;">
|
||||||
|
<MudLink Href="@($"https://music.yandex.ru/playlists/{_playlist.YandexPlaylistUuid}")" Typo="Typo.h5" Target="_blank" Underline="Underline.Hover">
|
||||||
|
@_playlist.Title
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.OpenInNew" Size="Size.Small" Class="ml-1" />
|
||||||
|
</MudLink>
|
||||||
|
|
||||||
|
<MudIconButton Icon="@(_isFavorite? Icons.Material.Filled.Star : Icons.Material.Outlined.StarBorder)"
|
||||||
|
Color="Color.Warning"
|
||||||
|
OnClick="ToggleFavorite"
|
||||||
|
Disabled="_favoriteLoading"
|
||||||
|
Size="Size.Medium" />
|
||||||
|
</div>
|
||||||
<MudText Typo="Typo.body2" Color="Color.Secondary">Владелец: @_playlist.Creator?.UserName</MudText>
|
<MudText Typo="Typo.body2" Color="Color.Secondary">Владелец: @_playlist.Creator?.UserName</MudText>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -50,6 +61,12 @@
|
|||||||
<MudSelectItem Value="ViewPermission.AuthorizedOnly">Только авторизованные</MudSelectItem>
|
<MudSelectItem Value="ViewPermission.AuthorizedOnly">Только авторизованные</MudSelectItem>
|
||||||
</MudSelect>
|
</MudSelect>
|
||||||
</MudItem>
|
</MudItem>
|
||||||
|
<MudItem xs="12" sm="4">
|
||||||
|
<MudSelect T="ViewPermission" Label="Воспроизведение" @bind-Value="_editPermissions.PlayPermission" Variant="Variant.Outlined" FullWidth="true">
|
||||||
|
<MudSelectItem Value="ViewPermission.Everyone">Все</MudSelectItem>
|
||||||
|
<MudSelectItem Value="ViewPermission.AuthorizedOnly">Только авторизованные</MudSelectItem>
|
||||||
|
</MudSelect>
|
||||||
|
</MudItem>
|
||||||
<MudItem xs="12" sm="4">
|
<MudItem xs="12" sm="4">
|
||||||
<MudSelect T="EditPermission" Label="Добавление треков" @bind-Value="_editPermissions.AddPermission" Variant="Variant.Outlined" FullWidth="true">
|
<MudSelect T="EditPermission" Label="Добавление треков" @bind-Value="_editPermissions.AddPermission" Variant="Variant.Outlined" FullWidth="true">
|
||||||
<MudSelectItem Value="EditPermission.Everyone">Все</MudSelectItem>
|
<MudSelectItem Value="EditPermission.Everyone">Все</MudSelectItem>
|
||||||
@@ -143,6 +160,8 @@
|
|||||||
<MudTd DataLabel="#" Style="font-weight: normal;">@context.Index</MudTd>
|
<MudTd DataLabel="#" Style="font-weight: normal;">@context.Index</MudTd>
|
||||||
<MudTd DataLabel="Обложка">
|
<MudTd DataLabel="Обложка">
|
||||||
@if (!string.IsNullOrEmpty(context.CoverUri))
|
@if (!string.IsNullOrEmpty(context.CoverUri))
|
||||||
|
{
|
||||||
|
@if (@_canPlay)
|
||||||
{
|
{
|
||||||
<TrackCoverWithPlay CoverUrl="@context.CoverUri"
|
<TrackCoverWithPlay CoverUrl="@context.CoverUri"
|
||||||
TrackId="@context.Id"
|
TrackId="@context.Id"
|
||||||
@@ -150,6 +169,12 @@
|
|||||||
IsPlaying="@(_currentTrackId == context.Id && _isPlaying)"
|
IsPlaying="@(_currentTrackId == context.Id && _isPlaying)"
|
||||||
OnPlay="PlayTrack" />
|
OnPlay="PlayTrack" />
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
<MudImage Src="@FormatCoverUrl(context.CoverUri, "50x50")" Height="50" Width="50" Class="rounded" Style="display: block;" />
|
||||||
|
}
|
||||||
|
}
|
||||||
</MudTd>
|
</MudTd>
|
||||||
<MudTd DataLabel="Название">
|
<MudTd DataLabel="Название">
|
||||||
<MudLink Href="@($"https://music.yandex.ru/track/{context.Id}")" Target="_blank" Underline="Underline.Hover">
|
<MudLink Href="@($"https://music.yandex.ru/track/{context.Id}")" Target="_blank" Underline="Underline.Hover">
|
||||||
@@ -174,7 +199,7 @@
|
|||||||
|
|
||||||
<!-- Фиксированный плеер внизу -->
|
<!-- Фиксированный плеер внизу -->
|
||||||
<div class="fixed-player" style="display: @(_isPlayerVisible ? "block" : "none");">
|
<div class="fixed-player" style="display: @(_isPlayerVisible ? "block" : "none");">
|
||||||
<AudioPlayer @ref="_audioPlayer" OnTrackEnded="OnTrackEnded" />
|
<AudioPlayer @ref="_audioPlayer" OnTrackEnded="OnTrackEnded" RequireAuth="false" SharedPlaylistId="@Token"/>
|
||||||
</div>
|
</div>
|
||||||
</MudContainer>
|
</MudContainer>
|
||||||
|
|
||||||
@@ -191,12 +216,16 @@
|
|||||||
private bool _loading = true;
|
private bool _loading = true;
|
||||||
private bool _isAuthenticated;
|
private bool _isAuthenticated;
|
||||||
private bool _isCreator;
|
private bool _isCreator;
|
||||||
|
private bool _canPlay;
|
||||||
private bool _canAdd;
|
private bool _canAdd;
|
||||||
private bool _canRemove;
|
private bool _canRemove;
|
||||||
private UpdatePermissionsDto _editPermissions = new();
|
private UpdatePermissionsDto _editPermissions = new();
|
||||||
private bool _savingPermissions;
|
private bool _savingPermissions;
|
||||||
private string? _currentUserId;
|
private string? _currentUserId;
|
||||||
|
|
||||||
|
private bool _isFavorite = false;
|
||||||
|
private bool _favoriteLoading = false;
|
||||||
|
|
||||||
private List<YandexTrackDisplay> _tracks = new();
|
private List<YandexTrackDisplay> _tracks = new();
|
||||||
private bool _tracksLoading;
|
private bool _tracksLoading;
|
||||||
|
|
||||||
@@ -211,14 +240,78 @@
|
|||||||
await LoadPlaylist();
|
await LoadPlaylist();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task LoadPlaylist()
|
private async Task CheckFavoriteStatus()
|
||||||
{
|
{
|
||||||
|
if (!_isAuthenticated || _playlist == null) return;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var response = await Http.GetFromJsonAsync<ApiResponse<SharedPlaylistDto>>($"/api/sharedplaylist/{Token}");
|
var response = await Http.GetFromJsonAsync<ApiResponse<bool>>($"/api/favorites/{Token}/check");
|
||||||
if (response?.Success == true)
|
if (response?.Success == true)
|
||||||
|
_isFavorite = response.Data;
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ToggleFavorite()
|
||||||
|
{
|
||||||
|
if (!_isAuthenticated)
|
||||||
|
{
|
||||||
|
Snackbar.Add("Добавление в избранное доступно только авторизованным пользователям", Severity.Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_favoriteLoading = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_isFavorite)
|
||||||
|
{
|
||||||
|
var response = await Http.DeleteAsync($"/api/favorites/{Token}");
|
||||||
|
if (response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
_isFavorite = false;
|
||||||
|
Snackbar.Add("Плейлист удалён из избранного", Severity.Success);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Snackbar.Add("Ошибка удаления из избранного", Severity.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var response = await Http.PostAsync($"/api/favorites/{Token}", null);
|
||||||
|
if (response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
_isFavorite = true;
|
||||||
|
Snackbar.Add("Плейлист добавлен в избранное", Severity.Success);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Snackbar.Add("Ошибка добавления в избранное", Severity.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Snackbar.Add($"Ошибка: {ex.Message}", Severity.Error);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_favoriteLoading = false;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ConfigurePermissions()
|
||||||
|
{
|
||||||
|
if (_playlist is null)
|
||||||
|
{
|
||||||
|
_isCreator = false;
|
||||||
|
_canAdd = false;
|
||||||
|
_canRemove = false;
|
||||||
|
_canPlay = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
_playlist = response.Data;
|
|
||||||
_isCreator = _playlist.CreatorUserId.ToString() == _currentUserId;
|
_isCreator = _playlist.CreatorUserId.ToString() == _currentUserId;
|
||||||
|
|
||||||
_canAdd = _isCreator
|
_canAdd = _isCreator
|
||||||
@@ -229,17 +322,36 @@
|
|||||||
|| _playlist.RemovePermission == EditPermission.Everyone
|
|| _playlist.RemovePermission == EditPermission.Everyone
|
||||||
|| (_playlist.RemovePermission == EditPermission.AuthorizedOnly && _isAuthenticated);
|
|| (_playlist.RemovePermission == EditPermission.AuthorizedOnly && _isAuthenticated);
|
||||||
|
|
||||||
|
_canPlay = _isCreator
|
||||||
|
|| _playlist.PlayPermission == ViewPermission.Everyone
|
||||||
|
|| (_playlist.PlayPermission == ViewPermission.AuthorizedOnly && _isAuthenticated);
|
||||||
|
|
||||||
if (_isCreator && _isAuthenticated)
|
if (_isCreator && _isAuthenticated)
|
||||||
{
|
{
|
||||||
_editPermissions = new UpdatePermissionsDto
|
_editPermissions = new UpdatePermissionsDto
|
||||||
{
|
{
|
||||||
ViewPermission = _playlist.ViewPermission,
|
ViewPermission = _playlist.ViewPermission,
|
||||||
AddPermission = _playlist.AddPermission,
|
AddPermission = _playlist.AddPermission,
|
||||||
RemovePermission = _playlist.RemovePermission
|
RemovePermission = _playlist.RemovePermission,
|
||||||
|
PlayPermission = _playlist.PlayPermission,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task LoadPlaylist()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await Http.GetFromJsonAsync<ApiResponse<SharedPlaylistDto>>($"/api/sharedplaylist/{Token}");
|
||||||
|
if (response?.Success == true)
|
||||||
|
{
|
||||||
|
_playlist = response.Data;
|
||||||
|
|
||||||
|
await ConfigurePermissions();
|
||||||
await LoadTracks();
|
await LoadTracks();
|
||||||
|
await CheckFavoriteStatus();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -371,11 +483,8 @@
|
|||||||
if (result?.Success == true)
|
if (result?.Success == true)
|
||||||
{
|
{
|
||||||
_playlist = result.Data;
|
_playlist = result.Data;
|
||||||
Snackbar.Add("Права доступа обновлены", Severity.Success);
|
|
||||||
_canAdd = _isCreator || _playlist.AddPermission == EditPermission.Everyone ||
|
await ConfigurePermissions();
|
||||||
(_playlist.AddPermission == EditPermission.AuthorizedOnly && _isAuthenticated);
|
|
||||||
_canRemove = _isCreator || _playlist.RemovePermission == EditPermission.Everyone ||
|
|
||||||
(_playlist.RemovePermission == EditPermission.AuthorizedOnly && _isAuthenticated);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ internal class Program
|
|||||||
});
|
});
|
||||||
|
|
||||||
builder.Services.AddScoped<TokenStorage>();
|
builder.Services.AddScoped<TokenStorage>();
|
||||||
|
builder.Services.AddScoped<PlayerStorage>();
|
||||||
builder.Services.AddScoped<AuthStateProvider>();
|
builder.Services.AddScoped<AuthStateProvider>();
|
||||||
builder.Services.AddScoped<AuthenticationStateProvider>(sp => sp.GetRequiredService<AuthStateProvider>());
|
builder.Services.AddScoped<AuthenticationStateProvider>(sp => sp.GetRequiredService<AuthStateProvider>());
|
||||||
builder.Services.AddScoped<ApiClient>();
|
builder.Services.AddScoped<ApiClient>();
|
||||||
|
|||||||
30
PlaylistShared.Pwa/Services/PlayerStorage.cs
Normal file
30
PlaylistShared.Pwa/Services/PlayerStorage.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
using Microsoft.JSInterop;
|
||||||
|
|
||||||
|
namespace PlaylistShared.Pwa.Services;
|
||||||
|
|
||||||
|
public class PlayerStorage
|
||||||
|
{
|
||||||
|
private readonly IJSRuntime _js;
|
||||||
|
private const string VolumeKey = "audio_player_volume";
|
||||||
|
|
||||||
|
public PlayerStorage(IJSRuntime js) => _js = js;
|
||||||
|
|
||||||
|
public async Task SetVolumeAsync(double volume)
|
||||||
|
{
|
||||||
|
await _js.InvokeVoidAsync("localStorage.setItem", VolumeKey, volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<double> GetVolumeAsync()
|
||||||
|
{
|
||||||
|
var volume = await _js.InvokeAsync<string>("localStorage.getItem", VolumeKey);
|
||||||
|
|
||||||
|
if (double.TryParse(volume, out var result))
|
||||||
|
{
|
||||||
|
result = Math.Clamp(result, 0, 100);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,74 +16,6 @@
|
|||||||
<link rel="apple-touch-icon" sizes="512x512" href="icon-512.png" />
|
<link rel="apple-touch-icon" sizes="512x512" href="icon-512.png" />
|
||||||
<link rel="apple-touch-icon" sizes="192x192" href="icon-192.png" />
|
<link rel="apple-touch-icon" sizes="192x192" href="icon-192.png" />
|
||||||
<script type="importmap"></script>
|
<script type="importmap"></script>
|
||||||
<style>
|
|
||||||
html, body {
|
|
||||||
background-color: #1a1a27 !important;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Кастомный спиннер в стиле MudBlazor (тёмная тема) */
|
|
||||||
.loading-progress {
|
|
||||||
position: relative;
|
|
||||||
display: block;
|
|
||||||
width: 64px;
|
|
||||||
height: 64px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-progress circle {
|
|
||||||
fill: none;
|
|
||||||
stroke: #2a2833;
|
|
||||||
stroke-width: 4;
|
|
||||||
transform-origin: 50% 50%;
|
|
||||||
animation: spin 1.5s linear infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-progress circle:last-child {
|
|
||||||
stroke: #7e6fff;
|
|
||||||
stroke-dasharray: 126;
|
|
||||||
stroke-dashoffset: 126;
|
|
||||||
animation: dash 1.5s ease-in-out infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes spin {
|
|
||||||
100% {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes dash {
|
|
||||||
0% {
|
|
||||||
stroke-dashoffset: 126;
|
|
||||||
}
|
|
||||||
|
|
||||||
50% {
|
|
||||||
stroke-dashoffset: 63;
|
|
||||||
transform: rotate(135deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
100% {
|
|
||||||
stroke-dashoffset: 126;
|
|
||||||
transform: rotate(450deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-progress-text {
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 16px;
|
|
||||||
color: #b2b0bf;
|
|
||||||
font-family: 'Roboto', sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Убираем белые вспышки */
|
|
||||||
#app {
|
|
||||||
background-color: #1a1a27;
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -10,9 +10,10 @@
|
|||||||
return isNaN(num) ? 0 : num;
|
return isNaN(num) ? 0 : num;
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadAndPlay = (src, token) => {
|
const loadAndPlay = (src, token, sharedPlaylistId) => {
|
||||||
const url = new URL(src, window.location.href);
|
const url = new URL(src, window.location.href);
|
||||||
if (token) url.searchParams.set('access_token', token);
|
if (token) url.searchParams.set('access_token', token);
|
||||||
|
if (sharedPlaylistId) url.searchParams.set('shared_id', sharedPlaylistId);
|
||||||
audio.src = url.toString();
|
audio.src = url.toString();
|
||||||
audio.load();
|
audio.load();
|
||||||
durationReady = false;
|
durationReady = false;
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ namespace PlaylistShared.Shared.Playlist;
|
|||||||
/// <summary>Запрос на создание нового шеринг-плейлиста.</summary>
|
/// <summary>Запрос на создание нового шеринг-плейлиста.</summary>
|
||||||
public class SharePlaylistDto
|
public class SharePlaylistDto
|
||||||
{
|
{
|
||||||
|
/// <summary>Идентификатор плейлиста в Яндекс.Музыке (guid).</summary>
|
||||||
|
[JsonPropertyName("yandexPlaylistId")]
|
||||||
|
public string YandexPlaylistUuid { get; set; } = null!;
|
||||||
|
|
||||||
/// <summary>Идентификатор плейлиста в Яндекс.Музыке (kind).</summary>
|
/// <summary>Идентификатор плейлиста в Яндекс.Музыке (kind).</summary>
|
||||||
[JsonPropertyName("yandexPlaylistKind")]
|
[JsonPropertyName("yandexPlaylistKind")]
|
||||||
public string YandexPlaylistKind { get; set; } = null!;
|
public string YandexPlaylistKind { get; set; } = null!;
|
||||||
@@ -38,6 +42,10 @@ public class SharePlaylistDto
|
|||||||
[JsonPropertyName("viewPermission")]
|
[JsonPropertyName("viewPermission")]
|
||||||
public ViewPermission ViewPermission { get; set; }
|
public ViewPermission ViewPermission { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Права на воспроизведение.</summary>
|
||||||
|
[JsonPropertyName("playPermission")]
|
||||||
|
public ViewPermission PlayPermission { get; set; }
|
||||||
|
|
||||||
/// <summary>Права на добавление треков.</summary>
|
/// <summary>Права на добавление треков.</summary>
|
||||||
[JsonPropertyName("addPermission")]
|
[JsonPropertyName("addPermission")]
|
||||||
public EditPermission AddPermission { get; set; }
|
public EditPermission AddPermission { get; set; }
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ public class SharedPlaylistDto
|
|||||||
[JsonPropertyName("creatorUserId")]
|
[JsonPropertyName("creatorUserId")]
|
||||||
public Guid CreatorUserId { get; set; }
|
public Guid CreatorUserId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Uuid на яндекс плейлист</summary>
|
||||||
|
[JsonPropertyName("yandexPlaylistUuid")]
|
||||||
|
public string? YandexPlaylistUuid { get; set; }
|
||||||
|
|
||||||
/// <summary>Идентификатор плейлиста в Яндекс.Музыке (kind).</summary>
|
/// <summary>Идентификатор плейлиста в Яндекс.Музыке (kind).</summary>
|
||||||
[JsonPropertyName("yandexPlaylistKind")]
|
[JsonPropertyName("yandexPlaylistKind")]
|
||||||
public string YandexPlaylistKind { get; set; } = null!;
|
public string YandexPlaylistKind { get; set; } = null!;
|
||||||
@@ -55,6 +59,10 @@ public class SharedPlaylistDto
|
|||||||
[JsonPropertyName("viewPermission")]
|
[JsonPropertyName("viewPermission")]
|
||||||
public ViewPermission ViewPermission { get; set; }
|
public ViewPermission ViewPermission { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Права на воспроизведение.</summary>
|
||||||
|
[JsonPropertyName("playPermission")]
|
||||||
|
public ViewPermission PlayPermission { get; set; }
|
||||||
|
|
||||||
/// <summary>Права на добавление треков.</summary>
|
/// <summary>Права на добавление треков.</summary>
|
||||||
[JsonPropertyName("addPermission")]
|
[JsonPropertyName("addPermission")]
|
||||||
public EditPermission AddPermission { get; set; }
|
public EditPermission AddPermission { get; set; }
|
||||||
|
|||||||
@@ -6,15 +6,19 @@ namespace PlaylistShared.Shared.Shared;
|
|||||||
/// <summary>Запрос на обновление прав доступа шеринг-плейлиста.</summary>
|
/// <summary>Запрос на обновление прав доступа шеринг-плейлиста.</summary>
|
||||||
public class UpdatePermissionsDto
|
public class UpdatePermissionsDto
|
||||||
{
|
{
|
||||||
/// <summary>Новые права на просмотр.</summary>
|
/// <summary>Права на просмотр.</summary>
|
||||||
[JsonPropertyName("viewPermission")]
|
[JsonPropertyName("viewPermission")]
|
||||||
public ViewPermission ViewPermission { get; set; }
|
public ViewPermission ViewPermission { get; set; }
|
||||||
|
|
||||||
/// <summary>Новые права на добавление треков.</summary>
|
/// <summary>Права на воспроизведение треков.</summary>
|
||||||
|
[JsonPropertyName("playPermission")]
|
||||||
|
public ViewPermission PlayPermission { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Права на добавление треков.</summary>
|
||||||
[JsonPropertyName("addPermission")]
|
[JsonPropertyName("addPermission")]
|
||||||
public EditPermission AddPermission { get; set; }
|
public EditPermission AddPermission { get; set; }
|
||||||
|
|
||||||
/// <summary>Новые права на удаление треков.</summary>
|
/// <summary>Права на удаление треков.</summary>
|
||||||
[JsonPropertyName("removePermission")]
|
[JsonPropertyName("removePermission")]
|
||||||
public EditPermission RemovePermission { get; set; }
|
public EditPermission RemovePermission { get; set; }
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
services:
|
services:
|
||||||
playlistshared.api:
|
playlistshared.api:
|
||||||
environment:
|
environment:
|
||||||
- ASPNETCORE_ENVIRONMENT=Development
|
|
||||||
- ASPNETCORE_HTTP_PORTS=8080
|
- ASPNETCORE_HTTP_PORTS=8080
|
||||||
- ASPNETCORE_HTTPS_PORTS=8081
|
- ASPNETCORE_HTTPS_PORTS=8081
|
||||||
ports:
|
ports:
|
||||||
|
|||||||
Reference in New Issue
Block a user