Добавлено управление сессиями
This commit is contained in:
@@ -13,12 +13,14 @@ public class AccountController : ControllerBase
|
|||||||
private readonly UserManager<ApplicationUser> _userManager;
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
private readonly SignInManager<ApplicationUser> _signInManager;
|
private readonly SignInManager<ApplicationUser> _signInManager;
|
||||||
private readonly JwtService _jwtService;
|
private readonly JwtService _jwtService;
|
||||||
|
private readonly UserSessionService _userSessionService;
|
||||||
|
|
||||||
public AccountController(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager, JwtService jwtService)
|
public AccountController(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager, JwtService jwtService, UserSessionService userSessionService)
|
||||||
{
|
{
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
_signInManager = signInManager;
|
_signInManager = signInManager;
|
||||||
_jwtService = jwtService;
|
_jwtService = jwtService;
|
||||||
|
_userSessionService = userSessionService;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("register")]
|
[HttpPost("register")]
|
||||||
@@ -56,6 +58,8 @@ public class AccountController : ControllerBase
|
|||||||
|
|
||||||
private async Task<ActionResult<ApiResponse<LoginResponse>>> GenerateTokenResponse(ApplicationUser user)
|
private async Task<ActionResult<ApiResponse<LoginResponse>>> GenerateTokenResponse(ApplicationUser user)
|
||||||
{
|
{
|
||||||
|
await _userSessionService.GetOrCreateCurrentSessionAsync(user.Id);
|
||||||
|
|
||||||
var (token, refreshToken, expiration) = await _jwtService.GenerateTokenAsync(user);
|
var (token, refreshToken, expiration) = await _jwtService.GenerateTokenAsync(user);
|
||||||
return Ok(ApiResponse<LoginResponse>.Ok(new LoginResponse
|
return Ok(ApiResponse<LoginResponse>.Ok(new LoginResponse
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -14,17 +14,20 @@ public class OpenIdController : ControllerBase
|
|||||||
private readonly UserManager<ApplicationUser> _userManager;
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
private readonly JwtService _jwtService;
|
private readonly JwtService _jwtService;
|
||||||
private readonly IConfiguration _configuration;
|
private readonly IConfiguration _configuration;
|
||||||
|
private readonly UserSessionService _userSessionService;
|
||||||
|
|
||||||
public OpenIdController(
|
public OpenIdController(
|
||||||
SignInManager<ApplicationUser> signInManager,
|
SignInManager<ApplicationUser> signInManager,
|
||||||
UserManager<ApplicationUser> userManager,
|
UserManager<ApplicationUser> userManager,
|
||||||
JwtService jwtService,
|
JwtService jwtService,
|
||||||
IConfiguration configuration)
|
IConfiguration configuration,
|
||||||
|
UserSessionService userSessionService)
|
||||||
{
|
{
|
||||||
_signInManager = signInManager;
|
_signInManager = signInManager;
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
_jwtService = jwtService;
|
_jwtService = jwtService;
|
||||||
_configuration = configuration;
|
_configuration = configuration;
|
||||||
|
_userSessionService = userSessionService;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("login")]
|
[HttpGet("login")]
|
||||||
@@ -70,6 +73,7 @@ public class OpenIdController : ControllerBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
await _signInManager.SignInAsync(user, isPersistent: false);
|
await _signInManager.SignInAsync(user, isPersistent: false);
|
||||||
|
await _userSessionService.GetOrCreateCurrentSessionAsync(user.Id);
|
||||||
var (token, refreshToken, _) = await _jwtService.GenerateTokenAsync(user);
|
var (token, refreshToken, _) = await _jwtService.GenerateTokenAsync(user);
|
||||||
return Redirect($"{_configuration["Client:BaseUrl"]}/auth-callback?token={token}&refreshToken={refreshToken}");
|
return Redirect($"{_configuration["Client:BaseUrl"]}/auth-callback?token={token}&refreshToken={refreshToken}");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,18 +16,24 @@ public class SharedPlaylistController : ControllerBase
|
|||||||
private readonly SharedPlaylistService _sharedService;
|
private readonly SharedPlaylistService _sharedService;
|
||||||
private readonly YandexMusicService _yandexService;
|
private readonly YandexMusicService _yandexService;
|
||||||
private readonly UserManager<ApplicationUser> _userManager;
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
private readonly TrackAdditionLogService _trackLogService;
|
private readonly UserSessionService _userSessionService;
|
||||||
|
private readonly TrackAdditionLogService _trackAdditionLogService;
|
||||||
|
private readonly TrackRemovalLogService _trackRemovalLogService;
|
||||||
|
|
||||||
public SharedPlaylistController(
|
public SharedPlaylistController(
|
||||||
SharedPlaylistService sharedService,
|
SharedPlaylistService sharedService,
|
||||||
YandexMusicService yandexService,
|
YandexMusicService yandexService,
|
||||||
UserManager<ApplicationUser> userManager,
|
UserManager<ApplicationUser> userManager,
|
||||||
TrackAdditionLogService trackLogService)
|
TrackAdditionLogService trackAdditionLogService,
|
||||||
|
TrackRemovalLogService trackRemovalLogService,
|
||||||
|
UserSessionService userSessionService)
|
||||||
{
|
{
|
||||||
_sharedService = sharedService;
|
_sharedService = sharedService;
|
||||||
_yandexService = yandexService;
|
_yandexService = yandexService;
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
_trackLogService = trackLogService;
|
_trackAdditionLogService = trackAdditionLogService;
|
||||||
|
_trackRemovalLogService = trackRemovalLogService;
|
||||||
|
_userSessionService = userSessionService;
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET /api/sharedplaylist/{token}
|
// GET /api/sharedplaylist/{token}
|
||||||
@@ -113,6 +119,13 @@ public class SharedPlaylistController : ControllerBase
|
|||||||
if (updatedPlaylist == null)
|
if (updatedPlaylist == null)
|
||||||
return StatusCode(500, ApiResponse<object>.Fail(new ErrorResponse { StatusCode = 500, Message = "Ошибка при добавлении треков" }));
|
return StatusCode(500, ApiResponse<object>.Fail(new ErrorResponse { StatusCode = 500, Message = "Ошибка при добавлении треков" }));
|
||||||
|
|
||||||
|
var session = await _userSessionService.GetOrCreateCurrentSessionAsync(currentUserId);
|
||||||
|
var sessionId = session.SessionId;
|
||||||
|
foreach (var trackId in request.TrackIds)
|
||||||
|
{
|
||||||
|
await _trackAdditionLogService.LogAdditionAsync(playlist.Id, trackId, currentUserId, sessionId);
|
||||||
|
}
|
||||||
|
|
||||||
return Ok(ApiResponse<object>.Ok(new { message = "Треки добавлены" }));
|
return Ok(ApiResponse<object>.Ok(new { message = "Треки добавлены" }));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,9 +138,12 @@ public class SharedPlaylistController : ControllerBase
|
|||||||
if (playlist == null)
|
if (playlist == null)
|
||||||
return NotFound(ApiResponse<object>.Fail(new ErrorResponse { StatusCode = 404, Message = "Плейлист не найден" }));
|
return NotFound(ApiResponse<object>.Fail(new ErrorResponse { StatusCode = 404, Message = "Плейлист не найден" }));
|
||||||
|
|
||||||
|
var session = await _userSessionService.GetOrCreateCurrentSessionAsync(currentUserId);
|
||||||
|
var sessionId = session.SessionId;
|
||||||
|
|
||||||
foreach (var trackId in request.TrackIds)
|
foreach (var trackId in request.TrackIds)
|
||||||
{
|
{
|
||||||
if (!await _sharedService.CanRemoveTrackAsync(playlist, currentUserId, trackId))
|
if (!await _sharedService.CanRemoveTrackAsync(playlist, currentUserId, trackId, sessionId))
|
||||||
return StatusCode(403, ApiResponse<object>.Fail(new ErrorResponse { StatusCode = 403, Message = $"Недостаточно прав для удаления трека {trackId}" }));
|
return StatusCode(403, ApiResponse<object>.Fail(new ErrorResponse { StatusCode = 403, Message = $"Недостаточно прав для удаления трека {trackId}" }));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,7 +156,10 @@ public class SharedPlaylistController : ControllerBase
|
|||||||
return StatusCode(500, ApiResponse<object>.Fail(new ErrorResponse { StatusCode = 500, Message = "Ошибка при удалении треков" }));
|
return StatusCode(500, ApiResponse<object>.Fail(new ErrorResponse { StatusCode = 500, Message = "Ошибка при удалении треков" }));
|
||||||
|
|
||||||
foreach (var trackId in request.TrackIds)
|
foreach (var trackId in request.TrackIds)
|
||||||
await _trackLogService.RemoveLogsForTrackAsync(playlist.Id, trackId);
|
{
|
||||||
|
await _trackRemovalLogService.LogRemovalAsync(playlist.Id, trackId, currentUserId, sessionId);
|
||||||
|
await _trackAdditionLogService.RemoveLogsForTrackAsync(playlist.Id, trackId);
|
||||||
|
}
|
||||||
|
|
||||||
return Ok(ApiResponse<object>.Ok(new { message = "Треки удалены" }));
|
return Ok(ApiResponse<object>.Ok(new { message = "Треки удалены" }));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,14 +9,16 @@ public class ApplicationDbContext : IdentityDbContext<ApplicationUser, IdentityR
|
|||||||
{
|
{
|
||||||
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { }
|
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { }
|
||||||
|
|
||||||
public DbSet<SharedPlaylistEntity> SharedPlaylists => Set<SharedPlaylistEntity>();
|
public DbSet<SharedPlaylist> SharedPlaylists => Set<SharedPlaylist>();
|
||||||
public DbSet<TrackAdditionLogEntity> TrackAdditionLogs => Set<TrackAdditionLogEntity>();
|
public DbSet<TrackAdditionLog> TrackAdditionLogs => Set<TrackAdditionLog>();
|
||||||
|
public DbSet<UserSession> UserSessions => Set<UserSession>();
|
||||||
|
public DbSet<TrackRemovalLog> TrackRemovalLogs => Set<TrackRemovalLog>();
|
||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder builder)
|
protected override void OnModelCreating(ModelBuilder builder)
|
||||||
{
|
{
|
||||||
base.OnModelCreating(builder);
|
base.OnModelCreating(builder);
|
||||||
|
|
||||||
builder.Entity<SharedPlaylistEntity>(entity =>
|
builder.Entity<SharedPlaylist>(entity =>
|
||||||
{
|
{
|
||||||
entity.HasKey(e => e.Id);
|
entity.HasKey(e => e.Id);
|
||||||
entity.HasIndex(e => e.ShareToken).IsUnique();
|
entity.HasIndex(e => e.ShareToken).IsUnique();
|
||||||
@@ -29,7 +31,18 @@ public class ApplicationDbContext : IdentityDbContext<ApplicationUser, IdentityR
|
|||||||
entity.Property(e => e.Title).IsRequired().HasMaxLength(255);
|
entity.Property(e => e.Title).IsRequired().HasMaxLength(255);
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.Entity<TrackAdditionLogEntity>(entity =>
|
builder.Entity<UserSession>(entity =>
|
||||||
|
{
|
||||||
|
entity.HasKey(e => e.SessionId);
|
||||||
|
entity.Property(e => e.SessionId).HasMaxLength(449);
|
||||||
|
entity.HasIndex(e => e.AssociatedUserId);
|
||||||
|
entity.HasOne(e => e.User)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey(e => e.AssociatedUserId)
|
||||||
|
.OnDelete(DeleteBehavior.SetNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Entity<TrackAdditionLog>(entity =>
|
||||||
{
|
{
|
||||||
entity.HasKey(e => e.Id);
|
entity.HasKey(e => e.Id);
|
||||||
entity.HasIndex(e => new { e.SharedPlaylistId, e.TrackId });
|
entity.HasIndex(e => new { e.SharedPlaylistId, e.TrackId });
|
||||||
@@ -41,6 +54,28 @@ public class ApplicationDbContext : IdentityDbContext<ApplicationUser, IdentityR
|
|||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey(e => e.AddedByUserId)
|
.HasForeignKey(e => e.AddedByUserId)
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
entity.HasOne(e => e.Session)
|
||||||
|
.WithMany(s => s.TrackAdditionLogs)
|
||||||
|
.HasForeignKey(e => e.SessionId)
|
||||||
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Entity<TrackRemovalLog>(entity =>
|
||||||
|
{
|
||||||
|
entity.HasKey(e => e.Id);
|
||||||
|
entity.HasIndex(e => new { e.SharedPlaylistId, e.TrackId });
|
||||||
|
entity.HasOne(e => e.SharedPlaylist)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey(e => e.SharedPlaylistId)
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
entity.HasOne(e => e.RemovedByUser)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey(e => e.RemovedByUserId)
|
||||||
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
entity.HasOne(e => e.Session)
|
||||||
|
.WithMany(s => s.TrackRemovalLogs)
|
||||||
|
.HasForeignKey(e => e.SessionId)
|
||||||
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
426
PlaylistShared.Api/Data/Migrations/20260413221546_AddSessionCacheTable.Designer.cs
generated
Normal file
426
PlaylistShared.Api/Data/Migrations/20260413221546_AddSessionCacheTable.Designer.cs
generated
Normal file
@@ -0,0 +1,426 @@
|
|||||||
|
// <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("20260413221546_AddSessionCacheTable")]
|
||||||
|
partial class AddSessionCacheTable
|
||||||
|
{
|
||||||
|
/// <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.SharedPlaylistEntity", 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>("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.TrackAdditionLogEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedAtUtc")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid>("AddedByUserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<Guid>("SharedPlaylistId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("TrackId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("AddedByUserId");
|
||||||
|
|
||||||
|
b.HasIndex("SharedPlaylistId", "TrackId");
|
||||||
|
|
||||||
|
b.ToTable("TrackAdditionLogs");
|
||||||
|
});
|
||||||
|
|
||||||
|
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.SharedPlaylistEntity", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", "Creator")
|
||||||
|
.WithMany("OwnedPlaylists")
|
||||||
|
.HasForeignKey("CreatorUserId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Creator");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.TrackAdditionLogEntity", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", "AddedByUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("AddedByUserId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.SharedPlaylistEntity", "SharedPlaylist")
|
||||||
|
.WithMany("TrackAdditionLogs")
|
||||||
|
.HasForeignKey("SharedPlaylistId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("AddedByUser");
|
||||||
|
|
||||||
|
b.Navigation("SharedPlaylist");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.ApplicationUser", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("OwnedPlaylists");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.SharedPlaylistEntity", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("TrackAdditionLogs");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace PlaylistShared.Api.Data.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddSessionCacheTable : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql(@"
|
||||||
|
CREATE TABLE [dbo].[SessionCache] (
|
||||||
|
[Id] NVARCHAR(449) NOT NULL,
|
||||||
|
[Value] VARBINARY(MAX) NOT NULL,
|
||||||
|
[ExpiresAtTime] DATETIMEOFFSET NOT NULL,
|
||||||
|
[SlidingExpirationInSeconds] BIGINT NULL,
|
||||||
|
[AbsoluteExpiration] DATETIMEOFFSET NULL,
|
||||||
|
CONSTRAINT [pk_SessionCache] PRIMARY KEY ([Id])
|
||||||
|
);
|
||||||
|
CREATE NONCLUSTERED INDEX [Index_ExpiresAtTime] ON [dbo].[SessionCache] ([ExpiresAtTime]);
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql("DROP TABLE [dbo].[SessionCache]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
544
PlaylistShared.Api/Data/Migrations/20260413223451_AddUserSessionsAndLogging.Designer.cs
generated
Normal file
544
PlaylistShared.Api/Data/Migrations/20260413223451_AddUserSessionsAndLogging.Designer.cs
generated
Normal file
@@ -0,0 +1,544 @@
|
|||||||
|
// <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("20260413223451_AddUserSessionsAndLogging")]
|
||||||
|
partial class AddUserSessionsAndLogging
|
||||||
|
{
|
||||||
|
/// <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>("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,151 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace PlaylistShared.Api.Data.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddUserSessionsAndLogging : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AlterColumn<Guid>(
|
||||||
|
name: "AddedByUserId",
|
||||||
|
table: "TrackAdditionLogs",
|
||||||
|
type: "uniqueidentifier",
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(Guid),
|
||||||
|
oldType: "uniqueidentifier");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "SessionId",
|
||||||
|
table: "TrackAdditionLogs",
|
||||||
|
type: "nvarchar(449)",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "UserSessions",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
SessionId = table.Column<string>(type: "nvarchar(449)", maxLength: 449, nullable: false),
|
||||||
|
ClientIpAddress = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
UserAgent = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
FirstSeenUtc = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
|
LastSeenUtc = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
|
AssociatedUserId = table.Column<Guid>(type: "uniqueidentifier", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_UserSessions", x => x.SessionId);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_UserSessions_AspNetUsers_AssociatedUserId",
|
||||||
|
column: x => x.AssociatedUserId,
|
||||||
|
principalTable: "AspNetUsers",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.SetNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "TrackRemovalLogs",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
SharedPlaylistId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
TrackId = table.Column<string>(type: "nvarchar(450)", nullable: false),
|
||||||
|
RemovedByUserId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
RemovedAtUtc = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
|
SessionId = table.Column<string>(type: "nvarchar(449)", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_TrackRemovalLogs", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_TrackRemovalLogs_AspNetUsers_RemovedByUserId",
|
||||||
|
column: x => x.RemovedByUserId,
|
||||||
|
principalTable: "AspNetUsers",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_TrackRemovalLogs_SharedPlaylists_SharedPlaylistId",
|
||||||
|
column: x => x.SharedPlaylistId,
|
||||||
|
principalTable: "SharedPlaylists",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_TrackRemovalLogs_UserSessions_SessionId",
|
||||||
|
column: x => x.SessionId,
|
||||||
|
principalTable: "UserSessions",
|
||||||
|
principalColumn: "SessionId",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_TrackAdditionLogs_SessionId",
|
||||||
|
table: "TrackAdditionLogs",
|
||||||
|
column: "SessionId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_TrackRemovalLogs_RemovedByUserId",
|
||||||
|
table: "TrackRemovalLogs",
|
||||||
|
column: "RemovedByUserId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_TrackRemovalLogs_SessionId",
|
||||||
|
table: "TrackRemovalLogs",
|
||||||
|
column: "SessionId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_TrackRemovalLogs_SharedPlaylistId_TrackId",
|
||||||
|
table: "TrackRemovalLogs",
|
||||||
|
columns: new[] { "SharedPlaylistId", "TrackId" });
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_UserSessions_AssociatedUserId",
|
||||||
|
table: "UserSessions",
|
||||||
|
column: "AssociatedUserId");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_TrackAdditionLogs_UserSessions_SessionId",
|
||||||
|
table: "TrackAdditionLogs",
|
||||||
|
column: "SessionId",
|
||||||
|
principalTable: "UserSessions",
|
||||||
|
principalColumn: "SessionId",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_TrackAdditionLogs_UserSessions_SessionId",
|
||||||
|
table: "TrackAdditionLogs");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "TrackRemovalLogs");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "UserSessions");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_TrackAdditionLogs_SessionId",
|
||||||
|
table: "TrackAdditionLogs");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "SessionId",
|
||||||
|
table: "TrackAdditionLogs");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<Guid>(
|
||||||
|
name: "AddedByUserId",
|
||||||
|
table: "TrackAdditionLogs",
|
||||||
|
type: "uniqueidentifier",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"),
|
||||||
|
oldClrType: typeof(Guid),
|
||||||
|
oldType: "uniqueidentifier",
|
||||||
|
oldNullable: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -237,7 +237,7 @@ namespace PlaylistShared.Api.Data.Migrations
|
|||||||
b.ToTable("AspNetUsers", (string)null);
|
b.ToTable("AspNetUsers", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("PlaylistShared.Api.Entities.SharedPlaylistEntity", b =>
|
modelBuilder.Entity("PlaylistShared.Api.Entities.SharedPlaylist", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -299,7 +299,7 @@ namespace PlaylistShared.Api.Data.Migrations
|
|||||||
b.ToTable("SharedPlaylists");
|
b.ToTable("SharedPlaylists");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("PlaylistShared.Api.Entities.TrackAdditionLogEntity", b =>
|
modelBuilder.Entity("PlaylistShared.Api.Entities.TrackAdditionLog", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -308,9 +308,13 @@ namespace PlaylistShared.Api.Data.Migrations
|
|||||||
b.Property<DateTime>("AddedAtUtc")
|
b.Property<DateTime>("AddedAtUtc")
|
||||||
.HasColumnType("datetime2");
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
b.Property<Guid>("AddedByUserId")
|
b.Property<Guid?>("AddedByUserId")
|
||||||
.HasColumnType("uniqueidentifier");
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("SessionId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(449)");
|
||||||
|
|
||||||
b.Property<Guid>("SharedPlaylistId")
|
b.Property<Guid>("SharedPlaylistId")
|
||||||
.HasColumnType("uniqueidentifier");
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
@@ -322,11 +326,75 @@ namespace PlaylistShared.Api.Data.Migrations
|
|||||||
|
|
||||||
b.HasIndex("AddedByUserId");
|
b.HasIndex("AddedByUserId");
|
||||||
|
|
||||||
|
b.HasIndex("SessionId");
|
||||||
|
|
||||||
b.HasIndex("SharedPlaylistId", "TrackId");
|
b.HasIndex("SharedPlaylistId", "TrackId");
|
||||||
|
|
||||||
b.ToTable("TrackAdditionLogs");
|
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 =>
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", null)
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", null)
|
||||||
@@ -378,7 +446,7 @@ namespace PlaylistShared.Api.Data.Migrations
|
|||||||
.IsRequired();
|
.IsRequired();
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("PlaylistShared.Api.Entities.SharedPlaylistEntity", b =>
|
modelBuilder.Entity("PlaylistShared.Api.Entities.SharedPlaylist", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", "Creator")
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", "Creator")
|
||||||
.WithMany("OwnedPlaylists")
|
.WithMany("OwnedPlaylists")
|
||||||
@@ -389,15 +457,20 @@ namespace PlaylistShared.Api.Data.Migrations
|
|||||||
b.Navigation("Creator");
|
b.Navigation("Creator");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("PlaylistShared.Api.Entities.TrackAdditionLogEntity", b =>
|
modelBuilder.Entity("PlaylistShared.Api.Entities.TrackAdditionLog", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", "AddedByUser")
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", "AddedByUser")
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("AddedByUserId")
|
.HasForeignKey("AddedByUserId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.UserSession", "Session")
|
||||||
|
.WithMany("TrackAdditionLogs")
|
||||||
|
.HasForeignKey("SessionId")
|
||||||
.OnDelete(DeleteBehavior.Restrict)
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
b.HasOne("PlaylistShared.Api.Entities.SharedPlaylistEntity", "SharedPlaylist")
|
b.HasOne("PlaylistShared.Api.Entities.SharedPlaylist", "SharedPlaylist")
|
||||||
.WithMany("TrackAdditionLogs")
|
.WithMany("TrackAdditionLogs")
|
||||||
.HasForeignKey("SharedPlaylistId")
|
.HasForeignKey("SharedPlaylistId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
@@ -405,6 +478,44 @@ namespace PlaylistShared.Api.Data.Migrations
|
|||||||
|
|
||||||
b.Navigation("AddedByUser");
|
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");
|
b.Navigation("SharedPlaylist");
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -413,10 +524,17 @@ namespace PlaylistShared.Api.Data.Migrations
|
|||||||
b.Navigation("OwnedPlaylists");
|
b.Navigation("OwnedPlaylists");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("PlaylistShared.Api.Entities.SharedPlaylistEntity", b =>
|
modelBuilder.Entity("PlaylistShared.Api.Entities.SharedPlaylist", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("TrackAdditionLogs");
|
b.Navigation("TrackAdditionLogs");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.UserSession", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("TrackAdditionLogs");
|
||||||
|
|
||||||
|
b.Navigation("TrackRemovalLogs");
|
||||||
|
});
|
||||||
#pragma warning restore 612, 618
|
#pragma warning restore 612, 618
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,5 +24,5 @@ public class ApplicationUser : IdentityUser<Guid>
|
|||||||
public DateTime RefreshTokenExpiryUtc { get; set; }
|
public DateTime RefreshTokenExpiryUtc { get; set; }
|
||||||
|
|
||||||
/// <summary>Плейлисты, созданные пользователем.</summary>
|
/// <summary>Плейлисты, созданные пользователем.</summary>
|
||||||
public ICollection<SharedPlaylistEntity> OwnedPlaylists { get; set; } = new List<SharedPlaylistEntity>();
|
public ICollection<SharedPlaylist> OwnedPlaylists { get; set; } = new List<SharedPlaylist>();
|
||||||
}
|
}
|
||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
namespace PlaylistShared.Api.Entities;
|
namespace PlaylistShared.Api.Entities;
|
||||||
|
|
||||||
/// <summary>Сущность шеринг-плейлиста (таблица в БД).</summary>
|
/// <summary>Сущность шеринг-плейлиста.</summary>
|
||||||
public class SharedPlaylistEntity
|
public class SharedPlaylist
|
||||||
{
|
{
|
||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
public Guid CreatorUserId { get; set; }
|
public Guid CreatorUserId { get; set; }
|
||||||
@@ -22,5 +22,5 @@ public class SharedPlaylistEntity
|
|||||||
|
|
||||||
// Навигационные свойства
|
// Навигационные свойства
|
||||||
public ApplicationUser Creator { get; set; } = null!;
|
public ApplicationUser Creator { get; set; } = null!;
|
||||||
public ICollection<TrackAdditionLogEntity> TrackAdditionLogs { get; set; } = new List<TrackAdditionLogEntity>();
|
public ICollection<TrackAdditionLog> TrackAdditionLogs { get; set; } = new List<TrackAdditionLog>();
|
||||||
}
|
}
|
||||||
16
PlaylistShared.Api/Entities/TrackAdditionLog.cs
Normal file
16
PlaylistShared.Api/Entities/TrackAdditionLog.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
namespace PlaylistShared.Api.Entities;
|
||||||
|
|
||||||
|
/// <summary>Лог добавления трека.</summary>
|
||||||
|
public class TrackAdditionLog
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public Guid SharedPlaylistId { get; set; }
|
||||||
|
public string TrackId { get; set; } = null!;
|
||||||
|
public Guid? AddedByUserId { get; set; }
|
||||||
|
public DateTime AddedAtUtc { get; set; }
|
||||||
|
public string SessionId { get; set; } = null!;
|
||||||
|
|
||||||
|
public SharedPlaylist SharedPlaylist { get; set; } = null!;
|
||||||
|
public ApplicationUser? AddedByUser { get; set; }
|
||||||
|
public UserSession Session { get; set; } = null!;
|
||||||
|
}
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
namespace PlaylistShared.Api.Entities;
|
|
||||||
|
|
||||||
/// <summary>Лог добавления трека (таблица в БД).</summary>
|
|
||||||
public class TrackAdditionLogEntity
|
|
||||||
{
|
|
||||||
public Guid Id { get; set; }
|
|
||||||
public Guid SharedPlaylistId { get; set; }
|
|
||||||
public string TrackId { get; set; } = null!;
|
|
||||||
public Guid AddedByUserId { get; set; }
|
|
||||||
public DateTime AddedAtUtc { get; set; }
|
|
||||||
|
|
||||||
// Навигационные свойства
|
|
||||||
public SharedPlaylistEntity SharedPlaylist { get; set; } = null!;
|
|
||||||
public ApplicationUser AddedByUser { get; set; } = null!;
|
|
||||||
}
|
|
||||||
15
PlaylistShared.Api/Entities/TrackRemovalLog.cs
Normal file
15
PlaylistShared.Api/Entities/TrackRemovalLog.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using PlaylistShared.Api.Entities;
|
||||||
|
|
||||||
|
public class TrackRemovalLog
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public Guid SharedPlaylistId { get; set; }
|
||||||
|
public string TrackId { get; set; } = null!;
|
||||||
|
public Guid? RemovedByUserId { get; set; }
|
||||||
|
public DateTime RemovedAtUtc { get; set; }
|
||||||
|
public string SessionId { get; set; } = null!;
|
||||||
|
|
||||||
|
public SharedPlaylist SharedPlaylist { get; set; } = null!;
|
||||||
|
public ApplicationUser? RemovedByUser { get; set; }
|
||||||
|
public UserSession Session { get; set; } = null!;
|
||||||
|
}
|
||||||
15
PlaylistShared.Api/Entities/UserSession.cs
Normal file
15
PlaylistShared.Api/Entities/UserSession.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
namespace PlaylistShared.Api.Entities;
|
||||||
|
|
||||||
|
public class UserSession
|
||||||
|
{
|
||||||
|
public string SessionId { get; set; } = null!; // HttpContext.Session.Id
|
||||||
|
public string? ClientIpAddress { get; set; }
|
||||||
|
public string? UserAgent { get; set; }
|
||||||
|
public DateTime FirstSeenUtc { get; set; }
|
||||||
|
public DateTime LastSeenUtc { get; set; }
|
||||||
|
public Guid? AssociatedUserId { get; set; } // если позже залогинился
|
||||||
|
|
||||||
|
public ApplicationUser? User { get; set; }
|
||||||
|
public ICollection<TrackAdditionLog> TrackAdditionLogs { get; set; } = new List<TrackAdditionLog>();
|
||||||
|
public ICollection<TrackRemovalLog> TrackRemovalLogs { get; set; } = new List<TrackRemovalLog>();
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@ public class AppMappingProfile : Profile
|
|||||||
{
|
{
|
||||||
public AppMappingProfile()
|
public AppMappingProfile()
|
||||||
{
|
{
|
||||||
CreateMap<SharedPlaylistEntity, SharedPlaylistDto>()
|
CreateMap<SharedPlaylist, SharedPlaylistDto>()
|
||||||
.ForMember(dest => dest.Creator, opt => opt.MapFrom(src => src.Creator));
|
.ForMember(dest => dest.Creator, opt => opt.MapFrom(src => src.Creator));
|
||||||
CreateMap<ApplicationUser, ApplicationUserDto>();
|
CreateMap<ApplicationUser, ApplicationUserDto>();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.5" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.5" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Caching.SqlServer" Version="10.0.5" />
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.23.0" />
|
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.23.0" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.1.7" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.1.7" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="10.1.7" />
|
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="10.1.7" />
|
||||||
|
|||||||
@@ -35,6 +35,22 @@ public class Program
|
|||||||
.AddEntityFrameworkStores<ApplicationDbContext>()
|
.AddEntityFrameworkStores<ApplicationDbContext>()
|
||||||
.AddDefaultTokenProviders();
|
.AddDefaultTokenProviders();
|
||||||
|
|
||||||
|
// Session
|
||||||
|
builder.Services.AddDistributedSqlServerCache(options =>
|
||||||
|
{
|
||||||
|
options.ConnectionString = builder.Configuration.GetConnectionString("DefaultConnection");
|
||||||
|
options.SchemaName = "dbo";
|
||||||
|
options.TableName = "SessionCache";
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Services.AddSession(options =>
|
||||||
|
{
|
||||||
|
options.IdleTimeout = TimeSpan.FromDays(30);
|
||||||
|
options.Cookie.HttpOnly = true;
|
||||||
|
options.Cookie.IsEssential = true;
|
||||||
|
options.Cookie.SameSite = SameSiteMode.Lax;
|
||||||
|
});
|
||||||
|
|
||||||
// JWT
|
// JWT
|
||||||
var jwtKey = builder.Configuration["Jwt:Key"] ?? throw new Exception("Jwt:Key missing");
|
var jwtKey = builder.Configuration["Jwt:Key"] ?? throw new Exception("Jwt:Key missing");
|
||||||
builder.Services.AddAuthentication(options =>
|
builder.Services.AddAuthentication(options =>
|
||||||
@@ -70,12 +86,15 @@ public class Program
|
|||||||
options.SignInScheme = IdentityConstants.ExternalScheme;
|
options.SignInScheme = IdentityConstants.ExternalScheme;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
builder.Services.AddHttpContextAccessor();
|
||||||
builder.Services.AddAuthorization();
|
builder.Services.AddAuthorization();
|
||||||
builder.Services.AddAutoMapper(t => t.AddProfile<AppMappingProfile>());
|
builder.Services.AddAutoMapper(t => t.AddProfile<AppMappingProfile>());
|
||||||
|
builder.Services.AddScoped<JwtService>();
|
||||||
|
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<JwtService>();
|
builder.Services.AddScoped<TrackRemovalLogService>();
|
||||||
builder.Services.AddDataProtection();
|
builder.Services.AddDataProtection();
|
||||||
|
|
||||||
builder.Services.AddHttpClient();
|
builder.Services.AddHttpClient();
|
||||||
@@ -123,6 +142,7 @@ public class Program
|
|||||||
app.UseCors("Production");
|
app.UseCors("Production");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
app.UseSession();
|
||||||
app.UseAuthentication();
|
app.UseAuthentication();
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public class SharedPlaylistService
|
|||||||
|
|
||||||
public async Task<SharedPlaylistDto> CreateAsync(Guid creatorUserId, SharePlaylistDto dto)
|
public async Task<SharedPlaylistDto> CreateAsync(Guid creatorUserId, SharePlaylistDto dto)
|
||||||
{
|
{
|
||||||
var entity = new SharedPlaylistEntity
|
var entity = new SharedPlaylist
|
||||||
{
|
{
|
||||||
Id = Guid.NewGuid(),
|
Id = Guid.NewGuid(),
|
||||||
CreatorUserId = creatorUserId,
|
CreatorUserId = creatorUserId,
|
||||||
@@ -51,7 +51,7 @@ public class SharedPlaylistService
|
|||||||
return entity == null ? null : _mapper.Map<SharedPlaylistDto>(entity);
|
return entity == null ? null : _mapper.Map<SharedPlaylistDto>(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<SharedPlaylistEntity?> GetEntityByTokenAsync(string token)
|
public async Task<SharedPlaylist?> GetEntityByTokenAsync(string token)
|
||||||
{
|
{
|
||||||
return await _db.SharedPlaylists
|
return await _db.SharedPlaylists
|
||||||
.Include(sp => sp.Creator)
|
.Include(sp => sp.Creator)
|
||||||
@@ -80,21 +80,21 @@ public class SharedPlaylistService
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> CanViewAsync(SharedPlaylistEntity playlist, Guid? currentUserId)
|
public async Task<bool> CanViewAsync(SharedPlaylist playlist, Guid? currentUserId)
|
||||||
{
|
{
|
||||||
if (currentUserId == playlist.CreatorUserId) return true;
|
if (currentUserId == playlist.CreatorUserId) return true;
|
||||||
return playlist.ViewPermission == ViewPermission.Everyone ||
|
return playlist.ViewPermission == ViewPermission.Everyone ||
|
||||||
(playlist.ViewPermission == ViewPermission.AuthorizedOnly && currentUserId.HasValue);
|
(playlist.ViewPermission == ViewPermission.AuthorizedOnly && currentUserId.HasValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> CanAddTrackAsync(SharedPlaylistEntity playlist, Guid? currentUserId)
|
public async Task<bool> CanAddTrackAsync(SharedPlaylist playlist, Guid? currentUserId)
|
||||||
{
|
{
|
||||||
if (currentUserId == playlist.CreatorUserId) return true;
|
if (currentUserId == playlist.CreatorUserId) return true;
|
||||||
return playlist.AddPermission == EditPermission.Everyone ||
|
return playlist.AddPermission == EditPermission.Everyone ||
|
||||||
(playlist.AddPermission == EditPermission.AuthorizedOnly && currentUserId.HasValue);
|
(playlist.AddPermission == EditPermission.AuthorizedOnly && currentUserId.HasValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> CanRemoveTrackAsync(SharedPlaylistEntity playlist, Guid? currentUserId, string trackId)
|
public async Task<bool> CanRemoveTrackAsync(SharedPlaylist playlist, Guid? currentUserId, string trackId, string sessionId)
|
||||||
{
|
{
|
||||||
if (currentUserId == playlist.CreatorUserId) return true;
|
if (currentUserId == playlist.CreatorUserId) return true;
|
||||||
return playlist.RemovePermission switch
|
return playlist.RemovePermission switch
|
||||||
@@ -102,7 +102,9 @@ public class SharedPlaylistService
|
|||||||
EditPermission.Everyone => true,
|
EditPermission.Everyone => true,
|
||||||
EditPermission.AuthorizedOnly => currentUserId.HasValue,
|
EditPermission.AuthorizedOnly => currentUserId.HasValue,
|
||||||
EditPermission.AddedByUserOnly when currentUserId.HasValue =>
|
EditPermission.AddedByUserOnly when currentUserId.HasValue =>
|
||||||
await _trackLogService.IsTrackAddedByUserAsync(playlist.Id, trackId, currentUserId.Value),
|
await _trackLogService.IsTrackAddedByCurrentUserOrSessionAsync(playlist.Id, trackId, currentUserId, sessionId),
|
||||||
|
EditPermission.AddedByUserOnly when !currentUserId.HasValue =>
|
||||||
|
await _trackLogService.IsTrackAddedByCurrentUserOrSessionAsync(playlist.Id, trackId, null, sessionId),
|
||||||
_ => false
|
_ => false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -121,7 +123,7 @@ public class SharedPlaylistService
|
|||||||
.TrimEnd('=');
|
.TrimEnd('=');
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<SharedPlaylistEntity>> GetAllByUserAsync(Guid userId)
|
public async Task<List<SharedPlaylist>> GetAllByUserAsync(Guid userId)
|
||||||
{
|
{
|
||||||
return await _db.SharedPlaylists
|
return await _db.SharedPlaylists
|
||||||
.Where(sp => sp.CreatorUserId == userId && !sp.IsDeleted)
|
.Where(sp => sp.CreatorUserId == userId && !sp.IsDeleted)
|
||||||
|
|||||||
@@ -13,24 +13,26 @@ public class TrackAdditionLogService
|
|||||||
_db = db;
|
_db = db;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task LogAdditionAsync(Guid sharedPlaylistId, string trackId, Guid addedByUserId)
|
public async Task LogAdditionAsync(Guid sharedPlaylistId, string trackId, Guid? addedByUserId, string sessionId)
|
||||||
{
|
{
|
||||||
var log = new TrackAdditionLogEntity
|
var log = new TrackAdditionLog
|
||||||
{
|
{
|
||||||
Id = Guid.NewGuid(),
|
Id = Guid.NewGuid(),
|
||||||
SharedPlaylistId = sharedPlaylistId,
|
SharedPlaylistId = sharedPlaylistId,
|
||||||
TrackId = trackId,
|
TrackId = trackId,
|
||||||
AddedByUserId = addedByUserId,
|
AddedByUserId = addedByUserId,
|
||||||
AddedAtUtc = DateTime.UtcNow
|
AddedAtUtc = DateTime.UtcNow,
|
||||||
|
SessionId = sessionId
|
||||||
};
|
};
|
||||||
_db.TrackAdditionLogs.Add(log);
|
_db.TrackAdditionLogs.Add(log);
|
||||||
await _db.SaveChangesAsync();
|
await _db.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> IsTrackAddedByUserAsync(Guid sharedPlaylistId, string trackId, Guid userId)
|
public async Task<bool> IsTrackAddedByCurrentUserOrSessionAsync(Guid sharedPlaylistId, string trackId, Guid? userId, string sessionId)
|
||||||
{
|
{
|
||||||
return await _db.TrackAdditionLogs
|
return await _db.TrackAdditionLogs
|
||||||
.AnyAsync(l => l.SharedPlaylistId == sharedPlaylistId && l.TrackId == trackId && l.AddedByUserId == userId);
|
.AnyAsync(l => l.SharedPlaylistId == sharedPlaylistId && l.TrackId == trackId &&
|
||||||
|
(userId != null ? l.AddedByUserId == userId : l.SessionId == sessionId));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RemoveLogsForTrackAsync(Guid sharedPlaylistId, string trackId)
|
public async Task RemoveLogsForTrackAsync(Guid sharedPlaylistId, string trackId)
|
||||||
|
|||||||
22
PlaylistShared.Api/Services/TrackRemovalLogService.cs
Normal file
22
PlaylistShared.Api/Services/TrackRemovalLogService.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using PlaylistShared.Api.Data;
|
||||||
|
|
||||||
|
public class TrackRemovalLogService
|
||||||
|
{
|
||||||
|
private readonly ApplicationDbContext _db;
|
||||||
|
public TrackRemovalLogService(ApplicationDbContext db) => _db = db;
|
||||||
|
|
||||||
|
public async Task LogRemovalAsync(Guid sharedPlaylistId, string trackId, Guid? removedByUserId, string sessionId)
|
||||||
|
{
|
||||||
|
var log = new TrackRemovalLog
|
||||||
|
{
|
||||||
|
Id = Guid.NewGuid(),
|
||||||
|
SharedPlaylistId = sharedPlaylistId,
|
||||||
|
TrackId = trackId,
|
||||||
|
RemovedByUserId = removedByUserId,
|
||||||
|
RemovedAtUtc = DateTime.UtcNow,
|
||||||
|
SessionId = sessionId
|
||||||
|
};
|
||||||
|
_db.TrackRemovalLogs.Add(log);
|
||||||
|
await _db.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
47
PlaylistShared.Api/Services/UserSessionService.cs
Normal file
47
PlaylistShared.Api/Services/UserSessionService.cs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
using PlaylistShared.Api.Data;
|
||||||
|
using PlaylistShared.Api.Entities;
|
||||||
|
|
||||||
|
public class UserSessionService
|
||||||
|
{
|
||||||
|
private readonly ApplicationDbContext _db;
|
||||||
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||||
|
|
||||||
|
public UserSessionService(ApplicationDbContext db, IHttpContextAccessor httpContextAccessor)
|
||||||
|
{
|
||||||
|
_db = db;
|
||||||
|
_httpContextAccessor = httpContextAccessor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<UserSession> GetOrCreateCurrentSessionAsync(Guid? associatedUserId = null)
|
||||||
|
{
|
||||||
|
var httpContext = _httpContextAccessor.HttpContext
|
||||||
|
?? throw new InvalidOperationException("No HttpContext available");
|
||||||
|
|
||||||
|
var sessionId = httpContext.Session.Id;
|
||||||
|
var now = DateTime.UtcNow;
|
||||||
|
|
||||||
|
var session = await _db.UserSessions.FindAsync(sessionId);
|
||||||
|
if (session == null)
|
||||||
|
{
|
||||||
|
session = new UserSession
|
||||||
|
{
|
||||||
|
SessionId = sessionId,
|
||||||
|
ClientIpAddress = httpContext.Connection.RemoteIpAddress?.ToString(),
|
||||||
|
UserAgent = httpContext.Request.Headers["User-Agent"].ToString(),
|
||||||
|
FirstSeenUtc = now,
|
||||||
|
LastSeenUtc = now,
|
||||||
|
AssociatedUserId = associatedUserId
|
||||||
|
};
|
||||||
|
_db.UserSessions.Add(session);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
session.LastSeenUtc = now;
|
||||||
|
if (session.AssociatedUserId == null && associatedUserId != null)
|
||||||
|
session.AssociatedUserId = associatedUserId;
|
||||||
|
_db.UserSessions.Update(session);
|
||||||
|
}
|
||||||
|
await _db.SaveChangesAsync();
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user