fix(admin): fixed various issues lmao idk it was a ballache and is probably quite shit. sorry to future me

This commit is contained in:
Fishandchips321 2026-03-05 19:09:26 +00:00
parent 56ea7fb7f0
commit 2cbbc00489
29 changed files with 382 additions and 131 deletions

View file

@ -1,4 +1,5 @@
using JellyGlass.Exceptions;
using JellyGlass.Models.ControllerBodies.Auth;
using JellyGlass.Services;
using Microsoft.AspNetCore.Mvc;
@ -64,20 +65,20 @@ public class AuthController : ControllerBase
}
[HttpPost]
public async Task<IActionResult> CreateLogin([FromForm] string username, [FromForm] string password)
public async Task<IActionResult> CreateLogin([FromBody] CreateLoginDTO login)
{
if (!await IsAdminAuthenticated())
{
return Forbid();
}
var newLogin = await _service.CreateLogin(username, password);
var newLogin = await _service.CreateLogin(login.Username, login.Password);
return Ok(newLogin);
}
[HttpPut]
public async Task<IActionResult> UpdateLogin([FromForm] string username, [FromForm] string password, [FromForm] string newPassword)
public async Task<IActionResult> UpdateLogin([FromBody] UpdateLoginDTO login)
{
if (!await IsAdminAuthenticated())
{
@ -86,7 +87,7 @@ public class AuthController : ControllerBase
try
{
await _service.UpdateLoginOwnPassword(username, newPassword, password);
await _service.UpdateLoginOwnPassword(GetSessionToken()!, login.NewPassword, login.Password); //TODO: should take in session token
}
catch (AuthNotFoundException)
{
@ -101,7 +102,7 @@ public class AuthController : ControllerBase
}
[HttpDelete]
public async Task<IActionResult> DeleteLogin([FromForm] string username)
public async Task<IActionResult> DeleteLogin([FromBody] DeleteLoginDTO login)
{
if (!await IsAdminAuthenticated())
{
@ -110,7 +111,7 @@ public class AuthController : ControllerBase
try
{
var deletedLogin = await _service.DeleteLogin(username);
var deletedLogin = await _service.DeleteLogin(login.Username);
return Ok(deletedLogin);
}

View file

@ -21,7 +21,7 @@ public class SearchController : ControllerBase
}
[HttpGet]
public async Task<IActionResult> handleSearch([FromQuery] string searchTerm, string serverId)
public async Task<IActionResult> handleSearch([FromQuery] string searchTerm, string serverUrl)
{
var sessionToken = Request.Cookies["session"];
@ -34,7 +34,7 @@ public class SearchController : ControllerBase
try
{
var results = await _service.Search(decodedSearchTerm, serverId);
var results = await _service.Search(decodedSearchTerm, serverUrl);
return Ok(results);
}
catch (ClientNotFoundException)

View file

@ -1,4 +1,6 @@
using System.Web;
using JellyGlass.Exceptions;
using JellyGlass.Models.ControllerBodies.Servers;
using JellyGlass.Services;
using Microsoft.AspNetCore.Mvc;
@ -33,11 +35,17 @@ public class ServersController : ControllerBase
}
[HttpGet]
public async Task<IActionResult> GetServer([FromQuery] int id)
public async Task<IActionResult> GetServer([FromQuery] string url)
{
if (!await IsAuthenticated())
{
return Unauthorized();
}
try
{
var server = await _serverService.GetServerByID(id);
var decodedUrl = HttpUtility.UrlDecode(url);
var server = await _serverService.GetServerByUrl(decodedUrl);
return Ok(server);
}
catch (ServerNotFoundException)
@ -47,7 +55,7 @@ public class ServersController : ControllerBase
}
[HttpPut]
public async Task<IActionResult> UpdateServer([FromBody] int id, string owner, string url, string apiToken)
public async Task<IActionResult> UpdateServer([FromBody] UpdateServerDTO server)
{
if (!await IsAdminAuthenticated())
{
@ -56,7 +64,7 @@ public class ServersController : ControllerBase
try
{
var updatedServer = await _serverService.UpdateServer(id, owner, url, apiToken);
var updatedServer = await _serverService.UpdateServer(server.Owner, server.Url, server.ApiToken);
return Ok(updatedServer);
}
catch (ServerNotFoundException)
@ -66,20 +74,7 @@ public class ServersController : ControllerBase
}
[HttpPost]
public async Task<IActionResult> AddServer([FromBody] string owner, string url, string apiToken)
{
if (!await IsAdminAuthenticated())
{
return Forbid();
}
var newServer = await _serverService.CreateServer(owner, url, apiToken);
return Ok(newServer);
}
[HttpDelete]
public async Task<IActionResult> DeleteServer([FromBody] int id)
public async Task<IActionResult> AddServer([FromBody] AddServerDTO server)
{
if (!await IsAdminAuthenticated())
{
@ -88,7 +83,27 @@ public class ServersController : ControllerBase
try
{
var deletedServer = await _serverService.DeleteServer(id);
var newServer = await _serverService.CreateServer(server.Owner, server.Url, server.ApiToken);
return Ok(newServer);
}
catch (ServerAlreadyExistsException)
{
return Conflict("A server with this url already exists");
}
}
[HttpDelete]
public async Task<IActionResult> DeleteServer([FromBody] DeleteServerDTO server)
{
if (!await IsAdminAuthenticated())
{
return Forbid();
}
try
{
var decodedUrl = HttpUtility.UrlDecode(server.Url);
var deletedServer = await _serverService.DeleteServer(decodedUrl);
return Ok(deletedServer);
}
catch (ServerNotFoundException)

View file

@ -7,7 +7,6 @@ public class AuthServiceException : Exception
public AuthServiceException(string message, Exception inner) : base(message, inner) { }
}
[System.Serializable]
public class LoginFailedException : AuthServiceException
{
public LoginFailedException() { }

View file

@ -12,4 +12,11 @@ public class ServerNotFoundException : ServerRepositoryException
public ServerNotFoundException() { }
public ServerNotFoundException(string message) : base(message) { }
public ServerNotFoundException(string message, Exception inner) : base(message, inner) { }
}
public class ServerAlreadyExistsException : ServerRepositoryException
{
public ServerAlreadyExistsException() { }
public ServerAlreadyExistsException(string message) : base(message) { }
public ServerAlreadyExistsException(string message, System.Exception inner) : base(message, inner) { }
}

View file

@ -0,0 +1,87 @@
// <auto-generated />
using System;
using JellyGlass.Repositories;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace JellyGlassBackend.Migrations
{
[DbContext(typeof(DatabaseContext))]
[Migration("20260305184556_changedServerId")]
partial class changedServerId
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "9.0.11");
modelBuilder.Entity("JellyGlass.Models.Server", b =>
{
b.Property<string>("Url")
.HasColumnType("TEXT");
b.Property<string>("ApiToken")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Owner")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Url");
b.ToTable("Servers");
});
modelBuilder.Entity("JellyGlass.Models.UserLogin", b =>
{
b.Property<string>("Username")
.HasColumnType("TEXT");
b.Property<string>("HashedPassword")
.IsRequired()
.HasColumnType("TEXT");
b.Property<bool>("IsAdmin")
.HasColumnType("INTEGER");
b.HasKey("Username");
b.ToTable("Logins");
});
modelBuilder.Entity("JellyGlass.Models.UserSession", b =>
{
b.Property<string>("SessionToken")
.HasColumnType("TEXT");
b.Property<DateTime>("ExpiresOn")
.HasColumnType("TEXT");
b.Property<string>("LoginUsername")
.HasColumnType("TEXT");
b.HasKey("SessionToken");
b.HasIndex("LoginUsername");
b.ToTable("Sessions");
});
modelBuilder.Entity("JellyGlass.Models.UserSession", b =>
{
b.HasOne("JellyGlass.Models.UserLogin", "Login")
.WithMany()
.HasForeignKey("LoginUsername");
b.Navigation("Login");
});
#pragma warning restore 612, 618
}
}
}

View file

@ -0,0 +1,48 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace JellyGlassBackend.Migrations
{
/// <inheritdoc />
public partial class changedServerId : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropPrimaryKey(
name: "PK_Servers",
table: "Servers");
migrationBuilder.DropColumn(
name: "Id",
table: "Servers");
migrationBuilder.AddPrimaryKey(
name: "PK_Servers",
table: "Servers",
column: "Url");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropPrimaryKey(
name: "PK_Servers",
table: "Servers");
migrationBuilder.AddColumn<int>(
name: "Id",
table: "Servers",
type: "INTEGER",
nullable: false,
defaultValue: 0)
.Annotation("Sqlite:Autoincrement", true);
migrationBuilder.AddPrimaryKey(
name: "PK_Servers",
table: "Servers",
column: "Id");
}
}
}

View file

@ -19,9 +19,8 @@ namespace JellyGlassBackend.Migrations
modelBuilder.Entity("JellyGlass.Models.Server", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Url")
.HasColumnType("TEXT");
b.Property<string>("ApiToken")
.IsRequired()
@ -31,11 +30,7 @@ namespace JellyGlassBackend.Migrations
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Url")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasKey("Url");
b.ToTable("Servers");
});

View file

@ -0,0 +1,7 @@
namespace JellyGlass.Models.ControllerBodies.Auth;
public class CreateLoginDTO
{
public required string Username { get; set; }
public required string Password { get; set; }
}

View file

@ -0,0 +1,6 @@
namespace JellyGlass.Models.ControllerBodies.Auth;
public class DeleteLoginDTO
{
public required string Username { get; set; }
}

View file

@ -0,0 +1,7 @@
namespace JellyGlass.Models.ControllerBodies.Auth;
public class UpdateLoginDTO
{
public required string Password { get; set; }
public required string NewPassword { get; set; }
}

View file

@ -0,0 +1,8 @@
namespace JellyGlass.Models.ControllerBodies.Servers;
public class AddServerDTO
{
public required string Owner { get; set; }
public required string Url { get; set; }
public required string ApiToken { get; set; }
}

View file

@ -0,0 +1,6 @@
namespace JellyGlass.Models.ControllerBodies.Servers;
public class DeleteServerDTO
{
public required string Url { get; set; }
}

View file

@ -0,0 +1,8 @@
namespace JellyGlass.Models.ControllerBodies.Servers;
public class UpdateServerDTO
{
public required string Owner { get; set; }
public required string Url { get; set; }
public required string ApiToken { get; set; }
}

View file

@ -1,14 +1,11 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace JellyGlass.Models;
public class Server
{
public string Owner { get; set; } = string.Empty;
public string Url { get; set; } = string.Empty;
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Url { get; set; } = string.Empty;
public string ApiToken { get; set; } = string.Empty;
}

View file

@ -8,12 +8,10 @@ public class ServerDTO
{
Owner = s.Owner;
Url = s.Url;
Id = s.Id;
}
public string Owner { get; set; } = string.Empty;
public string Url { get; set; } = string.Empty;
public int Id { get; set; }
public string JellyfinServerID { get; set; } = string.Empty;
public bool Errored { get; set; } = false;
}

View file

@ -5,8 +5,8 @@ namespace JellyGlass.Repositories;
public interface IServerRepository
{
public Task<Server[]> GetServers();
public Task<Server> GetServerById(int id);
public Task<Server> GetServerByUrl(string url);
public Task<Server> CreateServer(string owner, string url, string apiToken);
public Task<Server> UpdateServer(int id, string owner, string url, string apiToken);
public Task<Server> DeleteServer(int id);
public Task<Server> UpdateServer(string owner, string url, string apiToken);
public Task<Server> DeleteServer(string url);
}

View file

@ -20,9 +20,9 @@ public class ServerRepository : IServerRepository
return servers;
}
public async Task<Server> GetServerById(int id)
public async Task<Server> GetServerByUrl(string url)
{
var server = await _context.Servers.FirstOrDefaultAsync(s => s.Id == id);
var server = await _context.Servers.FirstOrDefaultAsync(s => s.Url == url);
if (server == null)
{
@ -34,6 +34,10 @@ public class ServerRepository : IServerRepository
public async Task<Server> CreateServer(string owner, string url, string apiToken)
{
if (_context.Servers.Where(s => s.Url == url).Any())
{
throw new ServerAlreadyExistsException();
}
var newServer = new Server()
{
@ -48,12 +52,11 @@ public class ServerRepository : IServerRepository
return newServer;
}
public async Task<Server> UpdateServer(int id, string owner, string url, string apiToken)
public async Task<Server> UpdateServer(string owner, string url, string apiToken)
{
var server = await GetServerById(id);
var server = await GetServerByUrl(url);
server.Owner = owner;
server.Url = url;
server.ApiToken = apiToken;
_context.Servers.Update(server);
@ -62,9 +65,9 @@ public class ServerRepository : IServerRepository
return server;
}
public async Task<Server> DeleteServer(int id)
public async Task<Server> DeleteServer(string url)
{
var server = await GetServerById(id);
var server = await GetServerByUrl(url);
_context.Servers.Remove(server);
await _context.SaveChangesAsync();

View file

@ -107,16 +107,23 @@ public class AuthService : IAuthService
return new UserLoginDTO(newLogin);
}
public async Task UpdateLoginOwnPassword(string username, string newPassword, string oldPassword)
public async Task UpdateLoginOwnPassword(string sessionToken, string newPassword, string oldPassword)
{
var login = await _loginRepo.GetUserLogin(username);
var session = await _sessionRepo.GetUserSession(sessionToken);
if (session == null)
{
throw new LoginFailedException();
}
var login = session.Login;
if (!BCrypt.Net.BCrypt.Verify(oldPassword, login.HashedPassword))
{
throw new LoginFailedException();
}
await UpdateLoginPassword(username, newPassword);
await UpdateLoginPassword(login.Username, newPassword);
}
public async Task UpdateLoginPassword(string username, string newPassword)

View file

@ -1,4 +1,5 @@
using JellyGlass.Exceptions;
using JellyGlass.Models;
using JellyGlass.Repositories;
namespace JellyGlass.Services;
@ -6,7 +7,7 @@ namespace JellyGlass.Services;
public class ClientService : IClientService
{
private IServerRepository _repository;
private static JellyfinApiClient[] _clients = [];
private static List<JellyfinApiClient> _clients = new List<JellyfinApiClient>();
private ILogger<ClientService> _logger;
public ClientService(IServerRepository repository, ILogger<ClientService> logger)
@ -22,59 +23,70 @@ public class ClientService : IClientService
await LoadClients();
}
return _clients;
return _clients.ToArray();
}
public async Task<JellyfinApiClient> GetClientForServerId(string serverId)
public async Task<JellyfinApiClient> GetClientFromUrl(string url)
{
if (!_clients.Any())
{
await LoadClients();
}
foreach (var client in _clients)
var client = _clients.FirstOrDefault(c => c.InstanceUrl == url);
if (client == null)
{
if (client.ID == serverId)
{
return client;
}
throw new Exception($"Could not find client with url {url}");
}
throw new Exception($"Client with ID {serverId} not found");
return client;
}
public async Task LoadNewClient(Server server)
{
var client = new JellyfinApiClient(server.Url, server.ApiToken);
try
{
await client.GetServerInfo();
}
catch (JellyfinApiClientException e)
{
_logger.LogError($"Error authenticating to {server.Url}. Error: {e.Message} Client will not be used");
throw;
}
_clients.Add(client);
}
public void UnloadClient(JellyfinApiClient client)
{
if (_clients.Contains(client))
{
_clients.Remove(client);
}
else
{
throw new Exception($"Haven't loaded Jellyfin api client with ID of {client.ID}");
}
}
private async Task LoadClients()
{
var servers = await _repository.GetServers();
var clients = new List<JellyfinApiClient>();
foreach (var server in servers)
{
var client = new JellyfinApiClient(server.Url, server.ApiToken);
// try
// {
// await client.Authenticate();
// }
// catch (JellyfinApiClientException e)
// {
// _logger.LogError($"Error authenticating to {server.Url}. Error: {e.Message} Client will not be used");
// continue;
// }
try
{
await client.GetServerInfo(); //test the connection
await LoadNewClient(server);
}
catch (JellyfinApiClientException e)
catch (JellyfinApiClientException)
{
_logger.LogError($"Error authenticating to {server.Url}. Error: {e.Message} Client will not be used");
continue;
}
clients.Add(client);
}
_clients = clients.ToArray();
}
}

View file

@ -10,7 +10,7 @@ public interface IAuthService
public Task<UserLoginDTO[]> GetLogins();
public Task<UserLoginDTO> GetLogin(string username);
public Task<UserLoginDTO> CreateLogin(string username, string password);
public Task UpdateLoginOwnPassword(string username, string newPassword, string oldPassword);
public Task UpdateLoginOwnPassword(string sessionToken, string newPassword, string oldPassword);
public Task UpdateLoginPassword(string username, string newPassword);
public Task<UserLoginDTO> DeleteLogin(string username);
}

View file

@ -1,3 +1,4 @@
using JellyGlass.Models;
using JellyGlass.Repositories;
namespace JellyGlass.Services;
@ -5,6 +6,7 @@ namespace JellyGlass.Services;
public interface IClientService
{
public Task<JellyfinApiClient[]> GetClients();
// public JellyfinApiClient GetClientForServer(string url);
public Task<JellyfinApiClient> GetClientForServerId(string serverId);
public Task<JellyfinApiClient> GetClientFromUrl(string url);
public Task LoadNewClient(Server server);
public void UnloadClient(JellyfinApiClient client);
}

View file

@ -5,9 +5,9 @@ namespace JellyGlass.Services;
public interface ILibraryService
{
public Task<Library[]> GetLibraries();
public Task<ItemDTO[]> GetItemsFromLibrary(string libraryName, string serverId);
public Task<ItemDTO[]> GetItemsFromLibrary(string libraryName, string serverUrl);
public Task<Library[]> GetLibrariesFromServer(string serverId);
public Task<Library[]> GetLibrariesFromServer(string serverUrl);
// public Task<ItemDTO[]> GetChildrenFromItems(ItemDTO[] items);

View file

@ -4,5 +4,5 @@ namespace JellyGlass.Services;
public interface ISearchService
{
public Task<ItemDTO[]> Search(string searchTerm, string serverId);
public Task<ItemDTO[]> Search(string searchTerm, string serverUrl);
}

View file

@ -7,8 +7,8 @@ namespace JellyGlass.Services;
public interface IServerService
{
public Task<ServerDTO[]> GetServers();
public Task<ServerDTO> GetServerByID(int id);
public Task<ServerDTO> GetServerByUrl(string url);
public Task<ServerDTO> CreateServer(string owner, string url, string apiToken);
public Task<ServerDTO> UpdateServer(int id, string owner, string url, string apiToken);
public Task<ServerDTO> DeleteServer(int id);
public Task<ServerDTO> UpdateServer(string owner, string url, string apiToken);
public Task<ServerDTO> DeleteServer(string url);
}

View file

@ -44,13 +44,13 @@ public class LibraryService : ILibraryService
return libraries.Values.ToArray();
}
public async Task<Item> GetLibrary(string libraryName, string serverId)
public async Task<Item> GetLibrary(string libraryName, string serverUrl)
{
var client = await _clientService.GetClientForServerId(serverId);
var client = await _clientService.GetClientFromUrl(serverUrl); ;
if (client == null)
{
throw new Exception($"Could not find client with ID of {serverId}");
throw new Exception($"Could not find client with url of {serverUrl}");
}
var libraries = await client.GetInstanceLibraries();
@ -66,11 +66,11 @@ public class LibraryService : ILibraryService
throw new Exception("Couldn't find library");
}
public async Task<ItemDTO[]> GetItemsFromLibrary(string libraryName, string serverId)
public async Task<ItemDTO[]> GetItemsFromLibrary(string libraryName, string serverUrl)
{
var client = await _clientService.GetClientForServerId(serverId);
var client = await _clientService.GetClientFromUrl(serverUrl);
var library = await GetLibrary(libraryName, serverId);
var library = await GetLibrary(libraryName, serverUrl);
var items = await client.GetItemChildren(library.Id);
@ -84,9 +84,9 @@ public class LibraryService : ILibraryService
return dtos.ToArray();
}
public async Task<Library[]> GetLibrariesFromServer(string serverId)
public async Task<Library[]> GetLibrariesFromServer(string serverUrl)
{
var client = await _clientService.GetClientForServerId(serverId);
var client = await _clientService.GetClientFromUrl(serverUrl);
var libraries = new Dictionary<string, Library>();

View file

@ -14,9 +14,9 @@ public class SearchService : ISearchService
_clientService = clientService;
}
public async Task<ItemDTO[]> Search(string searchTerm, string serverId)
public async Task<ItemDTO[]> Search(string searchTerm, string serverUrl)
{
var client = await _clientService.GetClientForServerId(serverId);
var client = await _clientService.GetClientFromUrl(serverUrl);
var items = await client.GetItems(searchTerm: searchTerm);
@ -30,25 +30,25 @@ public class SearchService : ISearchService
return dtos.ToArray();
}
public async Task<ItemDTO[]> Search2(string searchTerm, string serverId)
// public async Task<ItemDTO[]> Search2(string searchTerm, int serverId)
// {
// var libraries = await _libraryService.GetLibrariesFromServer(serverId);
// var foundItems = new List<ItemDTO>();
// foreach (var library in libraries)
// {
// var found = await SearchLibraryForTerm(searchTerm, serverId, library);
// foundItems.AddRange(found);
// }
// return foundItems.ToArray();
// }
private async Task<ItemDTO[]> SearchLibraryForTerm(string searchTerm, string serverUrl, Library library)
{
var libraries = await _libraryService.GetLibrariesFromServer(serverId);
var foundItems = new List<ItemDTO>();
foreach (var library in libraries)
{
var found = await SearchLibraryForTerm(searchTerm, serverId, library);
foundItems.AddRange(found);
}
return foundItems.ToArray();
}
private async Task<ItemDTO[]> SearchLibraryForTerm(string searchTerm, string serverId, Library library)
{
var items = await _libraryService.GetItemsFromLibrary(library.Name, serverId);
var items = await _libraryService.GetItemsFromLibrary(library.Name, serverUrl);
var foundItems = new List<ItemDTO>();

View file

@ -1,5 +1,6 @@
using JellyGlass.Exceptions;
using JellyGlass.Models;
using JellyGlass.Repositories;
@ -49,31 +50,68 @@ public class ServerService : IServerService
return dtos.ToArray();
}
public async Task<ServerDTO> GetServerByID(int id)
public async Task<ServerDTO> GetServerByUrl(string url)
{
var server = await _repository.GetServerById(id);
var server = await _repository.GetServerByUrl(url);
return new ServerDTO(server);
return await GetServerDTO(server);
}
public async Task<ServerDTO> CreateServer(string owner, string url, string apiToken)
{
var newServer = await _repository.CreateServer(owner, url, apiToken);
var dto = new ServerDTO(newServer);
return new ServerDTO(newServer);
try
{
await _service.LoadNewClient(newServer);
}
catch (JellyfinApiClientException)
{
dto.Errored = true;
}
return dto;
}
public async Task<ServerDTO> UpdateServer(int id, string owner, string url, string apiToken)
public async Task<ServerDTO> UpdateServer(string owner, string url, string apiToken)
{
var updatedServer = await _repository.UpdateServer(id, owner, url, apiToken);
var updatedServer = await _repository.UpdateServer(owner, url, apiToken);
return new ServerDTO(updatedServer);
return await GetServerDTO(updatedServer);
}
public async Task<ServerDTO> DeleteServer(int id)
public async Task<ServerDTO> DeleteServer(string url)
{
var deletedServer = await _repository.DeleteServer(id);
var deletedServer = await _repository.DeleteServer(url);
try
{
var client = await _service.GetClientFromUrl(deletedServer.Url);
_service.UnloadClient(client);
}
catch (Exception)
{
//if it's not loaded, we don't care as we want to unload it anyway
}
return new ServerDTO(deletedServer);
}
private async Task<ServerDTO> GetServerDTO(Server server)
{
var dto = new ServerDTO(server);
try
{
var client = await _service.GetClientFromUrl(server.Url);
dto.JellyfinServerID = client.ID;
}
catch (Exception)
{
dto.Errored = true;
}
return dto;
}
}

View file

@ -33,9 +33,9 @@ const ServerSearch = ({ searchTerm, server }: ServerSearchProps) => {
searchResults.length > 0 ?
searchResults.map(result => {
return (
<tr>
<tr key={result!.id}>
<td>
<ServerSearchResult key={result!.id} searchResult={result!} server={server} />
<ServerSearchResult searchResult={result!} server={server} />
</td>
</tr>
)