Răsfoiți Sursa

Merge pull request #57 from Timmoth/Add-Resources

Add router / firewall / laptop usecases / commands
Tim Jones 2 luni în urmă
părinte
comite
d1a8fb0b08
100 a modificat fișierele cu 4020 adăugiri și 1736 ștergeri
  1. 36 0
      CommandIndex.md
  2. 578 0
      Commands.md
  3. 36 0
      README.md
  4. 21 0
      RackPeek.Domain/Resources/Hardware/Firewalls/AddFirewallUseCase.cs
  5. 14 0
      RackPeek.Domain/Resources/Hardware/Firewalls/DeleteFirewallUseCase.cs
  6. 54 0
      RackPeek.Domain/Resources/Hardware/Firewalls/DescribeFirewallUseCase.cs
  7. 12 0
      RackPeek.Domain/Resources/Hardware/Firewalls/GetFirewallUseCase.cs
  8. 12 0
      RackPeek.Domain/Resources/Hardware/Firewalls/GetFirewallsUseCase.cs
  9. 29 0
      RackPeek.Domain/Resources/Hardware/Firewalls/UpdateFirewallUseCase.cs
  10. 24 0
      RackPeek.Domain/Resources/Hardware/Laptops/AddLaptopUseCase.cs
  11. 17 0
      RackPeek.Domain/Resources/Hardware/Laptops/Cpus/AddDesktopCpuUseCase.cs
  12. 19 0
      RackPeek.Domain/Resources/Hardware/Laptops/Cpus/RemoveDesktopCpuUseCase.cs
  13. 19 0
      RackPeek.Domain/Resources/Hardware/Laptops/Cpus/UpdateDesktopCpuUseCase.cs
  14. 13 0
      RackPeek.Domain/Resources/Hardware/Laptops/DeleteLaptopUseCase.cs
  15. 33 0
      RackPeek.Domain/Resources/Hardware/Laptops/DescribeLaptopUseCase.cs
  16. 17 0
      RackPeek.Domain/Resources/Hardware/Laptops/Drives/AddDesktopDriveUseCase.cs
  17. 19 0
      RackPeek.Domain/Resources/Hardware/Laptops/Drives/RemoveDesktopDriveUseCase.cs
  18. 19 0
      RackPeek.Domain/Resources/Hardware/Laptops/Drives/UpdateDesktopDriveUseCase.cs
  19. 12 0
      RackPeek.Domain/Resources/Hardware/Laptops/GetDesktopUseCase.cs
  20. 12 0
      RackPeek.Domain/Resources/Hardware/Laptops/GetLaptopsUseCase.cs
  21. 17 0
      RackPeek.Domain/Resources/Hardware/Laptops/Gpus/AddDesktopGpuUseCase.cs
  22. 19 0
      RackPeek.Domain/Resources/Hardware/Laptops/Gpus/RemoveDesktopGpuUseCase.cs
  23. 19 0
      RackPeek.Domain/Resources/Hardware/Laptops/Gpus/UpdateDesktopGpuUseCase.cs
  24. 72 0
      RackPeek.Domain/Resources/Hardware/Laptops/LaptopHardwareReportUseCase.cs
  25. 21 0
      RackPeek.Domain/Resources/Hardware/Routers/AddRouterUseCase.cs
  26. 14 0
      RackPeek.Domain/Resources/Hardware/Routers/DeleteRouterUseCase.cs
  27. 54 0
      RackPeek.Domain/Resources/Hardware/Routers/DescribeRouterUseCase.cs
  28. 12 0
      RackPeek.Domain/Resources/Hardware/Routers/GetRouterUseCase.cs
  29. 12 0
      RackPeek.Domain/Resources/Hardware/Routers/GetRoutersUseCase.cs
  30. 58 0
      RackPeek.Domain/Resources/Hardware/Routers/RouterHardwareReport.cs
  31. 29 0
      RackPeek.Domain/Resources/Hardware/Routers/UpdateRouterUseCase.cs
  32. 1 3
      RackPeek.Web/Components/AccessPoints/AccessPointCardComponent.razor
  33. 0 1
      RackPeek.Web/Components/AccessPoints/AccessPointsListPage.razor
  34. 6 6
      RackPeek.Web/Components/Components/HardwareDependencyTreeComponent.razor
  35. 10 11
      RackPeek.Web/Components/Components/ResourceBreadCrumbComponent.razor
  36. 1 3
      RackPeek.Web/Components/Desktops/DesktopCardComponent.razor
  37. 0 1
      RackPeek.Web/Components/Desktops/DesktopsListPage.razor
  38. 8 5
      RackPeek.Web/Components/Hardware/HardwareDetailsPage.razor
  39. 5 5
      RackPeek.Web/Components/Hardware/HardwareTreePage.razor
  40. 2 1
      RackPeek.Web/Components/Servers/AddServerComponent.razor
  41. 1 1
      RackPeek.Web/Components/Servers/ServerCardComponent.razor
  42. 3 2
      RackPeek.Web/Components/Servers/ServersListComponent.razor
  43. 0 1
      RackPeek.Web/Components/Servers/ServersListPage.razor
  44. 3 3
      RackPeek.Web/Components/Services/AddServiceComponent.razor
  45. 1 1
      RackPeek.Web/Components/Services/ServiceCardComponent.razor
  46. 1 1
      RackPeek.Web/Components/Services/ServiceDetailsPage.razor
  47. 4 2
      RackPeek.Web/Components/Services/ServicesListComponent.razor
  48. 0 1
      RackPeek.Web/Components/Services/ServicesListPage.razor
  49. 1 3
      RackPeek.Web/Components/Switches/SwitchCardComponent.razor
  50. 2 2
      RackPeek.Web/Components/Switches/SwitchListComponent.razor
  51. 0 1
      RackPeek.Web/Components/Switches/SwitchListPage.razor
  52. 2 1
      RackPeek.Web/Components/Systems/AddSystemComponent.razor
  53. 2 3
      RackPeek.Web/Components/Systems/SystemCardComponent.razor
  54. 8 11
      RackPeek.Web/Components/Systems/SystemDependencyTreeComponent.razor
  55. 1 1
      RackPeek.Web/Components/Systems/SystemsDetailsPage.razor
  56. 5 4
      RackPeek.Web/Components/Systems/SystemsListComponent.razor
  57. 0 1
      RackPeek.Web/Components/Systems/SystemsListPage.razor
  58. 28 28
      RackPeek.Web/RackPeek.Web.csproj
  59. 635 635
      RackPeek.Web/config copy/Services.yaml
  60. 207 207
      RackPeek.Web/config copy/Systems.yaml
  61. 35 35
      RackPeek.Web/config copy/accesspoints.yaml
  62. 21 21
      RackPeek.Web/config copy/desktops.yaml
  63. 13 13
      RackPeek.Web/config copy/firewalls.yaml
  64. 16 16
      RackPeek.Web/config copy/laptops.yaml
  65. 13 13
      RackPeek.Web/config copy/routers.yaml
  66. 420 420
      RackPeek.Web/config copy/servers.yaml
  67. 1 1
      RackPeek.Web/config copy/switches.yaml
  68. 5 5
      RackPeek.Web/config copy/ups.yaml
  69. 390 267
      RackPeek/CliBootstrap.cs
  70. 32 0
      RackPeek/Commands/Firewalls/FirewallAddCommand.cs
  71. 8 0
      RackPeek/Commands/Firewalls/FirewallCommands.cs
  72. 25 0
      RackPeek/Commands/Firewalls/FirewallDeleteCommand.cs
  73. 47 0
      RackPeek/Commands/Firewalls/FirewallDescribeCommand.cs
  74. 33 0
      RackPeek/Commands/Firewalls/FirewallGetByNameCommand.cs
  75. 49 0
      RackPeek/Commands/Firewalls/FirewallGetCommand.cs
  76. 51 0
      RackPeek/Commands/Firewalls/FirewallReportCommand.cs
  77. 39 0
      RackPeek/Commands/Firewalls/FirewallSetCommand.cs
  78. 32 0
      RackPeek/Commands/Laptops/Cpus/LaptopCpuAddCommand.cs
  79. 23 0
      RackPeek/Commands/Laptops/Cpus/LaptopCpuAddSettings.cs
  80. 24 0
      RackPeek/Commands/Laptops/Cpus/LaptopCpuRemoveCommand.cs
  81. 15 0
      RackPeek/Commands/Laptops/Cpus/LaptopCpuRemoveSettings.cs
  82. 32 0
      RackPeek/Commands/Laptops/Cpus/LaptopCpuSetCommand.cs
  83. 27 0
      RackPeek/Commands/Laptops/Cpus/LaptopCpuSetSettings.cs
  84. 30 0
      RackPeek/Commands/Laptops/Drive/LaptopDriveAddCommand.cs
  85. 19 0
      RackPeek/Commands/Laptops/Drive/LaptopDriveAddSettings.cs
  86. 24 0
      RackPeek/Commands/Laptops/Drive/LaptopDriveRemoveCommand.cs
  87. 15 0
      RackPeek/Commands/Laptops/Drive/LaptopDriveRemoveSettings.cs
  88. 30 0
      RackPeek/Commands/Laptops/Drive/LaptopDriveSetCommand.cs
  89. 23 0
      RackPeek/Commands/Laptops/Drive/LaptopDriveSetSettings.cs
  90. 31 0
      RackPeek/Commands/Laptops/Gpus/LaptopGpuAddCommand.cs
  91. 19 0
      RackPeek/Commands/Laptops/Gpus/LaptopGpuAddSettings.cs
  92. 24 0
      RackPeek/Commands/Laptops/Gpus/LaptopGpuRemoveCommand.cs
  93. 15 0
      RackPeek/Commands/Laptops/Gpus/LaptopGpuRemoveSettings.cs
  94. 31 0
      RackPeek/Commands/Laptops/Gpus/LaptopGpuSetCommand.cs
  95. 23 0
      RackPeek/Commands/Laptops/Gpus/LaptopGpuSetSettings.cs
  96. 24 0
      RackPeek/Commands/Laptops/LaptopAddCommand.cs
  97. 8 0
      RackPeek/Commands/Laptops/LaptopCommands.cs
  98. 24 0
      RackPeek/Commands/Laptops/LaptopDeleteCommand.cs
  99. 39 0
      RackPeek/Commands/Laptops/LaptopDescribeCommand.cs
  100. 30 0
      RackPeek/Commands/Laptops/LaptopGetByNameCommand.cs

+ 36 - 0
CommandIndex.md

@@ -33,6 +33,22 @@
     - [describe](Commands.md#rpk-switches-describe) - Show detailed information about a switch
     - [set](Commands.md#rpk-switches-set) - Update properties of a switch
     - [del](Commands.md#rpk-switches-del) - Delete a switch from the inventory
+  - [routers](Commands.md#rpk-routers) - Manage network routers
+    - [summary](Commands.md#rpk-routers-summary) - Show a hardware report for all routers
+    - [add](Commands.md#rpk-routers-add) - Add a new network router to the inventory
+    - [list](Commands.md#rpk-routers-list) - List all routers in the system
+    - [get](Commands.md#rpk-routers-get) - Retrieve details of a specific router by name
+    - [describe](Commands.md#rpk-routers-describe) - Show detailed information about a router
+    - [set](Commands.md#rpk-routers-set) - Update properties of a router
+    - [del](Commands.md#rpk-routers-del) - Delete a router from the inventory
+  - [firewalls](Commands.md#rpk-firewalls) - Manage firewalls
+    - [summary](Commands.md#rpk-firewalls-summary) - Show a hardware report for all firewalls
+    - [add](Commands.md#rpk-firewalls-add) - Add a new firewall to the inventory
+    - [list](Commands.md#rpk-firewalls-list) - List all firewalls in the system
+    - [get](Commands.md#rpk-firewalls-get) - Retrieve details of a specific firewall by name
+    - [describe](Commands.md#rpk-firewalls-describe) - Show detailed information about a firewall
+    - [set](Commands.md#rpk-firewalls-set) - Update properties of a firewall
+    - [del](Commands.md#rpk-firewalls-del) - Delete a firewall from the inventory
   - [systems](Commands.md#rpk-systems) - Manage systems and their dependencies
     - [summary](Commands.md#rpk-systems-summary) - Show a summary report for all systems
     - [add](Commands.md#rpk-systems-add) - Add a new system to the inventory
@@ -83,6 +99,26 @@
       - [add](Commands.md#rpk-desktops-nic-add) - Add a NIC to a desktop
       - [set](Commands.md#rpk-desktops-nic-set) - Update a desktop NIC
       - [del](Commands.md#rpk-desktops-nic-del) - Remove a NIC from a desktop
+  - [Laptops](Commands.md#rpk-laptops) - Manage Laptop computers and their components
+    - [add](Commands.md#rpk-laptops-add) - Add a new Laptop
+    - [list](Commands.md#rpk-laptops-list) - List all Laptops
+    - [get](Commands.md#rpk-laptops-get) - Retrieve a Laptop by name
+    - [describe](Commands.md#rpk-laptops-describe) - Show detailed information about a Laptop
+    - [del](Commands.md#rpk-laptops-del) - Delete a Laptop from the inventory
+    - [summary](Commands.md#rpk-laptops-summary) - Show a summarized hardware report for all Laptops
+    - [tree](Commands.md#rpk-laptops-tree) - Display the dependency tree for a Laptop
+    - [cpu](Commands.md#rpk-laptops-cpu) - Manage CPUs attached to Laptops
+      - [add](Commands.md#rpk-laptops-cpu-add) - Add a CPU to a Laptop
+      - [set](Commands.md#rpk-laptops-cpu-set) - Update a Laptop CPU
+      - [del](Commands.md#rpk-laptops-cpu-del) - Remove a CPU from a Laptop
+    - [drive](Commands.md#rpk-laptops-drive) - Manage storage drives attached to Laptops
+      - [add](Commands.md#rpk-laptops-drive-add) - Add a drive to a Laptop
+      - [set](Commands.md#rpk-laptops-drive-set) - Update a Laptop drive
+      - [del](Commands.md#rpk-laptops-drive-del) - Remove a drive from a Laptop
+    - [gpu](Commands.md#rpk-laptops-gpu) - Manage GPUs attached to Laptops
+      - [add](Commands.md#rpk-laptops-gpu-add) - Add a GPU to a Laptop
+      - [set](Commands.md#rpk-laptops-gpu-set) - Update a Laptop GPU
+      - [del](Commands.md#rpk-laptops-gpu-del) - Remove a GPU from a Laptop
   - [services](Commands.md#rpk-services) - Manage services and their configurations
     - [summary](Commands.md#rpk-services-summary) - Show a summary report for all services
     - [add](Commands.md#rpk-services-add) - Add a new service

+ 578 - 0
Commands.md

@@ -12,10 +12,13 @@ COMMANDS:
     summary         Show a summarized report of all resources in the system
     servers         Manage servers and their components                    
     switches        Manage network switches                                
+    routers         Manage network routers                                 
+    firewalls       Manage firewalls                                       
     systems         Manage systems and their dependencies                  
     accesspoints    Manage access points                                   
     ups             Manage UPS units                                       
     desktops        Manage desktop computers and their components          
+    Laptops         Manage Laptop computers and their components           
     services        Manage services and their configurations               
 ```
 
@@ -559,6 +562,252 @@ OPTIONS:
     -h, --help    Prints help information
 ```
 
+## `rpk routers`
+```
+DESCRIPTION:
+Manage network routers
+
+USAGE:
+    rpk routers [OPTIONS] <COMMAND>
+
+OPTIONS:
+    -h, --help    Prints help information
+
+COMMANDS:
+    summary            Show a hardware report for all routers       
+    add <name>         Add a new network router to the inventory    
+    list               List all routers in the system               
+    get <name>         Retrieve details of a specific router by name
+    describe <name>    Show detailed information about a router     
+    set <name>         Update properties of a router                
+    del <name>         Delete a router from the inventory           
+```
+
+## `rpk routers summary`
+```
+DESCRIPTION:
+Show a hardware report for all routers
+
+USAGE:
+    rpk routers summary [OPTIONS]
+
+OPTIONS:
+    -h, --help    Prints help information
+```
+
+## `rpk routers add`
+```
+DESCRIPTION:
+Add a new network router to the inventory
+
+USAGE:
+    rpk routers add <name> [OPTIONS]
+
+ARGUMENTS:
+    <name>     
+
+OPTIONS:
+    -h, --help    Prints help information
+```
+
+## `rpk routers list`
+```
+DESCRIPTION:
+List all routers in the system
+
+USAGE:
+    rpk routers list [OPTIONS]
+
+OPTIONS:
+    -h, --help    Prints help information
+```
+
+## `rpk routers get`
+```
+DESCRIPTION:
+Retrieve details of a specific router by name
+
+USAGE:
+    rpk routers get <name> [OPTIONS]
+
+ARGUMENTS:
+    <name>     
+
+OPTIONS:
+    -h, --help    Prints help information
+```
+
+## `rpk routers describe`
+```
+DESCRIPTION:
+Show detailed information about a router
+
+USAGE:
+    rpk routers describe <name> [OPTIONS]
+
+ARGUMENTS:
+    <name>     
+
+OPTIONS:
+    -h, --help    Prints help information
+```
+
+## `rpk routers set`
+```
+DESCRIPTION:
+Update properties of a router
+
+USAGE:
+    rpk routers set <name> [OPTIONS]
+
+ARGUMENTS:
+    <name>     
+
+OPTIONS:
+    -h, --help       Prints help information
+        --Model                             
+        --managed                           
+        --poe                               
+```
+
+## `rpk routers del`
+```
+DESCRIPTION:
+Delete a router from the inventory
+
+USAGE:
+    rpk routers del <name> [OPTIONS]
+
+ARGUMENTS:
+    <name>     
+
+OPTIONS:
+    -h, --help    Prints help information
+```
+
+## `rpk firewalls`
+```
+DESCRIPTION:
+Manage firewalls
+
+USAGE:
+    rpk firewalls [OPTIONS] <COMMAND>
+
+OPTIONS:
+    -h, --help    Prints help information
+
+COMMANDS:
+    summary            Show a hardware report for all firewalls       
+    add <name>         Add a new firewall to the inventory            
+    list               List all firewalls in the system               
+    get <name>         Retrieve details of a specific firewall by name
+    describe <name>    Show detailed information about a firewall     
+    set <name>         Update properties of a firewall                
+    del <name>         Delete a firewall from the inventory           
+```
+
+## `rpk firewalls summary`
+```
+DESCRIPTION:
+Show a hardware report for all firewalls
+
+USAGE:
+    rpk firewalls summary [OPTIONS]
+
+OPTIONS:
+    -h, --help    Prints help information
+```
+
+## `rpk firewalls add`
+```
+DESCRIPTION:
+Add a new firewall to the inventory
+
+USAGE:
+    rpk firewalls add <name> [OPTIONS]
+
+ARGUMENTS:
+    <name>     
+
+OPTIONS:
+    -h, --help    Prints help information
+```
+
+## `rpk firewalls list`
+```
+DESCRIPTION:
+List all firewalls in the system
+
+USAGE:
+    rpk firewalls list [OPTIONS]
+
+OPTIONS:
+    -h, --help    Prints help information
+```
+
+## `rpk firewalls get`
+```
+DESCRIPTION:
+Retrieve details of a specific firewall by name
+
+USAGE:
+    rpk firewalls get <name> [OPTIONS]
+
+ARGUMENTS:
+    <name>     
+
+OPTIONS:
+    -h, --help    Prints help information
+```
+
+## `rpk firewalls describe`
+```
+DESCRIPTION:
+Show detailed information about a firewall
+
+USAGE:
+    rpk firewalls describe <name> [OPTIONS]
+
+ARGUMENTS:
+    <name>     
+
+OPTIONS:
+    -h, --help    Prints help information
+```
+
+## `rpk firewalls set`
+```
+DESCRIPTION:
+Update properties of a firewall
+
+USAGE:
+    rpk firewalls set <name> [OPTIONS]
+
+ARGUMENTS:
+    <name>     
+
+OPTIONS:
+    -h, --help       Prints help information
+        --Model                             
+        --managed                           
+        --poe                               
+```
+
+## `rpk firewalls del`
+```
+DESCRIPTION:
+Delete a firewall from the inventory
+
+USAGE:
+    rpk firewalls del <name> [OPTIONS]
+
+ARGUMENTS:
+    <name>     
+
+OPTIONS:
+    -h, --help    Prints help information
+```
+
 ## `rpk systems`
 ```
 DESCRIPTION:
@@ -1361,6 +1610,335 @@ OPTIONS:
     -h, --help    Prints help information
 ```
 
+## `rpk Laptops`
+```
+DESCRIPTION:
+Manage Laptop computers and their components
+
+USAGE:
+    rpk Laptops [OPTIONS] <COMMAND>
+
+OPTIONS:
+    -h, --help    Prints help information
+
+COMMANDS:
+    add <name>         Add a new Laptop                                 
+    list               List all Laptops                                 
+    get <name>         Retrieve a Laptop by name                        
+    describe <name>    Show detailed information about a Laptop         
+    del <name>         Delete a Laptop from the inventory               
+    summary            Show a summarized hardware report for all Laptops
+    tree <name>        Display the dependency tree for a Laptop         
+    cpu                Manage CPUs attached to Laptops                  
+    drive              Manage storage drives attached to Laptops        
+    gpu                Manage GPUs attached to Laptops                  
+```
+
+## `rpk Laptops add`
+```
+DESCRIPTION:
+Add a new Laptop
+
+USAGE:
+    rpk Laptops add <name> [OPTIONS]
+
+ARGUMENTS:
+    <name>     
+
+OPTIONS:
+    -h, --help    Prints help information
+```
+
+## `rpk Laptops list`
+```
+DESCRIPTION:
+List all Laptops
+
+USAGE:
+    rpk Laptops list [OPTIONS]
+
+OPTIONS:
+    -h, --help    Prints help information
+```
+
+## `rpk Laptops get`
+```
+DESCRIPTION:
+Retrieve a Laptop by name
+
+USAGE:
+    rpk Laptops get <name> [OPTIONS]
+
+ARGUMENTS:
+    <name>     
+
+OPTIONS:
+    -h, --help    Prints help information
+```
+
+## `rpk Laptops describe`
+```
+DESCRIPTION:
+Show detailed information about a Laptop
+
+USAGE:
+    rpk Laptops describe <name> [OPTIONS]
+
+ARGUMENTS:
+    <name>     
+
+OPTIONS:
+    -h, --help    Prints help information
+```
+
+## `rpk Laptops del`
+```
+DESCRIPTION:
+Delete a Laptop from the inventory
+
+USAGE:
+    rpk Laptops del <name> [OPTIONS]
+
+ARGUMENTS:
+    <name>     
+
+OPTIONS:
+    -h, --help    Prints help information
+```
+
+## `rpk Laptops summary`
+```
+DESCRIPTION:
+Show a summarized hardware report for all Laptops
+
+USAGE:
+    rpk Laptops summary [OPTIONS]
+
+OPTIONS:
+    -h, --help    Prints help information
+```
+
+## `rpk Laptops tree`
+```
+DESCRIPTION:
+Display the dependency tree for a Laptop
+
+USAGE:
+    rpk Laptops tree <name> [OPTIONS]
+
+ARGUMENTS:
+    <name>     
+
+OPTIONS:
+    -h, --help    Prints help information
+```
+
+## `rpk Laptops cpu`
+```
+DESCRIPTION:
+Manage CPUs attached to Laptops
+
+USAGE:
+    rpk Laptops cpu [OPTIONS] <COMMAND>
+
+OPTIONS:
+    -h, --help    Prints help information
+
+COMMANDS:
+    add <Laptop>            Add a CPU to a Laptop     
+    set <Laptop> <index>    Update a Laptop CPU       
+    del <Laptop> <index>    Remove a CPU from a Laptop
+```
+
+## `rpk Laptops cpu add`
+```
+DESCRIPTION:
+Add a CPU to a Laptop
+
+USAGE:
+    rpk Laptops cpu add <Laptop> [OPTIONS]
+
+ARGUMENTS:
+    <Laptop>    The Laptop name
+
+OPTIONS:
+    -h, --help       Prints help information  
+        --model      The model name           
+        --cores      The number of cpu cores  
+        --threads    The number of cpu threads
+```
+
+## `rpk Laptops cpu set`
+```
+DESCRIPTION:
+Update a Laptop CPU
+
+USAGE:
+    rpk Laptops cpu set <Laptop> <index> [OPTIONS]
+
+ARGUMENTS:
+    <Laptop>    The Laptop name            
+    <index>     The index of the Laptop cpu
+
+OPTIONS:
+    -h, --help       Prints help information  
+        --model      The cpu model            
+        --cores      The number of cpu cores  
+        --threads    The number of cpu threads
+```
+
+## `rpk Laptops cpu del`
+```
+DESCRIPTION:
+Remove a CPU from a Laptop
+
+USAGE:
+    rpk Laptops cpu del <Laptop> <index> [OPTIONS]
+
+ARGUMENTS:
+    <Laptop>    The name of the Laptop               
+    <index>     The index of the Laptop cpu to remove
+
+OPTIONS:
+    -h, --help    Prints help information
+```
+
+## `rpk Laptops drive`
+```
+DESCRIPTION:
+Manage storage drives attached to Laptops
+
+USAGE:
+    rpk Laptops drive [OPTIONS] <COMMAND>
+
+OPTIONS:
+    -h, --help    Prints help information
+
+COMMANDS:
+    add <Laptop>            Add a drive to a Laptop     
+    set <Laptop> <index>    Update a Laptop drive       
+    del <Laptop> <index>    Remove a drive from a Laptop
+```
+
+## `rpk Laptops drive add`
+```
+DESCRIPTION:
+Add a drive to a Laptop
+
+USAGE:
+    rpk Laptops drive add <Laptop> [OPTIONS]
+
+ARGUMENTS:
+    <Laptop>    The name of the Laptop
+
+OPTIONS:
+    -h, --help    Prints help information     
+        --type    The drive type e.g hdd / ssd
+        --size    The drive capacity in Gb    
+```
+
+## `rpk Laptops drive set`
+```
+DESCRIPTION:
+Update a Laptop drive
+
+USAGE:
+    rpk Laptops drive set <Laptop> <index> [OPTIONS]
+
+ARGUMENTS:
+    <Laptop>    The Laptop name          
+    <index>     The drive index to update
+
+OPTIONS:
+    -h, --help    Prints help information     
+        --type    The drive type e.g hdd / ssd
+        --size    The drive capacity in Gb    
+```
+
+## `rpk Laptops drive del`
+```
+DESCRIPTION:
+Remove a drive from a Laptop
+
+USAGE:
+    rpk Laptops drive del <Laptop> <index> [OPTIONS]
+
+ARGUMENTS:
+    <Laptop>    The name of the Laptop          
+    <index>     The index of the drive to remove
+
+OPTIONS:
+    -h, --help    Prints help information
+```
+
+## `rpk Laptops gpu`
+```
+DESCRIPTION:
+Manage GPUs attached to Laptops
+
+USAGE:
+    rpk Laptops gpu [OPTIONS] <COMMAND>
+
+OPTIONS:
+    -h, --help    Prints help information
+
+COMMANDS:
+    add <Laptop>            Add a GPU to a Laptop     
+    set <Laptop> <index>    Update a Laptop GPU       
+    del <Laptop> <index>    Remove a GPU from a Laptop
+```
+
+## `rpk Laptops gpu add`
+```
+DESCRIPTION:
+Add a GPU to a Laptop
+
+USAGE:
+    rpk Laptops gpu add <Laptop> [OPTIONS]
+
+ARGUMENTS:
+    <Laptop>    The name of the Laptop
+
+OPTIONS:
+    -h, --help     Prints help information     
+        --model    The Gpu model               
+        --vram     The amount of gpu vram in Gb
+```
+
+## `rpk Laptops gpu set`
+```
+DESCRIPTION:
+Update a Laptop GPU
+
+USAGE:
+    rpk Laptops gpu set <Laptop> <index> [OPTIONS]
+
+ARGUMENTS:
+    <Laptop>    The Laptop name               
+    <index>     The index of the gpu to update
+
+OPTIONS:
+    -h, --help     Prints help information     
+        --model    The gpu model name          
+        --vram     The amount of gpu vram in Gb
+```
+
+## `rpk Laptops gpu del`
+```
+DESCRIPTION:
+Remove a GPU from a Laptop
+
+USAGE:
+    rpk Laptops gpu del <Laptop> <index> [OPTIONS]
+
+ARGUMENTS:
+    <Laptop>    The Laptop name               
+    <index>     The index of the Gpu to remove
+
+OPTIONS:
+    -h, --help    Prints help information
+```
+
 ## `rpk services`
 ```
 DESCRIPTION:

+ 36 - 0
README.md

@@ -89,6 +89,22 @@ The project is optimized for home labs and self-hosted environments, not enterpr
     - [describe](Commands.md#rpk-switches-describe) - Show detailed information about a switch
     - [set](Commands.md#rpk-switches-set) - Update properties of a switch
     - [del](Commands.md#rpk-switches-del) - Delete a switch from the inventory
+  - [routers](Commands.md#rpk-routers) - Manage network routers
+    - [summary](Commands.md#rpk-routers-summary) - Show a hardware report for all routers
+    - [add](Commands.md#rpk-routers-add) - Add a new network router to the inventory
+    - [list](Commands.md#rpk-routers-list) - List all routers in the system
+    - [get](Commands.md#rpk-routers-get) - Retrieve details of a specific router by name
+    - [describe](Commands.md#rpk-routers-describe) - Show detailed information about a router
+    - [set](Commands.md#rpk-routers-set) - Update properties of a router
+    - [del](Commands.md#rpk-routers-del) - Delete a router from the inventory
+  - [firewalls](Commands.md#rpk-firewalls) - Manage firewalls
+    - [summary](Commands.md#rpk-firewalls-summary) - Show a hardware report for all firewalls
+    - [add](Commands.md#rpk-firewalls-add) - Add a new firewall to the inventory
+    - [list](Commands.md#rpk-firewalls-list) - List all firewalls in the system
+    - [get](Commands.md#rpk-firewalls-get) - Retrieve details of a specific firewall by name
+    - [describe](Commands.md#rpk-firewalls-describe) - Show detailed information about a firewall
+    - [set](Commands.md#rpk-firewalls-set) - Update properties of a firewall
+    - [del](Commands.md#rpk-firewalls-del) - Delete a firewall from the inventory
   - [systems](Commands.md#rpk-systems) - Manage systems and their dependencies
     - [summary](Commands.md#rpk-systems-summary) - Show a summary report for all systems
     - [add](Commands.md#rpk-systems-add) - Add a new system to the inventory
@@ -139,6 +155,26 @@ The project is optimized for home labs and self-hosted environments, not enterpr
       - [add](Commands.md#rpk-desktops-nic-add) - Add a NIC to a desktop
       - [set](Commands.md#rpk-desktops-nic-set) - Update a desktop NIC
       - [del](Commands.md#rpk-desktops-nic-del) - Remove a NIC from a desktop
+  - [Laptops](Commands.md#rpk-laptops) - Manage Laptop computers and their components
+    - [add](Commands.md#rpk-laptops-add) - Add a new Laptop
+    - [list](Commands.md#rpk-laptops-list) - List all Laptops
+    - [get](Commands.md#rpk-laptops-get) - Retrieve a Laptop by name
+    - [describe](Commands.md#rpk-laptops-describe) - Show detailed information about a Laptop
+    - [del](Commands.md#rpk-laptops-del) - Delete a Laptop from the inventory
+    - [summary](Commands.md#rpk-laptops-summary) - Show a summarized hardware report for all Laptops
+    - [tree](Commands.md#rpk-laptops-tree) - Display the dependency tree for a Laptop
+    - [cpu](Commands.md#rpk-laptops-cpu) - Manage CPUs attached to Laptops
+      - [add](Commands.md#rpk-laptops-cpu-add) - Add a CPU to a Laptop
+      - [set](Commands.md#rpk-laptops-cpu-set) - Update a Laptop CPU
+      - [del](Commands.md#rpk-laptops-cpu-del) - Remove a CPU from a Laptop
+    - [drive](Commands.md#rpk-laptops-drive) - Manage storage drives attached to Laptops
+      - [add](Commands.md#rpk-laptops-drive-add) - Add a drive to a Laptop
+      - [set](Commands.md#rpk-laptops-drive-set) - Update a Laptop drive
+      - [del](Commands.md#rpk-laptops-drive-del) - Remove a drive from a Laptop
+    - [gpu](Commands.md#rpk-laptops-gpu) - Manage GPUs attached to Laptops
+      - [add](Commands.md#rpk-laptops-gpu-add) - Add a GPU to a Laptop
+      - [set](Commands.md#rpk-laptops-gpu-set) - Update a Laptop GPU
+      - [del](Commands.md#rpk-laptops-gpu-del) - Remove a GPU from a Laptop
   - [services](Commands.md#rpk-services) - Manage services and their configurations
     - [summary](Commands.md#rpk-services-summary) - Show a summary report for all services
     - [add](Commands.md#rpk-services-add) - Add a new service

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

@@ -0,0 +1,21 @@
+using RackPeek.Domain.Resources.Hardware.Models;
+
+namespace RackPeek.Domain.Resources.Hardware.Firewalls;
+
+public class AddFirewallUseCase(IHardwareRepository repository) : IUseCase
+{
+    public async Task ExecuteAsync(string name)
+    {
+        // basic guard rails
+        var existing = await repository.GetByNameAsync(name);
+        if (existing != null)
+            throw new InvalidOperationException($"Firewall '{name}' already exists.");
+
+        var FirewallResource = new Firewall
+        {
+            Name = name
+        };
+
+        await repository.AddAsync(FirewallResource);
+    }
+}

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

@@ -0,0 +1,14 @@
+using RackPeek.Domain.Resources.Hardware.Models;
+
+namespace RackPeek.Domain.Resources.Hardware.Firewalls;
+
+public class DeleteFirewallUseCase(IHardwareRepository repository) : IUseCase
+{
+    public async Task ExecuteAsync(string name)
+    {
+        if (await repository.GetByNameAsync(name) is not Firewall hardware)
+            throw new InvalidOperationException($"Firewall '{name}' not found.");
+
+        await repository.DeleteAsync(name);
+    }
+}

+ 54 - 0
RackPeek.Domain/Resources/Hardware/Firewalls/DescribeFirewallUseCase.cs

@@ -0,0 +1,54 @@
+using RackPeek.Domain.Resources.Hardware.Models;
+
+namespace RackPeek.Domain.Resources.Hardware.Firewalls;
+
+public record FirewallDescription(
+    string Name,
+    string? Model,
+    bool? Managed,
+    bool? Poe,
+    int TotalPorts,
+    int TotalSpeedGb,
+    string PortSummary
+);
+
+public class DescribeFirewallUseCase(IHardwareRepository repository) : IUseCase
+{
+    public async Task<FirewallDescription?> ExecuteAsync(string name)
+    {
+        var firewallResource = await repository.GetByNameAsync(name) as Firewall;
+        if (firewallResource == null)
+            return null;
+
+        // If no ports exist, return defaults
+        var ports = firewallResource.Ports ?? new List<Port>();
+
+        // Total ports count
+        var totalPorts = ports.Sum(p => p.Count ?? 0);
+
+        // Total speed (sum of each port speed * count)
+        var totalSpeedGb = ports.Sum(p => (p.Speed ?? 0) * (p.Count ?? 0));
+
+        // Build a port summary string
+        var portGroups = ports
+            .GroupBy(p => p.Type ?? "Unknown")
+            .Select(g =>
+            {
+                var count = g.Sum(x => x.Count ?? 0);
+                var speed = g.Sum(x => (x.Speed ?? 0) * (x.Count ?? 0));
+                return $"{g.Key}: {count} ports ({speed} Gb total)";
+            });
+
+        var portSummary = string.Join(", ", portGroups);
+
+        return new FirewallDescription(
+            firewallResource.Name,
+            firewallResource.Model,
+            firewallResource.Managed,
+            firewallResource.Poe,
+            totalPorts,
+            totalSpeedGb,
+            portSummary
+        );
+    }
+}

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

@@ -0,0 +1,12 @@
+using RackPeek.Domain.Resources.Hardware.Models;
+
+namespace RackPeek.Domain.Resources.Hardware.Firewalls;
+
+public class GetFirewallUseCase(IHardwareRepository repository) : IUseCase
+{
+    public async Task<Firewall?> ExecuteAsync(string name)
+    {
+        var hardware = await repository.GetByNameAsync(name);
+        return hardware as Firewall;
+    }
+}

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

@@ -0,0 +1,12 @@
+using RackPeek.Domain.Resources.Hardware.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();
+    }
+}

+ 29 - 0
RackPeek.Domain/Resources/Hardware/Firewalls/UpdateFirewallUseCase.cs

@@ -0,0 +1,29 @@
+using RackPeek.Domain.Resources.Hardware.Models;
+
+namespace RackPeek.Domain.Resources.Hardware.Firewalls;
+
+public class UpdateFirewallUseCase(IHardwareRepository repository) : IUseCase
+{
+    public async Task ExecuteAsync(
+        string name,
+        string? model = null,
+        bool? managed = null,
+        bool? poe = null
+    )
+    {
+        var firewallResource = await repository.GetByNameAsync(name) as Firewall;
+        if (firewallResource == null)
+            throw new InvalidOperationException($"Firewall '{name}' not found.");
+
+        if (!string.IsNullOrWhiteSpace(model))
+            firewallResource.Model = model;
+
+        if (managed.HasValue)
+            firewallResource.Managed = managed.Value;
+
+        if (poe.HasValue)
+            firewallResource.Poe = poe.Value;
+
+        await repository.UpdateAsync(firewallResource);
+    }
+}

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

@@ -0,0 +1,24 @@
+using RackPeek.Domain.Resources.Hardware.Models;
+
+namespace RackPeek.Domain.Resources.Hardware.Laptops;
+
+public class AddLaptopUseCase(IHardwareRepository repository) : IUseCase
+{
+    public async Task ExecuteAsync(string name)
+    {
+        var existing = await repository.GetByNameAsync(name);
+        if (existing != null)
+            throw new InvalidOperationException($"Laptop '{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);
+    }
+}

+ 17 - 0
RackPeek.Domain/Resources/Hardware/Laptops/Cpus/AddDesktopCpuUseCase.cs

@@ -0,0 +1,17 @@
+using RackPeek.Domain.Resources.Hardware.Models;
+
+namespace RackPeek.Domain.Resources.Hardware.Laptops.Cpus;
+
+public class AddLaptopCpuUseCase(IHardwareRepository repository) : IUseCase
+{
+    public async Task ExecuteAsync(string LaptopName, Cpu cpu)
+    {
+        var Laptop = await repository.GetByNameAsync(LaptopName) as Laptop
+                     ?? throw new InvalidOperationException($"Laptop '{LaptopName}' not found.");
+
+        Laptop.Cpus ??= new List<Cpu>();
+        Laptop.Cpus.Add(cpu);
+
+        await repository.UpdateAsync(Laptop);
+    }
+}

+ 19 - 0
RackPeek.Domain/Resources/Hardware/Laptops/Cpus/RemoveDesktopCpuUseCase.cs

@@ -0,0 +1,19 @@
+using RackPeek.Domain.Resources.Hardware.Models;
+
+namespace RackPeek.Domain.Resources.Hardware.Laptops.Cpus;
+
+public class RemoveLaptopCpuUseCase(IHardwareRepository repository) : IUseCase
+{
+    public async Task ExecuteAsync(string LaptopName, int index)
+    {
+        var Laptop = await repository.GetByNameAsync(LaptopName) as Laptop
+                     ?? throw new InvalidOperationException($"Laptop '{LaptopName}' not found.");
+
+        if (Laptop.Cpus == null || index < 0 || index >= Laptop.Cpus.Count)
+            throw new InvalidOperationException($"CPU index {index} not found on Laptop '{LaptopName}'.");
+
+        Laptop.Cpus.RemoveAt(index);
+
+        await repository.UpdateAsync(Laptop);
+    }
+}

+ 19 - 0
RackPeek.Domain/Resources/Hardware/Laptops/Cpus/UpdateDesktopCpuUseCase.cs

@@ -0,0 +1,19 @@
+using RackPeek.Domain.Resources.Hardware.Models;
+
+namespace RackPeek.Domain.Resources.Hardware.Laptops.Cpus;
+
+public class UpdateLaptopCpuUseCase(IHardwareRepository repository) : IUseCase
+{
+    public async Task ExecuteAsync(string LaptopName, int index, Cpu updated)
+    {
+        var Laptop = await repository.GetByNameAsync(LaptopName) as Laptop
+                     ?? throw new InvalidOperationException($"Laptop '{LaptopName}' not found.");
+
+        if (Laptop.Cpus == null || index < 0 || index >= Laptop.Cpus.Count)
+            throw new InvalidOperationException($"CPU index {index} not found on Laptop '{LaptopName}'.");
+
+        Laptop.Cpus[index] = updated;
+
+        await repository.UpdateAsync(Laptop);
+    }
+}

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

@@ -0,0 +1,13 @@
+namespace RackPeek.Domain.Resources.Hardware.Laptops;
+
+public class DeleteLaptopUseCase(IHardwareRepository repository) : IUseCase
+{
+    public async Task ExecuteAsync(string name)
+    {
+        var hardware = await repository.GetByNameAsync(name);
+        if (hardware == null)
+            throw new InvalidOperationException($"Laptop '{name}' not found.");
+
+        await repository.DeleteAsync(name);
+    }
+}

+ 33 - 0
RackPeek.Domain/Resources/Hardware/Laptops/DescribeLaptopUseCase.cs

@@ -0,0 +1,33 @@
+using RackPeek.Domain.Resources.Hardware.Models;
+
+namespace RackPeek.Domain.Resources.Hardware.Laptops;
+
+public class DescribeLaptopUseCase(IHardwareRepository repository) : IUseCase
+{
+    public async Task<LaptopDescription?> ExecuteAsync(string name)
+    {
+        var Laptop = await repository.GetByNameAsync(name) as Laptop;
+        if (Laptop == null)
+            return null;
+
+        var ramSummary = Laptop.Ram == null
+            ? "None"
+            : $"{Laptop.Ram.Size} GB @ {Laptop.Ram.Mts} MT/s";
+
+        return new LaptopDescription(
+            Laptop.Name,
+            Laptop.Cpus?.Count ?? 0,
+            ramSummary,
+            Laptop.Drives?.Count ?? 0,
+            Laptop.Gpus?.Count ?? 0
+        );
+    }
+}
+
+public record LaptopDescription(
+    string Name,
+    int CpuCount,
+    string? RamSummary,
+    int DriveCount,
+    int GpuCount
+);

+ 17 - 0
RackPeek.Domain/Resources/Hardware/Laptops/Drives/AddDesktopDriveUseCase.cs

@@ -0,0 +1,17 @@
+using RackPeek.Domain.Resources.Hardware.Models;
+
+namespace RackPeek.Domain.Resources.Hardware.Laptops.Drives;
+
+public class AddLaptopDriveUseCase(IHardwareRepository repository) : IUseCase
+{
+    public async Task ExecuteAsync(string LaptopName, Drive drive)
+    {
+        var Laptop = await repository.GetByNameAsync(LaptopName) as Laptop
+                     ?? throw new InvalidOperationException($"Laptop '{LaptopName}' not found.");
+
+        Laptop.Drives ??= new List<Drive>();
+        Laptop.Drives.Add(drive);
+
+        await repository.UpdateAsync(Laptop);
+    }
+}

+ 19 - 0
RackPeek.Domain/Resources/Hardware/Laptops/Drives/RemoveDesktopDriveUseCase.cs

@@ -0,0 +1,19 @@
+using RackPeek.Domain.Resources.Hardware.Models;
+
+namespace RackPeek.Domain.Resources.Hardware.Laptops.Drives;
+
+public class RemoveLaptopDriveUseCase(IHardwareRepository repository) : IUseCase
+{
+    public async Task ExecuteAsync(string LaptopName, int index)
+    {
+        var Laptop = await repository.GetByNameAsync(LaptopName) as Laptop
+                     ?? throw new InvalidOperationException($"Laptop '{LaptopName}' not found.");
+
+        if (Laptop.Drives == null || index < 0 || index >= Laptop.Drives.Count)
+            throw new InvalidOperationException($"Drive index {index} not found on Laptop '{LaptopName}'.");
+
+        Laptop.Drives.RemoveAt(index);
+
+        await repository.UpdateAsync(Laptop);
+    }
+}

+ 19 - 0
RackPeek.Domain/Resources/Hardware/Laptops/Drives/UpdateDesktopDriveUseCase.cs

@@ -0,0 +1,19 @@
+using RackPeek.Domain.Resources.Hardware.Models;
+
+namespace RackPeek.Domain.Resources.Hardware.Laptops.Drives;
+
+public class UpdateLaptopDriveUseCase(IHardwareRepository repository) : IUseCase
+{
+    public async Task ExecuteAsync(string LaptopName, int index, Drive updated)
+    {
+        var Laptop = await repository.GetByNameAsync(LaptopName) as Laptop
+                     ?? throw new InvalidOperationException($"Laptop '{LaptopName}' not found.");
+
+        if (Laptop.Drives == null || index < 0 || index >= Laptop.Drives.Count)
+            throw new InvalidOperationException($"Drive index {index} not found on Laptop '{LaptopName}'.");
+
+        Laptop.Drives[index] = updated;
+
+        await repository.UpdateAsync(Laptop);
+    }
+}

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

@@ -0,0 +1,12 @@
+using RackPeek.Domain.Resources.Hardware.Models;
+
+namespace RackPeek.Domain.Resources.Hardware.Laptops;
+
+public class GetLaptopUseCase(IHardwareRepository repository) : IUseCase
+{
+    public async Task<Laptop?> ExecuteAsync(string name)
+    {
+        var hardware = await repository.GetByNameAsync(name);
+        return hardware as Laptop;
+    }
+}

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

@@ -0,0 +1,12 @@
+using RackPeek.Domain.Resources.Hardware.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();
+    }
+}

+ 17 - 0
RackPeek.Domain/Resources/Hardware/Laptops/Gpus/AddDesktopGpuUseCase.cs

@@ -0,0 +1,17 @@
+using RackPeek.Domain.Resources.Hardware.Models;
+
+namespace RackPeek.Domain.Resources.Hardware.Laptops.Gpus;
+
+public class AddLaptopGpuUseCase(IHardwareRepository repository) : IUseCase
+{
+    public async Task ExecuteAsync(string LaptopName, Gpu gpu)
+    {
+        var Laptop = await repository.GetByNameAsync(LaptopName) as Laptop
+                     ?? throw new InvalidOperationException($"Laptop '{LaptopName}' not found.");
+
+        Laptop.Gpus ??= new List<Gpu>();
+        Laptop.Gpus.Add(gpu);
+
+        await repository.UpdateAsync(Laptop);
+    }
+}

+ 19 - 0
RackPeek.Domain/Resources/Hardware/Laptops/Gpus/RemoveDesktopGpuUseCase.cs

@@ -0,0 +1,19 @@
+using RackPeek.Domain.Resources.Hardware.Models;
+
+namespace RackPeek.Domain.Resources.Hardware.Laptops.Gpus;
+
+public class RemoveLaptopGpuUseCase(IHardwareRepository repository) : IUseCase
+{
+    public async Task ExecuteAsync(string LaptopName, int index)
+    {
+        var Laptop = await repository.GetByNameAsync(LaptopName) as Laptop
+                     ?? throw new InvalidOperationException($"Laptop '{LaptopName}' not found.");
+
+        if (Laptop.Gpus == null || index < 0 || index >= Laptop.Gpus.Count)
+            throw new InvalidOperationException($"GPU index {index} not found on Laptop '{LaptopName}'.");
+
+        Laptop.Gpus.RemoveAt(index);
+
+        await repository.UpdateAsync(Laptop);
+    }
+}

+ 19 - 0
RackPeek.Domain/Resources/Hardware/Laptops/Gpus/UpdateDesktopGpuUseCase.cs

@@ -0,0 +1,19 @@
+using RackPeek.Domain.Resources.Hardware.Models;
+
+namespace RackPeek.Domain.Resources.Hardware.Laptops.Gpus;
+
+public class UpdateLaptopGpuUseCase(IHardwareRepository repository) : IUseCase
+{
+    public async Task ExecuteAsync(string LaptopName, int index, Gpu updated)
+    {
+        var Laptop = await repository.GetByNameAsync(LaptopName) as Laptop
+                     ?? throw new InvalidOperationException($"Laptop '{LaptopName}' not found.");
+
+        if (Laptop.Gpus == null || index < 0 || index >= Laptop.Gpus.Count)
+            throw new InvalidOperationException($"GPU index {index} not found on Laptop '{LaptopName}'.");
+
+        Laptop.Gpus[index] = updated;
+
+        await repository.UpdateAsync(Laptop);
+    }
+}

+ 72 - 0
RackPeek.Domain/Resources/Hardware/Laptops/LaptopHardwareReportUseCase.cs

@@ -0,0 +1,72 @@
+using RackPeek.Domain.Resources.Hardware.Models;
+
+namespace RackPeek.Domain.Resources.Hardware.Laptops;
+
+public class LaptopHardwareReportUseCase(IHardwareRepository repository) : IUseCase
+{
+    public async Task<LaptopHardwareReport> ExecuteAsync()
+    {
+        var hardware = await repository.GetAllAsync();
+        var Laptops = hardware.OfType<Laptop>();
+
+        var rows = Laptops.Select(Laptop =>
+        {
+            var totalCores = Laptop.Cpus?.Sum(c => c.Cores) ?? 0;
+            var totalThreads = Laptop.Cpus?.Sum(c => c.Threads) ?? 0;
+
+            var cpuSummary = Laptop.Cpus == null
+                ? "Unknown"
+                : string.Join(", ",
+                    Laptop.Cpus
+                        .GroupBy(c => c.Model)
+                        .Select(g => $"{g.Count()}× {g.Key}"));
+
+            var ramGb = Laptop.Ram?.Size ?? 0;
+
+            var totalStorage = Laptop.Drives?.Sum(d => d.Size) ?? 0;
+            var ssdStorage = Laptop.Drives?
+                .Where(d => d.Type == "ssd")
+                .Sum(d => d.Size) ?? 0;
+            var hddStorage = Laptop.Drives?
+                .Where(d => d.Type == "hdd")
+                .Sum(d => d.Size) ?? 0;
+
+            var gpuSummary = Laptop.Gpus == null
+                ? "None"
+                : string.Join(", ",
+                    Laptop.Gpus
+                        .GroupBy(g => g.Model)
+                        .Select(g => $"{g.Count()}× {g.Key}"));
+
+            return new LaptopHardwareRow(
+                Laptop.Name,
+                cpuSummary,
+                totalCores,
+                totalThreads,
+                ramGb,
+                totalStorage,
+                ssdStorage,
+                hddStorage,
+                gpuSummary
+            );
+        }).ToList();
+
+        return new LaptopHardwareReport(rows);
+    }
+}
+
+public record LaptopHardwareReport(
+    IReadOnlyList<LaptopHardwareRow> Laptops
+);
+
+public record LaptopHardwareRow(
+    string Name,
+    string CpuSummary,
+    int TotalCores,
+    int TotalThreads,
+    int RamGb,
+    int TotalStorageGb,
+    int SsdStorageGb,
+    int HddStorageGb,
+    string GpuSummary
+);

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

@@ -0,0 +1,21 @@
+using RackPeek.Domain.Resources.Hardware.Models;
+
+namespace RackPeek.Domain.Resources.Hardware.Routers;
+
+public class AddRouterUseCase(IHardwareRepository repository) : IUseCase
+{
+    public async Task ExecuteAsync(string name)
+    {
+        // basic guard rails
+        var existing = await repository.GetByNameAsync(name);
+        if (existing != null)
+            throw new InvalidOperationException($"Router '{name}' already exists.");
+
+        var RouterResource = new Router
+        {
+            Name = name
+        };
+
+        await repository.AddAsync(RouterResource);
+    }
+}

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

@@ -0,0 +1,14 @@
+using RackPeek.Domain.Resources.Hardware.Models;
+
+namespace RackPeek.Domain.Resources.Hardware.Routers;
+
+public class DeleteRouterUseCase(IHardwareRepository repository) : IUseCase
+{
+    public async Task ExecuteAsync(string name)
+    {
+        if (await repository.GetByNameAsync(name) is not Router hardware)
+            throw new InvalidOperationException($"Router '{name}' not found.");
+
+        await repository.DeleteAsync(name);
+    }
+}

+ 54 - 0
RackPeek.Domain/Resources/Hardware/Routers/DescribeRouterUseCase.cs

@@ -0,0 +1,54 @@
+using RackPeek.Domain.Resources.Hardware.Models;
+
+namespace RackPeek.Domain.Resources.Hardware.Routers;
+
+public record RouterDescription(
+    string Name,
+    string? Model,
+    bool? Managed,
+    bool? Poe,
+    int TotalPorts,
+    int TotalSpeedGb,
+    string PortSummary
+);
+
+public class DescribeRouterUseCase(IHardwareRepository repository) : IUseCase
+{
+    public async Task<RouterDescription?> ExecuteAsync(string name)
+    {
+        var RouterResource = await repository.GetByNameAsync(name) as Router;
+        if (RouterResource == null)
+            return null;
+
+        // If no ports exist, return defaults
+        var ports = RouterResource.Ports ?? new List<Port>();
+
+        // Total ports count
+        var totalPorts = ports.Sum(p => p.Count ?? 0);
+
+        // Total speed (sum of each port speed * count)
+        var totalSpeedGb = ports.Sum(p => (p.Speed ?? 0) * (p.Count ?? 0));
+
+        // Build a port summary string
+        var portGroups = ports
+            .GroupBy(p => p.Type ?? "Unknown")
+            .Select(g =>
+            {
+                var count = g.Sum(x => x.Count ?? 0);
+                var speed = g.Sum(x => (x.Speed ?? 0) * (x.Count ?? 0));
+                return $"{g.Key}: {count} ports ({speed} Gb total)";
+            });
+
+        var portSummary = string.Join(", ", portGroups);
+
+        return new RouterDescription(
+            RouterResource.Name,
+            RouterResource.Model,
+            RouterResource.Managed,
+            RouterResource.Poe,
+            totalPorts,
+            totalSpeedGb,
+            portSummary
+        );
+    }
+}

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

@@ -0,0 +1,12 @@
+using RackPeek.Domain.Resources.Hardware.Models;
+
+namespace RackPeek.Domain.Resources.Hardware.Routers;
+
+public class GetRouterUseCase(IHardwareRepository repository) : IUseCase
+{
+    public async Task<Router?> ExecuteAsync(string name)
+    {
+        var hardware = await repository.GetByNameAsync(name);
+        return hardware as Router;
+    }
+}

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

@@ -0,0 +1,12 @@
+using RackPeek.Domain.Resources.Hardware.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();
+    }
+}

+ 58 - 0
RackPeek.Domain/Resources/Hardware/Routers/RouterHardwareReport.cs

@@ -0,0 +1,58 @@
+using RackPeek.Domain.Resources.Hardware.Models;
+
+namespace RackPeek.Domain.Resources.Hardware.Routers;
+
+public record RouterHardwareReport(
+    IReadOnlyList<RouterHardwareRow> Routers
+);
+
+public record RouterHardwareRow(
+    string Name,
+    string Model,
+    bool Managed,
+    bool Poe,
+    int TotalPorts,
+    int MaxPortSpeedGb,
+    string PortSummary
+);
+
+public class RouterHardwareReportUseCase(IHardwareRepository repository) : IUseCase
+{
+    public async Task<RouterHardwareReport> ExecuteAsync()
+    {
+        var hardware = await repository.GetAllAsync();
+        var Routers = hardware.OfType<Router>();
+
+        var rows = Routers.Select(sw =>
+        {
+            var totalPorts = sw.Ports?.Sum(p => p.Count ?? 0) ?? 0;
+
+            var maxSpeed = sw.Ports?
+                .Max(p => p.Speed ?? 0) ?? 0;
+
+            var portSummary = sw.Ports == null
+                ? "Unknown"
+                : string.Join(", ",
+                    sw.Ports
+                        .GroupBy(p => p.Speed ?? 0)
+                        .OrderBy(g => g.Key)
+                        .Select(g =>
+                        {
+                            var count = g.Sum(p => p.Count ?? 0);
+                            return $"{count}×{g.Key}G";
+                        }));
+
+            return new RouterHardwareRow(
+                sw.Name,
+                sw.Model ?? "Unknown",
+                sw.Managed ?? false,
+                sw.Poe ?? false,
+                totalPorts,
+                maxSpeed,
+                portSummary
+            );
+        }).ToList();
+
+        return new RouterHardwareReport(rows);
+    }
+}

+ 29 - 0
RackPeek.Domain/Resources/Hardware/Routers/UpdateRouterUseCase.cs

@@ -0,0 +1,29 @@
+using RackPeek.Domain.Resources.Hardware.Models;
+
+namespace RackPeek.Domain.Resources.Hardware.Routers;
+
+public class UpdateRouterUseCase(IHardwareRepository repository) : IUseCase
+{
+    public async Task ExecuteAsync(
+        string name,
+        string? model = null,
+        bool? managed = null,
+        bool? poe = null
+    )
+    {
+        var RouterResource = await repository.GetByNameAsync(name) as Router;
+        if (RouterResource == null)
+            throw new InvalidOperationException($"Router '{name}' not found.");
+
+        if (!string.IsNullOrWhiteSpace(model))
+            RouterResource.Model = model;
+
+        if (managed.HasValue)
+            RouterResource.Managed = managed.Value;
+
+        if (poe.HasValue)
+            RouterResource.Poe = poe.Value;
+
+        await repository.UpdateAsync(RouterResource);
+    }
+}

+ 1 - 3
RackPeek.Web/Components/AccessPoints/AccessPointCardComponent.razor

@@ -1,5 +1,4 @@
 @using RackPeek.Domain.Resources.Hardware.Models
-
 <div class="border border-zinc-800 rounded p-4 bg-zinc-900">
     <div class="flex justify-between items-center mb-3">
         <div class="text-zinc-100">
@@ -30,6 +29,5 @@
 </div>
 
 @code {
-    [Parameter][EditorRequired]
-    public AccessPoint AccessPoint { get; set; } = default!;
+    [Parameter] [EditorRequired] public AccessPoint AccessPoint { get; set; } = default!;
 }

+ 0 - 1
RackPeek.Web/Components/AccessPoints/AccessPointsListPage.razor

@@ -1,5 +1,4 @@
 @page "/accesspoints/list"
-@using RackPeek.Web.Components.Components
 
 <PageTitle>AccessPoints</PageTitle>
 

+ 6 - 6
RackPeek.Web/Components/Components/HardwareDependencyTreeComponent.razor

@@ -37,7 +37,7 @@ else
                                 var url = service.NetworkString();
                                 <NavLink href="@($"/resources/services/{service.Name}")" class="block">
                                     <div class="border border-zinc-800 rounded bg-zinc-900 p-2 hover:border-zinc-700">
-        
+
                                         <div class="text-zinc-200 text-sm">
                                             @service.Name
                                         </div>
@@ -45,10 +45,10 @@ else
                                         @{
                                             var srv = BuildServiceSubtitle(service);
                                         }
-                
+
 
                                         <div class="text-xs text-zinc-500 mt-1">
-                                            Service - 
+                                            Service -
                                             @if (!string.IsNullOrEmpty(srv))
                                             {
                                                 <a href="@url"
@@ -58,7 +58,6 @@ else
                                                    @onclick:stopPropagation>
                                                     @srv
                                                 </a>
-
                                             }
 
                                         </div>
@@ -82,8 +81,8 @@ else
 }
 
 @code {
-    [Parameter][EditorRequired] public HardwareDependencyTree? Tree { get; set; }
-    
+    [Parameter] [EditorRequired] public HardwareDependencyTree? Tree { get; set; }
+
     private static string? BuildServiceSubtitle(Service service)
     {
         var endpoint = service.NetworkString();
@@ -93,4 +92,5 @@ else
 
         return endpoint;
     }
+
 }

+ 10 - 11
RackPeek.Web/Components/Components/ResourceBreadCrumbComponent.razor

@@ -19,13 +19,11 @@
 </div>
 
 @code {
-    [Parameter][EditorRequired]
-    public ResourceType ResourceType { get; set; }
+    [Parameter] [EditorRequired] public ResourceType ResourceType { get; set; }
 
-    [Parameter][EditorRequired]
-    public string ResourceName { get; set; } = default!;
+    [Parameter] [EditorRequired] public string ResourceName { get; set; } = default!;
 
-    private List<Breadcrumb> Breadcrumbs { get; set; } = new();
+    private List<Breadcrumb> Breadcrumbs { get; } = new();
 
     protected override async Task OnParametersSetAsync()
     {
@@ -49,7 +47,7 @@
 
     private async Task BuildHardwarePath(string hardwareName)
     {
-        Breadcrumbs.Add(new(hardwareName, $"resources/hardware/{hardwareName}"));
+        Breadcrumbs.Add(new Breadcrumb(hardwareName, $"resources/hardware/{hardwareName}"));
     }
 
     private async Task BuildSystemPath(string systemName)
@@ -58,13 +56,13 @@
 
         if (system?.RunsOn is not null)
         {
-            Breadcrumbs.Add(new(
+            Breadcrumbs.Add(new Breadcrumb(
                 system.RunsOn,
                 $"resources/hardware/{system.RunsOn}"
             ));
         }
 
-        Breadcrumbs.Add(new(
+        Breadcrumbs.Add(new Breadcrumb(
             systemName,
             $"resources/systems/{systemName}"
         ));
@@ -80,23 +78,24 @@
 
             if (system?.RunsOn is not null)
             {
-                Breadcrumbs.Add(new(
+                Breadcrumbs.Add(new Breadcrumb(
                     system.RunsOn,
                     $"resources/hardware/{system.RunsOn}"
                 ));
             }
 
-            Breadcrumbs.Add(new(
+            Breadcrumbs.Add(new Breadcrumb(
                 service.RunsOn,
                 $"resources/systems/{service.RunsOn}"
             ));
         }
 
-        Breadcrumbs.Add(new(
+        Breadcrumbs.Add(new Breadcrumb(
             serviceName,
             $"resources/services/{serviceName}"
         ));
     }
 
     private record Breadcrumb(string Label, string Href);
+
 }

+ 1 - 3
RackPeek.Web/Components/Desktops/DesktopCardComponent.razor

@@ -1,5 +1,4 @@
 @using RackPeek.Domain.Resources.Hardware.Models
-
 <div class="border border-zinc-800 rounded p-4 bg-zinc-900">
     <div class="flex justify-between items-center mb-3">
         <div class="text-zinc-100">
@@ -82,6 +81,5 @@
 </div>
 
 @code {
-    [Parameter][EditorRequired]
-    public Desktop Desktop { get; set; } = default!;
+    [Parameter] [EditorRequired] public Desktop Desktop { get; set; } = default!;
 }

+ 0 - 1
RackPeek.Web/Components/Desktops/DesktopsListPage.razor

@@ -1,5 +1,4 @@
 @page "/desktops/list"
-@using RackPeek.Web.Components.Components
 
 <PageTitle>Desktops</PageTitle>
 

+ 8 - 5
RackPeek.Web/Components/Hardware/HardwareDetailsPage.razor

@@ -1,9 +1,9 @@
 @page "/resources/hardware/{HardwareName}"
 @using RackPeek.Domain.Resources.Hardware
 @using RackPeek.Domain.Resources.Hardware.Models
+@using RackPeek.Web.Components.AccessPoints
 @using RackPeek.Web.Components.Components
 @using RackPeek.Web.Components.Desktops
-@using RackPeek.Web.Components.AccessPoints
 @using RackPeek.Web.Components.Switches
 @inject IHardwareRepository HardwareRepository
 @inject GetHardwareSystemTreeUseCase GetHardwareSystemTreeUseCase
@@ -11,7 +11,7 @@
 
 <ResourceBreadCrumbComponent
     ResourceType="ResourceType.Hardware"
-    ResourceName="@HardwareName" />
+    ResourceName="@HardwareName"/>
 
 <div class="min-h-screen bg-zinc-950 text-zinc-200 font-mono p-6">
     @if (_hardware is null && !_loading)
@@ -31,13 +31,16 @@
         @if (_hardware is Server server)
         {
             <ServerCardComponent Server="server"/>
-        } else if (_hardware is Desktop desktop)
+        }
+        else if (_hardware is Desktop desktop)
         {
             <DesktopCardComponent Desktop="desktop"/>
-        }else if (_hardware is AccessPoint accessPoint)
+        }
+        else if (_hardware is AccessPoint accessPoint)
         {
             <AccessPointCardComponent AccessPoint="accessPoint"/>
-        }else if (_hardware is Switch _switch)
+        }
+        else if (_hardware is Switch _switch)
         {
             <SwitchCardComponent Switch="_switch"/>
         }

+ 5 - 5
RackPeek.Web/Components/Hardware/HardwareTreePage.razor

@@ -17,12 +17,12 @@
         </NavLink>
         <NavLink href="/desktops/list" class="hover:text-emerald-400" activeClass="text-emerald-400 font-semibold">
             Desktops
-        </NavLink>     
+        </NavLink>
         <NavLink href="/accesspoints/list" class="hover:text-emerald-400" activeClass="text-emerald-400 font-semibold">
             AccessPoints
         </NavLink>
     </nav>
-    
+
     @if (_tree is null)
     {
         <div class="text-zinc-500">loading tree…</div>
@@ -51,7 +51,7 @@
                         <li>
                             <!-- Hardware -->
                             <NavLink href="@($"/resources/hardware/{hardware.HardwareName}")" class="block">
-                               
+
                                 @if (hardware.Systems.Any())
                                 {
                                     <div class="text-zinc-100">
@@ -64,7 +64,7 @@
                                         @hardware.HardwareName
                                     </div>
                                 }
-               
+
                             </NavLink>
 
                             @if (hardware.Systems.Any())
@@ -87,7 +87,7 @@
                                                         └─ @system.SystemName
                                                     </div>
                                                 }
-                                 
+
                                             </NavLink>
 
                                             @if (system.Services.Any())

+ 2 - 1
RackPeek.Web/Components/Servers/AddServerComponent.razor

@@ -11,7 +11,7 @@
             class="flex-1 bg-zinc-950 border border-zinc-800 rounded px-3 py-2 text-sm text-zinc-200 placeholder-zinc-600 focus:outline-none focus:border-zinc-600"
             placeholder="server name"
             @bind="_name"
-            @bind:event="oninput" />
+            @bind:event="oninput"/>
 
         <button
             class="px-4 py-2 text-sm rounded bg-zinc-800 hover:bg-zinc-700 text-zinc-100 disabled:opacity-50"
@@ -64,4 +64,5 @@
             _isSubmitting = false;
         }
     }
+
 }

+ 1 - 1
RackPeek.Web/Components/Servers/ServerCardComponent.razor

@@ -80,5 +80,5 @@
 </div>
 
 @code {
-    [Parameter][EditorRequired] public TServer Server { get; set; } = default!;
+    [Parameter] [EditorRequired] public TServer Server { get; set; } = default!;
 }

+ 3 - 2
RackPeek.Web/Components/Servers/ServersListComponent.razor

@@ -7,7 +7,7 @@
 
 <div class="min-h-screen bg-zinc-950 text-zinc-200 font-mono p-6 space-y-6">
 
-    <AddServerComponent OnCreated="NavigateToNewResource" />
+    <AddServerComponent OnCreated="NavigateToNewResource"/>
 
     @if (_servers is null)
     {
@@ -23,7 +23,7 @@
             @foreach (var server in _servers.OrderBy(s => s.Name))
             {
                 <NavLink href="@($"/resources/hardware/{server.Name}")" class="block">
-                    <ServerCardComponent Server="server" />
+                    <ServerCardComponent Server="server"/>
                 </NavLink>
             }
         </div>
@@ -43,4 +43,5 @@
         Nav.NavigateTo($"/resources/hardware/{serverName}");
         return Task.CompletedTask;
     }
+
 }

+ 0 - 1
RackPeek.Web/Components/Servers/ServersListPage.razor

@@ -1,5 +1,4 @@
 @page "/servers/list"
-@using RackPeek.Web.Components.Components
 
 <PageTitle>Servers</PageTitle>
 <h1 class="text-lg text-zinc-100">

+ 3 - 3
RackPeek.Web/Components/Services/AddServiceComponent.razor

@@ -1,5 +1,4 @@
-@using RackPeek.Domain.Resources.Services
-@using RackPeek.Domain.Resources.Services.UseCases
+@using RackPeek.Domain.Resources.Services.UseCases
 @inject AddServiceUseCase AddService
 
 <div class="border border-zinc-800 rounded p-4 bg-zinc-900">
@@ -12,7 +11,7 @@
             class="flex-1 bg-zinc-950 border border-zinc-800 rounded px-3 py-2 text-sm text-zinc-200 placeholder-zinc-600 focus:outline-none focus:border-zinc-600"
             placeholder="service name"
             @bind="_name"
-            @bind:event="oninput" />
+            @bind:event="oninput"/>
 
         <button
             class="px-4 py-2 text-sm rounded bg-zinc-800 hover:bg-zinc-700 text-zinc-100 disabled:opacity-50"
@@ -65,4 +64,5 @@
             _isSubmitting = false;
         }
     }
+
 }

+ 1 - 1
RackPeek.Web/Components/Services/ServiceCardComponent.razor

@@ -113,7 +113,7 @@
 </div>
 
 @code {
-    [Parameter][EditorRequired] public TService Service { get; set; } = default!;
+    [Parameter] [EditorRequired] public TService Service { get; set; } = default!;
 
     [Parameter] public EventCallback<ServiceEditModel> OnSave { get; set; }
 

+ 1 - 1
RackPeek.Web/Components/Services/ServiceDetailsPage.razor

@@ -8,7 +8,7 @@
 
 <ResourceBreadCrumbComponent
     ResourceType="ResourceType.Service"
-    ResourceName="@ServiceName" />
+    ResourceName="@ServiceName"/>
 
 <div class="min-h-screen bg-zinc-950 text-zinc-200 font-mono p-6">
     @if (_service is null && !_loading)

+ 4 - 2
RackPeek.Web/Components/Services/ServicesListComponent.razor

@@ -7,8 +7,8 @@
 <PageTitle>Services</PageTitle>
 
 <div class="min-h-screen bg-zinc-950 text-zinc-200 font-mono p-6 space-y-6">
-    <AddServiceComponent OnCreated="NavigateToNewResource" />
-    
+    <AddServiceComponent OnCreated="NavigateToNewResource"/>
+
     @if (_services is null)
     {
         <div class="text-zinc-500">loading services…</div>
@@ -52,9 +52,11 @@
             edit.RunsOn
         );
     }
+
     private Task NavigateToNewResource(string serverName)
     {
         Nav.NavigateTo($"/resources/services/{serverName}");
         return Task.CompletedTask;
     }
+
 }

+ 0 - 1
RackPeek.Web/Components/Services/ServicesListPage.razor

@@ -1,5 +1,4 @@
 @page "/services/list"
-@using RackPeek.Web.Components.Components
 
 <PageTitle>Services</PageTitle>
 <h1 class="text-lg text-zinc-100">

+ 1 - 3
RackPeek.Web/Components/Switches/SwitchCardComponent.razor

@@ -1,5 +1,4 @@
 @using RackPeek.Domain.Resources.Hardware.Models
-
 <div class="border border-zinc-800 rounded p-4 bg-zinc-900">
     <div class="flex justify-between items-center mb-3">
         <div class="text-zinc-100">
@@ -54,6 +53,5 @@
 </div>
 
 @code {
-    [Parameter][EditorRequired]
-    public Switch Switch { get; set; } = default!;
+    [Parameter] [EditorRequired] public Switch Switch { get; set; } = default!;
 }

+ 2 - 2
RackPeek.Web/Components/Switches/SwitchListComponent.razor

@@ -1,5 +1,5 @@
-@using RackPeek.Domain.Resources.Hardware.Switches
-@using RackPeek.Domain.Resources.Hardware.Models
+@using RackPeek.Domain.Resources.Hardware.Models
+@using RackPeek.Domain.Resources.Hardware.Switches
 @inject GetSwitchesUseCase GetSwitches
 
 <PageTitle>Switches</PageTitle>

+ 0 - 1
RackPeek.Web/Components/Switches/SwitchListPage.razor

@@ -1,5 +1,4 @@
 @page "/switches/list"
-@using RackPeek.Web.Components.Components
 
 <PageTitle>Switches</PageTitle>
 

+ 2 - 1
RackPeek.Web/Components/Systems/AddSystemComponent.razor

@@ -11,7 +11,7 @@
             class="flex-1 bg-zinc-950 border border-zinc-800 rounded px-3 py-2 text-sm text-zinc-200 placeholder-zinc-600 focus:outline-none focus:border-zinc-600"
             placeholder="system resource name"
             @bind="_name"
-            @bind:event="oninput" />
+            @bind:event="oninput"/>
 
         <button
             class="px-4 py-2 text-sm rounded bg-zinc-800 hover:bg-zinc-700 text-zinc-100 disabled:opacity-50"
@@ -64,4 +64,5 @@
             _isSubmitting = false;
         }
     }
+
 }

+ 2 - 3
RackPeek.Web/Components/Systems/SystemCardComponent.razor

@@ -1,5 +1,4 @@
-@using RackPeek.Web.Components.Components
-@typeparam TSystem where TSystem : RackPeek.Domain.Resources.SystemResources.SystemResource
+@typeparam TSystem where TSystem : RackPeek.Domain.Resources.SystemResources.SystemResource
 
 <div class="border border-zinc-800 rounded p-4 bg-zinc-900">
     <div class="flex justify-between items-center mb-3">
@@ -124,7 +123,7 @@
 </div>
 
 @code {
-    [Parameter][EditorRequired] public TSystem System { get; set; } = default!;
+    [Parameter] [EditorRequired] public TSystem System { get; set; } = default!;
 
     [Parameter] public EventCallback<SystemEditModel> OnSave { get; set; }
 

+ 8 - 11
RackPeek.Web/Components/Systems/SystemDependencyTreeComponent.razor

@@ -1,6 +1,5 @@
 @using RackPeek.Domain.Resources.Hardware
 @using RackPeek.Domain.Resources.Services
-@using RackPeek.Domain.Resources.Services.UseCases
 @if (Tree is null)
 {
     <div class="text-zinc-500 text-sm">
@@ -18,7 +17,7 @@ else
                 var url = service.NetworkString();
                 <NavLink href="@($"/resources/services/{service.Name}")" class="block">
                     <div class="border border-zinc-800 rounded bg-zinc-900 p-2 hover:border-zinc-700">
-        
+
                         <div class="text-zinc-200 text-sm">
                             @service.Name
                         </div>
@@ -26,10 +25,10 @@ else
                         @{
                             var srv = BuildServiceSubtitle(service);
                         }
-                
+
 
                         <div class="text-xs text-zinc-500 mt-1">
-                            Service - 
+                            Service -
                             @if (!string.IsNullOrEmpty(srv))
                             {
                                 <a href="@url"
@@ -39,16 +38,13 @@ else
                                    @onclick:stopPropagation>
                                     @srv
                                 </a>
-
                             }
 
                         </div>
 
-                            </div>
+                    </div>
                 </NavLink>
-
             }
-
         }
         else
         {
@@ -61,9 +57,9 @@ else
 }
 
 @code {
-    [Parameter][EditorRequired] public SystemDependencyTree? Tree { get; set; }
-    
-    
+    [Parameter] [EditorRequired] public SystemDependencyTree? Tree { get; set; }
+
+
     private static string? BuildServiceSubtitle(Service service)
     {
         var endpoint = service.NetworkString();
@@ -73,4 +69,5 @@ else
 
         return endpoint;
     }
+
 }

+ 1 - 1
RackPeek.Web/Components/Systems/SystemsDetailsPage.razor

@@ -10,7 +10,7 @@
 
 <ResourceBreadCrumbComponent
     ResourceType="ResourceType.System"
-    ResourceName="@SystemName" />
+    ResourceName="@SystemName"/>
 
 <div class="min-h-screen bg-zinc-950 text-zinc-200 font-mono p-6">
     @if (_system is null && !_loading)

+ 5 - 4
RackPeek.Web/Components/Systems/SystemsListComponent.razor

@@ -5,9 +5,9 @@
 @inject NavigationManager Nav
 
 <div class="min-h-screen bg-zinc-950 text-zinc-200 font-mono p-6 space-y-6">
-    
-    <AddSystemComponent OnCreated="NavigateToNewResource" />
-    
+
+    <AddSystemComponent OnCreated="NavigateToNewResource"/>
+
     @if (_systems is null)
     {
         <div class="text-zinc-500">loading systems…</div>
@@ -27,7 +27,6 @@
                         <SystemCardComponent System="systemResource" OnSave="UpdateSystem"/>
                     </NavLink>
                 }
-      
             }
         </div>
     }
@@ -52,9 +51,11 @@
             edit.RunsOn
         );
     }
+
     private Task NavigateToNewResource(string serverName)
     {
         Nav.NavigateTo($"/resources/systems/{serverName}");
         return Task.CompletedTask;
     }
+
 }

+ 0 - 1
RackPeek.Web/Components/Systems/SystemsListPage.razor

@@ -1,5 +1,4 @@
 @page "/systems/list"
-@using RackPeek.Web.Components.Components
 
 <PageTitle>Systems</PageTitle>
 

+ 28 - 28
RackPeek.Web/RackPeek.Web.csproj

@@ -12,37 +12,37 @@
     </ItemGroup>
 
     <ItemGroup>
-      <_ContentIncludedByDefault Remove="Components\Components\Desktops\DesktopCardComponent.razor" />
-      <_ContentIncludedByDefault Remove="Components\Components\Desktops\DesktopsListComponent.razor" />
-      <_ContentIncludedByDefault Remove="Components\FireWalls\FireWallCardComponent.razor" />
-      <_ContentIncludedByDefault Remove="Components\FireWalls\FireWallsListComponent.razor" />
-      <_ContentIncludedByDefault Remove="Components\FireWalls\FireWallsListPage.razor" />
-      <_ContentIncludedByDefault Remove="Components\Laptops\LaptopCardComponent.razor" />
-      <_ContentIncludedByDefault Remove="Components\Laptops\LaptopsListComponent.razor" />
-      <_ContentIncludedByDefault Remove="Components\Laptops\LaptopsListPage.razor" />
-      <_ContentIncludedByDefault Remove="Components\Routers\RouterCardComponent.razor" />
-      <_ContentIncludedByDefault Remove="Components\Routers\RoutersListComponent.razor" />
-      <_ContentIncludedByDefault Remove="Components\Routers\RoutersListPage.razor" />
-      <_ContentIncludedByDefault Remove="Components\Servers\Components\HardwareDependencyTreeComponent.razor" />
-      <_ContentIncludedByDefault Remove="Components\Servers\Components\ResourceBreadCrumbComponent.razor" />
-      <_ContentIncludedByDefault Remove="Components\Servers\Components\ServerCardComponent.razor" />
-      <_ContentIncludedByDefault Remove="Components\Servers\Components\ServersListComponent.razor" />
-      <_ContentIncludedByDefault Remove="Components\Servers\Components\ServiceCardComponent.razor" />
-      <_ContentIncludedByDefault Remove="Components\Servers\Components\ServicesListComponent.razor" />
-      <_ContentIncludedByDefault Remove="Components\Servers\Components\SystemCardComponent.razor" />
-      <_ContentIncludedByDefault Remove="Components\Servers\Components\SystemDependencyTreeComponent.razor" />
-      <_ContentIncludedByDefault Remove="Components\Servers\Components\SystemsListComponent.razor" />
+        <_ContentIncludedByDefault Remove="Components\Components\Desktops\DesktopCardComponent.razor"/>
+        <_ContentIncludedByDefault Remove="Components\Components\Desktops\DesktopsListComponent.razor"/>
+        <_ContentIncludedByDefault Remove="Components\FireWalls\FireWallCardComponent.razor"/>
+        <_ContentIncludedByDefault Remove="Components\FireWalls\FireWallsListComponent.razor"/>
+        <_ContentIncludedByDefault Remove="Components\FireWalls\FireWallsListPage.razor"/>
+        <_ContentIncludedByDefault Remove="Components\Laptops\LaptopCardComponent.razor"/>
+        <_ContentIncludedByDefault Remove="Components\Laptops\LaptopsListComponent.razor"/>
+        <_ContentIncludedByDefault Remove="Components\Laptops\LaptopsListPage.razor"/>
+        <_ContentIncludedByDefault Remove="Components\Routers\RouterCardComponent.razor"/>
+        <_ContentIncludedByDefault Remove="Components\Routers\RoutersListComponent.razor"/>
+        <_ContentIncludedByDefault Remove="Components\Routers\RoutersListPage.razor"/>
+        <_ContentIncludedByDefault Remove="Components\Servers\Components\HardwareDependencyTreeComponent.razor"/>
+        <_ContentIncludedByDefault Remove="Components\Servers\Components\ResourceBreadCrumbComponent.razor"/>
+        <_ContentIncludedByDefault Remove="Components\Servers\Components\ServerCardComponent.razor"/>
+        <_ContentIncludedByDefault Remove="Components\Servers\Components\ServersListComponent.razor"/>
+        <_ContentIncludedByDefault Remove="Components\Servers\Components\ServiceCardComponent.razor"/>
+        <_ContentIncludedByDefault Remove="Components\Servers\Components\ServicesListComponent.razor"/>
+        <_ContentIncludedByDefault Remove="Components\Servers\Components\SystemCardComponent.razor"/>
+        <_ContentIncludedByDefault Remove="Components\Servers\Components\SystemDependencyTreeComponent.razor"/>
+        <_ContentIncludedByDefault Remove="Components\Servers\Components\SystemsListComponent.razor"/>
     </ItemGroup>
 
     <ItemGroup>
-      <AdditionalFiles Include="Components\Components\HardwareDependencyTreeComponent.razor" />
-      <AdditionalFiles Include="Components\Components\ServerCardComponent.razor" />
-      <AdditionalFiles Include="Components\Components\ServersListComponent.razor" />
-      <AdditionalFiles Include="Components\Components\ServiceCardComponent.razor" />
-      <AdditionalFiles Include="Components\Components\ServicesListComponent.razor" />
-      <AdditionalFiles Include="Components\Components\SystemCardComponent.razor" />
-      <AdditionalFiles Include="Components\Components\SystemDependencyTreeComponent.razor" />
-      <AdditionalFiles Include="Components\Components\SystemsListComponent.razor" />
+        <AdditionalFiles Include="Components\Components\HardwareDependencyTreeComponent.razor"/>
+        <AdditionalFiles Include="Components\Components\ServerCardComponent.razor"/>
+        <AdditionalFiles Include="Components\Components\ServersListComponent.razor"/>
+        <AdditionalFiles Include="Components\Components\ServiceCardComponent.razor"/>
+        <AdditionalFiles Include="Components\Components\ServicesListComponent.razor"/>
+        <AdditionalFiles Include="Components\Components\SystemCardComponent.razor"/>
+        <AdditionalFiles Include="Components\Components\SystemDependencyTreeComponent.razor"/>
+        <AdditionalFiles Include="Components\Components\SystemsListComponent.razor"/>
     </ItemGroup>
 
 </Project>

+ 635 - 635
RackPeek.Web/config copy/Services.yaml

@@ -1,636 +1,636 @@
 resources:
-- kind: Service
-  network:
-    ip: 192.168.0.10
-    port: 8096
-    protocol: TCP
-    url: http://jellyfin.lan:8096
-  runsOn: docker-host
-  name: jellyfin
-  tags: 
-- kind: Service
-  network:
-    ip: 192.168.0.11
-    port: 32400
-    protocol: TCP
-    url: http://plex.lan:32400
-  runsOn: proxmox-host
-  name: plex
-  tags: 
-- kind: Service
-  network:
-    ip: 192.168.1.20
-    port: 8123
-    protocol: TCP
-    url: http://ha.lan:8123
-  runsOn: k8s-node-1
-  name: home-assistant
-  tags: 
-- kind: Service
-  network:
-    ip: 192.168.1.2
-    port: 53
-    protocol: UDP
-    url: http://pihole.lan/admin
-  runsOn: baremetal-rpi4
-  name: pihole
-  tags: 
-- kind: Service
-  network:
-    ip: 192.168.1.5
-    port: 8443
-    protocol: TCP
-    url: https://unifi.lan:8443
-  runsOn: vm-cluster-1
-  name: unifi-controller
-  tags: 
-- kind: Service
-  network:
-    ip: 10.0.0.15
-    port: 8384
-    protocol: TCP
-    url: http://sync.internal:8384
-  runsOn: docker-host
-  name: syncthing
-  tags: 
-- kind: Service
-  network:
-    ip: 10.0.0.20
-    port: 3000
-    protocol: TCP
-    url: http://grafana.internal:3000
-  runsOn: monitoring-node
-  name: grafana
-  tags: 
-- kind: Service
-  network:
-    ip: 10.0.0.21
-    port: 9090
-    protocol: TCP
-    url: http://prometheus.internal:9090
-  runsOn: monitoring-node
-  name: prometheus
-  tags: 
-- kind: Service
-  network:
-    ip: 10.0.0.22
-    port: 3100
-    protocol: TCP
-    url: http://loki.internal:3100
-  runsOn: monitoring-node
-  name: loki
-  tags: 
-- kind: Service
-  network:
-    ip: 172.16.0.10
-    port: 9000
-    protocol: TCP
-    url: http://minio.storage:9000
-  runsOn: storage-node-1
-  name: minio
-  tags: 
-- kind: Service
-  network:
-    ip: 172.16.0.11
-    port: 443
-    protocol: TCP
-    url: https://nextcloud.storage
-  runsOn: storage-node-2
-  name: nextcloud
-  tags: 
-- kind: Service
-  network:
-    ip: 192.168.0.30
-    port: 8081
-    protocol: TCP
-    url: http://vault.lan:8081
-  runsOn: docker-host
-  name: vaultwarden
-  tags: 
-- kind: Service
-  network:
-    ip: 192.168.0.2
-    port: 80
-    protocol: TCP
-    url: http://traefik.lan
-  runsOn: k8s-node-1
-  name: traefik
-  tags: 
-- kind: Service
-  network:
-    ip: 192.168.0.3
-    port: 443
-    protocol: TCP
-    url: https://proxy.lan
-  runsOn: docker-host
-  name: nginx-reverse-proxy
-  tags: 
-- kind: Service
-  network:
-    ip: 192.168.0.40
-    port: 8080
-    protocol: TCP
-    url: http://torrent.lan:8080
-  runsOn: proxmox-host
-  name: qbittorrent
-  tags: 
-- kind: Service
-  network:
-    ip: 192.168.0.41
-    port: 7878
-    protocol: TCP
-    url: http://radarr.lan:7878
-  runsOn: docker-host
-  name: radarr
-  tags: 
-- kind: Service
-  network:
-    ip: 192.168.0.43
-    port: 9696
-    protocol: TCP
-    url: http://prowlarr.lan:9696
-  runsOn: docker-host
-  name: prowlarr
-  tags: 
-- kind: Service
-  network:
-    ip: 192.168.0.43
-    port: 9696
-    protocol: TCP
-    url: http://prowlarr.lan:9696
-  runsOn: docker-host
-  name: prowlarr
-  tags: 
-- kind: Service
-  network:
-    ip: 192.168.0.44
-    port: 8085
-    protocol: TCP
-    url: http://sabnzbd.lan:8085
-  runsOn: docker-host
-  name: sabnzbd
-  tags: 
-- kind: Service
-  network:
-    ip: 192.168.1.31
-    port: 1883
-    protocol: TCP
-    url: mqtt://mqtt.lan:1883
-  runsOn: docker-host
-  name: mosquitto-mqtt
-  tags: 
-- kind: Service
-  network:
-    ip: 192.168.1.32
-    port: 8080
-    protocol: TCP
-    url: http://z2m.lan:8080
-  runsOn: docker-host
-  name: zigbee2mqtt
-  tags: 
-- kind: Service
-  network:
-    ip: 10.0.1.10
-    port: 5432
-    protocol: TCP
-    url: postgres://db.internal:5432
-  runsOn: db-node-1
-  name: postgres-main
-  tags: 
-- kind: Service
-  network:
-    ip: 10.0.1.11
-    port: 3306
-    protocol: TCP
-    url: mysql://mariadb.internal:3306
-  runsOn: db-node-2
-  name: mariadb
-  tags: 
-- kind: Service
-  network:
-    ip: 10.0.1.12
-    port: 6379
-    protocol: TCP
-    url: redis://redis.internal:6379
-  runsOn: cache-node
-  name: redis-cache
-  tags: 
-- kind: Service
-  network:
-    ip: 10.0.2.10
-    port: 9200
-    protocol: TCP
-    url: http://es.internal:9200
-  runsOn: search-node
-  name: elasticsearch
-  tags: 
-- kind: Service
-  network:
-    ip: 10.0.2.11
-    port: 5601
-    protocol: TCP
-    url: http://kibana.internal:5601
-  runsOn: search-node
-  name: kibana
-  tags: 
-- kind: Service
-  network:
-    ip: 192.168.0.50
-    port: 3001
-    protocol: TCP
-    url: http://uptime.lan:3001
-  runsOn: docker-host
-  name: uptime-kuma
-  tags: 
-- kind: Service
-  network:
-    ip: 192.168.1.100
-    port: 51820
-    protocol: UDP
-    url: wg://vpn.lan
-  runsOn: baremetal-rpi4
-  name: wireguard-vpn
-  tags: 
-- kind: Service
-  network:
-    ip: 192.168.1.101
-    port: 1194
-    protocol: UDP
-    url: ovpn://openvpn.lan
-  runsOn: vm-cluster-2
-  name: openvpn
-  tags: 
-- kind: Service
-  network:
-    ip: 10.0.3.10
-    port: 443
-    protocol: TCP
-    url: https://gitlab.internal
-  runsOn: dev-node-1
-  name: gitlab
-  tags: 
-- kind: Service
-  network:
-    ip: 10.0.3.11
-    port: 3000
-    protocol: TCP
-    url: http://gitea.internal:3000
-  runsOn: dev-node-2
-  name: gitea
-  tags: 
-- kind: Service
-  network:
-    ip: 10.0.3.12
-    port: 8080
-    protocol: TCP
-    url: http://drone.internal:8080
-  runsOn: dev-node-2
-  name: drone-ci
-  tags: 
-- kind: Service
-  network:
-    ip: 10.0.3.13
-    port: 5000
-    protocol: TCP
-    url: http://harbor.internal:5000
-  runsOn: dev-node-3
-  name: harbor-registry
-  tags: 
-- kind: Service
-  network:
-    ip: 10.0.4.1
-    port: 6443
-    protocol: TCP
-    url: https://k8s-api.internal:6443
-  runsOn: k8s-control-plane
-  name: kubernetes-api
-  tags: 
-- kind: Service
-  network:
-    ip: 10.0.4.20
-    port: 9500
-    protocol: TCP
-    url: http://longhorn.internal:9500
-  runsOn: k8s-node-3
-  name: longhorn-ui
-  tags: 
-- kind: Service
-  network:
-    ip: 10.0.4.21
-    port: 8443
-    protocol: TCP
-    url: https://ceph.internal:8443
-  runsOn: k8s-node-3
-  name: rook-ceph-dashboard
-  tags: 
-- kind: Service
-  network:
-    ip: 192.168.0.60
-    port: 445
-    protocol: TCP
-    url: smb://fileserver.lan
-  runsOn: storage-node-1
-  name: samba-fileserver
-  tags: 
-- kind: Service
-  network:
-    ip: 192.168.0.61
-    port: 2049
-    protocol: TCP
-    url: nfs://nfs.lan
-  runsOn: dell-c6400-node01
-  name: nfs-server
-  tags: 
-- kind: Service
-  network:
-    ip: 172.16.1.10
-    port: 3260
-    protocol: TCP
-    url: iscsi://iscsi.storage
-  runsOn: storage-node-3
-  name: iscsi-target
-  tags: 
-- kind: Service
-  network:
-    ip: 192.168.0.70
-    port: 8083
-    protocol: TCP
-    url: http://books.lan:8083
-  runsOn: docker-host
-  name: calibre-web
-  tags: 
-- kind: Service
-  network:
-    ip: 192.168.0.71
-    port: 8000
-    protocol: TCP
-    url: http://docs.lan:8000
-  runsOn: dell-c6400-node01
-  name: paperless-ngx
-  tags: 
-- kind: Service
-  network:
-    ip: 10.0.5.10
-    port: 389
-    protocol: TCP
-    url: ldap://ldap.internal:389
-  runsOn: dell-c6400-node01
-  name: openldap
-  tags: 
-- kind: Service
-  network:
-    ip: 10.0.5.10
-    port: 389
-    protocol: TCP
-    url: ldap://ldap.internal:389
-  runsOn: dell-c6400-node01
-  name: openldap
-  tags: 
-- kind: Service
-  network:
-    ip: 192.168.1.50
-    port: 123
-    protocol: UDP
-    url: ntp://ntp.lan
-  runsOn: baremetal-rpi3
-  name: ntp-server
-  tags: 
-- kind: Service
-  network:
-    ip: 10.0.6.10
-    port: 514
-    protocol: UDP
-    url: syslog://syslog.internal
-  runsOn: monitoring-node
-  name: syslog-server
-  tags: 
-- kind: Service
-  network:
-    ip: 192.168.1.1
-    port: 67
-    protocol: UDP
-    url: dhcp://dhcp.lan
-  runsOn: router-appliance
-  name: dhcp-server
-  tags: 
-- kind: Service
-  network:
-    ip: 10.0.7.10
-    port: 53
-    protocol: UDP
-    url: dns://dns.internal
-  runsOn: infra-node
-  name: bind-dns
-  tags: 
-- kind: Service
-  network:
-    ip: 10.0.7.11
-    port: 8200
-    protocol: TCP
-    url: http://vault.internal:8200
-  runsOn: infra-node
-  name: vault
-  tags: 
-- kind: Service
-  network:
-    ip: 10.0.7.12
-    port: 8500
-    protocol: TCP
-    url: http://consul.internal:8500
-  runsOn: infra-node
-  name: consul
-  tags: 
-- kind: Service
-  network:
-    ip: 10.0.7.13
-    port: 4646
-    protocol: TCP
-    url: http://nomad.internal:4646
-  runsOn: infra-node
-  name: nomad
-  tags: 
-- kind: Service
-  network:
-    ip: 192.168.1.40
-    port: 8080
-    protocol: TCP
-    url: http://openhab.lan:8080
-  runsOn: k8s-node-2
-  name: openhab
-  tags: 
-- kind: Service
-  network:
-    ip: 192.168.1.41
-    port: 4000
-    protocol: TCP
-    url: http://mqtt-explorer.lan:4000
-  runsOn: docker-host
-  name: mqtt-explorer
-  tags: 
-- kind: Service
-  network:
-    ip: 10.0.8.10
-    port: 8086
-    protocol: TCP
-    url: http://influx.internal:8086
-  runsOn: monitoring-node
-  name: influxdb
-  tags: 
-- kind: Service
-  network:
-    ip: 10.0.8.11
-    port: 8125
-    protocol: UDP
-    url: statsd://telegraf.internal
-  runsOn: monitoring-node
-  name: telegraf
-  tags: 
-- kind: Service
-  network:
-    ip: 192.168.0.80
-    port: 8080
-    protocol: TCP
-    url: http://speedtest.lan:8080
-  runsOn: docker-host
-  name: speedtest-tracker
-  tags: 
-- kind: Service
-  network:
-    ip: 192.168.0.81
-    port: 4533
-    protocol: TCP
-    url: http://music.lan:4533
-  runsOn: docker-host
-  name: navidrome
-  tags: 
-- kind: Service
-  network:
-    ip: 192.168.0.82
-    port: 2342
-    protocol: TCP
-    url: http://photos.lan:2342
-  runsOn: docker-host
-  name: photoprism
-  tags: 
-- kind: Service
-  network:
-    ip: 10.0.9.10
-    port: 53
-    protocol: UDP
-    url: dns://dnsdist.internal
-  runsOn: infra-node
-  name: dnsdist
-  tags: 
-- kind: Service
-  network:
-    ip: 10.0.9.11
-    port: 8081
-    protocol: TCP
-    url: http://pdns.internal:8081
-  runsOn: infra-node
-  name: powerdns
-  tags: 
-- kind: Service
-  network:
-    ip: 10.0.10.10
-    port: 8080
-    protocol: TCP
-    url: http://openproject.internal:8080
-  runsOn: dev-node-3
-  name: openproject
-  tags: 
-- kind: Service
-  network:
-    ip: 10.0.10.11
-    port: 8065
-    protocol: TCP
-    url: http://chat.internal:8065
-  runsOn: dev-node-3
-  name: mattermost
-  tags: 
-- kind: Service
-  network:
-    ip: 10.0.10.12
-    port: 3000
-    protocol: TCP
-    url: http://rocket.internal:3000
-  runsOn: dev-node-3
-  name: rocket-chat
-  tags: 
-- kind: Service
-  network:
-    ip: 192.168.0.4
-    port: 80801
-    protocol: TCP
-    url: http://immich.lan:8080
-  runsOn: proxmox-host
-  name: immich
-  tags: 
-- kind: Service
-  network:
-    ip: 192.168.1.3
-    port: 3002
-    protocol: TCP
-    url: http://adguard.lan:3002
-  runsOn: docker-host
-  name: adguard-home
-  tags: 
-- kind: Server
-  cpus: 
-  ram: 
-  drives: 
-  nics: 
-  gpus: 
-  ipmi: 
-  name: test
-  tags: 
-- kind: Server
-  cpus: 
-  ram: 
-  drives: 
-  nics: 
-  gpus: 
-  ipmi: 
-  name: mr-server
-  tags: 
-- kind: Server
-  cpus: 
-  ram: 
-  drives: 
-  nics: 
-  gpus: 
-  ipmi: 
-  name: new server
-  tags: 
-- kind: Server
-  cpus: 
-  ram: 
-  drives: 
-  nics: 
-  gpus: 
-  ipmi: 
-  name: new server
-  tags: 
-- kind: System
-  type: 
-  os: 
-  cores: 
-  ram: 
-  drives: 
-  runsOn: 
-  name: new-system
-  tags: 
-- kind: System
-  type: 
-  os: 
-  cores: 
-  ram: 
-  drives: 
-  runsOn: 
-  name: new-system
-  tags: 
-- kind: Service
-  network: 
-  runsOn: 
-  name: new-service
-  tags: 
+  - kind: Service
+    network:
+      ip: 192.168.0.10
+      port: 8096
+      protocol: TCP
+      url: http://jellyfin.lan:8096
+    runsOn: docker-host
+    name: jellyfin
+    tags:
+  - kind: Service
+    network:
+      ip: 192.168.0.11
+      port: 32400
+      protocol: TCP
+      url: http://plex.lan:32400
+    runsOn: proxmox-host
+    name: plex
+    tags:
+  - kind: Service
+    network:
+      ip: 192.168.1.20
+      port: 8123
+      protocol: TCP
+      url: http://ha.lan:8123
+    runsOn: k8s-node-1
+    name: home-assistant
+    tags:
+  - kind: Service
+    network:
+      ip: 192.168.1.2
+      port: 53
+      protocol: UDP
+      url: http://pihole.lan/admin
+    runsOn: baremetal-rpi4
+    name: pihole
+    tags:
+  - kind: Service
+    network:
+      ip: 192.168.1.5
+      port: 8443
+      protocol: TCP
+      url: https://unifi.lan:8443
+    runsOn: vm-cluster-1
+    name: unifi-controller
+    tags:
+  - kind: Service
+    network:
+      ip: 10.0.0.15
+      port: 8384
+      protocol: TCP
+      url: http://sync.internal:8384
+    runsOn: docker-host
+    name: syncthing
+    tags:
+  - kind: Service
+    network:
+      ip: 10.0.0.20
+      port: 3000
+      protocol: TCP
+      url: http://grafana.internal:3000
+    runsOn: monitoring-node
+    name: grafana
+    tags:
+  - kind: Service
+    network:
+      ip: 10.0.0.21
+      port: 9090
+      protocol: TCP
+      url: http://prometheus.internal:9090
+    runsOn: monitoring-node
+    name: prometheus
+    tags:
+  - kind: Service
+    network:
+      ip: 10.0.0.22
+      port: 3100
+      protocol: TCP
+      url: http://loki.internal:3100
+    runsOn: monitoring-node
+    name: loki
+    tags:
+  - kind: Service
+    network:
+      ip: 172.16.0.10
+      port: 9000
+      protocol: TCP
+      url: http://minio.storage:9000
+    runsOn: storage-node-1
+    name: minio
+    tags:
+  - kind: Service
+    network:
+      ip: 172.16.0.11
+      port: 443
+      protocol: TCP
+      url: https://nextcloud.storage
+    runsOn: storage-node-2
+    name: nextcloud
+    tags:
+  - kind: Service
+    network:
+      ip: 192.168.0.30
+      port: 8081
+      protocol: TCP
+      url: http://vault.lan:8081
+    runsOn: docker-host
+    name: vaultwarden
+    tags:
+  - kind: Service
+    network:
+      ip: 192.168.0.2
+      port: 80
+      protocol: TCP
+      url: http://traefik.lan
+    runsOn: k8s-node-1
+    name: traefik
+    tags:
+  - kind: Service
+    network:
+      ip: 192.168.0.3
+      port: 443
+      protocol: TCP
+      url: https://proxy.lan
+    runsOn: docker-host
+    name: nginx-reverse-proxy
+    tags:
+  - kind: Service
+    network:
+      ip: 192.168.0.40
+      port: 8080
+      protocol: TCP
+      url: http://torrent.lan:8080
+    runsOn: proxmox-host
+    name: qbittorrent
+    tags:
+  - kind: Service
+    network:
+      ip: 192.168.0.41
+      port: 7878
+      protocol: TCP
+      url: http://radarr.lan:7878
+    runsOn: docker-host
+    name: radarr
+    tags:
+  - kind: Service
+    network:
+      ip: 192.168.0.43
+      port: 9696
+      protocol: TCP
+      url: http://prowlarr.lan:9696
+    runsOn: docker-host
+    name: prowlarr
+    tags:
+  - kind: Service
+    network:
+      ip: 192.168.0.43
+      port: 9696
+      protocol: TCP
+      url: http://prowlarr.lan:9696
+    runsOn: docker-host
+    name: prowlarr
+    tags:
+  - kind: Service
+    network:
+      ip: 192.168.0.44
+      port: 8085
+      protocol: TCP
+      url: http://sabnzbd.lan:8085
+    runsOn: docker-host
+    name: sabnzbd
+    tags:
+  - kind: Service
+    network:
+      ip: 192.168.1.31
+      port: 1883
+      protocol: TCP
+      url: mqtt://mqtt.lan:1883
+    runsOn: docker-host
+    name: mosquitto-mqtt
+    tags:
+  - kind: Service
+    network:
+      ip: 192.168.1.32
+      port: 8080
+      protocol: TCP
+      url: http://z2m.lan:8080
+    runsOn: docker-host
+    name: zigbee2mqtt
+    tags:
+  - kind: Service
+    network:
+      ip: 10.0.1.10
+      port: 5432
+      protocol: TCP
+      url: postgres://db.internal:5432
+    runsOn: db-node-1
+    name: postgres-main
+    tags:
+  - kind: Service
+    network:
+      ip: 10.0.1.11
+      port: 3306
+      protocol: TCP
+      url: mysql://mariadb.internal:3306
+    runsOn: db-node-2
+    name: mariadb
+    tags:
+  - kind: Service
+    network:
+      ip: 10.0.1.12
+      port: 6379
+      protocol: TCP
+      url: redis://redis.internal:6379
+    runsOn: cache-node
+    name: redis-cache
+    tags:
+  - kind: Service
+    network:
+      ip: 10.0.2.10
+      port: 9200
+      protocol: TCP
+      url: http://es.internal:9200
+    runsOn: search-node
+    name: elasticsearch
+    tags:
+  - kind: Service
+    network:
+      ip: 10.0.2.11
+      port: 5601
+      protocol: TCP
+      url: http://kibana.internal:5601
+    runsOn: search-node
+    name: kibana
+    tags:
+  - kind: Service
+    network:
+      ip: 192.168.0.50
+      port: 3001
+      protocol: TCP
+      url: http://uptime.lan:3001
+    runsOn: docker-host
+    name: uptime-kuma
+    tags:
+  - kind: Service
+    network:
+      ip: 192.168.1.100
+      port: 51820
+      protocol: UDP
+      url: wg://vpn.lan
+    runsOn: baremetal-rpi4
+    name: wireguard-vpn
+    tags:
+  - kind: Service
+    network:
+      ip: 192.168.1.101
+      port: 1194
+      protocol: UDP
+      url: ovpn://openvpn.lan
+    runsOn: vm-cluster-2
+    name: openvpn
+    tags:
+  - kind: Service
+    network:
+      ip: 10.0.3.10
+      port: 443
+      protocol: TCP
+      url: https://gitlab.internal
+    runsOn: dev-node-1
+    name: gitlab
+    tags:
+  - kind: Service
+    network:
+      ip: 10.0.3.11
+      port: 3000
+      protocol: TCP
+      url: http://gitea.internal:3000
+    runsOn: dev-node-2
+    name: gitea
+    tags:
+  - kind: Service
+    network:
+      ip: 10.0.3.12
+      port: 8080
+      protocol: TCP
+      url: http://drone.internal:8080
+    runsOn: dev-node-2
+    name: drone-ci
+    tags:
+  - kind: Service
+    network:
+      ip: 10.0.3.13
+      port: 5000
+      protocol: TCP
+      url: http://harbor.internal:5000
+    runsOn: dev-node-3
+    name: harbor-registry
+    tags:
+  - kind: Service
+    network:
+      ip: 10.0.4.1
+      port: 6443
+      protocol: TCP
+      url: https://k8s-api.internal:6443
+    runsOn: k8s-control-plane
+    name: kubernetes-api
+    tags:
+  - kind: Service
+    network:
+      ip: 10.0.4.20
+      port: 9500
+      protocol: TCP
+      url: http://longhorn.internal:9500
+    runsOn: k8s-node-3
+    name: longhorn-ui
+    tags:
+  - kind: Service
+    network:
+      ip: 10.0.4.21
+      port: 8443
+      protocol: TCP
+      url: https://ceph.internal:8443
+    runsOn: k8s-node-3
+    name: rook-ceph-dashboard
+    tags:
+  - kind: Service
+    network:
+      ip: 192.168.0.60
+      port: 445
+      protocol: TCP
+      url: smb://fileserver.lan
+    runsOn: storage-node-1
+    name: samba-fileserver
+    tags:
+  - kind: Service
+    network:
+      ip: 192.168.0.61
+      port: 2049
+      protocol: TCP
+      url: nfs://nfs.lan
+    runsOn: dell-c6400-node01
+    name: nfs-server
+    tags:
+  - kind: Service
+    network:
+      ip: 172.16.1.10
+      port: 3260
+      protocol: TCP
+      url: iscsi://iscsi.storage
+    runsOn: storage-node-3
+    name: iscsi-target
+    tags:
+  - kind: Service
+    network:
+      ip: 192.168.0.70
+      port: 8083
+      protocol: TCP
+      url: http://books.lan:8083
+    runsOn: docker-host
+    name: calibre-web
+    tags:
+  - kind: Service
+    network:
+      ip: 192.168.0.71
+      port: 8000
+      protocol: TCP
+      url: http://docs.lan:8000
+    runsOn: dell-c6400-node01
+    name: paperless-ngx
+    tags:
+  - kind: Service
+    network:
+      ip: 10.0.5.10
+      port: 389
+      protocol: TCP
+      url: ldap://ldap.internal:389
+    runsOn: dell-c6400-node01
+    name: openldap
+    tags:
+  - kind: Service
+    network:
+      ip: 10.0.5.10
+      port: 389
+      protocol: TCP
+      url: ldap://ldap.internal:389
+    runsOn: dell-c6400-node01
+    name: openldap
+    tags:
+  - kind: Service
+    network:
+      ip: 192.168.1.50
+      port: 123
+      protocol: UDP
+      url: ntp://ntp.lan
+    runsOn: baremetal-rpi3
+    name: ntp-server
+    tags:
+  - kind: Service
+    network:
+      ip: 10.0.6.10
+      port: 514
+      protocol: UDP
+      url: syslog://syslog.internal
+    runsOn: monitoring-node
+    name: syslog-server
+    tags:
+  - kind: Service
+    network:
+      ip: 192.168.1.1
+      port: 67
+      protocol: UDP
+      url: dhcp://dhcp.lan
+    runsOn: router-appliance
+    name: dhcp-server
+    tags:
+  - kind: Service
+    network:
+      ip: 10.0.7.10
+      port: 53
+      protocol: UDP
+      url: dns://dns.internal
+    runsOn: infra-node
+    name: bind-dns
+    tags:
+  - kind: Service
+    network:
+      ip: 10.0.7.11
+      port: 8200
+      protocol: TCP
+      url: http://vault.internal:8200
+    runsOn: infra-node
+    name: vault
+    tags:
+  - kind: Service
+    network:
+      ip: 10.0.7.12
+      port: 8500
+      protocol: TCP
+      url: http://consul.internal:8500
+    runsOn: infra-node
+    name: consul
+    tags:
+  - kind: Service
+    network:
+      ip: 10.0.7.13
+      port: 4646
+      protocol: TCP
+      url: http://nomad.internal:4646
+    runsOn: infra-node
+    name: nomad
+    tags:
+  - kind: Service
+    network:
+      ip: 192.168.1.40
+      port: 8080
+      protocol: TCP
+      url: http://openhab.lan:8080
+    runsOn: k8s-node-2
+    name: openhab
+    tags:
+  - kind: Service
+    network:
+      ip: 192.168.1.41
+      port: 4000
+      protocol: TCP
+      url: http://mqtt-explorer.lan:4000
+    runsOn: docker-host
+    name: mqtt-explorer
+    tags:
+  - kind: Service
+    network:
+      ip: 10.0.8.10
+      port: 8086
+      protocol: TCP
+      url: http://influx.internal:8086
+    runsOn: monitoring-node
+    name: influxdb
+    tags:
+  - kind: Service
+    network:
+      ip: 10.0.8.11
+      port: 8125
+      protocol: UDP
+      url: statsd://telegraf.internal
+    runsOn: monitoring-node
+    name: telegraf
+    tags:
+  - kind: Service
+    network:
+      ip: 192.168.0.80
+      port: 8080
+      protocol: TCP
+      url: http://speedtest.lan:8080
+    runsOn: docker-host
+    name: speedtest-tracker
+    tags:
+  - kind: Service
+    network:
+      ip: 192.168.0.81
+      port: 4533
+      protocol: TCP
+      url: http://music.lan:4533
+    runsOn: docker-host
+    name: navidrome
+    tags:
+  - kind: Service
+    network:
+      ip: 192.168.0.82
+      port: 2342
+      protocol: TCP
+      url: http://photos.lan:2342
+    runsOn: docker-host
+    name: photoprism
+    tags:
+  - kind: Service
+    network:
+      ip: 10.0.9.10
+      port: 53
+      protocol: UDP
+      url: dns://dnsdist.internal
+    runsOn: infra-node
+    name: dnsdist
+    tags:
+  - kind: Service
+    network:
+      ip: 10.0.9.11
+      port: 8081
+      protocol: TCP
+      url: http://pdns.internal:8081
+    runsOn: infra-node
+    name: powerdns
+    tags:
+  - kind: Service
+    network:
+      ip: 10.0.10.10
+      port: 8080
+      protocol: TCP
+      url: http://openproject.internal:8080
+    runsOn: dev-node-3
+    name: openproject
+    tags:
+  - kind: Service
+    network:
+      ip: 10.0.10.11
+      port: 8065
+      protocol: TCP
+      url: http://chat.internal:8065
+    runsOn: dev-node-3
+    name: mattermost
+    tags:
+  - kind: Service
+    network:
+      ip: 10.0.10.12
+      port: 3000
+      protocol: TCP
+      url: http://rocket.internal:3000
+    runsOn: dev-node-3
+    name: rocket-chat
+    tags:
+  - kind: Service
+    network:
+      ip: 192.168.0.4
+      port: 80801
+      protocol: TCP
+      url: http://immich.lan:8080
+    runsOn: proxmox-host
+    name: immich
+    tags:
+  - kind: Service
+    network:
+      ip: 192.168.1.3
+      port: 3002
+      protocol: TCP
+      url: http://adguard.lan:3002
+    runsOn: docker-host
+    name: adguard-home
+    tags:
+  - kind: Server
+    cpus:
+    ram:
+    drives:
+    nics:
+    gpus:
+    ipmi:
+    name: test
+    tags:
+  - kind: Server
+    cpus:
+    ram:
+    drives:
+    nics:
+    gpus:
+    ipmi:
+    name: mr-server
+    tags:
+  - kind: Server
+    cpus:
+    ram:
+    drives:
+    nics:
+    gpus:
+    ipmi:
+    name: new server
+    tags:
+  - kind: Server
+    cpus:
+    ram:
+    drives:
+    nics:
+    gpus:
+    ipmi:
+    name: new server
+    tags:
+  - kind: System
+    type:
+    os:
+    cores:
+    ram:
+    drives:
+    runsOn:
+    name: new-system
+    tags:
+  - kind: System
+    type:
+    os:
+    cores:
+    ram:
+    drives:
+    runsOn:
+    name: new-system
+    tags:
+  - kind: Service
+    network:
+    runsOn:
+    name: new-service
+    tags: 

+ 207 - 207
RackPeek.Web/config copy/Systems.yaml

@@ -1,208 +1,208 @@
 resources:
-- kind: System
-  type: KubernetesNode
-  os: ubuntu
-  cores: 8
-  ram: 32
-  drives: 
-  runsOn: dell-c6400-node01
-  name: k8s-node-1
-  tags: 
-- kind: System
-  type: KubernetesNode
-  os: ubuntu
-  cores: 8
-  ram: 32
-  drives: 
-  runsOn: dell-c6400-node01
-  name: k8s-node-2
-  tags: 
-- kind: System
-  type: KubernetesNode
-  os: ubuntu
-  cores: 8
-  ram: 32
-  drives: 
-  runsOn: dell-c6400-node01
-  name: k8s-node-3
-  tags: 
-- kind: System
-  type: KubernetesControlPlane
-  os: ubuntu
-  cores: 4
-  ram: 16
-  drives: 
-  runsOn: dell-c6400-node01
-  name: k8s-control-plane
-  tags: 
-- kind: System
-  type: Monitoring
-  os: ubuntu
-  cores: 8
-  ram: 32
-  drives: 
-  runsOn: dell-c6400-node01
-  name: monitoring-node
-  tags: 
-- kind: System
-  type: Storage
-  os: truenas
-  cores: 8
-  ram: 64
-  drives: 
-  runsOn: dell-c6400-node01
-  name: storage-node-1
-  tags: 
-- kind: System
-  type: Storage
-  os: truenas
-  cores: 8
-  ram: 64
-  drives: 
-  runsOn: dell-c6400-node01
-  name: storage-node-2
-  tags: 
-- kind: System
-  type: Storage
-  os: truenas
-  cores: 8
-  ram: 64
-  drives: 
-  runsOn: dell-c6400-node01
-  name: storage-node-3
-  tags: 
-- kind: System
-  type: Database
-  os: ubuntu
-  cores: 8
-  ram: 32
-  drives: 
-  runsOn: dell-c6400-node01
-  name: db-node-1
-  tags: 
-- kind: System
-  type: Database
-  os: ubuntu
-  cores: 8
-  ram: 32
-  drives: 
-  runsOn: dell-c6400-node01
-  name: db-node-2
-  tags: 
-- kind: System
-  type: Cache
-  os: ubuntu
-  cores: 4
-  ram: 16
-  drives: 
-  runsOn: dell-c6400-node01
-  name: cache-node
-  tags: 
-- kind: System
-  type: Search
-  os: ubuntu
-  cores: 8
-  ram: 32
-  drives: 
-  runsOn: dell-c6400-node01
-  name: search-node
-  tags: 
-- kind: System
-  type: Development
-  os: ubuntu
-  cores: 4
-  ram: 16
-  drives: 
-  runsOn: dell-c6400-node01
-  name: dev-node-1
-  tags: 
-- kind: System
-  type: Development
-  os: ubuntu
-  cores: 4
-  ram: 16
-  drives: 
-  runsOn: dell-c6400-node01
-  name: dev-node-2
-  tags: 
-- kind: System
-  type: Development
-  os: ubuntu
-  cores: 6
-  ram: 24
-  drives: 
-  runsOn: dell-c6400-node01
-  name: dev-node-3
-  tags: 
-- kind: System
-  type: VirtualMachineCluster
-  os: proxmox
-  cores: 12
-  ram: 48
-  drives: 
-  runsOn: dell-c6400-node01
-  name: vm-cluster-1
-  tags: 
-- kind: System
-  type: BareMetal
-  os: raspbian
-  cores: 4
-  ram: 8
-  drives: 
-  runsOn: rack-edge
-  name: baremetal-rpi4
-  tags: 
-- kind: System
-  type: BareMetal
-  os: raspbian
-  cores: 4
-  ram: 4
-  drives: 
-  runsOn: rack-edge
-  name: baremetal-rpi3
-  tags: 
-- kind: System
-  type: Infrastructure
-  os: ubuntu
-  cores: 4
-  ram: 16
-  drives: 
-  runsOn: dell-c6400-node01
-  name: infra-node
-  tags: 
-- kind: System
-  type: NetworkAppliance
-  os: openwrt
-  cores: 2
-  ram: 2
-  drives: 
-  runsOn: network-rack
-  name: router-appliance
-  tags: 
-- kind: System
-  type: Hypervisor
-  os: proxmox
-  cores: 16
-  ram: 61
-  drives: 
-  runsOn: dell-c6400-node01
-  name: proxmox-host
-  tags: 
-- kind: System
-  type: ContainerHost
-  os: ubuntu
-  cores: 12
-  ram: 26
-  drives: 
-  runsOn: dell-c6400-node01
-  name: docker-host
-  tags: 
-- kind: System
-  type: VirtualMachineCluster
-  os: proxmox
-  cores: 13
-  ram: 44
-  drives: 
-  runsOn: dell-c6400-node01
-  name: vm-cluster-2
-  tags: 
+  - kind: System
+    type: KubernetesNode
+    os: ubuntu
+    cores: 8
+    ram: 32
+    drives:
+    runsOn: dell-c6400-node01
+    name: k8s-node-1
+    tags:
+  - kind: System
+    type: KubernetesNode
+    os: ubuntu
+    cores: 8
+    ram: 32
+    drives:
+    runsOn: dell-c6400-node01
+    name: k8s-node-2
+    tags:
+  - kind: System
+    type: KubernetesNode
+    os: ubuntu
+    cores: 8
+    ram: 32
+    drives:
+    runsOn: dell-c6400-node01
+    name: k8s-node-3
+    tags:
+  - kind: System
+    type: KubernetesControlPlane
+    os: ubuntu
+    cores: 4
+    ram: 16
+    drives:
+    runsOn: dell-c6400-node01
+    name: k8s-control-plane
+    tags:
+  - kind: System
+    type: Monitoring
+    os: ubuntu
+    cores: 8
+    ram: 32
+    drives:
+    runsOn: dell-c6400-node01
+    name: monitoring-node
+    tags:
+  - kind: System
+    type: Storage
+    os: truenas
+    cores: 8
+    ram: 64
+    drives:
+    runsOn: dell-c6400-node01
+    name: storage-node-1
+    tags:
+  - kind: System
+    type: Storage
+    os: truenas
+    cores: 8
+    ram: 64
+    drives:
+    runsOn: dell-c6400-node01
+    name: storage-node-2
+    tags:
+  - kind: System
+    type: Storage
+    os: truenas
+    cores: 8
+    ram: 64
+    drives:
+    runsOn: dell-c6400-node01
+    name: storage-node-3
+    tags:
+  - kind: System
+    type: Database
+    os: ubuntu
+    cores: 8
+    ram: 32
+    drives:
+    runsOn: dell-c6400-node01
+    name: db-node-1
+    tags:
+  - kind: System
+    type: Database
+    os: ubuntu
+    cores: 8
+    ram: 32
+    drives:
+    runsOn: dell-c6400-node01
+    name: db-node-2
+    tags:
+  - kind: System
+    type: Cache
+    os: ubuntu
+    cores: 4
+    ram: 16
+    drives:
+    runsOn: dell-c6400-node01
+    name: cache-node
+    tags:
+  - kind: System
+    type: Search
+    os: ubuntu
+    cores: 8
+    ram: 32
+    drives:
+    runsOn: dell-c6400-node01
+    name: search-node
+    tags:
+  - kind: System
+    type: Development
+    os: ubuntu
+    cores: 4
+    ram: 16
+    drives:
+    runsOn: dell-c6400-node01
+    name: dev-node-1
+    tags:
+  - kind: System
+    type: Development
+    os: ubuntu
+    cores: 4
+    ram: 16
+    drives:
+    runsOn: dell-c6400-node01
+    name: dev-node-2
+    tags:
+  - kind: System
+    type: Development
+    os: ubuntu
+    cores: 6
+    ram: 24
+    drives:
+    runsOn: dell-c6400-node01
+    name: dev-node-3
+    tags:
+  - kind: System
+    type: VirtualMachineCluster
+    os: proxmox
+    cores: 12
+    ram: 48
+    drives:
+    runsOn: dell-c6400-node01
+    name: vm-cluster-1
+    tags:
+  - kind: System
+    type: BareMetal
+    os: raspbian
+    cores: 4
+    ram: 8
+    drives:
+    runsOn: rack-edge
+    name: baremetal-rpi4
+    tags:
+  - kind: System
+    type: BareMetal
+    os: raspbian
+    cores: 4
+    ram: 4
+    drives:
+    runsOn: rack-edge
+    name: baremetal-rpi3
+    tags:
+  - kind: System
+    type: Infrastructure
+    os: ubuntu
+    cores: 4
+    ram: 16
+    drives:
+    runsOn: dell-c6400-node01
+    name: infra-node
+    tags:
+  - kind: System
+    type: NetworkAppliance
+    os: openwrt
+    cores: 2
+    ram: 2
+    drives:
+    runsOn: network-rack
+    name: router-appliance
+    tags:
+  - kind: System
+    type: Hypervisor
+    os: proxmox
+    cores: 16
+    ram: 61
+    drives:
+    runsOn: dell-c6400-node01
+    name: proxmox-host
+    tags:
+  - kind: System
+    type: ContainerHost
+    os: ubuntu
+    cores: 12
+    ram: 26
+    drives:
+    runsOn: dell-c6400-node01
+    name: docker-host
+    tags:
+  - kind: System
+    type: VirtualMachineCluster
+    os: proxmox
+    cores: 13
+    ram: 44
+    drives:
+    runsOn: dell-c6400-node01
+    name: vm-cluster-2
+    tags: 

+ 35 - 35
RackPeek.Web/config copy/accesspoints.yaml

@@ -1,36 +1,36 @@
 resources:
-- kind: AccessPoint
-  model: Unifi-Ap-Pro-7
-  speed: 1
-  name: lounge-ap
-  tags: 
-- kind: AccessPoint
-  model: Unifi-Ap-Pro-7
-  speed: 1
-  name: lounge-ap
-  tags: 
-- kind: AccessPoint
-  model: Unifi-U6-Lite
-  speed: 1
-  name: office-ap
-  tags: 
-- kind: AccessPoint
-  model: TP-Link-EAP245
-  speed: 1
-  name: garage-ap
-  tags: 
-- kind: AccessPoint
-  model: Aruba-AP-515
-  speed: 2.5
-  name: upstairs-ap
-  tags: 
-- kind: AccessPoint
-  model: Unifi-U6-Mesh
-  speed: 1
-  name: guest-ap
-  tags: 
-- kind: AccessPoint
-  model: Cisco-Aironet-1832i
-  speed: 1
-  name: warehouse-ap
-  tags: 
+  - kind: AccessPoint
+    model: Unifi-Ap-Pro-7
+    speed: 1
+    name: lounge-ap
+    tags:
+  - kind: AccessPoint
+    model: Unifi-Ap-Pro-7
+    speed: 1
+    name: lounge-ap
+    tags:
+  - kind: AccessPoint
+    model: Unifi-U6-Lite
+    speed: 1
+    name: office-ap
+    tags:
+  - kind: AccessPoint
+    model: TP-Link-EAP245
+    speed: 1
+    name: garage-ap
+    tags:
+  - kind: AccessPoint
+    model: Aruba-AP-515
+    speed: 2.5
+    name: upstairs-ap
+    tags:
+  - kind: AccessPoint
+    model: Unifi-U6-Mesh
+    speed: 1
+    name: guest-ap
+    tags:
+  - kind: AccessPoint
+    model: Cisco-Aironet-1832i
+    speed: 1
+    name: warehouse-ap
+    tags: 

+ 21 - 21
RackPeek.Web/config copy/desktops.yaml

@@ -1,22 +1,22 @@
 resources:
-- kind: Desktop
-  cpus:
-  - model: Intel(R) Core(TM) i5-9500
-    cores: 6
-    threads: 6
-  ram:
-    size: 16
-    mts: 2666
-  drives:
-  - type: ssd
-    size: 512
-  nics:
-  - type: rj45
-    speed: 1
-    ports: 1
-  gpus:
-  - model: RTX 3080
-    vram: 12
-  model: 
-  name: dell-optiplex
-  tags: 
+  - kind: Desktop
+    cpus:
+      - model: Intel(R) Core(TM) i5-9500
+        cores: 6
+        threads: 6
+    ram:
+      size: 16
+      mts: 2666
+    drives:
+      - type: ssd
+        size: 512
+    nics:
+      - type: rj45
+        speed: 1
+        ports: 1
+    gpus:
+      - model: RTX 3080
+        vram: 12
+    model:
+    name: dell-optiplex
+    tags: 

+ 13 - 13
RackPeek.Web/config copy/firewalls.yaml

@@ -1,14 +1,14 @@
 resources:
-- kind: Firewall
-  model: pfSense-1100
-  managed: true
-  poe: true
-  ports:
-  - type: rj45
-    speed: 1
-    count: 8
-  - type: sfp
-    speed: 10
-    count: 2
-  name: pfsense
-  tags: 
+  - kind: Firewall
+    model: pfSense-1100
+    managed: true
+    poe: true
+    ports:
+      - type: rj45
+        speed: 1
+        count: 8
+      - type: sfp
+        speed: 10
+        count: 2
+    name: pfsense
+    tags: 

+ 16 - 16
RackPeek.Web/config copy/laptops.yaml

@@ -1,17 +1,17 @@
 resources:
-- kind: Laptop
-  cpus:
-  - model: Intel(R) Core(TM) i7-10510U
-    cores: 4
-    threads: 8
-  ram:
-    size: 16
-    mts: 2666
-  drives:
-  - type: ssd
-    size: 1024
-  gpus:
-  - model: RTX 3080
-    vram: 12
-  name: thinkpad-x1
-  tags: 
+  - kind: Laptop
+    cpus:
+      - model: Intel(R) Core(TM) i7-10510U
+        cores: 4
+        threads: 8
+    ram:
+      size: 16
+      mts: 2666
+    drives:
+      - type: ssd
+        size: 1024
+    gpus:
+      - model: RTX 3080
+        vram: 12
+    name: thinkpad-x1
+    tags: 

+ 13 - 13
RackPeek.Web/config copy/routers.yaml

@@ -1,14 +1,14 @@
 resources:
-- kind: Router
-  model: ER-4
-  managed: true
-  poe: true
-  ports:
-  - type: rj45
-    speed: 1
-    count: 8
-  - type: sfp
-    speed: 10
-    count: 2
-  name: ubiquiti-edge-router
-  tags: 
+  - kind: Router
+    model: ER-4
+    managed: true
+    poe: true
+    ports:
+      - type: rj45
+        speed: 1
+        count: 8
+      - type: sfp
+        speed: 10
+        count: 2
+    name: ubiquiti-edge-router
+    tags: 

+ 420 - 420
RackPeek.Web/config copy/servers.yaml

@@ -1,421 +1,421 @@
 resources:
-- kind: Server
-  cpus:
-  - model: Intel(R) Xeon(R) Silver 4110
-    cores: 8
-    threads: 16
-  ram:
-    size: 64
-    mts: 2400
-  drives:
-  - type: ssd
-    size: 480
-  - type: ssd
-    size: 480
-  nics:
-  - type: rj45
-    speed: 1
-    ports: 2
-  - type: sfp+
-    speed: 10
-    ports: 2
-  gpus: 
-  ipmi: true
-  name: dell-c6400-node01
-  tags: 
-- kind: Server
-  cpus:
-  - model: Intel(R) Xeon(R) Silver 4110
-    cores: 8
-    threads: 16
-  ram:
-    size: 64
-    mts: 2400
-  drives:
-  - type: ssd
-    size: 480
-  - type: ssd
-    size: 480
-  nics:
-  - type: rj45
-    speed: 1
-    ports: 2
-  - type: sfp+
-    speed: 10
-    ports: 2
-  gpus: 
-  ipmi: true
-  name: dell-c6400-node01
-  tags: 
-- kind: Server
-  cpus:
-  - model: Intel(R) Xeon(R) Silver 4110
-    cores: 8
-    threads: 16
-  ram:
-    size: 128
-    mts: 2400
-  drives:
-  - type: ssd
-    size: 960
-  nics:
-  - type: rj45
-    speed: 1
-    ports: 2
-  - type: sfp+
-    speed: 10
-    ports: 2
-  gpus: 
-  ipmi: true
-  name: dell-c6400-node02
-  tags: 
-- kind: Server
-  cpus:
-  - model: Intel(R) Xeon(R) Silver 4110
-    cores: 8
-    threads: 16
-  ram:
-    size: 64
-    mts: 2400
-  drives:
-  - type: ssd
-    size: 480
-  - type: ssd
-    size: 480
-  nics:
-  - type: rj45
-    speed: 1
-    ports: 2
-  - type: sfp+
-    speed: 10
-    ports: 2
-  gpus: 
-  ipmi: true
-  name: dell-c6400-node03
-  tags: 
-- kind: Server
-  cpus:
-  - model: Intel(R) Xeon(R) Silver 4110
-    cores: 8
-    threads: 16
-  ram:
-    size: 128
-    mts: 2400
-  drives:
-  - type: ssd
-    size: 960
-  nics:
-  - type: rj45
-    speed: 1
-    ports: 2
-  - type: sfp+
-    speed: 10
-    ports: 2
-  gpus: 
-  ipmi: true
-  name: dell-c6400-node04
-  tags: 
-- kind: Server
-  cpus:
-  - model: Intel(R) Xeon(R) E5-2620 v4
-    cores: 8
-    threads: 16
-  ram:
-    size: 64
-    mts: 2133
-  drives:
-  - type: hdd
-    size: 8192
-  - type: hdd
-    size: 8192
-  - type: hdd
-    size: 8192
-  - type: hdd
-    size: 8192
-  - type: ssd
-    size: 120
-  nics:
-  - type: rj45
-    speed: 1
-    ports: 1
-  - type: sfp+
-    speed: 10
-    ports: 1
-  gpus: 
-  ipmi: true
-  name: truenas-storage01
-  tags: 
-- kind: Server
-  cpus:
-  - model: Intel(R) Core(TM) i5-8500
-    cores: 6
-    threads: 6
-  ram:
-    size: 32
-    mts: 2666
-  drives:
-  - type: ssd
-    size: 512
-  nics:
-  - type: rj45
-    speed: 1
-    ports: 4
-  gpus: 
-  ipmi: false
-  name: proxmox-edge01
-  tags: 
-- kind: Server
-  cpus:
-  - model: Intel(R) Celeron(R) J4125
-    cores: 4
-    threads: 4
-  ram:
-    size: 8
-    mts: 2400
-  drives:
-  - type: ssd
-    size: 64
-  nics:
-  - type: rj45
-    speed: 1
-    ports: 4
-  gpus: 
-  ipmi: false
-  name: opnsense-fw01
-  tags: 
-- kind: Server
-  cpus:
-  - model: Intel(R) Xeon(R) E3-1270 v6
-    cores: 4
-    threads: 8
-  ram:
-    size: 16
-    mts: 2400
-  drives:
-  - type: ssd
-    size: 256
-  nics:
-  - type: rj45
-    speed: 1
-    ports: 1
-  gpus: 
-  ipmi: true
-  name: mgmt-bastion01
-  tags: 
-- kind: Server
-  cpus:
-  - model: Intel(R) Xeon(R) E5-2630 v4
-    cores: 10
-    threads: 20
-  ram:
-    size: 64
-    mts: 2133
-  drives:
-  - type: hdd
-    size: 6144
-  - type: hdd
-    size: 6144
-  - type: hdd
-    size: 6144
-  - type: hdd
-    size: 6144
-  - type: ssd
-    size: 240
-  nics:
-  - type: rj45
-    speed: 1
-    ports: 2
-  - type: sfp+
-    speed: 10
-    ports: 1
-  gpus: 
-  ipmi: true
-  name: truenas-backup01
-  tags: 
-- kind: Server
-  cpus:
-  - model: Intel(R) Xeon(R) Silver 4214
-    cores: 12
-    threads: 24
-  ram:
-    size: 128
-    mts: 2666
-  drives:
-  - type: ssd
-    size: 1024
-  nics:
-  - type: sfp+
-    speed: 10
-    ports: 2
-  gpus:
-  - model: NVIDIA Tesla P40
-    vram: 24
-  - model: NVIDIA Tesla P40
-    vram: 24
-  - model: NVIDIA Tesla P4
-    vram: 8
-  ipmi: true
-  name: compute-gpu01
-  tags: 
-- kind: Server
-  cpus:
-  - model: Intel(R) Xeon(R) E3-1240 v5
-    cores: 4
-    threads: 8
-  ram:
-    size: 32
-    mts: 2133
-  drives:
-  - type: ssd
-    size: 512
-  nics:
-  - type: rj45
-    speed: 1
-    ports: 2
-  gpus: 
-  ipmi: true
-  name: proxmox-lab01
-  tags: 
-- kind: Server
-  cpus:
-  - model: Intel(R) Xeon(R) E-2224
-    cores: 4
-    threads: 4
-  ram:
-    size: 16
-    mts: 2666
-  drives:
-  - type: ssd
-    size: 256
-  nics:
-  - type: rj45
-    speed: 1
-    ports: 2
-  gpus: 
-  ipmi: true
-  name: k8s-control01
-  tags: 
-- kind: Server
-  cpus:
-  - model: Intel(R) Xeon(R) E-2224
-    cores: 4
-    threads: 4
-  ram:
-    size: 16
-    mts: 2666
-  drives:
-  - type: ssd
-    size: 256
-  nics:
-  - type: rj45
-    speed: 1
-    ports: 2
-  gpus: 
-  ipmi: true
-  name: k8s-control02
-  tags: 
-- kind: Server
-  cpus:
-  - model: Intel(R) Xeon(R) Silver 4108
-    cores: 8
-    threads: 16
-  ram:
-    size: 64
-    mts: 2400
-  drives:
-  - type: ssd
-    size: 1024
-  - type: ssd
-    size: 1024
-  nics:
-  - type: sfp+
-    speed: 10
-    ports: 1
-  gpus: 
-  ipmi: true
-  name: elk-logging01
-  tags: 
-- kind: Server
-  cpus:
-  - model: Intel(R) Core(TM) i3-8100
-    cores: 4
-    threads: 4
-  ram:
-    size: 16
-    mts: 2400
-  drives:
-  - type: ssd
-    size: 256
-  nics:
-  - type: rj45
-    speed: 1
-    ports: 2
-  gpus: 
-  ipmi: false
-  name: edge-node01
-  tags: 
-- kind: Server
-  cpus:
-  - model: Intel(R) Xeon(R) E5-1650 v3
-    cores: 6
-    threads: 12
-  ram:
-    size: 64
-    mts: 2133
-  drives:
-  - type: ssd
-    size: 480
-  - type: hdd
-    size: 4096
-  nics:
-  - type: rj45
-    speed: 1
-    ports: 4
-  gpus: 
-  ipmi: true
-  name: backup-proxmox01
-  tags: 
-- kind: Server
-  cpus:
-  - model: Intel(R) Core(TM) i7-8700
-    cores: 6
-    threads: 12
-  ram:
-    size: 32
-    mts: 2666
-  drives:
-  - type: ssd
-    size: 512
-  nics:
-  - type: rj45
-    speed: 1
-    ports: 1
-  gpus: 
-  ipmi: false
-  name: lab-general01
-  tags: 
-- kind: Server
-  cpus:
-  - model: Intel(R) Xeon(R) E5-2650 v3
-    cores: 10
-    threads: 20
-  ram:
-    size: 128
-    mts: 2133
-  drives:
-  - type: hdd
-    size: 4096
-  - type: hdd
-    size: 4096
-  - type: hdd
-    size: 4096
-  - type: hdd
-    size: 4096
-  nics:
-  - type: sfp+
-    speed: 10
-    ports: 2
-  gpus: 
-  ipmi: true
-  name: dell-r730-archive01
-  tags: 
+  - kind: Server
+    cpus:
+      - model: Intel(R) Xeon(R) Silver 4110
+        cores: 8
+        threads: 16
+    ram:
+      size: 64
+      mts: 2400
+    drives:
+      - type: ssd
+        size: 480
+      - type: ssd
+        size: 480
+    nics:
+      - type: rj45
+        speed: 1
+        ports: 2
+      - type: sfp+
+        speed: 10
+        ports: 2
+    gpus:
+    ipmi: true
+    name: dell-c6400-node01
+    tags:
+  - kind: Server
+    cpus:
+      - model: Intel(R) Xeon(R) Silver 4110
+        cores: 8
+        threads: 16
+    ram:
+      size: 64
+      mts: 2400
+    drives:
+      - type: ssd
+        size: 480
+      - type: ssd
+        size: 480
+    nics:
+      - type: rj45
+        speed: 1
+        ports: 2
+      - type: sfp+
+        speed: 10
+        ports: 2
+    gpus:
+    ipmi: true
+    name: dell-c6400-node01
+    tags:
+  - kind: Server
+    cpus:
+      - model: Intel(R) Xeon(R) Silver 4110
+        cores: 8
+        threads: 16
+    ram:
+      size: 128
+      mts: 2400
+    drives:
+      - type: ssd
+        size: 960
+    nics:
+      - type: rj45
+        speed: 1
+        ports: 2
+      - type: sfp+
+        speed: 10
+        ports: 2
+    gpus:
+    ipmi: true
+    name: dell-c6400-node02
+    tags:
+  - kind: Server
+    cpus:
+      - model: Intel(R) Xeon(R) Silver 4110
+        cores: 8
+        threads: 16
+    ram:
+      size: 64
+      mts: 2400
+    drives:
+      - type: ssd
+        size: 480
+      - type: ssd
+        size: 480
+    nics:
+      - type: rj45
+        speed: 1
+        ports: 2
+      - type: sfp+
+        speed: 10
+        ports: 2
+    gpus:
+    ipmi: true
+    name: dell-c6400-node03
+    tags:
+  - kind: Server
+    cpus:
+      - model: Intel(R) Xeon(R) Silver 4110
+        cores: 8
+        threads: 16
+    ram:
+      size: 128
+      mts: 2400
+    drives:
+      - type: ssd
+        size: 960
+    nics:
+      - type: rj45
+        speed: 1
+        ports: 2
+      - type: sfp+
+        speed: 10
+        ports: 2
+    gpus:
+    ipmi: true
+    name: dell-c6400-node04
+    tags:
+  - kind: Server
+    cpus:
+      - model: Intel(R) Xeon(R) E5-2620 v4
+        cores: 8
+        threads: 16
+    ram:
+      size: 64
+      mts: 2133
+    drives:
+      - type: hdd
+        size: 8192
+      - type: hdd
+        size: 8192
+      - type: hdd
+        size: 8192
+      - type: hdd
+        size: 8192
+      - type: ssd
+        size: 120
+    nics:
+      - type: rj45
+        speed: 1
+        ports: 1
+      - type: sfp+
+        speed: 10
+        ports: 1
+    gpus:
+    ipmi: true
+    name: truenas-storage01
+    tags:
+  - kind: Server
+    cpus:
+      - model: Intel(R) Core(TM) i5-8500
+        cores: 6
+        threads: 6
+    ram:
+      size: 32
+      mts: 2666
+    drives:
+      - type: ssd
+        size: 512
+    nics:
+      - type: rj45
+        speed: 1
+        ports: 4
+    gpus:
+    ipmi: false
+    name: proxmox-edge01
+    tags:
+  - kind: Server
+    cpus:
+      - model: Intel(R) Celeron(R) J4125
+        cores: 4
+        threads: 4
+    ram:
+      size: 8
+      mts: 2400
+    drives:
+      - type: ssd
+        size: 64
+    nics:
+      - type: rj45
+        speed: 1
+        ports: 4
+    gpus:
+    ipmi: false
+    name: opnsense-fw01
+    tags:
+  - kind: Server
+    cpus:
+      - model: Intel(R) Xeon(R) E3-1270 v6
+        cores: 4
+        threads: 8
+    ram:
+      size: 16
+      mts: 2400
+    drives:
+      - type: ssd
+        size: 256
+    nics:
+      - type: rj45
+        speed: 1
+        ports: 1
+    gpus:
+    ipmi: true
+    name: mgmt-bastion01
+    tags:
+  - kind: Server
+    cpus:
+      - model: Intel(R) Xeon(R) E5-2630 v4
+        cores: 10
+        threads: 20
+    ram:
+      size: 64
+      mts: 2133
+    drives:
+      - type: hdd
+        size: 6144
+      - type: hdd
+        size: 6144
+      - type: hdd
+        size: 6144
+      - type: hdd
+        size: 6144
+      - type: ssd
+        size: 240
+    nics:
+      - type: rj45
+        speed: 1
+        ports: 2
+      - type: sfp+
+        speed: 10
+        ports: 1
+    gpus:
+    ipmi: true
+    name: truenas-backup01
+    tags:
+  - kind: Server
+    cpus:
+      - model: Intel(R) Xeon(R) Silver 4214
+        cores: 12
+        threads: 24
+    ram:
+      size: 128
+      mts: 2666
+    drives:
+      - type: ssd
+        size: 1024
+    nics:
+      - type: sfp+
+        speed: 10
+        ports: 2
+    gpus:
+      - model: NVIDIA Tesla P40
+        vram: 24
+      - model: NVIDIA Tesla P40
+        vram: 24
+      - model: NVIDIA Tesla P4
+        vram: 8
+    ipmi: true
+    name: compute-gpu01
+    tags:
+  - kind: Server
+    cpus:
+      - model: Intel(R) Xeon(R) E3-1240 v5
+        cores: 4
+        threads: 8
+    ram:
+      size: 32
+      mts: 2133
+    drives:
+      - type: ssd
+        size: 512
+    nics:
+      - type: rj45
+        speed: 1
+        ports: 2
+    gpus:
+    ipmi: true
+    name: proxmox-lab01
+    tags:
+  - kind: Server
+    cpus:
+      - model: Intel(R) Xeon(R) E-2224
+        cores: 4
+        threads: 4
+    ram:
+      size: 16
+      mts: 2666
+    drives:
+      - type: ssd
+        size: 256
+    nics:
+      - type: rj45
+        speed: 1
+        ports: 2
+    gpus:
+    ipmi: true
+    name: k8s-control01
+    tags:
+  - kind: Server
+    cpus:
+      - model: Intel(R) Xeon(R) E-2224
+        cores: 4
+        threads: 4
+    ram:
+      size: 16
+      mts: 2666
+    drives:
+      - type: ssd
+        size: 256
+    nics:
+      - type: rj45
+        speed: 1
+        ports: 2
+    gpus:
+    ipmi: true
+    name: k8s-control02
+    tags:
+  - kind: Server
+    cpus:
+      - model: Intel(R) Xeon(R) Silver 4108
+        cores: 8
+        threads: 16
+    ram:
+      size: 64
+      mts: 2400
+    drives:
+      - type: ssd
+        size: 1024
+      - type: ssd
+        size: 1024
+    nics:
+      - type: sfp+
+        speed: 10
+        ports: 1
+    gpus:
+    ipmi: true
+    name: elk-logging01
+    tags:
+  - kind: Server
+    cpus:
+      - model: Intel(R) Core(TM) i3-8100
+        cores: 4
+        threads: 4
+    ram:
+      size: 16
+      mts: 2400
+    drives:
+      - type: ssd
+        size: 256
+    nics:
+      - type: rj45
+        speed: 1
+        ports: 2
+    gpus:
+    ipmi: false
+    name: edge-node01
+    tags:
+  - kind: Server
+    cpus:
+      - model: Intel(R) Xeon(R) E5-1650 v3
+        cores: 6
+        threads: 12
+    ram:
+      size: 64
+      mts: 2133
+    drives:
+      - type: ssd
+        size: 480
+      - type: hdd
+        size: 4096
+    nics:
+      - type: rj45
+        speed: 1
+        ports: 4
+    gpus:
+    ipmi: true
+    name: backup-proxmox01
+    tags:
+  - kind: Server
+    cpus:
+      - model: Intel(R) Core(TM) i7-8700
+        cores: 6
+        threads: 12
+    ram:
+      size: 32
+      mts: 2666
+    drives:
+      - type: ssd
+        size: 512
+    nics:
+      - type: rj45
+        speed: 1
+        ports: 1
+    gpus:
+    ipmi: false
+    name: lab-general01
+    tags:
+  - kind: Server
+    cpus:
+      - model: Intel(R) Xeon(R) E5-2650 v3
+        cores: 10
+        threads: 20
+    ram:
+      size: 128
+      mts: 2133
+    drives:
+      - type: hdd
+        size: 4096
+      - type: hdd
+        size: 4096
+      - type: hdd
+        size: 4096
+      - type: hdd
+        size: 4096
+    nics:
+      - type: sfp+
+        speed: 10
+        ports: 2
+    gpus:
+    ipmi: true
+    name: dell-r730-archive01
+    tags: 

+ 1 - 1
RackPeek.Web/config copy/switches.yaml

@@ -1 +1 @@
-resources: []
+resources: [ ]

+ 5 - 5
RackPeek.Web/config copy/ups.yaml

@@ -1,6 +1,6 @@
 resources:
-- kind: Ups
-  model: Volta
-  va: 2200
-  name: rack-ups
-  tags: 
+  - kind: Ups
+    model: Volta
+    va: 2200
+    name: rack-ups
+    tags: 

+ 390 - 267
RackPeek/CliBootstrap.cs

@@ -7,6 +7,11 @@ using RackPeek.Commands.Desktops.Cpus;
 using RackPeek.Commands.Desktops.Drive;
 using RackPeek.Commands.Desktops.Gpus;
 using RackPeek.Commands.Desktops.Nics;
+using RackPeek.Commands.Firewalls;
+using RackPeek.Commands.Laptops;
+using RackPeek.Commands.Laptops.Cpus;
+using RackPeek.Commands.Laptops.Drive;
+using RackPeek.Commands.Laptops.Gpus;
 using RackPeek.Commands.Servers;
 using RackPeek.Commands.Servers.Cpus;
 using RackPeek.Commands.Servers.Drives;
@@ -67,329 +72,447 @@ public static class CliBootstrap
         services.AddCommands();
 
         // Spectre bootstrap
-app.Configure(config =>
-{
-    config.SetApplicationName("rpk");
-    config.ValidateExamples();
-
-    // Global summary
-    config.AddCommand<GetTotalSummaryCommand>("summary")
-        .WithDescription("Show a summarized report of all resources in the system.");
-
-    // ----------------------------
-    // Server commands (CRUD-style)
-    // ----------------------------
-    config.AddBranch("servers", server =>
-    {
-        server.SetDescription("Manage servers and their components.");
-
-        server.AddCommand<ServerReportCommand>("summary")
-            .WithDescription("Show a summarized hardware report for all servers.");
-
-        server.AddCommand<ServerAddCommand>("add")
-            .WithDescription("Add a new server to the inventory.");
-
-        server.AddCommand<ServerGetByNameCommand>("get")
-            .WithDescription("List all servers or retrieve a specific server by name.");
-
-        server.AddCommand<ServerDescribeCommand>("describe")
-            .WithDescription("Display detailed information about a specific server.");
-
-        server.AddCommand<ServerSetCommand>("set")
-            .WithDescription("Update properties of an existing server.");
-
-        server.AddCommand<ServerDeleteCommand>("del")
-            .WithDescription("Delete a server from the inventory.");
-
-        server.AddCommand<ServerTreeCommand>("tree")
-            .WithDescription("Display the dependency tree of a server.");
-
-        // Server CPUs
-        server.AddBranch("cpu", cpu =>
-        {
-            cpu.SetDescription("Manage CPUs attached to a server.");
-
-            cpu.AddCommand<ServerCpuAddCommand>("add")
-                .WithDescription("Add a CPU to a specific server.");
-
-            cpu.AddCommand<ServerCpuSetCommand>("set")
-                .WithDescription("Update configuration of a server CPU.");
-
-            cpu.AddCommand<ServerCpuRemoveCommand>("del")
-                .WithDescription("Remove a CPU from a server.");
-        });
-
-        // Server Drives
-        server.AddBranch("drive", drive =>
+        app.Configure(config =>
         {
-            drive.SetDescription("Manage drives attached to a server.");
-
-            drive.AddCommand<ServerDriveAddCommand>("add")
-                .WithDescription("Add a storage drive to a server.");
-
-            drive.AddCommand<ServerDriveUpdateCommand>("set")
-                .WithDescription("Update properties of a server drive.");
+            config.SetApplicationName("rpk");
+            config.ValidateExamples();
 
-            drive.AddCommand<ServerDriveRemoveCommand>("del")
-                .WithDescription("Remove a drive from a server.");
-        });
+            // Global summary
+            config.AddCommand<GetTotalSummaryCommand>("summary")
+                .WithDescription("Show a summarized report of all resources in the system.");
 
-        // Server GPUs
-        server.AddBranch("gpu", gpu =>
-        {
-            gpu.SetDescription("Manage GPUs attached to a server.");
+            // ----------------------------
+            // Server commands (CRUD-style)
+            // ----------------------------
+            config.AddBranch("servers", server =>
+            {
+                server.SetDescription("Manage servers and their components.");
 
-            gpu.AddCommand<ServerGpuAddCommand>("add")
-                .WithDescription("Add a GPU to a server.");
+                server.AddCommand<ServerReportCommand>("summary")
+                    .WithDescription("Show a summarized hardware report for all servers.");
 
-            gpu.AddCommand<ServerGpuUpdateCommand>("set")
-                .WithDescription("Update properties of a server GPU.");
+                server.AddCommand<ServerAddCommand>("add")
+                    .WithDescription("Add a new server to the inventory.");
 
-            gpu.AddCommand<ServerGpuRemoveCommand>("del")
-                .WithDescription("Remove a GPU from a server.");
-        });
+                server.AddCommand<ServerGetByNameCommand>("get")
+                    .WithDescription("List all servers or retrieve a specific server by name.");
 
-        // Server NICs
-        server.AddBranch("nic", nic =>
-        {
-            nic.SetDescription("Manage network interface cards (NICs) for a server.");
+                server.AddCommand<ServerDescribeCommand>("describe")
+                    .WithDescription("Display detailed information about a specific server.");
 
-            nic.AddCommand<ServerNicAddCommand>("add")
-                .WithDescription("Add a NIC to a server.");
+                server.AddCommand<ServerSetCommand>("set")
+                    .WithDescription("Update properties of an existing server.");
 
-            nic.AddCommand<ServerNicUpdateCommand>("set")
-                .WithDescription("Update properties of a server NIC.");
+                server.AddCommand<ServerDeleteCommand>("del")
+                    .WithDescription("Delete a server from the inventory.");
 
-            nic.AddCommand<ServerNicRemoveCommand>("del")
-                .WithDescription("Remove a NIC from a server.");
-        });
-    });
+                server.AddCommand<ServerTreeCommand>("tree")
+                    .WithDescription("Display the dependency tree of a server.");
 
-    // ----------------------------
-    // Switch commands
-    // ----------------------------
-    config.AddBranch("switches", switches =>
-    {
-        switches.SetDescription("Manage network switches.");
+                // Server CPUs
+                server.AddBranch("cpu", cpu =>
+                {
+                    cpu.SetDescription("Manage CPUs attached to a server.");
 
-        switches.AddCommand<SwitchReportCommand>("summary")
-            .WithDescription("Show a hardware report for all switches.");
+                    cpu.AddCommand<ServerCpuAddCommand>("add")
+                        .WithDescription("Add a CPU to a specific server.");
 
-        switches.AddCommand<SwitchAddCommand>("add")
-            .WithDescription("Add a new network switch to the inventory.");
+                    cpu.AddCommand<ServerCpuSetCommand>("set")
+                        .WithDescription("Update configuration of a server CPU.");
 
-        switches.AddCommand<SwitchGetCommand>("list")
-            .WithDescription("List all switches in the system.");
+                    cpu.AddCommand<ServerCpuRemoveCommand>("del")
+                        .WithDescription("Remove a CPU from a server.");
+                });
 
-        switches.AddCommand<SwitchGetByNameCommand>("get")
-            .WithDescription("Retrieve details of a specific switch by name.");
+                // Server Drives
+                server.AddBranch("drive", drive =>
+                {
+                    drive.SetDescription("Manage drives attached to a server.");
 
-        switches.AddCommand<SwitchDescribeCommand>("describe")
-            .WithDescription("Show detailed information about a switch.");
+                    drive.AddCommand<ServerDriveAddCommand>("add")
+                        .WithDescription("Add a storage drive to a server.");
 
-        switches.AddCommand<SwitchSetCommand>("set")
-            .WithDescription("Update properties of a switch.");
+                    drive.AddCommand<ServerDriveUpdateCommand>("set")
+                        .WithDescription("Update properties of a server drive.");
 
-        switches.AddCommand<SwitchDeleteCommand>("del")
-            .WithDescription("Delete a switch from the inventory.");
-    });
+                    drive.AddCommand<ServerDriveRemoveCommand>("del")
+                        .WithDescription("Remove a drive from a server.");
+                });
 
-    // ----------------------------
-    // System commands
-    // ----------------------------
-    config.AddBranch("systems", system =>
-    {
-        system.SetDescription("Manage systems and their dependencies.");
+                // Server GPUs
+                server.AddBranch("gpu", gpu =>
+                {
+                    gpu.SetDescription("Manage GPUs attached to a server.");
 
-        system.AddCommand<SystemReportCommand>("summary")
-            .WithDescription("Show a summary report for all systems.");
+                    gpu.AddCommand<ServerGpuAddCommand>("add")
+                        .WithDescription("Add a GPU to a server.");
 
-        system.AddCommand<SystemAddCommand>("add")
-            .WithDescription("Add a new system to the inventory.");
+                    gpu.AddCommand<ServerGpuUpdateCommand>("set")
+                        .WithDescription("Update properties of a server GPU.");
 
-        system.AddCommand<SystemGetCommand>("list")
-            .WithDescription("List all systems.");
+                    gpu.AddCommand<ServerGpuRemoveCommand>("del")
+                        .WithDescription("Remove a GPU from a server.");
+                });
 
-        system.AddCommand<SystemGetByNameCommand>("get")
-            .WithDescription("Retrieve a system by name.");
+                // Server NICs
+                server.AddBranch("nic", nic =>
+                {
+                    nic.SetDescription("Manage network interface cards (NICs) for a server.");
 
-        system.AddCommand<SystemDescribeCommand>("describe")
-            .WithDescription("Display detailed information about a system.");
+                    nic.AddCommand<ServerNicAddCommand>("add")
+                        .WithDescription("Add a NIC to a server.");
 
-        system.AddCommand<SystemSetCommand>("set")
-            .WithDescription("Update properties of a system.");
+                    nic.AddCommand<ServerNicUpdateCommand>("set")
+                        .WithDescription("Update properties of a server NIC.");
 
-        system.AddCommand<SystemDeleteCommand>("del")
-            .WithDescription("Delete a system from the inventory.");
+                    nic.AddCommand<ServerNicRemoveCommand>("del")
+                        .WithDescription("Remove a NIC from a server.");
+                });
+            });
 
-        system.AddCommand<SystemTreeCommand>("tree")
-            .WithDescription("Display the dependency tree for a system.");
-    });
+            // ----------------------------
+            // Switch commands
+            // ----------------------------
+            config.AddBranch("switches", switches =>
+            {
+                switches.SetDescription("Manage network switches.");
 
-    // ----------------------------
-    // Access Points
-    // ----------------------------
-    config.AddBranch("accesspoints", ap =>
-    {
-        ap.SetDescription("Manage access points.");
+                switches.AddCommand<SwitchReportCommand>("summary")
+                    .WithDescription("Show a hardware report for all switches.");
 
-        ap.AddCommand<AccessPointReportCommand>("summary")
-            .WithDescription("Show a hardware report for all access points.");
+                switches.AddCommand<SwitchAddCommand>("add")
+                    .WithDescription("Add a new network switch to the inventory.");
 
-        ap.AddCommand<AccessPointAddCommand>("add")
-            .WithDescription("Add a new access point.");
+                switches.AddCommand<SwitchGetCommand>("list")
+                    .WithDescription("List all switches in the system.");
 
-        ap.AddCommand<AccessPointGetCommand>("list")
-            .WithDescription("List all access points.");
+                switches.AddCommand<SwitchGetByNameCommand>("get")
+                    .WithDescription("Retrieve details of a specific switch by name.");
 
-        ap.AddCommand<AccessPointGetByNameCommand>("get")
-            .WithDescription("Retrieve an access point by name.");
+                switches.AddCommand<SwitchDescribeCommand>("describe")
+                    .WithDescription("Show detailed information about a switch.");
 
-        ap.AddCommand<AccessPointDescribeCommand>("describe")
-            .WithDescription("Show detailed information about an access point.");
+                switches.AddCommand<SwitchSetCommand>("set")
+                    .WithDescription("Update properties of a switch.");
 
-        ap.AddCommand<AccessPointSetCommand>("set")
-            .WithDescription("Update properties of an access point.");
+                switches.AddCommand<SwitchDeleteCommand>("del")
+                    .WithDescription("Delete a switch from the inventory.");
+            });
 
-        ap.AddCommand<AccessPointDeleteCommand>("del")
-            .WithDescription("Delete an access point.");
-    });
+            // ----------------------------
+            // Routers commands
+            // ----------------------------
+            config.AddBranch("routers", routers =>
+            {
+                routers.SetDescription("Manage network routers.");
 
-    // ----------------------------
-    // UPS units
-    // ----------------------------
-    config.AddBranch("ups", ups =>
-    {
-        ups.SetDescription("Manage UPS units.");
+                routers.AddCommand<FirewallReportCommand>("summary")
+                    .WithDescription("Show a hardware report for all routers.");
 
-        ups.AddCommand<UpsReportCommand>("summary")
-            .WithDescription("Show a hardware report for all UPS units.");
+                routers.AddCommand<FirewallAddCommand>("add")
+                    .WithDescription("Add a new network router to the inventory.");
 
-        ups.AddCommand<UpsAddCommand>("add")
-            .WithDescription("Add a new UPS unit.");
+                routers.AddCommand<FirewallGetCommand>("list")
+                    .WithDescription("List all routers in the system.");
 
-        ups.AddCommand<UpsGetCommand>("list")
-            .WithDescription("List all UPS units.");
+                routers.AddCommand<FirewallGetByNameCommand>("get")
+                    .WithDescription("Retrieve details of a specific router by name.");
 
-        ups.AddCommand<UpsGetByNameCommand>("get")
-            .WithDescription("Retrieve a UPS unit by name.");
+                routers.AddCommand<FirewallDescribeCommand>("describe")
+                    .WithDescription("Show detailed information about a router.");
 
-        ups.AddCommand<UpsDescribeCommand>("describe")
-            .WithDescription("Show detailed information about a UPS unit.");
+                routers.AddCommand<FirewallSetCommand>("set")
+                    .WithDescription("Update properties of a router.");
 
-        ups.AddCommand<UpsSetCommand>("set")
-            .WithDescription("Update properties of a UPS unit.");
+                routers.AddCommand<FirewallDeleteCommand>("del")
+                    .WithDescription("Delete a router from the inventory.");
+            });
 
-        ups.AddCommand<UpsDeleteCommand>("del")
-            .WithDescription("Delete a UPS unit.");
-    });
+            // ----------------------------
+            // Firewalls commands
+            // ----------------------------
+            config.AddBranch("firewalls", firewalls =>
+            {
+                firewalls.SetDescription("Manage firewalls.");
 
-    // ----------------------------
-    // Desktops
-    // ----------------------------
-    config.AddBranch("desktops", desktops =>
-    {
-        desktops.SetDescription("Manage desktop computers and their components.");
-
-        // CRUD
-        desktops.AddCommand<DesktopAddCommand>("add")
-            .WithDescription("Add a new desktop.");
-        desktops.AddCommand<DesktopGetCommand>("list")
-            .WithDescription("List all desktops.");
-        desktops.AddCommand<DesktopGetByNameCommand>("get")
-            .WithDescription("Retrieve a desktop by name.");
-        desktops.AddCommand<DesktopDescribeCommand>("describe")
-            .WithDescription("Show detailed information about a desktop.");
-        desktops.AddCommand<DesktopSetCommand>("set")
-            .WithDescription("Update properties of a desktop.");
-        desktops.AddCommand<DesktopDeleteCommand>("del")
-            .WithDescription("Delete a desktop from the inventory.");
-        desktops.AddCommand<DesktopReportCommand>("summary")
-            .WithDescription("Show a summarized hardware report for all desktops.");
-        desktops.AddCommand<DesktopTreeCommand>("tree")
-            .WithDescription("Display the dependency tree for a desktop.");
-
-        // CPU
-        desktops.AddBranch("cpu", cpu =>
-        {
-            cpu.SetDescription("Manage CPUs attached to desktops.");
-            cpu.AddCommand<DesktopCpuAddCommand>("add")
-                .WithDescription("Add a CPU to a desktop.");
-            cpu.AddCommand<DesktopCpuSetCommand>("set")
-                .WithDescription("Update a desktop CPU.");
-            cpu.AddCommand<DesktopCpuRemoveCommand>("del")
-                .WithDescription("Remove a CPU from a desktop.");
-        });
+                firewalls.AddCommand<FirewallReportCommand>("summary")
+                    .WithDescription("Show a hardware report for all firewalls.");
 
-        // Drives
-        desktops.AddBranch("drive", drive =>
-        {
-            drive.SetDescription("Manage storage drives attached to desktops.");
-            drive.AddCommand<DesktopDriveAddCommand>("add")
-                .WithDescription("Add a drive to a desktop.");
-            drive.AddCommand<DesktopDriveSetCommand>("set")
-                .WithDescription("Update a desktop drive.");
-            drive.AddCommand<DesktopDriveRemoveCommand>("del")
-                .WithDescription("Remove a drive from a desktop.");
-        });
+                firewalls.AddCommand<FirewallAddCommand>("add")
+                    .WithDescription("Add a new firewall to the inventory.");
 
-        // GPUs
-        desktops.AddBranch("gpu", gpu =>
-        {
-            gpu.SetDescription("Manage GPUs attached to desktops.");
-            gpu.AddCommand<DesktopGpuAddCommand>("add")
-                .WithDescription("Add a GPU to a desktop.");
-            gpu.AddCommand<DesktopGpuSetCommand>("set")
-                .WithDescription("Update a desktop GPU.");
-            gpu.AddCommand<DesktopGpuRemoveCommand>("del")
-                .WithDescription("Remove a GPU from a desktop.");
-        });
+                firewalls.AddCommand<FirewallGetCommand>("list")
+                    .WithDescription("List all firewalls in the system.");
 
-        // NICs
-        desktops.AddBranch("nic", nic =>
-        {
-            nic.SetDescription("Manage network interface cards (NICs) for desktops.");
-            nic.AddCommand<DesktopNicAddCommand>("add")
-                .WithDescription("Add a NIC to a desktop.");
-            nic.AddCommand<DesktopNicSetCommand>("set")
-                .WithDescription("Update a desktop NIC.");
-            nic.AddCommand<DesktopNicRemoveCommand>("del")
-                .WithDescription("Remove a NIC from a desktop.");
-        });
-    });
+                firewalls.AddCommand<FirewallGetByNameCommand>("get")
+                    .WithDescription("Retrieve details of a specific firewall by name.");
 
-    // ----------------------------
-    // Services
-    // ----------------------------
-    config.AddBranch("services", service =>
-    {
-        service.SetDescription("Manage services and their configurations.");
+                firewalls.AddCommand<FirewallDescribeCommand>("describe")
+                    .WithDescription("Show detailed information about a firewall.");
 
-        service.AddCommand<ServiceReportCommand>("summary")
-            .WithDescription("Show a summary report for all services.");
+                firewalls.AddCommand<FirewallSetCommand>("set")
+                    .WithDescription("Update properties of a firewall.");
 
-        service.AddCommand<ServiceAddCommand>("add")
-            .WithDescription("Add a new service.");
+                firewalls.AddCommand<FirewallDeleteCommand>("del")
+                    .WithDescription("Delete a firewall from the inventory.");
+            });
 
-        service.AddCommand<ServiceGetCommand>("list")
-            .WithDescription("List all services.");
+            // ----------------------------
+            // System commands
+            // ----------------------------
+            config.AddBranch("systems", system =>
+            {
+                system.SetDescription("Manage systems and their dependencies.");
 
-        service.AddCommand<ServiceGetByNameCommand>("get")
-            .WithDescription("Retrieve a service by name.");
+                system.AddCommand<SystemReportCommand>("summary")
+                    .WithDescription("Show a summary report for all systems.");
 
-        service.AddCommand<ServiceDescribeCommand>("describe")
-            .WithDescription("Show detailed information about a service.");
+                system.AddCommand<SystemAddCommand>("add")
+                    .WithDescription("Add a new system to the inventory.");
 
-        service.AddCommand<ServiceSetCommand>("set")
-            .WithDescription("Update properties of a service.");
+                system.AddCommand<SystemGetCommand>("list")
+                    .WithDescription("List all systems.");
 
-        service.AddCommand<ServiceDeleteCommand>("del")
-            .WithDescription("Delete a service.");
+                system.AddCommand<SystemGetByNameCommand>("get")
+                    .WithDescription("Retrieve a system by name.");
 
-        service.AddCommand<ServiceSubnetsCommand>("subnets")
-            .WithDescription("List subnets associated with a service, optionally filtered by CIDR.");
-    });
-});
+                system.AddCommand<SystemDescribeCommand>("describe")
+                    .WithDescription("Display detailed information about a system.");
 
+                system.AddCommand<SystemSetCommand>("set")
+                    .WithDescription("Update properties of a system.");
+
+                system.AddCommand<SystemDeleteCommand>("del")
+                    .WithDescription("Delete a system from the inventory.");
+
+                system.AddCommand<SystemTreeCommand>("tree")
+                    .WithDescription("Display the dependency tree for a system.");
+            });
+
+            // ----------------------------
+            // Access Points
+            // ----------------------------
+            config.AddBranch("accesspoints", ap =>
+            {
+                ap.SetDescription("Manage access points.");
+
+                ap.AddCommand<AccessPointReportCommand>("summary")
+                    .WithDescription("Show a hardware report for all access points.");
+
+                ap.AddCommand<AccessPointAddCommand>("add")
+                    .WithDescription("Add a new access point.");
+
+                ap.AddCommand<AccessPointGetCommand>("list")
+                    .WithDescription("List all access points.");
+
+                ap.AddCommand<AccessPointGetByNameCommand>("get")
+                    .WithDescription("Retrieve an access point by name.");
+
+                ap.AddCommand<AccessPointDescribeCommand>("describe")
+                    .WithDescription("Show detailed information about an access point.");
+
+                ap.AddCommand<AccessPointSetCommand>("set")
+                    .WithDescription("Update properties of an access point.");
+
+                ap.AddCommand<AccessPointDeleteCommand>("del")
+                    .WithDescription("Delete an access point.");
+            });
+
+            // ----------------------------
+            // UPS units
+            // ----------------------------
+            config.AddBranch("ups", ups =>
+            {
+                ups.SetDescription("Manage UPS units.");
+
+                ups.AddCommand<UpsReportCommand>("summary")
+                    .WithDescription("Show a hardware report for all UPS units.");
+
+                ups.AddCommand<UpsAddCommand>("add")
+                    .WithDescription("Add a new UPS unit.");
+
+                ups.AddCommand<UpsGetCommand>("list")
+                    .WithDescription("List all UPS units.");
+
+                ups.AddCommand<UpsGetByNameCommand>("get")
+                    .WithDescription("Retrieve a UPS unit by name.");
+
+                ups.AddCommand<UpsDescribeCommand>("describe")
+                    .WithDescription("Show detailed information about a UPS unit.");
+
+                ups.AddCommand<UpsSetCommand>("set")
+                    .WithDescription("Update properties of a UPS unit.");
+
+                ups.AddCommand<UpsDeleteCommand>("del")
+                    .WithDescription("Delete a UPS unit.");
+            });
+
+            // ----------------------------
+            // Desktops
+            // ----------------------------
+            config.AddBranch("desktops", desktops =>
+            {
+                desktops.SetDescription("Manage desktop computers and their components.");
+
+                // CRUD
+                desktops.AddCommand<DesktopAddCommand>("add")
+                    .WithDescription("Add a new desktop.");
+                desktops.AddCommand<DesktopGetCommand>("list")
+                    .WithDescription("List all desktops.");
+                desktops.AddCommand<DesktopGetByNameCommand>("get")
+                    .WithDescription("Retrieve a desktop by name.");
+                desktops.AddCommand<DesktopDescribeCommand>("describe")
+                    .WithDescription("Show detailed information about a desktop.");
+                desktops.AddCommand<DesktopSetCommand>("set")
+                    .WithDescription("Update properties of a desktop.");
+                desktops.AddCommand<DesktopDeleteCommand>("del")
+                    .WithDescription("Delete a desktop from the inventory.");
+                desktops.AddCommand<DesktopReportCommand>("summary")
+                    .WithDescription("Show a summarized hardware report for all desktops.");
+                desktops.AddCommand<DesktopTreeCommand>("tree")
+                    .WithDescription("Display the dependency tree for a desktop.");
+
+                // CPU
+                desktops.AddBranch("cpu", cpu =>
+                {
+                    cpu.SetDescription("Manage CPUs attached to desktops.");
+                    cpu.AddCommand<DesktopCpuAddCommand>("add")
+                        .WithDescription("Add a CPU to a desktop.");
+                    cpu.AddCommand<DesktopCpuSetCommand>("set")
+                        .WithDescription("Update a desktop CPU.");
+                    cpu.AddCommand<DesktopCpuRemoveCommand>("del")
+                        .WithDescription("Remove a CPU from a desktop.");
+                });
+
+                // Drives
+                desktops.AddBranch("drive", drive =>
+                {
+                    drive.SetDescription("Manage storage drives attached to desktops.");
+                    drive.AddCommand<DesktopDriveAddCommand>("add")
+                        .WithDescription("Add a drive to a desktop.");
+                    drive.AddCommand<DesktopDriveSetCommand>("set")
+                        .WithDescription("Update a desktop drive.");
+                    drive.AddCommand<DesktopDriveRemoveCommand>("del")
+                        .WithDescription("Remove a drive from a desktop.");
+                });
+
+                // GPUs
+                desktops.AddBranch("gpu", gpu =>
+                {
+                    gpu.SetDescription("Manage GPUs attached to desktops.");
+                    gpu.AddCommand<DesktopGpuAddCommand>("add")
+                        .WithDescription("Add a GPU to a desktop.");
+                    gpu.AddCommand<DesktopGpuSetCommand>("set")
+                        .WithDescription("Update a desktop GPU.");
+                    gpu.AddCommand<DesktopGpuRemoveCommand>("del")
+                        .WithDescription("Remove a GPU from a desktop.");
+                });
+
+                // NICs
+                desktops.AddBranch("nic", nic =>
+                {
+                    nic.SetDescription("Manage network interface cards (NICs) for desktops.");
+                    nic.AddCommand<DesktopNicAddCommand>("add")
+                        .WithDescription("Add a NIC to a desktop.");
+                    nic.AddCommand<DesktopNicSetCommand>("set")
+                        .WithDescription("Update a desktop NIC.");
+                    nic.AddCommand<DesktopNicRemoveCommand>("del")
+                        .WithDescription("Remove a NIC from a desktop.");
+                });
+            });
+
+            // ----------------------------
+            // Laptops
+            // ----------------------------
+            config.AddBranch("Laptops", Laptops =>
+            {
+                Laptops.SetDescription("Manage Laptop computers and their components.");
+
+                // CRUD
+                Laptops.AddCommand<LaptopAddCommand>("add")
+                    .WithDescription("Add a new Laptop.");
+                Laptops.AddCommand<LaptopGetCommand>("list")
+                    .WithDescription("List all Laptops.");
+                Laptops.AddCommand<LaptopGetByNameCommand>("get")
+                    .WithDescription("Retrieve a Laptop by name.");
+                Laptops.AddCommand<LaptopDescribeCommand>("describe")
+                    .WithDescription("Show detailed information about a Laptop.");
+                Laptops.AddCommand<LaptopDeleteCommand>("del")
+                    .WithDescription("Delete a Laptop from the inventory.");
+                Laptops.AddCommand<LaptopReportCommand>("summary")
+                    .WithDescription("Show a summarized hardware report for all Laptops.");
+                Laptops.AddCommand<LaptopTreeCommand>("tree")
+                    .WithDescription("Display the dependency tree for a Laptop.");
+
+                // CPU
+                Laptops.AddBranch("cpu", cpu =>
+                {
+                    cpu.SetDescription("Manage CPUs attached to Laptops.");
+                    cpu.AddCommand<LaptopCpuAddCommand>("add")
+                        .WithDescription("Add a CPU to a Laptop.");
+                    cpu.AddCommand<LaptopCpuSetCommand>("set")
+                        .WithDescription("Update a Laptop CPU.");
+                    cpu.AddCommand<LaptopCpuRemoveCommand>("del")
+                        .WithDescription("Remove a CPU from a Laptop.");
+                });
+
+                // Drives
+                Laptops.AddBranch("drive", drive =>
+                {
+                    drive.SetDescription("Manage storage drives attached to Laptops.");
+                    drive.AddCommand<LaptopDriveAddCommand>("add")
+                        .WithDescription("Add a drive to a Laptop.");
+                    drive.AddCommand<LaptopDriveSetCommand>("set")
+                        .WithDescription("Update a Laptop drive.");
+                    drive.AddCommand<LaptopDriveRemoveCommand>("del")
+                        .WithDescription("Remove a drive from a Laptop.");
+                });
+
+                // GPUs
+                Laptops.AddBranch("gpu", gpu =>
+                {
+                    gpu.SetDescription("Manage GPUs attached to Laptops.");
+                    gpu.AddCommand<LaptopGpuAddCommand>("add")
+                        .WithDescription("Add a GPU to a Laptop.");
+                    gpu.AddCommand<LaptopGpuSetCommand>("set")
+                        .WithDescription("Update a Laptop GPU.");
+                    gpu.AddCommand<LaptopGpuRemoveCommand>("del")
+                        .WithDescription("Remove a GPU from a Laptop.");
+                });
+            });
+
+
+            // ----------------------------
+            // Services
+            // ----------------------------
+            config.AddBranch("services", service =>
+            {
+                service.SetDescription("Manage services and their configurations.");
+
+                service.AddCommand<ServiceReportCommand>("summary")
+                    .WithDescription("Show a summary report for all services.");
+
+                service.AddCommand<ServiceAddCommand>("add")
+                    .WithDescription("Add a new service.");
+
+                service.AddCommand<ServiceGetCommand>("list")
+                    .WithDescription("List all services.");
+
+                service.AddCommand<ServiceGetByNameCommand>("get")
+                    .WithDescription("Retrieve a service by name.");
+
+                service.AddCommand<ServiceDescribeCommand>("describe")
+                    .WithDescription("Show detailed information about a service.");
+
+                service.AddCommand<ServiceSetCommand>("set")
+                    .WithDescription("Update properties of a service.");
+
+                service.AddCommand<ServiceDeleteCommand>("del")
+                    .WithDescription("Delete a service.");
+
+                service.AddCommand<ServiceSubnetsCommand>("subnets")
+                    .WithDescription("List subnets associated with a service, optionally filtered by CIDR.");
+            });
+        });
     }
 }

+ 32 - 0
RackPeek/Commands/Firewalls/FirewallAddCommand.cs

@@ -0,0 +1,32 @@
+using Microsoft.Extensions.DependencyInjection;
+using RackPeek.Domain.Resources.Hardware.Firewalls;
+using Spectre.Console;
+using Spectre.Console.Cli;
+
+namespace RackPeek.Commands.Firewalls;
+
+public class FirewallAddSettings : CommandSettings
+{
+    [CommandArgument(0, "<name>")] public string Name { get; set; } = default!;
+}
+
+public class FirewallAddCommand(
+    IServiceProvider serviceProvider
+) : AsyncCommand<FirewallAddSettings>
+{
+    public override async Task<int> ExecuteAsync(
+        CommandContext context,
+        FirewallAddSettings settings,
+        CancellationToken cancellationToken)
+    {
+        using var scope = serviceProvider.CreateScope();
+        var useCase = scope.ServiceProvider.GetRequiredService<AddFirewallUseCase>();
+
+        await useCase.ExecuteAsync(
+            settings.Name
+        );
+
+        AnsiConsole.MarkupLine($"[green]Firewall '{settings.Name}' added.[/]");
+        return 0;
+    }
+}

+ 8 - 0
RackPeek/Commands/Firewalls/FirewallCommands.cs

@@ -0,0 +1,8 @@
+using Spectre.Console.Cli;
+
+namespace RackPeek.Commands.Firewalls;
+
+public class FirewallNameSettings : CommandSettings
+{
+    [CommandArgument(0, "<name>")] public string Name { get; set; } = default!;
+}

+ 25 - 0
RackPeek/Commands/Firewalls/FirewallDeleteCommand.cs

@@ -0,0 +1,25 @@
+using Microsoft.Extensions.DependencyInjection;
+using RackPeek.Domain.Resources.Hardware.Firewalls;
+using Spectre.Console;
+using Spectre.Console.Cli;
+
+namespace RackPeek.Commands.Firewalls;
+
+public class FirewallDeleteCommand(
+    IServiceProvider serviceProvider
+) : AsyncCommand<FirewallNameSettings>
+{
+    public override async Task<int> ExecuteAsync(
+        CommandContext context,
+        FirewallNameSettings settings,
+        CancellationToken cancellationToken)
+    {
+        using var scope = serviceProvider.CreateScope();
+        var useCase = scope.ServiceProvider.GetRequiredService<DeleteFirewallUseCase>();
+
+        await useCase.ExecuteAsync(settings.Name);
+
+        AnsiConsole.MarkupLine($"[green]Firewall '{settings.Name}' deleted.[/]");
+        return 0;
+    }
+}

+ 47 - 0
RackPeek/Commands/Firewalls/FirewallDescribeCommand.cs

@@ -0,0 +1,47 @@
+using Microsoft.Extensions.DependencyInjection;
+using RackPeek.Domain.Resources.Hardware.Firewalls;
+using Spectre.Console;
+using Spectre.Console.Cli;
+
+namespace RackPeek.Commands.Firewalls;
+
+public class FirewallDescribeCommand(
+    IServiceProvider serviceProvider
+) : AsyncCommand<FirewallNameSettings>
+{
+    public override async Task<int> ExecuteAsync(
+        CommandContext context,
+        FirewallNameSettings settings,
+        CancellationToken cancellationToken)
+    {
+        using var scope = serviceProvider.CreateScope();
+        var useCase = scope.ServiceProvider.GetRequiredService<DescribeFirewallUseCase>();
+
+        var sw = await useCase.ExecuteAsync(settings.Name);
+
+        if (sw == null)
+        {
+            AnsiConsole.MarkupLine($"[red]Firewall '{settings.Name}' not found.[/]");
+            return 1;
+        }
+
+        var grid = new Grid()
+            .AddColumn(new GridColumn().NoWrap())
+            .AddColumn(new GridColumn().NoWrap());
+
+        grid.AddRow("Name:", sw.Name);
+        grid.AddRow("Model:", sw.Model ?? "Unknown");
+        grid.AddRow("Managed:", sw.Managed.HasValue ? sw.Managed.Value ? "Yes" : "No" : "Unknown");
+        grid.AddRow("PoE:", sw.Poe.HasValue ? sw.Poe.Value ? "Yes" : "No" : "Unknown");
+        grid.AddRow("Total Ports:", sw.TotalPorts.ToString());
+        grid.AddRow("Total Speed (Gb):", sw.TotalSpeedGb.ToString());
+        grid.AddRow("Ports:", sw.PortSummary);
+
+        AnsiConsole.Write(
+            new Panel(grid)
+                .Header("Firewall")
+                .Border(BoxBorder.Rounded));
+
+        return 0;
+    }
+}

+ 33 - 0
RackPeek/Commands/Firewalls/FirewallGetByNameCommand.cs

@@ -0,0 +1,33 @@
+using Microsoft.Extensions.DependencyInjection;
+using RackPeek.Domain.Resources.Hardware.Firewalls;
+using Spectre.Console;
+using Spectre.Console.Cli;
+
+namespace RackPeek.Commands.Firewalls;
+
+public class FirewallGetByNameCommand(
+    IServiceProvider serviceProvider
+) : AsyncCommand<FirewallNameSettings>
+{
+    public override async Task<int> ExecuteAsync(
+        CommandContext context,
+        FirewallNameSettings settings,
+        CancellationToken cancellationToken)
+    {
+        using var scope = serviceProvider.CreateScope();
+        var useCase = scope.ServiceProvider.GetRequiredService<DescribeFirewallUseCase>();
+
+        var sw = await useCase.ExecuteAsync(settings.Name);
+
+        if (sw == null)
+        {
+            AnsiConsole.MarkupLine($"[red]Firewall '{settings.Name}' not found.[/]");
+            return 1;
+        }
+
+        AnsiConsole.MarkupLine(
+            $"[green]{sw.Name}[/]  Model: {sw.Model ?? "Unknown"}, Managed: {(sw.Managed == true ? "Yes" : "No")}, PoE: {(sw.Poe == true ? "Yes" : "No")}");
+
+        return 0;
+    }
+}

+ 49 - 0
RackPeek/Commands/Firewalls/FirewallGetCommand.cs

@@ -0,0 +1,49 @@
+using Microsoft.Extensions.DependencyInjection;
+using RackPeek.Domain.Resources.Hardware.Firewalls;
+using Spectre.Console;
+using Spectre.Console.Cli;
+
+namespace RackPeek.Commands.Firewalls;
+
+public class FirewallGetCommand(
+    IServiceProvider serviceProvider
+) : AsyncCommand
+{
+    public override async Task<int> ExecuteAsync(
+        CommandContext context,
+        CancellationToken cancellationToken)
+    {
+        using var scope = serviceProvider.CreateScope();
+        var useCase = scope.ServiceProvider.GetRequiredService<FirewallHardwareReportUseCase>();
+
+        var report = await useCase.ExecuteAsync();
+
+        if (report.Firewalls.Count == 0)
+        {
+            AnsiConsole.MarkupLine("[yellow]No Firewalles found.[/]");
+            return 0;
+        }
+
+        var table = new Table()
+            .Border(TableBorder.Rounded)
+            .AddColumn("Name")
+            .AddColumn("Model")
+            .AddColumn("Managed")
+            .AddColumn("PoE")
+            .AddColumn("Ports")
+            .AddColumn("Port Summary");
+
+        foreach (var s in report.Firewalls)
+            table.AddRow(
+                s.Name,
+                s.Model ?? "Unknown",
+                s.Managed ? "[green]yes[/]" : "[red]no[/]",
+                s.Poe ? "[green]yes[/]" : "[red]no[/]",
+                s.TotalPorts.ToString(),
+                s.PortSummary
+            );
+
+        AnsiConsole.Write(table);
+        return 0;
+    }
+}

+ 51 - 0
RackPeek/Commands/Firewalls/FirewallReportCommand.cs

@@ -0,0 +1,51 @@
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using RackPeek.Domain.Resources.Hardware.Firewalls;
+using Spectre.Console;
+using Spectre.Console.Cli;
+
+namespace RackPeek.Commands.Firewalls;
+
+public class FirewallReportCommand(
+    ILogger<FirewallReportCommand> logger,
+    IServiceProvider serviceProvider
+) : AsyncCommand
+{
+    public override async Task<int> ExecuteAsync(CommandContext context, CancellationToken cancellationToken)
+    {
+        using var scope = serviceProvider.CreateScope();
+        var useCase = scope.ServiceProvider.GetRequiredService<FirewallHardwareReportUseCase>();
+
+        var report = await useCase.ExecuteAsync();
+
+        if (report.Firewalls.Count == 0)
+        {
+            AnsiConsole.MarkupLine("[yellow]No Firewalls found.[/]");
+            return 0;
+        }
+
+        var table = new Table()
+            .Border(TableBorder.Rounded)
+            .AddColumn("Name")
+            .AddColumn("Model")
+            .AddColumn("Managed")
+            .AddColumn("PoE")
+            .AddColumn("Ports")
+            .AddColumn("Max Speed")
+            .AddColumn("Port Summary");
+
+        foreach (var s in report.Firewalls)
+            table.AddRow(
+                s.Name,
+                s.Model,
+                s.Managed ? "[green]yes[/]" : "[red]no[/]",
+                s.Poe ? "[green]yes[/]" : "[red]no[/]",
+                s.TotalPorts.ToString(),
+                $"{s.MaxPortSpeedGb}G",
+                s.PortSummary
+            );
+
+        AnsiConsole.Write(table);
+        return 0;
+    }
+}

+ 39 - 0
RackPeek/Commands/Firewalls/FirewallSetCommand.cs

@@ -0,0 +1,39 @@
+using Microsoft.Extensions.DependencyInjection;
+using RackPeek.Commands.Servers;
+using RackPeek.Domain.Resources.Hardware.Firewalls;
+using Spectre.Console;
+using Spectre.Console.Cli;
+
+namespace RackPeek.Commands.Firewalls;
+
+public class FirewallSetSettings : ServerNameSettings
+{
+    [CommandOption("--Model")] public string Model { get; set; } = default!;
+
+    [CommandOption("--managed")] public bool Managed { get; set; }
+
+    [CommandOption("--poe")] public bool Poe { get; set; }
+}
+
+public class FirewallSetCommand(
+    IServiceProvider serviceProvider
+) : AsyncCommand<FirewallSetSettings>
+{
+    public override async Task<int> ExecuteAsync(
+        CommandContext context,
+        FirewallSetSettings settings,
+        CancellationToken cancellationToken)
+    {
+        using var scope = serviceProvider.CreateScope();
+        var useCase = scope.ServiceProvider.GetRequiredService<UpdateFirewallUseCase>();
+
+        await useCase.ExecuteAsync(
+            settings.Name,
+            settings.Model,
+            settings.Managed,
+            settings.Poe);
+
+        AnsiConsole.MarkupLine($"[green]Server '{settings.Name}' updated.[/]");
+        return 0;
+    }
+}

+ 32 - 0
RackPeek/Commands/Laptops/Cpus/LaptopCpuAddCommand.cs

@@ -0,0 +1,32 @@
+using Microsoft.Extensions.DependencyInjection;
+using RackPeek.Domain.Resources.Hardware.Laptops.Cpus;
+using RackPeek.Domain.Resources.Hardware.Models;
+using Spectre.Console;
+using Spectre.Console.Cli;
+
+namespace RackPeek.Commands.Laptops.Cpus;
+
+public class LaptopCpuAddCommand(IServiceProvider provider)
+    : AsyncCommand<LaptopCpuAddSettings>
+{
+    public override async Task<int> ExecuteAsync(
+        CommandContext context,
+        LaptopCpuAddSettings settings,
+        CancellationToken cancellationToken)
+    {
+        using var scope = provider.CreateScope();
+        var useCase = scope.ServiceProvider.GetRequiredService<AddLaptopCpuUseCase>();
+
+        var cpu = new Cpu
+        {
+            Model = settings.Model,
+            Cores = settings.Cores,
+            Threads = settings.Threads
+        };
+
+        await useCase.ExecuteAsync(settings.LaptopName, cpu);
+
+        AnsiConsole.MarkupLine($"[green]CPU added to Laptop '{settings.LaptopName}'.[/]");
+        return 0;
+    }
+}

+ 23 - 0
RackPeek/Commands/Laptops/Cpus/LaptopCpuAddSettings.cs

@@ -0,0 +1,23 @@
+using System.ComponentModel;
+using Spectre.Console.Cli;
+
+namespace RackPeek.Commands.Laptops.Cpus;
+
+public class LaptopCpuAddSettings : CommandSettings
+{
+    [CommandArgument(0, "<Laptop>")]
+    [Description("The Laptop name.")]
+    public string LaptopName { get; set; } = default!;
+
+    [CommandOption("--model")]
+    [Description("The model name.")]
+    public string? Model { get; set; }
+
+    [CommandOption("--cores")]
+    [Description("The number of cpu cores.")]
+    public int? Cores { get; set; }
+
+    [CommandOption("--threads")]
+    [Description("The number of cpu threads.")]
+    public int? Threads { get; set; }
+}

+ 24 - 0
RackPeek/Commands/Laptops/Cpus/LaptopCpuRemoveCommand.cs

@@ -0,0 +1,24 @@
+using Microsoft.Extensions.DependencyInjection;
+using RackPeek.Domain.Resources.Hardware.Laptops.Cpus;
+using Spectre.Console;
+using Spectre.Console.Cli;
+
+namespace RackPeek.Commands.Laptops.Cpus;
+
+public class LaptopCpuRemoveCommand(IServiceProvider provider)
+    : AsyncCommand<LaptopCpuRemoveSettings>
+{
+    public override async Task<int> ExecuteAsync(
+        CommandContext context,
+        LaptopCpuRemoveSettings settings,
+        CancellationToken cancellationToken)
+    {
+        using var scope = provider.CreateScope();
+        var useCase = scope.ServiceProvider.GetRequiredService<RemoveLaptopCpuUseCase>();
+
+        await useCase.ExecuteAsync(settings.LaptopName, settings.Index);
+
+        AnsiConsole.MarkupLine($"[green]CPU #{settings.Index} removed from Laptop '{settings.LaptopName}'.[/]");
+        return 0;
+    }
+}

+ 15 - 0
RackPeek/Commands/Laptops/Cpus/LaptopCpuRemoveSettings.cs

@@ -0,0 +1,15 @@
+using System.ComponentModel;
+using Spectre.Console.Cli;
+
+namespace RackPeek.Commands.Laptops.Cpus;
+
+public class LaptopCpuRemoveSettings : CommandSettings
+{
+    [CommandArgument(0, "<Laptop>")]
+    [Description("The name of the Laptop.")]
+    public string LaptopName { get; set; } = default!;
+
+    [CommandArgument(1, "<index>")]
+    [Description("The index of the Laptop cpu to remove.")]
+    public int Index { get; set; }
+}

+ 32 - 0
RackPeek/Commands/Laptops/Cpus/LaptopCpuSetCommand.cs

@@ -0,0 +1,32 @@
+using Microsoft.Extensions.DependencyInjection;
+using RackPeek.Domain.Resources.Hardware.Laptops.Cpus;
+using RackPeek.Domain.Resources.Hardware.Models;
+using Spectre.Console;
+using Spectre.Console.Cli;
+
+namespace RackPeek.Commands.Laptops.Cpus;
+
+public class LaptopCpuSetCommand(IServiceProvider provider)
+    : AsyncCommand<LaptopCpuSetSettings>
+{
+    public override async Task<int> ExecuteAsync(
+        CommandContext context,
+        LaptopCpuSetSettings settings,
+        CancellationToken cancellationToken)
+    {
+        using var scope = provider.CreateScope();
+        var useCase = scope.ServiceProvider.GetRequiredService<UpdateLaptopCpuUseCase>();
+
+        var cpu = new Cpu
+        {
+            Model = settings.Model,
+            Cores = settings.Cores,
+            Threads = settings.Threads
+        };
+
+        await useCase.ExecuteAsync(settings.LaptopName, settings.Index, cpu);
+
+        AnsiConsole.MarkupLine($"[green]CPU #{settings.Index} updated on Laptop '{settings.LaptopName}'.[/]");
+        return 0;
+    }
+}

+ 27 - 0
RackPeek/Commands/Laptops/Cpus/LaptopCpuSetSettings.cs

@@ -0,0 +1,27 @@
+using System.ComponentModel;
+using Spectre.Console.Cli;
+
+namespace RackPeek.Commands.Laptops.Cpus;
+
+public class LaptopCpuSetSettings : CommandSettings
+{
+    [CommandArgument(0, "<Laptop>")]
+    [Description("The Laptop name.")]
+    public string LaptopName { get; set; } = default!;
+
+    [CommandArgument(1, "<index>")]
+    [Description("The index of the Laptop cpu.")]
+    public int Index { get; set; }
+
+    [CommandOption("--model")]
+    [Description("The cpu model.")]
+    public string? Model { get; set; }
+
+    [CommandOption("--cores")]
+    [Description("The number of cpu cores.")]
+    public int? Cores { get; set; }
+
+    [CommandOption("--threads")]
+    [Description("The number of cpu threads.")]
+    public int? Threads { get; set; }
+}

+ 30 - 0
RackPeek/Commands/Laptops/Drive/LaptopDriveAddCommand.cs

@@ -0,0 +1,30 @@
+using Microsoft.Extensions.DependencyInjection;
+using RackPeek.Domain.Resources.Hardware.Laptops.Drives;
+using Spectre.Console;
+using Spectre.Console.Cli;
+
+namespace RackPeek.Commands.Laptops.Drive;
+
+public class LaptopDriveAddCommand(IServiceProvider provider)
+    : AsyncCommand<LaptopDriveAddSettings>
+{
+    public override async Task<int> ExecuteAsync(
+        CommandContext context,
+        LaptopDriveAddSettings settings,
+        CancellationToken cancellationToken)
+    {
+        using var scope = provider.CreateScope();
+        var useCase = scope.ServiceProvider.GetRequiredService<AddLaptopDriveUseCase>();
+
+        var drive = new Domain.Resources.Hardware.Models.Drive
+        {
+            Type = settings.Type,
+            Size = settings.Size
+        };
+
+        await useCase.ExecuteAsync(settings.LaptopName, drive);
+
+        AnsiConsole.MarkupLine($"[green]Drive added to Laptop '{settings.LaptopName}'.[/]");
+        return 0;
+    }
+}

+ 19 - 0
RackPeek/Commands/Laptops/Drive/LaptopDriveAddSettings.cs

@@ -0,0 +1,19 @@
+using System.ComponentModel;
+using Spectre.Console.Cli;
+
+namespace RackPeek.Commands.Laptops.Drive;
+
+public class LaptopDriveAddSettings : CommandSettings
+{
+    [CommandArgument(0, "<Laptop>")]
+    [Description("The name of the Laptop.")]
+    public string LaptopName { get; set; } = default!;
+
+    [CommandOption("--type")]
+    [Description("The drive type e.g hdd / ssd.")]
+    public string? Type { get; set; }
+
+    [CommandOption("--size")]
+    [Description("The drive capacity in Gb.")]
+    public int? Size { get; set; }
+}

+ 24 - 0
RackPeek/Commands/Laptops/Drive/LaptopDriveRemoveCommand.cs

@@ -0,0 +1,24 @@
+using Microsoft.Extensions.DependencyInjection;
+using RackPeek.Domain.Resources.Hardware.Laptops.Drives;
+using Spectre.Console;
+using Spectre.Console.Cli;
+
+namespace RackPeek.Commands.Laptops.Drive;
+
+public class LaptopDriveRemoveCommand(IServiceProvider provider)
+    : AsyncCommand<LaptopDriveRemoveSettings>
+{
+    public override async Task<int> ExecuteAsync(
+        CommandContext context,
+        LaptopDriveRemoveSettings settings,
+        CancellationToken cancellationToken)
+    {
+        using var scope = provider.CreateScope();
+        var useCase = scope.ServiceProvider.GetRequiredService<RemoveLaptopDriveUseCase>();
+
+        await useCase.ExecuteAsync(settings.LaptopName, settings.Index);
+
+        AnsiConsole.MarkupLine($"[green]Drive #{settings.Index} removed from Laptop '{settings.LaptopName}'.[/]");
+        return 0;
+    }
+}

+ 15 - 0
RackPeek/Commands/Laptops/Drive/LaptopDriveRemoveSettings.cs

@@ -0,0 +1,15 @@
+using System.ComponentModel;
+using Spectre.Console.Cli;
+
+namespace RackPeek.Commands.Laptops.Drive;
+
+public class LaptopDriveRemoveSettings : CommandSettings
+{
+    [CommandArgument(0, "<Laptop>")]
+    [Description("The name of the Laptop.")]
+    public string LaptopName { get; set; } = default!;
+
+    [CommandArgument(1, "<index>")]
+    [Description("The index of the drive to remove.")]
+    public int Index { get; set; }
+}

+ 30 - 0
RackPeek/Commands/Laptops/Drive/LaptopDriveSetCommand.cs

@@ -0,0 +1,30 @@
+using Microsoft.Extensions.DependencyInjection;
+using RackPeek.Domain.Resources.Hardware.Laptops.Drives;
+using Spectre.Console;
+using Spectre.Console.Cli;
+
+namespace RackPeek.Commands.Laptops.Drive;
+
+public class LaptopDriveSetCommand(IServiceProvider provider)
+    : AsyncCommand<LaptopDriveSetSettings>
+{
+    public override async Task<int> ExecuteAsync(
+        CommandContext context,
+        LaptopDriveSetSettings settings,
+        CancellationToken cancellationToken)
+    {
+        using var scope = provider.CreateScope();
+        var useCase = scope.ServiceProvider.GetRequiredService<UpdateLaptopDriveUseCase>();
+
+        var drive = new Domain.Resources.Hardware.Models.Drive
+        {
+            Type = settings.Type,
+            Size = settings.Size
+        };
+
+        await useCase.ExecuteAsync(settings.LaptopName, settings.Index, drive);
+
+        AnsiConsole.MarkupLine($"[green]Drive #{settings.Index} updated on Laptop '{settings.LaptopName}'.[/]");
+        return 0;
+    }
+}

+ 23 - 0
RackPeek/Commands/Laptops/Drive/LaptopDriveSetSettings.cs

@@ -0,0 +1,23 @@
+using System.ComponentModel;
+using Spectre.Console.Cli;
+
+namespace RackPeek.Commands.Laptops.Drive;
+
+public class LaptopDriveSetSettings : CommandSettings
+{
+    [CommandArgument(0, "<Laptop>")]
+    [Description("The Laptop name.")]
+    public string LaptopName { get; set; } = default!;
+
+    [CommandArgument(1, "<index>")]
+    [Description("The drive index to update.")]
+    public int Index { get; set; }
+
+    [CommandOption("--type")]
+    [Description("The drive type e.g hdd / ssd.")]
+    public string? Type { get; set; }
+
+    [CommandOption("--size")]
+    [Description("The drive capacity in Gb.")]
+    public int? Size { get; set; }
+}

+ 31 - 0
RackPeek/Commands/Laptops/Gpus/LaptopGpuAddCommand.cs

@@ -0,0 +1,31 @@
+using Microsoft.Extensions.DependencyInjection;
+using RackPeek.Domain.Resources.Hardware.Laptops.Gpus;
+using RackPeek.Domain.Resources.Hardware.Models;
+using Spectre.Console;
+using Spectre.Console.Cli;
+
+namespace RackPeek.Commands.Laptops.Gpus;
+
+public class LaptopGpuAddCommand(IServiceProvider provider)
+    : AsyncCommand<LaptopGpuAddSettings>
+{
+    public override async Task<int> ExecuteAsync(
+        CommandContext context,
+        LaptopGpuAddSettings settings,
+        CancellationToken cancellationToken)
+    {
+        using var scope = provider.CreateScope();
+        var useCase = scope.ServiceProvider.GetRequiredService<AddLaptopGpuUseCase>();
+
+        var gpu = new Gpu
+        {
+            Model = settings.Model,
+            Vram = settings.Vram
+        };
+
+        await useCase.ExecuteAsync(settings.LaptopName, gpu);
+
+        AnsiConsole.MarkupLine($"[green]GPU added to Laptop '{settings.LaptopName}'.[/]");
+        return 0;
+    }
+}

+ 19 - 0
RackPeek/Commands/Laptops/Gpus/LaptopGpuAddSettings.cs

@@ -0,0 +1,19 @@
+using System.ComponentModel;
+using Spectre.Console.Cli;
+
+namespace RackPeek.Commands.Laptops.Gpus;
+
+public class LaptopGpuAddSettings : CommandSettings
+{
+    [CommandArgument(0, "<Laptop>")]
+    [Description("The name of the Laptop.")]
+    public string LaptopName { get; set; } = default!;
+
+    [CommandOption("--model")]
+    [Description("The Gpu model.")]
+    public string? Model { get; set; }
+
+    [CommandOption("--vram")]
+    [Description("The amount of gpu vram in Gb.")]
+    public int? Vram { get; set; }
+}

+ 24 - 0
RackPeek/Commands/Laptops/Gpus/LaptopGpuRemoveCommand.cs

@@ -0,0 +1,24 @@
+using Microsoft.Extensions.DependencyInjection;
+using RackPeek.Domain.Resources.Hardware.Laptops.Gpus;
+using Spectre.Console;
+using Spectre.Console.Cli;
+
+namespace RackPeek.Commands.Laptops.Gpus;
+
+public class LaptopGpuRemoveCommand(IServiceProvider provider)
+    : AsyncCommand<LaptopGpuRemoveSettings>
+{
+    public override async Task<int> ExecuteAsync(
+        CommandContext context,
+        LaptopGpuRemoveSettings settings,
+        CancellationToken cancellationToken)
+    {
+        using var scope = provider.CreateScope();
+        var useCase = scope.ServiceProvider.GetRequiredService<RemoveLaptopGpuUseCase>();
+
+        await useCase.ExecuteAsync(settings.LaptopName, settings.Index);
+
+        AnsiConsole.MarkupLine($"[green]GPU #{settings.Index} removed from Laptop '{settings.LaptopName}'.[/]");
+        return 0;
+    }
+}

+ 15 - 0
RackPeek/Commands/Laptops/Gpus/LaptopGpuRemoveSettings.cs

@@ -0,0 +1,15 @@
+using System.ComponentModel;
+using Spectre.Console.Cli;
+
+namespace RackPeek.Commands.Laptops.Gpus;
+
+public class LaptopGpuRemoveSettings : CommandSettings
+{
+    [CommandArgument(0, "<Laptop>")]
+    [Description("The Laptop name.")]
+    public string LaptopName { get; set; } = default!;
+
+    [CommandArgument(1, "<index>")]
+    [Description("The index of the Gpu to remove.")]
+    public int Index { get; set; }
+}

+ 31 - 0
RackPeek/Commands/Laptops/Gpus/LaptopGpuSetCommand.cs

@@ -0,0 +1,31 @@
+using Microsoft.Extensions.DependencyInjection;
+using RackPeek.Domain.Resources.Hardware.Laptops.Gpus;
+using RackPeek.Domain.Resources.Hardware.Models;
+using Spectre.Console;
+using Spectre.Console.Cli;
+
+namespace RackPeek.Commands.Laptops.Gpus;
+
+public class LaptopGpuSetCommand(IServiceProvider provider)
+    : AsyncCommand<LaptopGpuSetSettings>
+{
+    public override async Task<int> ExecuteAsync(
+        CommandContext context,
+        LaptopGpuSetSettings settings,
+        CancellationToken cancellationToken)
+    {
+        using var scope = provider.CreateScope();
+        var useCase = scope.ServiceProvider.GetRequiredService<UpdateLaptopGpuUseCase>();
+
+        var gpu = new Gpu
+        {
+            Model = settings.Model,
+            Vram = settings.Vram
+        };
+
+        await useCase.ExecuteAsync(settings.LaptopName, settings.Index, gpu);
+
+        AnsiConsole.MarkupLine($"[green]GPU #{settings.Index} updated on Laptop '{settings.LaptopName}'.[/]");
+        return 0;
+    }
+}

+ 23 - 0
RackPeek/Commands/Laptops/Gpus/LaptopGpuSetSettings.cs

@@ -0,0 +1,23 @@
+using System.ComponentModel;
+using Spectre.Console.Cli;
+
+namespace RackPeek.Commands.Laptops.Gpus;
+
+public class LaptopGpuSetSettings : CommandSettings
+{
+    [CommandArgument(0, "<Laptop>")]
+    [Description("The Laptop name.")]
+    public string LaptopName { get; set; } = default!;
+
+    [CommandArgument(1, "<index>")]
+    [Description("The index of the gpu to update.")]
+    public int Index { get; set; }
+
+    [CommandOption("--model")]
+    [Description("The gpu model name.")]
+    public string? Model { get; set; }
+
+    [CommandOption("--vram")]
+    [Description("The amount of gpu vram in Gb.")]
+    public int? Vram { get; set; }
+}

+ 24 - 0
RackPeek/Commands/Laptops/LaptopAddCommand.cs

@@ -0,0 +1,24 @@
+using Microsoft.Extensions.DependencyInjection;
+using RackPeek.Domain.Resources.Hardware.Laptops;
+using Spectre.Console;
+using Spectre.Console.Cli;
+
+namespace RackPeek.Commands.Laptops;
+
+public class LaptopAddCommand(IServiceProvider provider)
+    : AsyncCommand<LaptopNameSettings>
+{
+    public override async Task<int> ExecuteAsync(
+        CommandContext context,
+        LaptopNameSettings settings,
+        CancellationToken cancellationToken)
+    {
+        using var scope = provider.CreateScope();
+        var useCase = scope.ServiceProvider.GetRequiredService<AddLaptopUseCase>();
+
+        await useCase.ExecuteAsync(settings.Name);
+
+        AnsiConsole.MarkupLine($"[green]Laptop '{settings.Name}' added.[/]");
+        return 0;
+    }
+}

+ 8 - 0
RackPeek/Commands/Laptops/LaptopCommands.cs

@@ -0,0 +1,8 @@
+using Spectre.Console.Cli;
+
+namespace RackPeek.Commands.Laptops;
+
+public class LaptopNameSettings : CommandSettings
+{
+    [CommandArgument(0, "<name>")] public string Name { get; set; } = default!;
+}

+ 24 - 0
RackPeek/Commands/Laptops/LaptopDeleteCommand.cs

@@ -0,0 +1,24 @@
+using Microsoft.Extensions.DependencyInjection;
+using RackPeek.Domain.Resources.Hardware.Laptops;
+using Spectre.Console;
+using Spectre.Console.Cli;
+
+namespace RackPeek.Commands.Laptops;
+
+public class LaptopDeleteCommand(IServiceProvider provider)
+    : AsyncCommand<LaptopNameSettings>
+{
+    public override async Task<int> ExecuteAsync(
+        CommandContext context,
+        LaptopNameSettings settings,
+        CancellationToken cancellationToken)
+    {
+        using var scope = provider.CreateScope();
+        var useCase = scope.ServiceProvider.GetRequiredService<DeleteLaptopUseCase>();
+
+        await useCase.ExecuteAsync(settings.Name);
+
+        AnsiConsole.MarkupLine($"[green]Laptop '{settings.Name}' deleted.[/]");
+        return 0;
+    }
+}

+ 39 - 0
RackPeek/Commands/Laptops/LaptopDescribeCommand.cs

@@ -0,0 +1,39 @@
+using Microsoft.Extensions.DependencyInjection;
+using RackPeek.Domain.Resources.Hardware.Laptops;
+using Spectre.Console;
+using Spectre.Console.Cli;
+
+namespace RackPeek.Commands.Laptops;
+
+public class LaptopDescribeCommand(IServiceProvider provider)
+    : AsyncCommand<LaptopNameSettings>
+{
+    public override async Task<int> ExecuteAsync(
+        CommandContext context,
+        LaptopNameSettings settings,
+        CancellationToken cancellationToken)
+    {
+        using var scope = provider.CreateScope();
+        var useCase = scope.ServiceProvider.GetRequiredService<DescribeLaptopUseCase>();
+
+        var result = await useCase.ExecuteAsync(settings.Name);
+
+        if (result == null)
+        {
+            AnsiConsole.MarkupLine($"[red]Laptop '{settings.Name}' not found.[/]");
+            return 1;
+        }
+
+        var grid = new Grid().AddColumn().AddColumn();
+
+        grid.AddRow("Name:", result.Name);
+        grid.AddRow("CPUs:", result.CpuCount.ToString());
+        grid.AddRow("RAM:", result.RamSummary ?? "None");
+        grid.AddRow("Drives:", result.DriveCount.ToString());
+        grid.AddRow("GPUs:", result.GpuCount.ToString());
+
+        AnsiConsole.Write(new Panel(grid).Header("Laptop").Border(BoxBorder.Rounded));
+
+        return 0;
+    }
+}

+ 30 - 0
RackPeek/Commands/Laptops/LaptopGetByNameCommand.cs

@@ -0,0 +1,30 @@
+using Microsoft.Extensions.DependencyInjection;
+using RackPeek.Domain.Resources.Hardware.Laptops;
+using Spectre.Console;
+using Spectre.Console.Cli;
+
+namespace RackPeek.Commands.Laptops;
+
+public class LaptopGetByNameCommand(IServiceProvider provider)
+    : AsyncCommand<LaptopNameSettings>
+{
+    public override async Task<int> ExecuteAsync(
+        CommandContext context,
+        LaptopNameSettings settings,
+        CancellationToken cancellationToken)
+    {
+        using var scope = provider.CreateScope();
+        var useCase = scope.ServiceProvider.GetRequiredService<GetLaptopUseCase>();
+
+        var Laptop = await useCase.ExecuteAsync(settings.Name);
+
+        if (Laptop == null)
+        {
+            AnsiConsole.MarkupLine($"[red]Laptop '{settings.Name}' not found.[/]");
+            return 1;
+        }
+
+        AnsiConsole.MarkupLine($"[green]{Laptop.Name}[/]");
+        return 0;
+    }
+}

Unele fișiere nu au fost afișate deoarece prea multe fișiere au fost modificate în acest diff