Добавлен playlist shared

This commit is contained in:
FrigaT
2026-04-11 15:41:24 +03:00
parent 8444fc5f8e
commit ba9d97239e
84 changed files with 61796 additions and 0 deletions

View File

@@ -0,0 +1,109 @@
using Microsoft.AspNetCore.Mvc;
using PlaylistShared.Models;
using System.Net.Http.Headers;
using System.Text.Json;
namespace PlaylistShared.Controllers;
[Microsoft.AspNetCore.Mvc.Route("auth")]
[ApiController]
public class AuthController : ControllerBase
{
private readonly IConfiguration _config;
private readonly HttpClient _httpClient;
private readonly AppDbContext _db;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly ILogger<AuthController> _logger;
public AuthController(
IConfiguration config,
IHttpClientFactory factory,
AppDbContext db,
SignInManager<ApplicationUser> signInManager,
ILogger<AuthController> logger)
{
_config = config;
_httpClient = factory.CreateClient();
_db = db;
_signInManager = signInManager;
_logger = logger;
}
[HttpGet("login")]
public IActionResult Login()
{
var clientId = _config["YandexOAuth:ClientId"];
var redirectUri = Url.Action(nameof(Callback), "Auth", null, Request.Scheme);
var authUrl = $"{_config["YandexOAuth:AuthorizationEndpoint"]}?response_type=code&client_id={clientId}&redirect_uri={Uri.EscapeDataString(redirectUri!)}";
return Redirect(authUrl);
}
[HttpGet("callback")]
public async Task<IActionResult> Callback(string code)
{
if (string.IsNullOrEmpty(code))
return BadRequest("Missing code");
var tokenResponse = await ExchangeCodeForTokenAsync(code);
if (tokenResponse is null)
return StatusCode(500, "Token exchange failed");
var userInfo = await GetUserInfoAsync(tokenResponse.AccessToken);
if (userInfo is null)
return StatusCode(500, "User info fetch failed");
var user = await _db.Users.FirstOrDefaultAsync(u => u.YandexId == userInfo.Id);
if (user is null)
{
user = new ApplicationUser
{
UserName = userInfo.Login,
YandexId = userInfo.Id,
Email = userInfo.DefaultEmail,
AccessToken = tokenResponse.AccessToken,
RefreshToken = tokenResponse.RefreshToken,
AccessTokenExpiresAt = DateTime.UtcNow.AddSeconds(tokenResponse.ExpiresIn)
};
_db.Users.Add(user);
}
else
{
user.AccessToken = tokenResponse.AccessToken;
user.RefreshToken = tokenResponse.RefreshToken;
user.AccessTokenExpiresAt = DateTime.UtcNow.AddSeconds(tokenResponse.ExpiresIn);
_db.Users.Update(user);
}
await _db.SaveChangesAsync();
await _signInManager.SignInAsync(user, isPersistent: false);
return Redirect("/");
}
private async Task<YandexTokenResponse?> ExchangeCodeForTokenAsync(string code)
{
var tokenUrl = _config["YandexOAuth:TokenEndpoint"];
var redirectUri = Url.Action(nameof(Callback), "Auth", null, Request.Scheme);
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("grant_type", "authorization_code"),
new KeyValuePair<string, string>("code", code),
new KeyValuePair<string, string>("client_id", _config["YandexOAuth:ClientId"]!),
new KeyValuePair<string, string>("client_secret", _config["YandexOAuth:ClientSecret"]!),
new KeyValuePair<string, string>("redirect_uri", redirectUri!)
});
var response = await _httpClient.PostAsync(tokenUrl!, content);
if (!response.IsSuccessStatusCode) return null;
var json = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<YandexTokenResponse>(json);
}
private async Task<YandexUserInfo?> GetUserInfoAsync(string accessToken)
{
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("OAuth", accessToken);
var response = await _httpClient.GetAsync(_config["YandexOAuth:UserInfoEndpoint"]!);
if (!response.IsSuccessStatusCode) return null;
var json = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<YandexUserInfo>(json);
}
}

View File

@@ -0,0 +1,93 @@
// Controllers/TrackController.cs
using Microsoft.AspNetCore.Mvc;
namespace PlaylistShared.Controllers;
[ApiController]
[Microsoft.AspNetCore.Mvc.Route("api/tracks")]
[Authorize]
public class TrackController : ControllerBase
{
private readonly AppDbContext _db;
private readonly IYandexMusicService _yandex;
public TrackController(AppDbContext db, IYandexMusicService yandex)
{
_db = db;
_yandex = yandex;
}
[HttpPost("add")]
public async Task<IActionResult> AddTrack(Guid playlistId, string trackId, string? trackTitle, string? artist)
{
var playlist = await _db.SharedPlaylists
.Include(p => p.Owner)
.FirstOrDefaultAsync(p => p.Id == playlistId);
if (playlist == null) return NotFound();
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
if (!CanAdd(playlist, userId)) return Forbid();
// Добавляем в Яндекс
await _yandex.AddTrackToPlaylistAsync(playlist.OwnerUserId, playlist.YandexPlaylistId, trackId);
// Сохраняем информацию о треке в кеш
var track = new PlaylistTrack
{
Id = Guid.NewGuid(),
PlaylistId = playlist.Id,
YandexTrackId = trackId,
Title = trackTitle ?? "Unknown",
Artist = artist,
AddedByUserId = userId,
AddedAt = DateTime.UtcNow
};
_db.PlaylistTracks.Add(track);
await _db.SaveChangesAsync();
return Ok();
}
[HttpDelete("remove")]
public async Task<IActionResult> RemoveTrack(Guid playlistId, string trackId)
{
var playlist = await _db.SharedPlaylists
.Include(p => p.Owner)
.FirstOrDefaultAsync(p => p.Id == playlistId);
if (playlist == null) return NotFound();
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
var trackEntry = await _db.PlaylistTracks
.FirstOrDefaultAsync(t => t.PlaylistId == playlistId && t.YandexTrackId == trackId);
if (!CanDelete(playlist, userId, trackEntry?.AddedByUserId))
return Forbid();
await _yandex.RemoveTrackFromPlaylistAsync(playlist.OwnerUserId, playlist.YandexPlaylistId, trackId);
if (trackEntry != null) _db.PlaylistTracks.Remove(trackEntry);
await _db.SaveChangesAsync();
return Ok();
}
private bool CanAdd(SharedPlaylist playlist, string? userId)
{
return playlist.Permissions.Add switch
{
AccessLevel.All => true,
AccessLevel.Authorized => userId != null,
AccessLevel.None => false,
_ => false
};
}
private bool CanDelete(SharedPlaylist playlist, string? userId, string? adderUserId)
{
if (userId == playlist.OwnerUserId) return true; // владелец всегда может удалить
return playlist.Permissions.Delete switch
{
DeleteAccessLevel.All => true,
DeleteAccessLevel.Authorized => userId != null,
DeleteAccessLevel.AdderOnly => userId != null && userId == adderUserId,
DeleteAccessLevel.OwnerOnly => false,
_ => false
};
}
}