Browse Source

Added generic usecases

Tim Jones 1 month ago
parent
commit
12a2efeff0
100 changed files with 161 additions and 1076 deletions
  1. 4 0
      RackPeek.Domain/Persistence/IResourceCollection.cs
  2. 22 0
      RackPeek.Domain/Persistence/InMemoryResourceCollection.cs
  3. 15 0
      RackPeek.Domain/Persistence/Yaml/YamlResourceCollection.cs
  4. 3 4
      RackPeek.Domain/Resources/Hardware/AccessPoints/AccessPointHardwareReport.cs
  5. 0 31
      RackPeek.Domain/Resources/Hardware/AccessPoints/CloneAccessPointUseCase.cs
  6. 0 27
      RackPeek.Domain/Resources/Hardware/AccessPoints/DeleteAccessPointUseCase.cs
  7. 0 28
      RackPeek.Domain/Resources/Hardware/AccessPoints/DescribeAccessPointUseCase.cs
  8. 0 17
      RackPeek.Domain/Resources/Hardware/AccessPoints/GetAccessPointUseCase.cs
  9. 0 12
      RackPeek.Domain/Resources/Hardware/AccessPoints/GetAccessPointsUseCase.cs
  10. 0 37
      RackPeek.Domain/Resources/Hardware/AccessPoints/RenameAccessPointUseCase.cs
  11. 2 1
      RackPeek.Domain/Resources/Hardware/AccessPoints/UpdateAccessPointUseCase.cs
  12. 0 29
      RackPeek.Domain/Resources/Hardware/Desktops/AddDesktopUseCase.cs
  13. 0 31
      RackPeek.Domain/Resources/Hardware/Desktops/CloneDesktopUsecase.cs
  14. 2 1
      RackPeek.Domain/Resources/Hardware/Desktops/Cpus/AddDesktopCpuUseCase.cs
  15. 2 1
      RackPeek.Domain/Resources/Hardware/Desktops/Cpus/RemoveDesktopCpuUseCase.cs
  16. 2 1
      RackPeek.Domain/Resources/Hardware/Desktops/Cpus/UpdateDesktopCpuUseCase.cs
  17. 0 27
      RackPeek.Domain/Resources/Hardware/Desktops/DeleteDesktopUseCase.cs
  18. 2 1
      RackPeek.Domain/Resources/Hardware/Desktops/DescribeDesktopUseCase.cs
  19. 3 3
      RackPeek.Domain/Resources/Hardware/Desktops/DesktopHardwareReport.cs
  20. 2 1
      RackPeek.Domain/Resources/Hardware/Desktops/Drives/AddDesktopDriveUseCase.cs
  21. 2 1
      RackPeek.Domain/Resources/Hardware/Desktops/Drives/RemoveDesktopDriveUseCase.cs
  22. 2 1
      RackPeek.Domain/Resources/Hardware/Desktops/Drives/UpdateDesktopDriveUseCase.cs
  23. 0 18
      RackPeek.Domain/Resources/Hardware/Desktops/GetDesktopUseCase.cs
  24. 0 12
      RackPeek.Domain/Resources/Hardware/Desktops/GetDesktopsUseCase.cs
  25. 2 1
      RackPeek.Domain/Resources/Hardware/Desktops/Gpus/AddDesktopGpuUseCase.cs
  26. 2 1
      RackPeek.Domain/Resources/Hardware/Desktops/Gpus/RemoveDesktopGpuUseCase.cs
  27. 2 1
      RackPeek.Domain/Resources/Hardware/Desktops/Gpus/UpdateDesktopGpuUseCase.cs
  28. 2 1
      RackPeek.Domain/Resources/Hardware/Desktops/Nics/AddDesktopNicUseCase.cs
  29. 2 1
      RackPeek.Domain/Resources/Hardware/Desktops/Nics/RemoveDesktopNicUseCase.cs
  30. 2 1
      RackPeek.Domain/Resources/Hardware/Desktops/Nics/UpdateDesktopNicUseCase.cs
  31. 0 37
      RackPeek.Domain/Resources/Hardware/Desktops/RenameDesktopUseCase.cs
  32. 2 1
      RackPeek.Domain/Resources/Hardware/Desktops/UpdateDesktopUseCase.cs
  33. 0 24
      RackPeek.Domain/Resources/Hardware/Firewalls/AddFirewallUseCase.cs
  34. 0 31
      RackPeek.Domain/Resources/Hardware/Firewalls/CloneFirewallUseCase.cs
  35. 0 27
      RackPeek.Domain/Resources/Hardware/Firewalls/DeleteFirewallUseCase.cs
  36. 2 1
      RackPeek.Domain/Resources/Hardware/Firewalls/DescribeFirewallUseCase.cs
  37. 3 3
      RackPeek.Domain/Resources/Hardware/Firewalls/FirewallHardwareReport.cs
  38. 0 18
      RackPeek.Domain/Resources/Hardware/Firewalls/GetFirewallUseCase.cs
  39. 0 12
      RackPeek.Domain/Resources/Hardware/Firewalls/GetFirewallsUseCase.cs
  40. 2 1
      RackPeek.Domain/Resources/Hardware/Firewalls/Ports/AddFirewallPortUseCase.cs
  41. 2 1
      RackPeek.Domain/Resources/Hardware/Firewalls/Ports/RemoveFirewallPortUseCase.cs
  42. 2 1
      RackPeek.Domain/Resources/Hardware/Firewalls/Ports/UpdateFirewallPortUseCase.cs
  43. 0 37
      RackPeek.Domain/Resources/Hardware/Firewalls/RenameFirewallUseCase.cs
  44. 2 1
      RackPeek.Domain/Resources/Hardware/Firewalls/UpdateFirewallUseCase.cs
  45. 0 28
      RackPeek.Domain/Resources/Hardware/Laptops/AddLaptopUseCase.cs
  46. 0 31
      RackPeek.Domain/Resources/Hardware/Laptops/CloneLaptopUseCase.cs
  47. 2 1
      RackPeek.Domain/Resources/Hardware/Laptops/Cpus/AddDesktopCpuUseCase.cs
  48. 2 1
      RackPeek.Domain/Resources/Hardware/Laptops/Cpus/RemoveDesktopCpuUseCase.cs
  49. 2 1
      RackPeek.Domain/Resources/Hardware/Laptops/Cpus/UpdateDesktopCpuUseCase.cs
  50. 0 27
      RackPeek.Domain/Resources/Hardware/Laptops/DeleteLaptopUseCase.cs
  51. 2 1
      RackPeek.Domain/Resources/Hardware/Laptops/DescribeLaptopUseCase.cs
  52. 2 1
      RackPeek.Domain/Resources/Hardware/Laptops/Drives/AddDesktopDriveUseCase.cs
  53. 2 1
      RackPeek.Domain/Resources/Hardware/Laptops/Drives/RemoveDesktopDriveUseCase.cs
  54. 2 1
      RackPeek.Domain/Resources/Hardware/Laptops/Drives/UpdateDesktopDriveUseCase.cs
  55. 0 18
      RackPeek.Domain/Resources/Hardware/Laptops/GetDesktopUseCase.cs
  56. 0 12
      RackPeek.Domain/Resources/Hardware/Laptops/GetLaptopsUseCase.cs
  57. 2 1
      RackPeek.Domain/Resources/Hardware/Laptops/Gpus/AddDesktopGpuUseCase.cs
  58. 2 1
      RackPeek.Domain/Resources/Hardware/Laptops/Gpus/RemoveDesktopGpuUseCase.cs
  59. 2 1
      RackPeek.Domain/Resources/Hardware/Laptops/Gpus/UpdateDesktopGpuUseCase.cs
  60. 3 3
      RackPeek.Domain/Resources/Hardware/Laptops/LaptopHardwareReportUseCase.cs
  61. 0 37
      RackPeek.Domain/Resources/Hardware/Laptops/RenameLaptopUseCase.cs
  62. 2 1
      RackPeek.Domain/Resources/Hardware/Laptops/UpdateLaptopUseCase.cs
  63. 0 24
      RackPeek.Domain/Resources/Hardware/Routers/AddRouterUseCase.cs
  64. 0 31
      RackPeek.Domain/Resources/Hardware/Routers/CloneRouterUseCase.cs
  65. 0 27
      RackPeek.Domain/Resources/Hardware/Routers/DeleteRouterUseCase.cs
  66. 2 1
      RackPeek.Domain/Resources/Hardware/Routers/DescribeRouterUseCase.cs
  67. 0 18
      RackPeek.Domain/Resources/Hardware/Routers/GetRouterUseCase.cs
  68. 0 12
      RackPeek.Domain/Resources/Hardware/Routers/GetRoutersUseCase.cs
  69. 2 1
      RackPeek.Domain/Resources/Hardware/Routers/Ports/AddRouterPortUseCase.cs
  70. 2 1
      RackPeek.Domain/Resources/Hardware/Routers/Ports/RemoveRouterPortUseCase.cs
  71. 2 1
      RackPeek.Domain/Resources/Hardware/Routers/Ports/UpdateRouterPortUseCase.cs
  72. 0 37
      RackPeek.Domain/Resources/Hardware/Routers/RenameRouterUseCase.cs
  73. 3 3
      RackPeek.Domain/Resources/Hardware/Routers/RouterHardwareReport.cs
  74. 2 1
      RackPeek.Domain/Resources/Hardware/Routers/UpdateRouterUseCase.cs
  75. 0 24
      RackPeek.Domain/Resources/Hardware/Servers/AddServerUseCase.cs
  76. 0 31
      RackPeek.Domain/Resources/Hardware/Servers/CloneServerUseCase.cs
  77. 2 1
      RackPeek.Domain/Resources/Hardware/Servers/Cpus/AddCpuUseCase.cs
  78. 2 1
      RackPeek.Domain/Resources/Hardware/Servers/Cpus/RemoveCpuUseCase.cs
  79. 2 1
      RackPeek.Domain/Resources/Hardware/Servers/Cpus/UpdateCpuUseCase.cs
  80. 0 28
      RackPeek.Domain/Resources/Hardware/Servers/DeleteServerUseCase.cs
  81. 2 1
      RackPeek.Domain/Resources/Hardware/Servers/DescribeServerUseCase.cs
  82. 2 1
      RackPeek.Domain/Resources/Hardware/Servers/Drives/AddDriveUseCase.cs
  83. 2 1
      RackPeek.Domain/Resources/Hardware/Servers/Drives/RemoveDriveUseCase.cs
  84. 2 1
      RackPeek.Domain/Resources/Hardware/Servers/Drives/UpdateDriveUseCase.cs
  85. 0 17
      RackPeek.Domain/Resources/Hardware/Servers/GetServerUseCase.cs
  86. 0 12
      RackPeek.Domain/Resources/Hardware/Servers/GetServersUseCase.cs
  87. 2 1
      RackPeek.Domain/Resources/Hardware/Servers/Gpus/AddGpuUseCase.cs
  88. 2 1
      RackPeek.Domain/Resources/Hardware/Servers/Gpus/RemoveGpuUseCase.cs
  89. 2 1
      RackPeek.Domain/Resources/Hardware/Servers/Gpus/UpdateGpuUseCase.cs
  90. 2 1
      RackPeek.Domain/Resources/Hardware/Servers/Nics/AddNicUseCase.cs
  91. 2 1
      RackPeek.Domain/Resources/Hardware/Servers/Nics/RemoveNicUseCase.cs
  92. 2 1
      RackPeek.Domain/Resources/Hardware/Servers/Nics/UpdateNicUseCase.cs
  93. 0 37
      RackPeek.Domain/Resources/Hardware/Servers/RenameSystemUseCase.cs
  94. 3 3
      RackPeek.Domain/Resources/Hardware/Servers/ServerHardwareReport.cs
  95. 2 1
      RackPeek.Domain/Resources/Hardware/Servers/UpdateServerUseCase.cs
  96. 0 24
      RackPeek.Domain/Resources/Hardware/Switches/AddSwitchUseCase.cs
  97. 0 31
      RackPeek.Domain/Resources/Hardware/Switches/CloneSwitchUseCase.cs
  98. 0 27
      RackPeek.Domain/Resources/Hardware/Switches/DeleteSwitchUseCase.cs
  99. 2 1
      RackPeek.Domain/Resources/Hardware/Switches/DescribeSwitchUseCase.cs
  100. 0 18
      RackPeek.Domain/Resources/Hardware/Switches/GetSwitchUseCase.cs

+ 4 - 0
RackPeek.Domain/Persistence/IResourceCollection.cs

@@ -1,3 +1,4 @@
+using System.Collections;
 using RackPeek.Domain.Resources;
 using RackPeek.Domain.Resources.Models;
 using RackPeek.Domain.Resources.Services;
@@ -14,6 +15,7 @@ public interface IResourceCollection
     Task AddAsync(Resource resource);
     Task UpdateAsync(Resource resource);
     Task DeleteAsync(string name);
+    Task<Resource?> GetByNameAsync(string name);
 
     Resource? GetByName(string name);
     Task<bool> Exists(string name);
@@ -22,4 +24,6 @@ public interface IResourceCollection
     Task<IReadOnlyList<Resource>> GetByTagAsync(string name);
     public Task<Dictionary<string, int>> GetTagsAsync();
 
+    Task<IReadOnlyList<T>> GetAllOfTypeAsync<T>();
+    Task<IReadOnlyList<Resource>>  GetDependantsAsync(string name);
 }

+ 22 - 0
RackPeek.Domain/Persistence/InMemoryResourceCollection.cs

@@ -69,6 +69,18 @@ public sealed class InMemoryResourceCollection(IEnumerable<Resource>? seed = nul
         }
     }
 
+    public Task<IReadOnlyList<T>> GetAllOfTypeAsync<T>()
+    {
+        lock (_lock)
+         return Task.FromResult<IReadOnlyList<T>>(_resources.OfType<T>().ToList());
+    }
+
+    public Task<IReadOnlyList<Resource>> GetDependantsAsync(string name)
+    {
+        lock (_lock)
+            return Task.FromResult<IReadOnlyList<Resource>>(_resources.Where(r => r.RunsOn?.Equals(name, StringComparison.OrdinalIgnoreCase) ?? false).ToList());
+    }
+
 
     public Task AddAsync(Resource resource)
     {
@@ -113,6 +125,16 @@ public sealed class InMemoryResourceCollection(IEnumerable<Resource>? seed = nul
         return Task.CompletedTask;
     }
 
+    public Task<Resource?> GetByNameAsync(string name)
+    {
+        lock (_lock)
+        {
+            return Task.FromResult(_resources.FirstOrDefault(r =>
+                r.Name.Equals(name, StringComparison.OrdinalIgnoreCase)));
+        }
+        
+    }
+
     public Resource? GetByName(string name)
     {
         lock (_lock)

+ 15 - 0
RackPeek.Domain/Persistence/Yaml/YamlResourceCollection.cs

@@ -39,6 +39,15 @@ public sealed class YamlResourceCollection(
         return Task.FromResult(result);
     }
 
+    public Task<IReadOnlyList<T>> GetAllOfTypeAsync<T>()
+    {
+        return Task.FromResult<IReadOnlyList<T>>(resourceCollection.Resources.OfType<T>().ToList());
+    }
+    
+    public Task<IReadOnlyList<Resource>> GetDependantsAsync(string name)
+    {
+        return Task.FromResult<IReadOnlyList<Resource>>(resourceCollection.Resources.Where(r => r.RunsOn?.Equals(name, StringComparison.OrdinalIgnoreCase) ?? false).ToList());
+    }
 
     public Task<IReadOnlyList<Resource>> GetByTagAsync(string name)
     {
@@ -69,6 +78,12 @@ public sealed class YamlResourceCollection(
     public IReadOnlyList<Service> ServiceResources =>
         resourceCollection.Resources.OfType<Service>().ToList();
 
+    public Task<Resource?> GetByNameAsync(string name)
+    {
+        return Task.FromResult(resourceCollection.Resources.FirstOrDefault(r =>
+            r.Name.Equals(name, StringComparison.OrdinalIgnoreCase)));
+    }
+
     public Resource? GetByName(string name) =>
         resourceCollection.Resources.FirstOrDefault(r =>
             r.Name.Equals(name, StringComparison.OrdinalIgnoreCase));

+ 3 - 4
RackPeek.Domain/Resources/Hardware/AccessPoints/AccessPointHardwareReport.cs

@@ -1,3 +1,4 @@
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.AccessPoints;
@@ -12,13 +13,11 @@ public record AccessPointHardwareRow(
     double SpeedGb
 );
 
-public class AccessPointHardwareReportUseCase(IHardwareRepository repository) : IUseCase
+public class AccessPointHardwareReportUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task<AccessPointHardwareReport> ExecuteAsync()
     {
-        var hardware = await repository.GetAllAsync();
-        var aps = hardware.OfType<AccessPoint>();
-
+        var aps = await repository.GetAllOfTypeAsync<AccessPoint>();
         var rows = aps.Select(ap => new AccessPointHardwareRow(
             ap.Name,
             ap.Model ?? "Unknown",

+ 0 - 31
RackPeek.Domain/Resources/Hardware/AccessPoints/CloneAccessPointUseCase.cs

@@ -1,31 +0,0 @@
-using RackPeek.Domain.Helpers;
-using RackPeek.Domain.Resources.Models;
-
-namespace RackPeek.Domain.Resources.Hardware.Desktops;
-
-public class CloneAccessPointUseCase(IHardwareRepository repository, IResourceRepository resourceRepo) : IUseCase
-{
-    public async Task ExecuteAsync(string originalName, string cloneName)
-    {
-        originalName = Normalize.HardwareName(originalName);
-        ThrowIfInvalid.ResourceName(originalName);
-
-        cloneName = Normalize.HardwareName(cloneName);
-        ThrowIfInvalid.ResourceName(cloneName);
-        
-        var existingResourceKind = await resourceRepo.GetResourceKindAsync(cloneName);
-        if (!string.IsNullOrEmpty(existingResourceKind))
-            throw new ConflictException($"{existingResourceKind} resource '{cloneName}' already exists.");
-
-        var original = await repository.GetByNameAsync(originalName) as AccessPoint;
-        if (original == null)
-        {
-            throw new NotFoundException($"Resource '{originalName}' not found.");
-        }
-        
-        var clone = Clone.DeepClone(original);
-        clone.Name = cloneName;
-        
-        await repository.AddAsync(clone);
-    }
-}

+ 0 - 27
RackPeek.Domain/Resources/Hardware/AccessPoints/DeleteAccessPointUseCase.cs

@@ -1,27 +0,0 @@
-using RackPeek.Domain.Helpers;
-using RackPeek.Domain.Resources.Models;
-using RackPeek.Domain.Resources.SystemResources;
-
-namespace RackPeek.Domain.Resources.Hardware.AccessPoints;
-
-public class DeleteAccessPointUseCase(IHardwareRepository repository, ISystemRepository systemsRepo) : IUseCase
-{
-    public async Task ExecuteAsync(string name)
-    {
-        name = Normalize.HardwareName(name);
-        ThrowIfInvalid.ResourceName(name);
-        var ap = await repository.GetByNameAsync(name);
-        if (ap is not AccessPoint)
-            throw new NotFoundException($"Access point '{name}' not found.");
-
-        // Break link to dependants
-        var dependants = await systemsRepo.GetByPhysicalHostAsync(name);
-        foreach (var systemResource in dependants)
-        {
-            systemResource.RunsOn = null;
-            await systemsRepo.UpdateAsync(systemResource);
-        }
-
-        await repository.DeleteAsync(name);
-    }
-}

+ 0 - 28
RackPeek.Domain/Resources/Hardware/AccessPoints/DescribeAccessPointUseCase.cs

@@ -1,28 +0,0 @@
-using RackPeek.Domain.Helpers;
-using RackPeek.Domain.Resources.Models;
-
-namespace RackPeek.Domain.Resources.Hardware.AccessPoints;
-
-public record AccessPointDescription(
-    string Name,
-    string? Model,
-    double? Speed
-);
-
-public class DescribeAccessPointUseCase(IHardwareRepository repository) : IUseCase
-{
-    public async Task<AccessPointDescription> ExecuteAsync(string name)
-    {
-        name = Normalize.HardwareName(name);
-        ThrowIfInvalid.ResourceName(name);
-        var ap = await repository.GetByNameAsync(name) as AccessPoint;
-        if (ap == null)
-            throw new NotFoundException($"Access point '{name}' not found.");
-
-        return new AccessPointDescription(
-            ap.Name,
-            ap.Model,
-            ap.Speed
-        );
-    }
-}

+ 0 - 17
RackPeek.Domain/Resources/Hardware/AccessPoints/GetAccessPointUseCase.cs

@@ -1,17 +0,0 @@
-using RackPeek.Domain.Helpers;
-using RackPeek.Domain.Resources.Models;
-
-namespace RackPeek.Domain.Resources.Hardware.AccessPoints;
-
-public class GetAccessPointUseCase(IHardwareRepository repository) : IUseCase
-{
-    public async Task<AccessPoint> ExecuteAsync(string name)
-    {
-        name = Normalize.HardwareName(name);
-        ThrowIfInvalid.ResourceName(name);
-        var hardware = await repository.GetByNameAsync(name);
-        if (hardware is not AccessPoint ap) throw new NotFoundException($"Access point '{name}' not found.");
-
-        return ap;
-    }
-}

+ 0 - 12
RackPeek.Domain/Resources/Hardware/AccessPoints/GetAccessPointsUseCase.cs

@@ -1,12 +0,0 @@
-using RackPeek.Domain.Resources.Models;
-
-namespace RackPeek.Domain.Resources.Hardware.AccessPoints;
-
-public class GetAccessPointsUseCase(IHardwareRepository repository) : IUseCase
-{
-    public async Task<IReadOnlyList<AccessPoint>> ExecuteAsync()
-    {
-        var hardware = await repository.GetAllAsync();
-        return hardware.OfType<AccessPoint>().ToList();
-    }
-}

+ 0 - 37
RackPeek.Domain/Resources/Hardware/AccessPoints/RenameAccessPointUseCase.cs

@@ -1,37 +0,0 @@
-using RackPeek.Domain.Helpers;
-using RackPeek.Domain.Resources.Models;
-using RackPeek.Domain.Resources.SystemResources;
-
-namespace RackPeek.Domain.Resources.Hardware.AccessPoints;
-
-public class RenameAccessPointUseCase(IHardwareRepository repository, ISystemRepository systemRepo, IResourceRepository resourceRepo) : IUseCase
-{
-    public async Task ExecuteAsync(string originalName, string newName)
-    {
-        originalName = Normalize.SystemName(originalName);
-        ThrowIfInvalid.ResourceName(originalName);
-        
-        newName = Normalize.SystemName(newName);
-        ThrowIfInvalid.ResourceName(newName);
-
-        var existingResourceKind = await resourceRepo.GetResourceKindAsync(newName);
-        if (!string.IsNullOrEmpty(existingResourceKind))
-            throw new ConflictException($"{existingResourceKind} resource '{newName}' already exists.");
-        
-        var original = await repository.GetByNameAsync(originalName) as AccessPoint;
-        if (original == null)
-        {
-            throw new NotFoundException($"Resource '{originalName}' not found.");
-        }
-
-        original.Name = newName;
-        await repository.UpdateAsync(original);
-        
-        var children = await systemRepo.GetByPhysicalHostAsync(originalName);
-        foreach (var child in children)
-        {
-            child.RunsOn = newName;
-            await systemRepo.UpdateAsync(child);
-        }
-    }
-}

+ 2 - 1
RackPeek.Domain/Resources/Hardware/AccessPoints/UpdateAccessPointUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.AccessPoints;
 
-public class UpdateAccessPointUseCase(IHardwareRepository repository) : IUseCase
+public class UpdateAccessPointUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(
         string name,

+ 0 - 29
RackPeek.Domain/Resources/Hardware/Desktops/AddDesktopUseCase.cs

@@ -1,29 +0,0 @@
-using RackPeek.Domain.Helpers;
-using RackPeek.Domain.Resources.Models;
-
-namespace RackPeek.Domain.Resources.Hardware.Desktops;
-
-public class AddDesktopUseCase(IHardwareRepository repository, IResourceRepository resourceRepo) : IUseCase
-{
-    public async Task ExecuteAsync(string name)
-    {
-        name = Normalize.HardwareName(name);
-        ThrowIfInvalid.ResourceName(name);
-
-        var existingResourceKind = await resourceRepo.GetResourceKindAsync(name);
-        if (!string.IsNullOrEmpty(existingResourceKind))
-            throw new ConflictException($"{existingResourceKind} resource '{name}' already exists.");
-
-        var desktop = new Desktop
-        {
-            Name = name,
-            Cpus = new List<Cpu>(),
-            Drives = new List<Drive>(),
-            Nics = new List<Nic>(),
-            Gpus = new List<Gpu>(),
-            Ram = null
-        };
-
-        await repository.AddAsync(desktop);
-    }
-}

+ 0 - 31
RackPeek.Domain/Resources/Hardware/Desktops/CloneDesktopUsecase.cs

@@ -1,31 +0,0 @@
-using RackPeek.Domain.Helpers;
-using RackPeek.Domain.Resources.Models;
-
-namespace RackPeek.Domain.Resources.Hardware.Desktops;
-
-public class CloneDesktopUseCase(IHardwareRepository repository, IResourceRepository resourceRepo) : IUseCase
-{
-    public async Task ExecuteAsync(string originalName, string cloneName)
-    {
-        originalName = Normalize.HardwareName(originalName);
-        ThrowIfInvalid.ResourceName(originalName);
-
-        cloneName = Normalize.HardwareName(cloneName);
-        ThrowIfInvalid.ResourceName(cloneName);
-        
-        var existingResourceKind = await resourceRepo.GetResourceKindAsync(cloneName);
-        if (!string.IsNullOrEmpty(existingResourceKind))
-            throw new ConflictException($"{existingResourceKind} resource '{cloneName}' already exists.");
-
-        var original = await repository.GetByNameAsync(originalName) as Desktop;
-        if (original == null)
-        {
-            throw new NotFoundException($"Resource '{originalName}' not found.");
-        }
-        
-        var clone = Clone.DeepClone(original);
-        clone.Name = cloneName;
-        
-        await repository.AddAsync(clone);
-    }
-}

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Desktops/Cpus/AddDesktopCpuUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Desktops.Cpus;
 
-public class AddDesktopCpuUseCase(IHardwareRepository repository) : IUseCase
+public class AddDesktopCpuUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(string name, string? model, int? cores, int? threads)
     {

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Desktops/Cpus/RemoveDesktopCpuUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Desktops.Cpus;
 
-public class RemoveDesktopCpuUseCase(IHardwareRepository repository) : IUseCase
+public class RemoveDesktopCpuUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(string name, int index)
     {

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Desktops/Cpus/UpdateDesktopCpuUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Desktops.Cpus;
 
-public class UpdateDesktopCpuUseCase(IHardwareRepository repository) : IUseCase
+public class UpdateDesktopCpuUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(
         string name,

+ 0 - 27
RackPeek.Domain/Resources/Hardware/Desktops/DeleteDesktopUseCase.cs

@@ -1,27 +0,0 @@
-using RackPeek.Domain.Helpers;
-using RackPeek.Domain.Resources.SystemResources;
-
-namespace RackPeek.Domain.Resources.Hardware.Desktops;
-
-public class DeleteDesktopUseCase(IHardwareRepository repository, ISystemRepository systemsRepo) : IUseCase
-{
-    public async Task ExecuteAsync(string name)
-    {
-        name = Normalize.HardwareName(name);
-        ThrowIfInvalid.ResourceName(name);
-
-        var hardware = await repository.GetByNameAsync(name);
-        if (hardware == null)
-            throw new NotFoundException($"Desktop '{name}' not found.");
-
-        // Break link to dependants
-        var dependants = await systemsRepo.GetByPhysicalHostAsync(name);
-        foreach (var systemResource in dependants)
-        {
-            systemResource.RunsOn = null;
-            await systemsRepo.UpdateAsync(systemResource);
-        }
-
-        await repository.DeleteAsync(name);
-    }
-}

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Desktops/DescribeDesktopUseCase.cs

@@ -1,4 +1,5 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Desktops;
@@ -13,7 +14,7 @@ public record DesktopDescription(
     int GpuCount
 );
 
-public class DescribeDesktopUseCase(IHardwareRepository repository) : IUseCase
+public class DescribeDesktopUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task<DesktopDescription> ExecuteAsync(string name)
     {

+ 3 - 3
RackPeek.Domain/Resources/Hardware/Desktops/DesktopHardwareReport.cs

@@ -1,3 +1,4 @@
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Desktops;
@@ -19,12 +20,11 @@ public record DesktopHardwareRow(
     string GpuSummary
 );
 
-public class DesktopHardwareReportUseCase(IHardwareRepository repository) : IUseCase
+public class DesktopHardwareReportUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task<DesktopHardwareReport> ExecuteAsync()
     {
-        var hardware = await repository.GetAllAsync();
-        var desktops = hardware.OfType<Desktop>();
+        var desktops = await repository.GetAllOfTypeAsync<Models.Desktop>();
 
         var rows = desktops.Select(desktop =>
         {

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Desktops/Drives/AddDesktopDriveUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Desktops.Drives;
 
-public class AddDesktopDriveUseCase(IHardwareRepository repository) : IUseCase
+public class AddDesktopDriveUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(
         string name,

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Desktops/Drives/RemoveDesktopDriveUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Desktops.Drives;
 
-public class RemoveDesktopDriveUseCase(IHardwareRepository repository) : IUseCase
+public class RemoveDesktopDriveUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(string name, int index)
     {

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Desktops/Drives/UpdateDesktopDriveUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Desktops.Drives;
 
-public class UpdateDesktopDriveUseCase(IHardwareRepository repository) : IUseCase
+public class UpdateDesktopDriveUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(string name, int index, string? type, int? size)
     {

+ 0 - 18
RackPeek.Domain/Resources/Hardware/Desktops/GetDesktopUseCase.cs

@@ -1,18 +0,0 @@
-using RackPeek.Domain.Helpers;
-using RackPeek.Domain.Resources.Models;
-
-namespace RackPeek.Domain.Resources.Hardware.Desktops;
-
-public class GetDesktopUseCase(IHardwareRepository repository) : IUseCase
-{
-    public async Task<Desktop> ExecuteAsync(string name)
-    {
-        name = Normalize.HardwareName(name);
-        ThrowIfInvalid.ResourceName(name);
-
-        var hardware = await repository.GetByNameAsync(name);
-        if (hardware is not Desktop desktop) throw new NotFoundException($"Desktop '{name}' not found.");
-
-        return desktop;
-    }
-}

+ 0 - 12
RackPeek.Domain/Resources/Hardware/Desktops/GetDesktopsUseCase.cs

@@ -1,12 +0,0 @@
-using RackPeek.Domain.Resources.Models;
-
-namespace RackPeek.Domain.Resources.Hardware.Desktops;
-
-public class GetDesktopsUseCase(IHardwareRepository repository) : IUseCase
-{
-    public async Task<IReadOnlyList<Desktop>> ExecuteAsync()
-    {
-        var hardware = await repository.GetAllAsync();
-        return hardware.OfType<Desktop>().ToList();
-    }
-}

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Desktops/Gpus/AddDesktopGpuUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Desktops.Gpus;
 
-public class AddDesktopGpuUseCase(IHardwareRepository repository) : IUseCase
+public class AddDesktopGpuUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(
         string name,

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Desktops/Gpus/RemoveDesktopGpuUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Desktops.Gpus;
 
-public class RemoveDesktopGpuUseCase(IHardwareRepository repository) : IUseCase
+public class RemoveDesktopGpuUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(string name, int index)
     {

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Desktops/Gpus/UpdateDesktopGpuUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Desktops.Gpus;
 
-public class UpdateDesktopGpuUseCase(IHardwareRepository repository) : IUseCase
+public class UpdateDesktopGpuUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(
         string name,

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Desktops/Nics/AddDesktopNicUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Desktops.Nics;
 
-public class AddDesktopNicUseCase(IHardwareRepository repository) : IUseCase
+public class AddDesktopNicUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(
         string name,

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Desktops/Nics/RemoveDesktopNicUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Desktops.Nics;
 
-public class RemoveDesktopNicUseCase(IHardwareRepository repository) : IUseCase
+public class RemoveDesktopNicUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(string name, int index)
     {

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Desktops/Nics/UpdateDesktopNicUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Desktops.Nics;
 
-public class UpdateDesktopNicUseCase(IHardwareRepository repository) : IUseCase
+public class UpdateDesktopNicUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(
         string name,

+ 0 - 37
RackPeek.Domain/Resources/Hardware/Desktops/RenameDesktopUseCase.cs

@@ -1,37 +0,0 @@
-using RackPeek.Domain.Helpers;
-using RackPeek.Domain.Resources.Models;
-using RackPeek.Domain.Resources.SystemResources;
-
-namespace RackPeek.Domain.Resources.Hardware.Desktops;
-
-public class RenameDesktopUseCase(IHardwareRepository repository, ISystemRepository systemRepo, IResourceRepository resourceRepo) : IUseCase
-{
-    public async Task ExecuteAsync(string originalName, string newName)
-    {
-        originalName = Normalize.SystemName(originalName);
-        ThrowIfInvalid.ResourceName(originalName);
-        
-        newName = Normalize.SystemName(newName);
-        ThrowIfInvalid.ResourceName(newName);
-
-        var existingResourceKind = await resourceRepo.GetResourceKindAsync(newName);
-        if (!string.IsNullOrEmpty(existingResourceKind))
-            throw new ConflictException($"{existingResourceKind} resource '{newName}' already exists.");
-        
-        var original = await repository.GetByNameAsync(originalName) as Desktop;
-        if (original == null)
-        {
-            throw new NotFoundException($"Resource '{originalName}' not found.");
-        }
-
-        original.Name = newName;
-        await repository.UpdateAsync(original);
-        
-        var children = await systemRepo.GetByPhysicalHostAsync(originalName);
-        foreach (var child in children)
-        {
-            child.RunsOn = newName;
-            await systemRepo.UpdateAsync(child);
-        }
-    }
-}

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Desktops/UpdateDesktopUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Desktops;
 
-public class UpdateDesktopUseCase(IHardwareRepository repository) : IUseCase
+public class UpdateDesktopUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(
         string name,

+ 0 - 24
RackPeek.Domain/Resources/Hardware/Firewalls/AddFirewallUseCase.cs

@@ -1,24 +0,0 @@
-using RackPeek.Domain.Helpers;
-using RackPeek.Domain.Resources.Models;
-
-namespace RackPeek.Domain.Resources.Hardware.Firewalls;
-
-public class AddFirewallUseCase(IHardwareRepository repository, IResourceRepository resourceRepo) : IUseCase
-{
-    public async Task ExecuteAsync(string name)
-    {
-        name = Normalize.HardwareName(name);
-        ThrowIfInvalid.ResourceName(name);
-
-        var existingResourceKind = await resourceRepo.GetResourceKindAsync(name);
-        if (!string.IsNullOrEmpty(existingResourceKind))
-            throw new ConflictException($"{existingResourceKind} resource '{name}' already exists.");
-
-        var firewallResource = new Firewall
-        {
-            Name = name
-        };
-
-        await repository.AddAsync(firewallResource);
-    }
-}

+ 0 - 31
RackPeek.Domain/Resources/Hardware/Firewalls/CloneFirewallUseCase.cs

@@ -1,31 +0,0 @@
-using RackPeek.Domain.Helpers;
-using RackPeek.Domain.Resources.Models;
-
-namespace RackPeek.Domain.Resources.Hardware.Firewalls;
-
-public class CloneFirewallUseCase(IHardwareRepository repository, IResourceRepository resourceRepo) : IUseCase
-{
-    public async Task ExecuteAsync(string originalName, string cloneName)
-    {
-        originalName = Normalize.HardwareName(originalName);
-        ThrowIfInvalid.ResourceName(originalName);
-
-        cloneName = Normalize.HardwareName(cloneName);
-        ThrowIfInvalid.ResourceName(cloneName);
-        
-        var existingResourceKind = await resourceRepo.GetResourceKindAsync(cloneName);
-        if (!string.IsNullOrEmpty(existingResourceKind))
-            throw new ConflictException($"{existingResourceKind} resource '{cloneName}' already exists.");
-
-        var original = await repository.GetByNameAsync(originalName) as Firewall;
-        if (original == null)
-        {
-            throw new NotFoundException($"Resource '{originalName}' not found.");
-        }
-        
-        var clone = Clone.DeepClone(original);
-        clone.Name = cloneName;
-        
-        await repository.AddAsync(clone);
-    }
-}

+ 0 - 27
RackPeek.Domain/Resources/Hardware/Firewalls/DeleteFirewallUseCase.cs

@@ -1,27 +0,0 @@
-using RackPeek.Domain.Helpers;
-using RackPeek.Domain.Resources.Models;
-using RackPeek.Domain.Resources.SystemResources;
-
-namespace RackPeek.Domain.Resources.Hardware.Firewalls;
-
-public class DeleteFirewallUseCase(IHardwareRepository repository, ISystemRepository systemsRepo) : IUseCase
-{
-    public async Task ExecuteAsync(string name)
-    {
-        name = Normalize.HardwareName(name);
-        ThrowIfInvalid.ResourceName(name);
-
-        if (await repository.GetByNameAsync(name) is not Firewall hardware)
-            throw new NotFoundException($"Firewall '{name}' not found.");
-
-        // Break link to dependants
-        var dependants = await systemsRepo.GetByPhysicalHostAsync(name);
-        foreach (var systemResource in dependants)
-        {
-            systemResource.RunsOn = null;
-            await systemsRepo.UpdateAsync(systemResource);
-        }
-
-        await repository.DeleteAsync(name);
-    }
-}

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Firewalls/DescribeFirewallUseCase.cs

@@ -1,4 +1,5 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Firewalls;
@@ -13,7 +14,7 @@ public record FirewallDescription(
     string PortSummary
 );
 
-public class DescribeFirewallUseCase(IHardwareRepository repository) : IUseCase
+public class DescribeFirewallUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task<FirewallDescription> ExecuteAsync(string name)
     {

+ 3 - 3
RackPeek.Domain/Resources/Hardware/Firewalls/FirewallHardwareReport.cs

@@ -1,3 +1,4 @@
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Firewalls;
@@ -16,12 +17,11 @@ public record FirewallHardwareRow(
     string PortSummary
 );
 
-public class FirewallHardwareReportUseCase(IHardwareRepository repository) : IUseCase
+public class FirewallHardwareReportUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task<FirewallHardwareReport> ExecuteAsync()
     {
-        var hardware = await repository.GetAllAsync();
-        var firewalls = hardware.OfType<Firewall>();
+        var firewalls = await repository.GetAllOfTypeAsync<Firewall>();
 
         var rows = firewalls.Select(sw =>
         {

+ 0 - 18
RackPeek.Domain/Resources/Hardware/Firewalls/GetFirewallUseCase.cs

@@ -1,18 +0,0 @@
-using RackPeek.Domain.Helpers;
-using RackPeek.Domain.Resources.Models;
-
-namespace RackPeek.Domain.Resources.Hardware.Firewalls;
-
-public class GetFirewallUseCase(IHardwareRepository repository) : IUseCase
-{
-    public async Task<Firewall> ExecuteAsync(string name)
-    {
-        name = Normalize.HardwareName(name);
-        ThrowIfInvalid.ResourceName(name);
-
-        var hardware = await repository.GetByNameAsync(name);
-        if (hardware is not Firewall firewall) throw new NotFoundException($"Firewall '{name}' not found.");
-
-        return firewall;
-    }
-}

+ 0 - 12
RackPeek.Domain/Resources/Hardware/Firewalls/GetFirewallsUseCase.cs

@@ -1,12 +0,0 @@
-using RackPeek.Domain.Resources.Models;
-
-namespace RackPeek.Domain.Resources.Hardware.Firewalls;
-
-public class GetFirewallsUseCase(IHardwareRepository repository) : IUseCase
-{
-    public async Task<IReadOnlyList<Firewall>> ExecuteAsync()
-    {
-        var hardware = await repository.GetAllAsync();
-        return hardware.OfType<Firewall>().ToList();
-    }
-}

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Firewalls/Ports/AddFirewallPortUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Firewalls.Ports;
 
-public class AddFirewallPortUseCase(IHardwareRepository repository) : IUseCase
+public class AddFirewallPortUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(
         string name,

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Firewalls/Ports/RemoveFirewallPortUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Firewalls.Ports;
 
-public class RemoveFirewallPortUseCase(IHardwareRepository repository) : IUseCase
+public class RemoveFirewallPortUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(string name, int index)
     {

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Firewalls/Ports/UpdateFirewallPortUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Firewalls.Ports;
 
-public class UpdateFirewallPortUseCase(IHardwareRepository repository) : IUseCase
+public class UpdateFirewallPortUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(
         string name,

+ 0 - 37
RackPeek.Domain/Resources/Hardware/Firewalls/RenameFirewallUseCase.cs

@@ -1,37 +0,0 @@
-using RackPeek.Domain.Helpers;
-using RackPeek.Domain.Resources.Models;
-using RackPeek.Domain.Resources.SystemResources;
-
-namespace RackPeek.Domain.Resources.Hardware.Firewalls;
-
-public class RenameFirewallUseCase(IHardwareRepository repository, ISystemRepository systemRepo, IResourceRepository resourceRepo) : IUseCase
-{
-    public async Task ExecuteAsync(string originalName, string newName)
-    {
-        originalName = Normalize.SystemName(originalName);
-        ThrowIfInvalid.ResourceName(originalName);
-        
-        newName = Normalize.SystemName(newName);
-        ThrowIfInvalid.ResourceName(newName);
-
-        var existingResourceKind = await resourceRepo.GetResourceKindAsync(newName);
-        if (!string.IsNullOrEmpty(existingResourceKind))
-            throw new ConflictException($"{existingResourceKind} resource '{newName}' already exists.");
-        
-        var original = await repository.GetByNameAsync(originalName) as Firewall;
-        if (original == null)
-        {
-            throw new NotFoundException($"Resource '{originalName}' not found.");
-        }
-
-        original.Name = newName;
-        await repository.UpdateAsync(original);
-        
-        var children = await systemRepo.GetByPhysicalHostAsync(originalName);
-        foreach (var child in children)
-        {
-            child.RunsOn = newName;
-            await systemRepo.UpdateAsync(child);
-        }
-    }
-}

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Firewalls/UpdateFirewallUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Firewalls;
 
-public class UpdateFirewallUseCase(IHardwareRepository repository) : IUseCase
+public class UpdateFirewallUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(
         string name,

+ 0 - 28
RackPeek.Domain/Resources/Hardware/Laptops/AddLaptopUseCase.cs

@@ -1,28 +0,0 @@
-using RackPeek.Domain.Helpers;
-using RackPeek.Domain.Resources.Models;
-
-namespace RackPeek.Domain.Resources.Hardware.Laptops;
-
-public class AddLaptopUseCase(IHardwareRepository repository, IResourceRepository resourceRepo) : IUseCase
-{
-    public async Task ExecuteAsync(string name)
-    {
-        name = Normalize.HardwareName(name);
-        ThrowIfInvalid.ResourceName(name);
-
-        var existingResourceKind = await resourceRepo.GetResourceKindAsync(name);
-        if (!string.IsNullOrEmpty(existingResourceKind))
-            throw new ConflictException($"{existingResourceKind} resource '{name}' already exists.");
-
-        var laptop = new Laptop
-        {
-            Name = name,
-            Cpus = new List<Cpu>(),
-            Drives = new List<Drive>(),
-            Gpus = new List<Gpu>(),
-            Ram = null
-        };
-
-        await repository.AddAsync(laptop);
-    }
-}

+ 0 - 31
RackPeek.Domain/Resources/Hardware/Laptops/CloneLaptopUseCase.cs

@@ -1,31 +0,0 @@
-using RackPeek.Domain.Helpers;
-using RackPeek.Domain.Resources.Models;
-
-namespace RackPeek.Domain.Resources.Hardware.Laptops;
-
-public class CloneLaptopUseCase(IHardwareRepository repository, IResourceRepository resourceRepo) : IUseCase
-{
-    public async Task ExecuteAsync(string originalName, string cloneName)
-    {
-        originalName = Normalize.HardwareName(originalName);
-        ThrowIfInvalid.ResourceName(originalName);
-
-        cloneName = Normalize.HardwareName(cloneName);
-        ThrowIfInvalid.ResourceName(cloneName);
-        
-        var existingResourceKind = await resourceRepo.GetResourceKindAsync(cloneName);
-        if (!string.IsNullOrEmpty(existingResourceKind))
-            throw new ConflictException($"{existingResourceKind} resource '{cloneName}' already exists.");
-
-        var original = await repository.GetByNameAsync(originalName) as Laptop;
-        if (original == null)
-        {
-            throw new NotFoundException($"Resource '{originalName}' not found.");
-        }
-        
-        var clone = Clone.DeepClone(original);
-        clone.Name = cloneName;
-        
-        await repository.AddAsync(clone);
-    }
-}

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Laptops/Cpus/AddDesktopCpuUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Laptops.Cpus;
 
-public class AddLaptopCpuUseCase(IHardwareRepository repository) : IUseCase
+public class AddLaptopCpuUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(string name, string? model, int? cores, int? threads)
     {

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Laptops/Cpus/RemoveDesktopCpuUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Laptops.Cpus;
 
-public class RemoveLaptopCpuUseCase(IHardwareRepository repository) : IUseCase
+public class RemoveLaptopCpuUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(string name, int index)
     {

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Laptops/Cpus/UpdateDesktopCpuUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Laptops.Cpus;
 
-public class UpdateLaptopCpuUseCase(IHardwareRepository repository) : IUseCase
+public class UpdateLaptopCpuUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(
         string name,

+ 0 - 27
RackPeek.Domain/Resources/Hardware/Laptops/DeleteLaptopUseCase.cs

@@ -1,27 +0,0 @@
-using RackPeek.Domain.Helpers;
-using RackPeek.Domain.Resources.SystemResources;
-
-namespace RackPeek.Domain.Resources.Hardware.Laptops;
-
-public class DeleteLaptopUseCase(IHardwareRepository repository, ISystemRepository systemsRepo) : IUseCase
-{
-    public async Task ExecuteAsync(string name)
-    {
-        name = Normalize.HardwareName(name);
-        ThrowIfInvalid.ResourceName(name);
-
-        var hardware = await repository.GetByNameAsync(name);
-        if (hardware == null)
-            throw new NotFoundException($"Laptop '{name}' not found.");
-
-        // Break link to dependants
-        var dependants = await systemsRepo.GetByPhysicalHostAsync(name);
-        foreach (var systemResource in dependants)
-        {
-            systemResource.RunsOn = null;
-            await systemsRepo.UpdateAsync(systemResource);
-        }
-
-        await repository.DeleteAsync(name);
-    }
-}

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Laptops/DescribeLaptopUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Laptops;
 
-public class DescribeLaptopUseCase(IHardwareRepository repository) : IUseCase
+public class DescribeLaptopUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task<LaptopDescription> ExecuteAsync(string name)
     {

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Laptops/Drives/AddDesktopDriveUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Laptops.Drives;
 
-public class AddLaptopDriveUseCase(IHardwareRepository repository) : IUseCase
+public class AddLaptopDriveUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(
         string name,

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Laptops/Drives/RemoveDesktopDriveUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Laptops.Drives;
 
-public class RemoveLaptopDriveUseCase(IHardwareRepository repository) : IUseCase
+public class RemoveLaptopDriveUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(string name, int index)
     {

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Laptops/Drives/UpdateDesktopDriveUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Laptops.Drives;
 
-public class UpdateLaptopDriveUseCase(IHardwareRepository repository) : IUseCase
+public class UpdateLaptopDriveUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(string name, int index, string? type, int? size)
     {

+ 0 - 18
RackPeek.Domain/Resources/Hardware/Laptops/GetDesktopUseCase.cs

@@ -1,18 +0,0 @@
-using RackPeek.Domain.Helpers;
-using RackPeek.Domain.Resources.Models;
-
-namespace RackPeek.Domain.Resources.Hardware.Laptops;
-
-public class GetLaptopUseCase(IHardwareRepository repository) : IUseCase
-{
-    public async Task<Laptop> ExecuteAsync(string name)
-    {
-        name = Normalize.HardwareName(name);
-        ThrowIfInvalid.ResourceName(name);
-
-        var hardware = await repository.GetByNameAsync(name);
-        if (hardware is not Laptop laptop) throw new NotFoundException($"Laptop '{name}' not found.");
-
-        return laptop;
-    }
-}

+ 0 - 12
RackPeek.Domain/Resources/Hardware/Laptops/GetLaptopsUseCase.cs

@@ -1,12 +0,0 @@
-using RackPeek.Domain.Resources.Models;
-
-namespace RackPeek.Domain.Resources.Hardware.Laptops;
-
-public class GetLaptopsUseCase(IHardwareRepository repository) : IUseCase
-{
-    public async Task<IReadOnlyList<Laptop>> ExecuteAsync()
-    {
-        var hardware = await repository.GetAllAsync();
-        return hardware.OfType<Laptop>().ToList();
-    }
-}

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Laptops/Gpus/AddDesktopGpuUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Laptops.Gpus;
 
-public class AddLaptopGpuUseCase(IHardwareRepository repository) : IUseCase
+public class AddLaptopGpuUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(
         string name,

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Laptops/Gpus/RemoveDesktopGpuUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Laptops.Gpus;
 
-public class RemoveLaptopGpuUseCase(IHardwareRepository repository) : IUseCase
+public class RemoveLaptopGpuUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(string name, int index)
     {

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Laptops/Gpus/UpdateDesktopGpuUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Laptops.Gpus;
 
-public class UpdateLaptopGpuUseCase(IHardwareRepository repository) : IUseCase
+public class UpdateLaptopGpuUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(
         string name,

+ 3 - 3
RackPeek.Domain/Resources/Hardware/Laptops/LaptopHardwareReportUseCase.cs

@@ -1,13 +1,13 @@
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Laptops;
 
-public class LaptopHardwareReportUseCase(IHardwareRepository repository) : IUseCase
+public class LaptopHardwareReportUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task<LaptopHardwareReport> ExecuteAsync()
     {
-        var hardware = await repository.GetAllAsync();
-        var laptops = hardware.OfType<Laptop>();
+        var laptops = await repository.GetAllOfTypeAsync<Laptop>();
 
         var rows = laptops.Select(laptop =>
         {

+ 0 - 37
RackPeek.Domain/Resources/Hardware/Laptops/RenameLaptopUseCase.cs

@@ -1,37 +0,0 @@
-using RackPeek.Domain.Helpers;
-using RackPeek.Domain.Resources.Models;
-using RackPeek.Domain.Resources.SystemResources;
-
-namespace RackPeek.Domain.Resources.Hardware.Laptops;
-
-public class RenameLaptopUseCase(IHardwareRepository repository, ISystemRepository systemRepo, IResourceRepository resourceRepo) : IUseCase
-{
-    public async Task ExecuteAsync(string originalName, string newName)
-    {
-        originalName = Normalize.SystemName(originalName);
-        ThrowIfInvalid.ResourceName(originalName);
-        
-        newName = Normalize.SystemName(newName);
-        ThrowIfInvalid.ResourceName(newName);
-
-        var existingResourceKind = await resourceRepo.GetResourceKindAsync(newName);
-        if (!string.IsNullOrEmpty(existingResourceKind))
-            throw new ConflictException($"{existingResourceKind} resource '{newName}' already exists.");
-        
-        var original = await repository.GetByNameAsync(originalName) as Laptop;
-        if (original == null)
-        {
-            throw new NotFoundException($"Resource '{originalName}' not found.");
-        }
-
-        original.Name = newName;
-        await repository.UpdateAsync(original);
-        
-        var children = await systemRepo.GetByPhysicalHostAsync(originalName);
-        foreach (var child in children)
-        {
-            child.RunsOn = newName;
-            await systemRepo.UpdateAsync(child);
-        }
-    }
-}

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Laptops/UpdateLaptopUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Laptops;
 
-public class UpdateLaptopUseCase(IHardwareRepository repository) : IUseCase
+public class UpdateLaptopUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(
         string name,

+ 0 - 24
RackPeek.Domain/Resources/Hardware/Routers/AddRouterUseCase.cs

@@ -1,24 +0,0 @@
-using RackPeek.Domain.Helpers;
-using RackPeek.Domain.Resources.Models;
-
-namespace RackPeek.Domain.Resources.Hardware.Routers;
-
-public class AddRouterUseCase(IHardwareRepository repository, IResourceRepository resourceRepo) : IUseCase
-{
-    public async Task ExecuteAsync(string name)
-    {
-        name = Normalize.HardwareName(name);
-        ThrowIfInvalid.ResourceName(name);
-
-        var existingResourceKind = await resourceRepo.GetResourceKindAsync(name);
-        if (!string.IsNullOrEmpty(existingResourceKind))
-            throw new ConflictException($"{existingResourceKind} resource '{name}' already exists.");
-
-        var routerResource = new Router
-        {
-            Name = name
-        };
-
-        await repository.AddAsync(routerResource);
-    }
-}

+ 0 - 31
RackPeek.Domain/Resources/Hardware/Routers/CloneRouterUseCase.cs

@@ -1,31 +0,0 @@
-using RackPeek.Domain.Helpers;
-using RackPeek.Domain.Resources.Models;
-
-namespace RackPeek.Domain.Resources.Hardware.Routers;
-
-public class CloneRouterUseCase(IHardwareRepository repository, IResourceRepository resourceRepo) : IUseCase
-{
-    public async Task ExecuteAsync(string originalName, string cloneName)
-    {
-        originalName = Normalize.HardwareName(originalName);
-        ThrowIfInvalid.ResourceName(originalName);
-
-        cloneName = Normalize.HardwareName(cloneName);
-        ThrowIfInvalid.ResourceName(cloneName);
-        
-        var existingResourceKind = await resourceRepo.GetResourceKindAsync(cloneName);
-        if (!string.IsNullOrEmpty(existingResourceKind))
-            throw new ConflictException($"{existingResourceKind} resource '{cloneName}' already exists.");
-
-        var original = await repository.GetByNameAsync(originalName) as Router;
-        if (original == null)
-        {
-            throw new NotFoundException($"Resource '{originalName}' not found.");
-        }
-        
-        var clone = Clone.DeepClone(original);
-        clone.Name = cloneName;
-        
-        await repository.AddAsync(clone);
-    }
-}

+ 0 - 27
RackPeek.Domain/Resources/Hardware/Routers/DeleteRouterUseCase.cs

@@ -1,27 +0,0 @@
-using RackPeek.Domain.Helpers;
-using RackPeek.Domain.Resources.Models;
-using RackPeek.Domain.Resources.SystemResources;
-
-namespace RackPeek.Domain.Resources.Hardware.Routers;
-
-public class DeleteRouterUseCase(IHardwareRepository repository, ISystemRepository systemsRepo) : IUseCase
-{
-    public async Task ExecuteAsync(string name)
-    {
-        name = Normalize.HardwareName(name);
-        ThrowIfInvalid.ResourceName(name);
-
-        if (await repository.GetByNameAsync(name) is not Router hardware)
-            throw new NotFoundException($"Router '{name}' not found.");
-
-        // Break link to dependants
-        var dependants = await systemsRepo.GetByPhysicalHostAsync(name);
-        foreach (var systemResource in dependants)
-        {
-            systemResource.RunsOn = null;
-            await systemsRepo.UpdateAsync(systemResource);
-        }
-
-        await repository.DeleteAsync(name);
-    }
-}

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Routers/DescribeRouterUseCase.cs

@@ -1,4 +1,5 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Routers;
@@ -13,7 +14,7 @@ public record RouterDescription(
     string PortSummary
 );
 
-public class DescribeRouterUseCase(IHardwareRepository repository) : IUseCase
+public class DescribeRouterUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task<RouterDescription> ExecuteAsync(string name)
     {

+ 0 - 18
RackPeek.Domain/Resources/Hardware/Routers/GetRouterUseCase.cs

@@ -1,18 +0,0 @@
-using RackPeek.Domain.Helpers;
-using RackPeek.Domain.Resources.Models;
-
-namespace RackPeek.Domain.Resources.Hardware.Routers;
-
-public class GetRouterUseCase(IHardwareRepository repository) : IUseCase
-{
-    public async Task<Router> ExecuteAsync(string name)
-    {
-        name = Normalize.HardwareName(name);
-        ThrowIfInvalid.ResourceName(name);
-
-        var hardware = await repository.GetByNameAsync(name);
-        if (hardware is not Router router) throw new NotFoundException($"Router '{name}' not found.");
-
-        return router;
-    }
-}

+ 0 - 12
RackPeek.Domain/Resources/Hardware/Routers/GetRoutersUseCase.cs

@@ -1,12 +0,0 @@
-using RackPeek.Domain.Resources.Models;
-
-namespace RackPeek.Domain.Resources.Hardware.Routers;
-
-public class GetRoutersUseCase(IHardwareRepository repository) : IUseCase
-{
-    public async Task<IReadOnlyList<Router>> ExecuteAsync()
-    {
-        var hardware = await repository.GetAllAsync();
-        return hardware.OfType<Router>().ToList();
-    }
-}

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Routers/Ports/AddRouterPortUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Routers.Ports;
 
-public class AddRouterPortUseCase(IHardwareRepository repository) : IUseCase
+public class AddRouterPortUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(
         string name,

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Routers/Ports/RemoveRouterPortUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Routers.Ports;
 
-public class RemoveRouterPortUseCase(IHardwareRepository repository) : IUseCase
+public class RemoveRouterPortUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(string name, int index)
     {

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Routers/Ports/UpdateRouterPortUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Routers.Ports;
 
-public class UpdateRouterPortUseCase(IHardwareRepository repository) : IUseCase
+public class UpdateRouterPortUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(
         string name,

+ 0 - 37
RackPeek.Domain/Resources/Hardware/Routers/RenameRouterUseCase.cs

@@ -1,37 +0,0 @@
-using RackPeek.Domain.Helpers;
-using RackPeek.Domain.Resources.Models;
-using RackPeek.Domain.Resources.SystemResources;
-
-namespace RackPeek.Domain.Resources.Hardware.Routers;
-
-public class RenameRouterUseCase(IHardwareRepository repository, ISystemRepository systemRepo, IResourceRepository resourceRepo) : IUseCase
-{
-    public async Task ExecuteAsync(string originalName, string newName)
-    {
-        originalName = Normalize.SystemName(originalName);
-        ThrowIfInvalid.ResourceName(originalName);
-        
-        newName = Normalize.SystemName(newName);
-        ThrowIfInvalid.ResourceName(newName);
-
-        var existingResourceKind = await resourceRepo.GetResourceKindAsync(newName);
-        if (!string.IsNullOrEmpty(existingResourceKind))
-            throw new ConflictException($"{existingResourceKind} resource '{newName}' already exists.");
-        
-        var original = await repository.GetByNameAsync(originalName) as Router;
-        if (original == null)
-        {
-            throw new NotFoundException($"Resource '{originalName}' not found.");
-        }
-
-        original.Name = newName;
-        await repository.UpdateAsync(original);
-        
-        var children = await systemRepo.GetByPhysicalHostAsync(originalName);
-        foreach (var child in children)
-        {
-            child.RunsOn = newName;
-            await systemRepo.UpdateAsync(child);
-        }
-    }
-}

+ 3 - 3
RackPeek.Domain/Resources/Hardware/Routers/RouterHardwareReport.cs

@@ -1,3 +1,4 @@
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Routers;
@@ -16,12 +17,11 @@ public record RouterHardwareRow(
     string PortSummary
 );
 
-public class RouterHardwareReportUseCase(IHardwareRepository repository) : IUseCase
+public class RouterHardwareReportUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task<RouterHardwareReport> ExecuteAsync()
     {
-        var hardware = await repository.GetAllAsync();
-        var routers = hardware.OfType<Router>();
+        var routers = await repository.GetAllOfTypeAsync<Router>();
 
         var rows = routers.Select(sw =>
         {

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Routers/UpdateRouterUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Routers;
 
-public class UpdateRouterUseCase(IHardwareRepository repository) : IUseCase
+public class UpdateRouterUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(
         string name,

+ 0 - 24
RackPeek.Domain/Resources/Hardware/Servers/AddServerUseCase.cs

@@ -1,24 +0,0 @@
-using RackPeek.Domain.Helpers;
-using RackPeek.Domain.Resources.Models;
-
-namespace RackPeek.Domain.Resources.Hardware.Servers;
-
-public class AddServerUseCase(IHardwareRepository repository, IResourceRepository resourceRepo) : IUseCase
-{
-    public async Task ExecuteAsync(string name)
-    {
-        name = Normalize.HardwareName(name);
-        ThrowIfInvalid.ResourceName(name);
-
-        var existingResourceKind = await resourceRepo.GetResourceKindAsync(name);
-        if (!string.IsNullOrEmpty(existingResourceKind))
-            throw new ConflictException($"{existingResourceKind} resource '{name}' already exists.");
-
-        var server = new Server
-        {
-            Name = name
-        };
-
-        await repository.AddAsync(server);
-    }
-}

+ 0 - 31
RackPeek.Domain/Resources/Hardware/Servers/CloneServerUseCase.cs

@@ -1,31 +0,0 @@
-using RackPeek.Domain.Helpers;
-using RackPeek.Domain.Resources.Models;
-
-namespace RackPeek.Domain.Resources.Hardware.Servers;
-
-public class CloneServerUseCase(IHardwareRepository repository, IResourceRepository resourceRepo) : IUseCase
-{
-    public async Task ExecuteAsync(string originalName, string cloneName)
-    {
-        originalName = Normalize.HardwareName(originalName);
-        ThrowIfInvalid.ResourceName(originalName);
-
-        cloneName = Normalize.HardwareName(cloneName);
-        ThrowIfInvalid.ResourceName(cloneName);
-        
-        var existingResourceKind = await resourceRepo.GetResourceKindAsync(cloneName);
-        if (!string.IsNullOrEmpty(existingResourceKind))
-            throw new ConflictException($"{existingResourceKind} resource '{cloneName}' already exists.");
-
-        var original = await repository.GetByNameAsync(originalName) as Server;
-        if (original == null)
-        {
-            throw new NotFoundException($"Resource '{originalName}' not found.");
-        }
-        
-        var clone = Clone.DeepClone(original);
-        clone.Name = cloneName;
-        
-        await repository.AddAsync(clone);
-    }
-}

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Servers/Cpus/AddCpuUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Servers.Cpus;
 
-public class AddCpuUseCase(IHardwareRepository repository) : IUseCase
+public class AddCpuUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(
         string name,

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Servers/Cpus/RemoveCpuUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Servers.Cpus;
 
-public class RemoveCpuUseCase(IHardwareRepository repository) : IUseCase
+public class RemoveCpuUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(
         string name,

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Servers/Cpus/UpdateCpuUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Servers.Cpus;
 
-public class UpdateCpuUseCase(IHardwareRepository repository) : IUseCase
+public class UpdateCpuUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(
         string name,

+ 0 - 28
RackPeek.Domain/Resources/Hardware/Servers/DeleteServerUseCase.cs

@@ -1,28 +0,0 @@
-using RackPeek.Domain.Helpers;
-using RackPeek.Domain.Resources.Models;
-using RackPeek.Domain.Resources.SystemResources;
-
-namespace RackPeek.Domain.Resources.Hardware.Servers;
-
-public class DeleteServerUseCase(IHardwareRepository repository, ISystemRepository systemsRepo) : IUseCase
-{
-    public async Task ExecuteAsync(string name)
-    {
-        name = Normalize.HardwareName(name);
-        ThrowIfInvalid.ResourceName(name);
-
-        var hardware = await repository.GetByNameAsync(name) as Server;
-        if (hardware == null)
-            throw new NotFoundException($"Server '{name}' not found.");
-
-        // Break link to dependants
-        var dependants = await systemsRepo.GetByPhysicalHostAsync(name);
-        foreach (var systemResource in dependants)
-        {
-            systemResource.RunsOn = null;
-            await systemsRepo.UpdateAsync(systemResource);
-        }
-
-        await repository.DeleteAsync(name);
-    }
-}

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Servers/DescribeServerUseCase.cs

@@ -1,4 +1,5 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Servers;
@@ -14,7 +15,7 @@ public record ServerDescription(
     bool Ipmi
 );
 
-public class DescribeServerUseCase(IHardwareRepository repository) : IUseCase
+public class DescribeServerUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task<ServerDescription> ExecuteAsync(string name)
     {

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Servers/Drives/AddDriveUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Servers.Drives;
 
-public class AddDrivesUseCase(IHardwareRepository repository) : IUseCase
+public class AddDrivesUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(
         string name,

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Servers/Drives/RemoveDriveUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Servers.Drives;
 
-public class RemoveDriveUseCase(IHardwareRepository repository) : IUseCase
+public class RemoveDriveUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(string name, int index)
     {

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Servers/Drives/UpdateDriveUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Servers.Drives;
 
-public class UpdateDriveUseCase(IHardwareRepository repository) : IUseCase
+public class UpdateDriveUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(string name, int index, string? type, int? size)
     {

+ 0 - 17
RackPeek.Domain/Resources/Hardware/Servers/GetServerUseCase.cs

@@ -1,17 +0,0 @@
-using RackPeek.Domain.Helpers;
-using RackPeek.Domain.Resources.Models;
-
-namespace RackPeek.Domain.Resources.Hardware.Servers;
-
-public class GetServerUseCase(IHardwareRepository repository) : IUseCase
-{
-    public async Task<Server> ExecuteAsync(string name)
-    {
-        name = Normalize.HardwareName(name);
-        ThrowIfInvalid.ResourceName(name);
-        var hardware = await repository.GetByNameAsync(name);
-        if (hardware is not Server server) throw new NotFoundException($"Server '{name}' not found.");
-
-        return server;
-    }
-}

+ 0 - 12
RackPeek.Domain/Resources/Hardware/Servers/GetServersUseCase.cs

@@ -1,12 +0,0 @@
-using RackPeek.Domain.Resources.Models;
-
-namespace RackPeek.Domain.Resources.Hardware.Servers;
-
-public class GetServersUseCase(IHardwareRepository repository) : IUseCase
-{
-    public async Task<IReadOnlyList<Server>> ExecuteAsync()
-    {
-        var hardware = await repository.GetAllAsync();
-        return hardware.OfType<Server>().ToList();
-    }
-}

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Servers/Gpus/AddGpuUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Servers.Gpus;
 
-public class AddGpuUseCase(IHardwareRepository repository) : IUseCase
+public class AddGpuUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(
         string name,

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Servers/Gpus/RemoveGpuUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Servers.Gpus;
 
-public class RemoveGpuUseCase(IHardwareRepository repository) : IUseCase
+public class RemoveGpuUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(string name, int index)
     {

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Servers/Gpus/UpdateGpuUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Servers.Gpus;
 
-public class UpdateGpuUseCase(IHardwareRepository repository) : IUseCase
+public class UpdateGpuUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(
         string name,

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Servers/Nics/AddNicUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Servers.Nics;
 
-public class AddNicUseCase(IHardwareRepository repository) : IUseCase
+public class AddNicUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(
         string name,

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Servers/Nics/RemoveNicUseCase.cs

@@ -1,10 +1,11 @@
 using System.ComponentModel.DataAnnotations;
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Servers.Nics;
 
-public class RemoveNicUseCase(IHardwareRepository repository) : IUseCase
+public class RemoveNicUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(string name, int index)
     {

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Servers/Nics/UpdateNicUseCase.cs

@@ -1,10 +1,11 @@
 using System.ComponentModel.DataAnnotations;
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Servers.Nics;
 
-public class UpdateNicUseCase(IHardwareRepository repository) : IUseCase
+public class UpdateNicUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(
         string name,

+ 0 - 37
RackPeek.Domain/Resources/Hardware/Servers/RenameSystemUseCase.cs

@@ -1,37 +0,0 @@
-using RackPeek.Domain.Helpers;
-using RackPeek.Domain.Resources.Models;
-using RackPeek.Domain.Resources.SystemResources;
-
-namespace RackPeek.Domain.Resources.Hardware.Servers;
-
-public class RenameServerUseCase(IHardwareRepository repository, ISystemRepository systemRepo, IResourceRepository resourceRepo) : IUseCase
-{
-    public async Task ExecuteAsync(string originalName, string newName)
-    {
-        originalName = Normalize.SystemName(originalName);
-        ThrowIfInvalid.ResourceName(originalName);
-        
-        newName = Normalize.SystemName(newName);
-        ThrowIfInvalid.ResourceName(newName);
-
-        var existingResourceKind = await resourceRepo.GetResourceKindAsync(newName);
-        if (!string.IsNullOrEmpty(existingResourceKind))
-            throw new ConflictException($"{existingResourceKind} resource '{newName}' already exists.");
-        
-        var original = await repository.GetByNameAsync(originalName) as Server;
-        if (original == null)
-        {
-            throw new NotFoundException($"Resource '{originalName}' not found.");
-        }
-
-        original.Name = newName;
-        await repository.UpdateAsync(original);
-        
-        var children = await systemRepo.GetByPhysicalHostAsync(originalName);
-        foreach (var child in children)
-        {
-            child.RunsOn = newName;
-            await systemRepo.UpdateAsync(child);
-        }
-    }
-}

+ 3 - 3
RackPeek.Domain/Resources/Hardware/Servers/ServerHardwareReport.cs

@@ -1,3 +1,4 @@
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Servers;
@@ -23,12 +24,11 @@ public record ServerHardwareRow(
     bool Ipmi
 );
 
-public class ServerHardwareReportUseCase(IHardwareRepository repository) : IUseCase
+public class ServerHardwareReportUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task<ServerHardwareReport> ExecuteAsync()
     {
-        var hardware = await repository.GetAllAsync();
-        var servers = hardware.OfType<Server>();
+        var servers = await repository.GetAllOfTypeAsync<Server>();
 
         var rows = servers.Select(server =>
         {

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Servers/UpdateServerUseCase.cs

@@ -1,9 +1,10 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Servers;
 
-public class UpdateServerUseCase(IHardwareRepository repository) : IUseCase
+public class UpdateServerUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task ExecuteAsync(
         string name,

+ 0 - 24
RackPeek.Domain/Resources/Hardware/Switches/AddSwitchUseCase.cs

@@ -1,24 +0,0 @@
-using RackPeek.Domain.Helpers;
-using RackPeek.Domain.Resources.Models;
-
-namespace RackPeek.Domain.Resources.Hardware.Switches;
-
-public class AddSwitchUseCase(IHardwareRepository repository, IResourceRepository resourceRepo) : IUseCase
-{
-    public async Task ExecuteAsync(string name)
-    {
-        name = Normalize.HardwareName(name);
-        ThrowIfInvalid.ResourceName(name);
-
-        var existingResourceKind = await resourceRepo.GetResourceKindAsync(name);
-        if (!string.IsNullOrEmpty(existingResourceKind))
-            throw new ConflictException($"{existingResourceKind} resource '{name}' already exists.");
-
-        var switchResource = new Switch
-        {
-            Name = name
-        };
-
-        await repository.AddAsync(switchResource);
-    }
-}

+ 0 - 31
RackPeek.Domain/Resources/Hardware/Switches/CloneSwitchUseCase.cs

@@ -1,31 +0,0 @@
-using RackPeek.Domain.Helpers;
-using RackPeek.Domain.Resources.Models;
-
-namespace RackPeek.Domain.Resources.Hardware.Switches;
-
-public class CloneSwitchUseCase(IHardwareRepository repository, IResourceRepository resourceRepo) : IUseCase
-{
-    public async Task ExecuteAsync(string originalName, string cloneName)
-    {
-        originalName = Normalize.HardwareName(originalName);
-        ThrowIfInvalid.ResourceName(originalName);
-
-        cloneName = Normalize.HardwareName(cloneName);
-        ThrowIfInvalid.ResourceName(cloneName);
-        
-        var existingResourceKind = await resourceRepo.GetResourceKindAsync(cloneName);
-        if (!string.IsNullOrEmpty(existingResourceKind))
-            throw new ConflictException($"{existingResourceKind} resource '{cloneName}' already exists.");
-
-        var original = await repository.GetByNameAsync(originalName) as Switch;
-        if (original == null)
-        {
-            throw new NotFoundException($"Resource '{originalName}' not found.");
-        }
-        
-        var clone = Clone.DeepClone(original);
-        clone.Name = cloneName;
-        
-        await repository.AddAsync(clone);
-    }
-}

+ 0 - 27
RackPeek.Domain/Resources/Hardware/Switches/DeleteSwitchUseCase.cs

@@ -1,27 +0,0 @@
-using RackPeek.Domain.Helpers;
-using RackPeek.Domain.Resources.Models;
-using RackPeek.Domain.Resources.SystemResources;
-
-namespace RackPeek.Domain.Resources.Hardware.Switches;
-
-public class DeleteSwitchUseCase(IHardwareRepository repository, ISystemRepository systemsRepo) : IUseCase
-{
-    public async Task ExecuteAsync(string name)
-    {
-        name = Normalize.HardwareName(name);
-        ThrowIfInvalid.ResourceName(name);
-
-        if (await repository.GetByNameAsync(name) is not Switch hardware)
-            throw new NotFoundException($"Switch '{name}' not found.");
-
-        // Break link to dependants
-        var dependants = await systemsRepo.GetByPhysicalHostAsync(name);
-        foreach (var systemResource in dependants)
-        {
-            systemResource.RunsOn = null;
-            await systemsRepo.UpdateAsync(systemResource);
-        }
-
-        await repository.DeleteAsync(name);
-    }
-}

+ 2 - 1
RackPeek.Domain/Resources/Hardware/Switches/DescribeSwitchUseCase.cs

@@ -1,4 +1,5 @@
 using RackPeek.Domain.Helpers;
+using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.Models;
 
 namespace RackPeek.Domain.Resources.Hardware.Switches;
@@ -13,7 +14,7 @@ public record SwitchDescription(
     string PortSummary
 );
 
-public class DescribeSwitchUseCase(IHardwareRepository repository) : IUseCase
+public class DescribeSwitchUseCase(IResourceCollection repository) : IUseCase
 {
     public async Task<SwitchDescription> ExecuteAsync(string name)
     {

+ 0 - 18
RackPeek.Domain/Resources/Hardware/Switches/GetSwitchUseCase.cs

@@ -1,18 +0,0 @@
-using RackPeek.Domain.Helpers;
-using RackPeek.Domain.Resources.Models;
-
-namespace RackPeek.Domain.Resources.Hardware.Switches;
-
-public class GetSwitchUseCase(IHardwareRepository repository) : IUseCase
-{
-    public async Task<Switch> ExecuteAsync(string name)
-    {
-        name = Normalize.HardwareName(name);
-        ThrowIfInvalid.ResourceName(name);
-
-        var hardware = await repository.GetByNameAsync(name);
-        if (hardware is not Switch _switch) throw new NotFoundException($"Switch '{name}' not found.");
-
-        return _switch;
-    }
-}

Some files were not shown because too many files changed in this diff