feat(networks): add name field to networks
- Migration 0007: ALTER TABLE networks ADD COLUMN name TEXT NOT NULL DEFAULT '' - Network model, repository, and API updated to include name - Networks page: name input in the add form, Name column as first column in table - Delete modal now shows "Name (CIDR)" for clarity - Hosts page: network dropdowns now show network name instead of CIDR - Seeds updated with names (LAN, DMZ, Corporate, VPN) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,7 @@ use crate::models::Network;
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct NetworkWithCounts {
|
||||
pub id: i64,
|
||||
pub name: String,
|
||||
pub cidr: String,
|
||||
/// Number of hosts whose IP falls within this network's CIDR range.
|
||||
pub host_count: i64,
|
||||
@@ -28,25 +29,15 @@ pub async fn get_networks() -> Result<Vec<Network>, ServerFnError> {
|
||||
use sqlx::AnyPool;
|
||||
use crate::server::repository::networks as repo;
|
||||
|
||||
// `use_context` retrieves a value previously registered with `provide_context`.
|
||||
// The pool was injected in main.rs before every request.
|
||||
// `ok_or_else` converts `None` into an error (defensive: should never happen).
|
||||
let pool = use_context::<AnyPool>()
|
||||
.ok_or_else(|| ServerFnError::new("Database pool not found in context"))?;
|
||||
|
||||
// Propagate any DB error as a ServerFnError so the client sees a clean message.
|
||||
repo::list_networks(&pool)
|
||||
.await
|
||||
.map_err(|e| ServerFnError::new(e.to_string()))
|
||||
}
|
||||
|
||||
/// Returns all networks enriched with host and application counts.
|
||||
///
|
||||
/// A single SQL query fetches everything at once using correlated subqueries,
|
||||
/// avoiding N+1 round-trips regardless of the number of networks.
|
||||
///
|
||||
/// `application_count` = distinct applications whose registered ports appear
|
||||
/// among the ports open on hosts in each network (via host_ports → application_ports).
|
||||
#[server]
|
||||
pub async fn get_networks_with_counts() -> Result<Vec<NetworkWithCounts>, ServerFnError> {
|
||||
use sqlx::{AnyPool, Row};
|
||||
@@ -57,6 +48,7 @@ pub async fn get_networks_with_counts() -> Result<Vec<NetworkWithCounts>, Server
|
||||
let rows = sqlx::query(
|
||||
"SELECT
|
||||
n.id,
|
||||
n.name,
|
||||
n.cidr,
|
||||
(SELECT COUNT(*) FROM hosts WHERE network_id = n.id) AS host_count,
|
||||
(SELECT COUNT(DISTINCT ap.application_id)
|
||||
@@ -75,6 +67,7 @@ pub async fn get_networks_with_counts() -> Result<Vec<NetworkWithCounts>, Server
|
||||
.into_iter()
|
||||
.map(|row| NetworkWithCounts {
|
||||
id: row.get("id"),
|
||||
name: row.get("name"),
|
||||
cidr: row.get("cidr"),
|
||||
host_count: row.get("host_count"),
|
||||
application_count: row.get("application_count"),
|
||||
@@ -86,23 +79,24 @@ pub async fn get_networks_with_counts() -> Result<Vec<NetworkWithCounts>, Server
|
||||
|
||||
// ─── Mutations ────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Creates a new network with the given CIDR block.
|
||||
/// Creates a new network with the given name and CIDR block.
|
||||
///
|
||||
/// Returns the created record (with its auto-generated id).
|
||||
/// Returns an error if the CIDR is malformed or already exists.
|
||||
#[server]
|
||||
pub async fn create_network(cidr: String) -> Result<Network, ServerFnError> {
|
||||
pub async fn create_network(name: String, cidr: String) -> Result<Network, ServerFnError> {
|
||||
use sqlx::AnyPool;
|
||||
use crate::server::{repository::networks as repo, validation::validate_cidr};
|
||||
|
||||
let pool = use_context::<AnyPool>()
|
||||
.ok_or_else(|| ServerFnError::new("Database pool not found in context"))?;
|
||||
|
||||
// Validate the CIDR before touching the database.
|
||||
// Example of a valid CIDR: "192.168.1.0/24"
|
||||
if name.trim().is_empty() {
|
||||
return Err(ServerFnError::new("Network name cannot be empty"));
|
||||
}
|
||||
|
||||
validate_cidr(&cidr).map_err(|e| ServerFnError::new(e.to_string()))?;
|
||||
|
||||
repo::create_network(&pool, &cidr)
|
||||
repo::create_network(&pool, name.trim(), &cidr)
|
||||
.await
|
||||
.map_err(|e| ServerFnError::new(e.to_string()))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user