Добавлен playlist shared
This commit is contained in:
109
PlaylistShared/Controllers/AuthController.cs
Normal file
109
PlaylistShared/Controllers/AuthController.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
93
PlaylistShared/Controllers/TrackController.cs
Normal file
93
PlaylistShared/Controllers/TrackController.cs
Normal 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
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user