Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 1 addition & 21 deletions backend/src/server/api_keys/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,6 @@ pub async fn create_handler(
ApiError::internal_error(&e.to_string())
})?;

tracing::info!(
api_key_id = %api_key.id,
api_key_name = %api_key.base.name,
user_id = %user.user_id,
"API key created via API (key shown to user)"
);

Ok(Json(ApiResponse::success(ApiKeyResponse {
key: api_key.base.key.clone(),
api_key,
Expand All @@ -69,7 +62,7 @@ pub async fn rotate_key_handler(
RequireMember(user): RequireMember,
Path(api_key_id): Path<Uuid>,
) -> ApiResult<Json<ApiResponse<String>>> {
tracing::info!(
tracing::debug!(
api_key_id = %api_key_id,
user_id = %user.user_id,
"API key rotation request received"
Expand All @@ -89,12 +82,6 @@ pub async fn rotate_key_handler(
ApiError::internal_error(&e.to_string())
})?;

tracing::info!(
api_key_id = %api_key_id,
user_id = %user.user_id,
"API key rotated via API (new key shown to user)"
);

Ok(Json(ApiResponse::success(key)))
}

Expand Down Expand Up @@ -150,12 +137,5 @@ pub async fn update_handler(
ApiError::internal_error(&e.to_string())
})?;

tracing::info!(
api_key_id = %id,
api_key_name = %updated.base.name,
user_id = %user.user_id,
"API key updated via API"
);

Ok(Json(ApiResponse::success(updated)))
}
13 changes: 13 additions & 0 deletions backend/src/server/auth/middleware.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::fmt::Display;

use crate::server::{
billing::types::base::BillingPlan,
config::AppState,
Expand Down Expand Up @@ -41,6 +43,17 @@ pub enum AuthenticatedEntity {
Anonymous,
}

impl Display for AuthenticatedEntity {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AuthenticatedEntity::Anonymous => write!(f, "Anonymous"),
AuthenticatedEntity::System => write!(f, "System"),
AuthenticatedEntity::Daemon { .. } => write!(f, "Daemon"),
AuthenticatedEntity::User { .. } => write!(f, "User"),
}
}
}

impl AuthenticatedEntity {
/// Get the user_id if this is a User, otherwise None
pub fn user_id(&self) -> Option<Uuid> {
Expand Down
1 change: 1 addition & 0 deletions backend/src/server/logging/subscriber.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ impl EventSubscriber for LoggingService {
// Log each event individually
for event in events {
event.log();
tracing::debug!("{}", event);
}

Ok(())
Expand Down
42 changes: 42 additions & 0 deletions backend/src/server/shared/events/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::server::{auth::middleware::AuthenticatedEntity, shared::entities::Ent
use chrono::{DateTime, Utc};
use serde::Serialize;
use std::{fmt::Display, net::IpAddr};
use strum::IntoDiscriminant;
use uuid::Uuid;

#[derive(Debug, Clone, Serialize)]
Expand Down Expand Up @@ -67,6 +68,47 @@ impl Event {
}
}

impl Display for Event {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Event::Auth(a) => write!(
f,
"{{ id: {}, user_id: {}, organization_id: {}, operation: {}, timestamp: {}, ip_address: {}, user_agent: {}, metadata: {}, authentication: {} }}",
a.id,
a.user_id
.map(|u| u.to_string())
.unwrap_or("None".to_string()),
a.organization_id
.map(|u| u.to_string())
.unwrap_or("None".to_string()),
a.operation,
a.timestamp,
a.ip_address,
a.user_agent.clone().unwrap_or("Unknown".to_string()),
a.metadata,
a.authentication
),
Event::Entity(e) => write!(
f,
"{{ id: {}, entity_type: {}, entity_id: {}, network_id: {}, organization_id: {}, operation: {}, timestamp: {}, metadata: {}, authentication: {} }}",
e.id,
e.entity_type.discriminant(),
e.entity_id,
e.network_id
.map(|u| u.to_string())
.unwrap_or("None".to_string()),
e.organization_id
.map(|u| u.to_string())
.unwrap_or("None".to_string()),
e.operation,
e.timestamp,
e.metadata,
e.authentication
),
}
}
}

impl PartialEq for Event {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
Expand Down
41 changes: 27 additions & 14 deletions backend/src/server/topology/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,11 @@ pub async fn create_handler(

let service = Topology::get_service(&state);

let (hosts, services, subnets, groups) =
service.get_entity_data(topology.base.network_id).await?;
let (hosts, subnets, groups) = service.get_entity_data(topology.base.network_id).await?;

let services = service
.get_service_data(topology.base.network_id, &topology.base.options)
.await?;

let (nodes, edges) = service.build_graph(BuildGraphParams {
options: &topology.base.options,
Expand All @@ -83,7 +86,7 @@ pub async fn create_handler(
topology.base.groups = groups;
topology.base.edges = edges;
topology.base.nodes = nodes;
topology.refresh();
topology.clear_stale();

let created = service
.create(topology, user.clone().into())
Expand Down Expand Up @@ -113,32 +116,40 @@ async fn refresh(
State(state): State<Arc<AppState>>,
RequireMember(user): RequireMember,
Json(mut topology): Json<Topology>,
) -> ApiResult<Json<ApiResponse<Topology>>> {
) -> ApiResult<Json<ApiResponse<()>>> {
let service = Topology::get_service(&state);

let (hosts, services, subnets, groups) =
service.get_entity_data(topology.base.network_id).await?;
let (hosts, subnets, groups) = service.get_entity_data(topology.base.network_id).await?;

let services = service
.get_service_data(topology.base.network_id, &topology.base.options)
.await?;

topology.base.hosts = hosts;
topology.base.services = services;
topology.base.subnets = subnets;
topology.base.groups = groups;

let updated = service.update(&mut topology, user.into()).await?;
service.update(&mut topology, user.into()).await?;

Ok(Json(ApiResponse::success(updated)))
// Return will be handled through event subscriber which triggers SSE

Ok(Json(ApiResponse::success(())))
}

/// Recalculate node and edges and refresh entity data
async fn rebuild(
State(state): State<Arc<AppState>>,
RequireMember(user): RequireMember,
Json(mut topology): Json<Topology>,
) -> ApiResult<Json<ApiResponse<Topology>>> {
) -> ApiResult<Json<ApiResponse<()>>> {
let service = Topology::get_service(&state);

let (hosts, services, subnets, groups) =
service.get_entity_data(topology.base.network_id).await?;
let (hosts, subnets, groups) = service.get_entity_data(topology.base.network_id).await?;

let services = service
.get_service_data(topology.base.network_id, &topology.base.options)
.await?;

let (nodes, edges) = service.build_graph(BuildGraphParams {
options: &topology.base.options,
Expand All @@ -156,11 +167,13 @@ async fn rebuild(
topology.base.groups = groups;
topology.base.edges = edges;
topology.base.nodes = nodes;
topology.refresh();
topology.clear_stale();

let updated = service.update(&mut topology, user.into()).await?;
service.update(&mut topology, user.into()).await?;

Ok(Json(ApiResponse::success(updated)))
// Return will be handled through event subscriber which triggers SSE

Ok(Json(ApiResponse::success(())))
}

async fn lock(
Expand Down
16 changes: 1 addition & 15 deletions backend/src/server/topology/service/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::server::{
pub struct TopologyContext<'a> {
pub hosts: &'a [Host],
pub subnets: &'a [Subnet],
services: &'a [Service],
pub services: &'a [Service],
pub groups: &'a [Group],
pub options: &'a TopologyOptions,
}
Expand All @@ -45,20 +45,6 @@ impl<'a> TopologyContext<'a> {
// Data Access Methods
// ============================================================================

pub fn services(&self) -> Vec<Service> {
self.services
.iter()
.filter(|s| {
!self
.options
.request
.hide_service_categories
.contains(&s.base.service_definition.category())
})
.cloned()
.collect()
}

pub fn get_subnet_by_id(&self, subnet_id: Uuid) -> Option<&'a Subnet> {
self.subnets.iter().find(|s| s.id == subnet_id)
}
Expand Down
8 changes: 4 additions & 4 deletions backend/src/server/topology/service/edge_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ impl EdgeBuilder {
let mut docker_service_to_containerized_service_ids: HashMap<Uuid, Vec<Uuid>> =
HashMap::new();

ctx.services().iter().for_each(|s| {
ctx.services.iter().for_each(|s| {
if let Some(ServiceVirtualization::Docker(docker_virtualization)) =
&s.base.virtualization
{
Expand All @@ -79,7 +79,7 @@ impl EdgeBuilder {
});

let edges = ctx
.services()
.services
.iter()
.filter(|s| {
docker_service_to_containerized_service_ids
Expand Down Expand Up @@ -415,14 +415,14 @@ impl EdgeBuilder {
target_binding_id: Uuid,
group: &Group,
) -> Option<Edge> {
let source_interface = ctx.services().iter().find_map(|s| {
let source_interface = ctx.services.iter().find_map(|s| {
if let Some(source_binding) = s.get_binding(source_binding_id) {
return Some(source_binding.interface_id());
}
None
});

let target_interface = ctx.services().iter().find_map(|s| {
let target_interface = ctx.services.iter().find_map(|s| {
if let Some(target_binding) = s.get_binding(target_binding_id) {
return Some(target_binding.interface_id());
}
Expand Down
27 changes: 24 additions & 3 deletions backend/src/server/topology/service/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,15 +98,36 @@ impl TopologyService {
pub async fn get_entity_data(
&self,
network_id: Uuid,
) -> Result<(Vec<Host>, Vec<Service>, Vec<Subnet>, Vec<Group>), Error> {
) -> Result<(Vec<Host>, Vec<Subnet>, Vec<Group>), Error> {
let network_filter = EntityFilter::unfiltered().network_ids(&[network_id]);
// Fetch all data
let hosts = self.host_service.get_all(network_filter.clone()).await?;
let subnets = self.subnet_service.get_all(network_filter.clone()).await?;
let groups = self.group_service.get_all(network_filter.clone()).await?;
let services = self.service_service.get_all(network_filter.clone()).await?;

Ok((hosts, services, subnets, groups))
Ok((hosts, subnets, groups))
}

pub async fn get_service_data(
&self,
network_id: Uuid,
options: &TopologyOptions,
) -> Result<Vec<Service>, Error> {
let network_filter = EntityFilter::unfiltered().network_ids(&[network_id]);

Ok(self
.service_service
.get_all(network_filter.clone())
.await?
.iter()
.filter(|s| {
!options
.request
.hide_service_categories
.contains(&s.base.service_definition.category())
})
.cloned()
.collect())
}

pub fn build_graph(&self, params: BuildGraphParams) -> (Vec<Node>, Vec<Edge>) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ impl SubnetLayoutPlanner {
for interface in &host.base.interfaces {
let subnet = ctx.get_subnet_by_id(interface.base.subnet_id);
let subnet_type = subnet.map(|s| s.base.subnet_type).unwrap_or_default();
let services = ctx.services();
let services = ctx.services;

let interface_bound_services: Vec<&Service> = services
.iter()
Expand Down
Loading