feat: Add core models and protocol definitions for NxMesh
This commit is contained in:
18
crates/nxmesh-core/Cargo.toml
Normal file
18
crates/nxmesh-core/Cargo.toml
Normal file
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "nxmesh-core"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
thiserror.workspace = true
|
||||
chrono.workspace = true
|
||||
uuid.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
tokio-test.workspace = true
|
||||
43
crates/nxmesh-core/src/error.rs
Normal file
43
crates/nxmesh-core/src/error.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
//! Core error types
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
/// Result type alias for core operations
|
||||
pub type Result<T> = std::result::Result<T, CoreError>;
|
||||
|
||||
/// Core error types used across NxMesh
|
||||
#[derive(Error, Debug, Clone)]
|
||||
pub enum CoreError {
|
||||
#[error("Validation error: {0}")]
|
||||
Validation(String),
|
||||
|
||||
#[error("Invalid configuration: {0}")]
|
||||
InvalidConfig(String),
|
||||
|
||||
#[error("Serialization error: {0}")]
|
||||
Serialization(String),
|
||||
|
||||
#[error("Crypto error: {0}")]
|
||||
Crypto(String),
|
||||
|
||||
#[error("Not found: {0}")]
|
||||
NotFound(String),
|
||||
|
||||
#[error("Permission denied: {0}")]
|
||||
PermissionDenied(String),
|
||||
|
||||
#[error("Internal error: {0}")]
|
||||
Internal(String),
|
||||
}
|
||||
|
||||
impl CoreError {
|
||||
/// Create a validation error
|
||||
pub fn validation<S: Into<String>>(msg: S) -> Self {
|
||||
Self::Validation(msg.into())
|
||||
}
|
||||
|
||||
/// Create a not found error
|
||||
pub fn not_found<S: Into<String>>(resource: S) -> Self {
|
||||
Self::NotFound(resource.into())
|
||||
}
|
||||
}
|
||||
9
crates/nxmesh-core/src/lib.rs
Normal file
9
crates/nxmesh-core/src/lib.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
//! NxMesh Core Library
|
||||
//!
|
||||
//! This crate contains shared types, models, and utilities used by both
|
||||
//! the master and agent components of NxMesh.
|
||||
|
||||
pub mod error;
|
||||
pub mod models;
|
||||
|
||||
pub use error::{CoreError, Result};
|
||||
152
crates/nxmesh-core/src/models/agent.rs
Normal file
152
crates/nxmesh-core/src/models/agent.rs
Normal file
@@ -0,0 +1,152 @@
|
||||
//! Agent model
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use uuid::Uuid;
|
||||
|
||||
/// Agent entity - represents a running nxmesh-agent instance
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Agent {
|
||||
pub id: Uuid,
|
||||
pub workspace_id: Uuid,
|
||||
pub name: String,
|
||||
pub hostname: String,
|
||||
pub ip_address: String,
|
||||
pub version: String,
|
||||
pub state: AgentState,
|
||||
pub deployment_mode: DeploymentMode,
|
||||
pub last_seen_at: DateTime<Utc>,
|
||||
pub capabilities: Vec<String>,
|
||||
pub labels: HashMap<String, String>,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
/// Agent connection state
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum AgentState {
|
||||
/// Registered but never connected
|
||||
Pending,
|
||||
/// Connected and healthy
|
||||
Online,
|
||||
/// Disconnected
|
||||
Offline,
|
||||
/// Connected but health checks failing
|
||||
Degraded,
|
||||
/// Manually placed in maintenance mode
|
||||
Maintenance,
|
||||
}
|
||||
|
||||
/// Deployment mode for the agent
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum DeploymentMode {
|
||||
/// Docker sidecar (shares PID namespace with nginx)
|
||||
DockerSidecar,
|
||||
/// Kubernetes sidecar
|
||||
KubernetesSidecar,
|
||||
/// Standalone mode (VM or bare metal)
|
||||
Standalone,
|
||||
}
|
||||
|
||||
/// Health report from agent
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct HealthReport {
|
||||
pub agent_id: Uuid,
|
||||
pub timestamp: DateTime<Utc>,
|
||||
pub nginx_status: NginxStatus,
|
||||
pub system_metrics: SystemMetrics,
|
||||
pub config_checksum: String,
|
||||
pub alerts: Vec<Alert>,
|
||||
}
|
||||
|
||||
/// Nginx process status
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct NginxStatus {
|
||||
pub is_running: bool,
|
||||
pub pid: Option<u32>,
|
||||
pub uptime_seconds: u64,
|
||||
pub active_connections: u32,
|
||||
pub requests_per_second: f64,
|
||||
}
|
||||
|
||||
/// System-level metrics
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SystemMetrics {
|
||||
pub cpu_percent: f64,
|
||||
pub memory_used_mb: u64,
|
||||
pub memory_total_mb: u64,
|
||||
pub disk_used_gb: u64,
|
||||
pub disk_total_gb: u64,
|
||||
}
|
||||
|
||||
/// Alert from agent
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Alert {
|
||||
pub id: String,
|
||||
pub severity: AlertSeverity,
|
||||
pub message: String,
|
||||
pub timestamp: DateTime<Utc>,
|
||||
}
|
||||
|
||||
/// Alert severity levels
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum AlertSeverity {
|
||||
Info,
|
||||
Warning,
|
||||
Error,
|
||||
Critical,
|
||||
}
|
||||
|
||||
impl Agent {
|
||||
/// Create a new agent registration
|
||||
pub fn new(
|
||||
workspace_id: Uuid,
|
||||
name: impl Into<String>,
|
||||
hostname: impl Into<String>,
|
||||
ip_address: impl Into<String>,
|
||||
version: impl Into<String>,
|
||||
deployment_mode: DeploymentMode,
|
||||
) -> Self {
|
||||
let now = Utc::now();
|
||||
Self {
|
||||
id: Uuid::new_v4(),
|
||||
workspace_id,
|
||||
name: name.into(),
|
||||
hostname: hostname.into(),
|
||||
ip_address: ip_address.into(),
|
||||
version: version.into(),
|
||||
state: AgentState::Pending,
|
||||
deployment_mode,
|
||||
last_seen_at: now,
|
||||
capabilities: Vec::new(),
|
||||
labels: HashMap::new(),
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_agent_creation() {
|
||||
let ws_id = Uuid::new_v4();
|
||||
let agent = Agent::new(
|
||||
ws_id,
|
||||
"web-server-01",
|
||||
"web-01.internal",
|
||||
"10.0.1.10",
|
||||
"0.1.0",
|
||||
DeploymentMode::DockerSidecar,
|
||||
);
|
||||
assert_eq!(agent.name, "web-server-01");
|
||||
assert_eq!(agent.state, AgentState::Pending);
|
||||
assert!(agent.capabilities.is_empty());
|
||||
}
|
||||
}
|
||||
146
crates/nxmesh-core/src/models/certificate.rs
Normal file
146
crates/nxmesh-core/src/models/certificate.rs
Normal file
@@ -0,0 +1,146 @@
|
||||
//! Certificate model
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
/// SSL/TLS Certificate entity
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Certificate {
|
||||
pub id: Uuid,
|
||||
pub workspace_id: Uuid,
|
||||
pub domain: String,
|
||||
pub is_wildcard: bool,
|
||||
pub provider: CertificateProvider,
|
||||
pub status: CertificateStatus,
|
||||
pub issued_at: Option<DateTime<Utc>>,
|
||||
pub expires_at: Option<DateTime<Utc>>,
|
||||
pub auto_renew: bool,
|
||||
pub certificate_pem: Option<String>,
|
||||
pub private_key_pem: Option<String>,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
/// Certificate provider
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum CertificateProvider {
|
||||
LetsEncrypt,
|
||||
Custom,
|
||||
}
|
||||
|
||||
/// Certificate status
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum CertificateStatus {
|
||||
Pending,
|
||||
Active,
|
||||
Expired,
|
||||
Error,
|
||||
}
|
||||
|
||||
/// ACME challenge info
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AcmeChallenge {
|
||||
pub certificate_id: Uuid,
|
||||
pub challenge_type: ChallengeType,
|
||||
pub token: String,
|
||||
pub key_authorization: String,
|
||||
pub status: ChallengeStatus,
|
||||
pub completed_at: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
/// ACME challenge type
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ChallengeType {
|
||||
Http01,
|
||||
Dns01,
|
||||
}
|
||||
|
||||
/// ACME challenge status
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ChallengeStatus {
|
||||
Pending,
|
||||
Processing,
|
||||
Valid,
|
||||
Invalid,
|
||||
}
|
||||
|
||||
impl Certificate {
|
||||
/// Create a new certificate request
|
||||
pub fn new(
|
||||
workspace_id: Uuid,
|
||||
domain: impl Into<String>,
|
||||
provider: CertificateProvider,
|
||||
auto_renew: bool,
|
||||
) -> Self {
|
||||
let now = Utc::now();
|
||||
Self {
|
||||
id: Uuid::new_v4(),
|
||||
workspace_id,
|
||||
domain: domain.into(),
|
||||
is_wildcard: false,
|
||||
provider,
|
||||
status: CertificateStatus::Pending,
|
||||
issued_at: None,
|
||||
expires_at: None,
|
||||
auto_renew,
|
||||
certificate_pem: None,
|
||||
private_key_pem: None,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if certificate is expired or expiring soon
|
||||
pub fn is_expiring_soon(&self, days: i64) -> bool {
|
||||
match self.expires_at {
|
||||
Some(expires) => {
|
||||
let threshold = Utc::now() + chrono::Duration::days(days);
|
||||
expires <= threshold
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_certificate_creation() {
|
||||
let ws_id = Uuid::new_v4();
|
||||
let cert = Certificate::new(
|
||||
ws_id,
|
||||
"example.com",
|
||||
CertificateProvider::LetsEncrypt,
|
||||
true,
|
||||
);
|
||||
assert_eq!(cert.domain, "example.com");
|
||||
assert_eq!(cert.status, CertificateStatus::Pending);
|
||||
assert!(cert.auto_renew);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_certificate_expiring_soon() {
|
||||
let ws_id = Uuid::new_v4();
|
||||
let mut cert = Certificate::new(
|
||||
ws_id,
|
||||
"example.com",
|
||||
CertificateProvider::LetsEncrypt,
|
||||
true,
|
||||
);
|
||||
|
||||
// Not expiring
|
||||
cert.expires_at = Some(Utc::now() + chrono::Duration::days(60));
|
||||
assert!(!cert.is_expiring_soon(30));
|
||||
|
||||
// Expiring soon
|
||||
cert.expires_at = Some(Utc::now() + chrono::Duration::days(20));
|
||||
assert!(cert.is_expiring_soon(30));
|
||||
}
|
||||
}
|
||||
195
crates/nxmesh-core/src/models/config.rs
Normal file
195
crates/nxmesh-core/src/models/config.rs
Normal file
@@ -0,0 +1,195 @@
|
||||
//! Configuration models
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use uuid::Uuid;
|
||||
|
||||
/// Virtual Host (server block) configuration
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct VirtualHost {
|
||||
pub id: Uuid,
|
||||
pub workspace_id: Uuid,
|
||||
pub name: String,
|
||||
pub server_name: String,
|
||||
pub listen_port: u16,
|
||||
pub ssl_enabled: bool,
|
||||
pub ssl_certificate_id: Option<Uuid>,
|
||||
pub locations: Vec<Location>,
|
||||
pub http2_enabled: bool,
|
||||
pub http3_enabled: bool,
|
||||
pub gzip_enabled: bool,
|
||||
pub rate_limiting: Option<RateLimitConfig>,
|
||||
pub target_agents: AgentSelector,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
/// Location block configuration
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Location {
|
||||
pub path: String,
|
||||
pub proxy_pass: Option<String>,
|
||||
pub upstream_id: Option<Uuid>,
|
||||
pub root: Option<String>,
|
||||
pub index: Option<String>,
|
||||
pub custom_headers: Vec<Header>,
|
||||
pub rewrite_rules: Vec<RewriteRule>,
|
||||
}
|
||||
|
||||
/// HTTP header
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Header {
|
||||
pub name: String,
|
||||
pub value: String,
|
||||
pub always: bool,
|
||||
}
|
||||
|
||||
/// Rewrite rule
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct RewriteRule {
|
||||
pub pattern: String,
|
||||
pub replacement: String,
|
||||
pub flag: String,
|
||||
}
|
||||
|
||||
/// Rate limiting configuration
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct RateLimitConfig {
|
||||
pub zone: String,
|
||||
pub burst: u32,
|
||||
pub per_minute: Option<u32>,
|
||||
pub per_second: Option<u32>,
|
||||
}
|
||||
|
||||
/// Upstream (backend pool) configuration
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Upstream {
|
||||
pub id: Uuid,
|
||||
pub workspace_id: Uuid,
|
||||
pub name: String,
|
||||
pub algorithm: LoadBalanceAlgorithm,
|
||||
pub servers: Vec<UpstreamServer>,
|
||||
pub health_check: Option<HealthCheckConfig>,
|
||||
pub keepalive_connections: Option<u32>,
|
||||
pub keepalive_timeout: Option<u32>,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
/// Upstream server definition
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct UpstreamServer {
|
||||
pub address: String,
|
||||
pub weight: u32,
|
||||
pub backup: bool,
|
||||
pub down: bool,
|
||||
pub max_fails: u32,
|
||||
pub fail_timeout: u32,
|
||||
}
|
||||
|
||||
/// Load balancing algorithm
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum LoadBalanceAlgorithm {
|
||||
RoundRobin,
|
||||
LeastConnections,
|
||||
IpHash,
|
||||
WeightedRoundRobin,
|
||||
}
|
||||
|
||||
/// Health check configuration for upstreams
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct HealthCheckConfig {
|
||||
pub enabled: bool,
|
||||
pub path: String,
|
||||
pub interval: u32,
|
||||
pub timeout: u32,
|
||||
pub healthy_threshold: u32,
|
||||
pub unhealthy_threshold: u32,
|
||||
}
|
||||
|
||||
/// Selector for targeting agents
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum AgentSelector {
|
||||
/// All agents in the workspace
|
||||
All,
|
||||
/// Agents matching label selector
|
||||
Selector { label_selector: String },
|
||||
/// Specific agent
|
||||
Agent { agent_id: Uuid },
|
||||
}
|
||||
|
||||
/// Configuration scope for distribution
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ConfigScope {
|
||||
/// All agents
|
||||
Global,
|
||||
/// All agents in workspace
|
||||
Workspace,
|
||||
/// Agents with specific label selector
|
||||
AgentGroup(String),
|
||||
/// Single agent
|
||||
Agent(Uuid),
|
||||
}
|
||||
|
||||
/// Configuration version for tracking changes
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ConfigVersion {
|
||||
pub id: Uuid,
|
||||
pub resource_type: String,
|
||||
pub resource_id: Uuid,
|
||||
pub version_number: u64,
|
||||
pub data: serde_json::Value,
|
||||
pub checksum: String,
|
||||
pub created_by: Uuid,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub change_summary: String,
|
||||
}
|
||||
|
||||
/// Configuration update message sent to agents
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ConfigUpdate {
|
||||
pub config_id: String,
|
||||
pub version: u64,
|
||||
pub virtual_hosts: Vec<VirtualHost>,
|
||||
pub upstreams: Vec<Upstream>,
|
||||
pub ssl_certificates: HashMap<String, Certificate>,
|
||||
}
|
||||
|
||||
/// Certificate data in config update
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Certificate {
|
||||
pub id: String,
|
||||
pub domain: String,
|
||||
pub certificate_pem: String,
|
||||
pub private_key_pem: String,
|
||||
pub expires_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
/// Configuration apply status
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ConfigApplyStatus {
|
||||
Pending,
|
||||
Validating,
|
||||
Applying,
|
||||
Success,
|
||||
Failed,
|
||||
RolledBack,
|
||||
}
|
||||
|
||||
impl Default for UpstreamServer {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
address: String::new(),
|
||||
weight: 1,
|
||||
backup: false,
|
||||
down: false,
|
||||
max_fails: 1,
|
||||
fail_timeout: 10,
|
||||
}
|
||||
}
|
||||
}
|
||||
13
crates/nxmesh-core/src/models/mod.rs
Normal file
13
crates/nxmesh-core/src/models/mod.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
//! Shared data models
|
||||
|
||||
pub mod agent;
|
||||
pub mod certificate;
|
||||
pub mod config;
|
||||
pub mod organization;
|
||||
pub mod workspace;
|
||||
|
||||
pub use agent::{Agent, AgentState, DeploymentMode};
|
||||
pub use certificate::{Certificate, CertificateProvider, CertificateStatus};
|
||||
pub use config::{AgentSelector, ConfigScope, Location, Upstream, UpstreamServer, VirtualHost};
|
||||
pub use organization::Organization;
|
||||
pub use workspace::Workspace;
|
||||
62
crates/nxmesh-core/src/models/organization.rs
Normal file
62
crates/nxmesh-core/src/models/organization.rs
Normal file
@@ -0,0 +1,62 @@
|
||||
//! Organization model
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
/// Organization entity
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Organization {
|
||||
pub id: Uuid,
|
||||
pub name: String,
|
||||
pub slug: String,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
pub settings: OrganizationSettings,
|
||||
}
|
||||
|
||||
/// Organization-specific settings
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct OrganizationSettings {
|
||||
pub allow_public_agents: bool,
|
||||
pub max_workspaces: Option<u32>,
|
||||
pub max_agents_per_workspace: Option<u32>,
|
||||
}
|
||||
|
||||
impl Default for OrganizationSettings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
allow_public_agents: false,
|
||||
max_workspaces: None,
|
||||
max_agents_per_workspace: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Organization {
|
||||
/// Create a new organization
|
||||
pub fn new(name: impl Into<String>, slug: impl Into<String>) -> Self {
|
||||
let now = Utc::now();
|
||||
Self {
|
||||
id: Uuid::new_v4(),
|
||||
name: name.into(),
|
||||
slug: slug.into(),
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
settings: OrganizationSettings::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_organization_creation() {
|
||||
let org = Organization::new("Acme Corp", "acme-corp");
|
||||
assert_eq!(org.name, "Acme Corp");
|
||||
assert_eq!(org.slug, "acme-corp");
|
||||
assert!(!org.settings.allow_public_agents);
|
||||
}
|
||||
}
|
||||
49
crates/nxmesh-core/src/models/workspace.rs
Normal file
49
crates/nxmesh-core/src/models/workspace.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
//! Workspace model
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
/// Workspace entity - isolated resource container within an organization
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Workspace {
|
||||
pub id: Uuid,
|
||||
pub organization_id: Uuid,
|
||||
pub name: String,
|
||||
pub slug: String,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
impl Workspace {
|
||||
/// Create a new workspace
|
||||
pub fn new(
|
||||
organization_id: Uuid,
|
||||
name: impl Into<String>,
|
||||
slug: impl Into<String>,
|
||||
) -> Self {
|
||||
let now = Utc::now();
|
||||
Self {
|
||||
id: Uuid::new_v4(),
|
||||
organization_id,
|
||||
name: name.into(),
|
||||
slug: slug.into(),
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_workspace_creation() {
|
||||
let org_id = Uuid::new_v4();
|
||||
let ws = Workspace::new(org_id, "Production", "production");
|
||||
assert_eq!(ws.name, "Production");
|
||||
assert_eq!(ws.slug, "production");
|
||||
assert_eq!(ws.organization_id, org_id);
|
||||
}
|
||||
}
|
||||
15
crates/nxmesh-proto/Cargo.toml
Normal file
15
crates/nxmesh-proto/Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "nxmesh-proto"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
tonic.workspace = true
|
||||
prost.workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
tonic-build = "0.11"
|
||||
9
crates/nxmesh-proto/build.rs
Normal file
9
crates/nxmesh-proto/build.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
use std::io::Result;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
tonic_build::configure()
|
||||
.build_server(true)
|
||||
.build_client(true)
|
||||
.compile(&["proto/agent.proto"], &["proto"])?;
|
||||
Ok(())
|
||||
}
|
||||
298
crates/nxmesh-proto/proto/agent.proto
Normal file
298
crates/nxmesh-proto/proto/agent.proto
Normal file
@@ -0,0 +1,298 @@
|
||||
syntax = "proto3";
|
||||
package nxmesh.agent.v1;
|
||||
|
||||
option go_package = "github.com/nxmesh/api/agent/v1";
|
||||
|
||||
// AgentService defines the bidirectional communication between master and agents
|
||||
service AgentService {
|
||||
// Stream establishes a persistent connection for real-time communication
|
||||
rpc Stream(stream AgentMessage) returns (stream MasterMessage);
|
||||
|
||||
// ReportHealth sends a health report to the master
|
||||
rpc ReportHealth(HealthReport) returns (Ack);
|
||||
|
||||
// ReportMetrics sends metrics batch to the master
|
||||
rpc ReportMetrics(MetricsBatch) returns (Ack);
|
||||
}
|
||||
|
||||
// Messages sent from agent to master
|
||||
message AgentMessage {
|
||||
string agent_id = 1;
|
||||
int64 timestamp = 2;
|
||||
oneof payload {
|
||||
RegistrationRequest registration = 3;
|
||||
HealthReport health = 4;
|
||||
ConfigStatus config_status = 5;
|
||||
MetricsBatch metrics = 6;
|
||||
LogBatch logs = 7;
|
||||
Event event = 8;
|
||||
}
|
||||
}
|
||||
|
||||
// Messages sent from master to agent
|
||||
message MasterMessage {
|
||||
int64 timestamp = 1;
|
||||
oneof payload {
|
||||
RegistrationResponse registration_response = 2;
|
||||
ConfigUpdate config_update = 3;
|
||||
Command command = 4;
|
||||
Ack ack = 5;
|
||||
Error error = 6;
|
||||
}
|
||||
}
|
||||
|
||||
// Registration
|
||||
message RegistrationRequest {
|
||||
string token = 1;
|
||||
string hostname = 2;
|
||||
string ip_address = 3;
|
||||
string version = 4;
|
||||
repeated string capabilities = 5;
|
||||
map<string, string> labels = 6;
|
||||
DeploymentMode deployment_mode = 7;
|
||||
}
|
||||
|
||||
message RegistrationResponse {
|
||||
string agent_id = 1;
|
||||
bool success = 2;
|
||||
string error_message = 3;
|
||||
int64 heartbeat_interval_seconds = 4;
|
||||
}
|
||||
|
||||
enum DeploymentMode {
|
||||
DEPLOYMENT_MODE_UNSPECIFIED = 0;
|
||||
DOCKER_SIDECAR = 1;
|
||||
KUBERNETES_SIDECAR = 2;
|
||||
STANDALONE = 3;
|
||||
}
|
||||
|
||||
// Health Reporting
|
||||
message HealthReport {
|
||||
NginxStatus nginx = 1;
|
||||
SystemMetrics system = 2;
|
||||
string config_checksum = 3;
|
||||
int64 config_version = 4;
|
||||
repeated Alert alerts = 5;
|
||||
}
|
||||
|
||||
message NginxStatus {
|
||||
bool is_running = 1;
|
||||
uint32 pid = 2;
|
||||
uint64 uptime_seconds = 3;
|
||||
uint32 active_connections = 4;
|
||||
uint64 total_requests = 5;
|
||||
float requests_per_second = 6;
|
||||
}
|
||||
|
||||
message SystemMetrics {
|
||||
float cpu_percent = 1;
|
||||
uint64 memory_used_bytes = 2;
|
||||
uint64 memory_total_bytes = 3;
|
||||
uint64 disk_used_bytes = 4;
|
||||
uint64 disk_total_bytes = 5;
|
||||
float load_average_1m = 6;
|
||||
}
|
||||
|
||||
message Alert {
|
||||
string id = 1;
|
||||
string severity = 2; // info, warning, error, critical
|
||||
string message = 3;
|
||||
int64 timestamp = 4;
|
||||
}
|
||||
|
||||
// Configuration
|
||||
message ConfigUpdate {
|
||||
string config_id = 1;
|
||||
int64 version = 2;
|
||||
repeated VirtualHost virtual_hosts = 3;
|
||||
repeated Upstream upstreams = 4;
|
||||
map<string, Certificate> certificates = 5;
|
||||
GlobalSettings global_settings = 6;
|
||||
}
|
||||
|
||||
message VirtualHost {
|
||||
string id = 1;
|
||||
string name = 2;
|
||||
string server_name = 3;
|
||||
uint32 listen_port = 4;
|
||||
bool ssl_enabled = 5;
|
||||
string ssl_certificate_id = 6;
|
||||
bool http2_enabled = 7;
|
||||
bool http3_enabled = 8;
|
||||
repeated Location locations = 9;
|
||||
map<string, string> custom_directives = 10;
|
||||
}
|
||||
|
||||
message Location {
|
||||
string path = 1;
|
||||
string proxy_pass = 2;
|
||||
string upstream_id = 3;
|
||||
string root = 4;
|
||||
string index = 5;
|
||||
repeated Header custom_headers = 6;
|
||||
repeated RewriteRule rewrite_rules = 7;
|
||||
map<string, string> custom_directives = 8;
|
||||
}
|
||||
|
||||
message Header {
|
||||
string name = 1;
|
||||
string value = 2;
|
||||
bool always = 3;
|
||||
}
|
||||
|
||||
message RewriteRule {
|
||||
string pattern = 1;
|
||||
string replacement = 2;
|
||||
string flag = 3;
|
||||
}
|
||||
|
||||
message Upstream {
|
||||
string id = 1;
|
||||
string name = 2;
|
||||
LoadBalanceAlgorithm algorithm = 3;
|
||||
repeated UpstreamServer servers = 4;
|
||||
HealthCheckConfig health_check = 5;
|
||||
uint32 keepalive_connections = 6;
|
||||
}
|
||||
|
||||
enum LoadBalanceAlgorithm {
|
||||
LOAD_BALANCE_ALGORITHM_UNSPECIFIED = 0;
|
||||
ROUND_ROBIN = 1;
|
||||
LEAST_CONNECTIONS = 2;
|
||||
IP_HASH = 3;
|
||||
WEIGHTED_ROUND_ROBIN = 4;
|
||||
}
|
||||
|
||||
message UpstreamServer {
|
||||
string address = 1;
|
||||
uint32 weight = 2;
|
||||
bool backup = 3;
|
||||
bool down = 4;
|
||||
uint32 max_fails = 5;
|
||||
uint32 fail_timeout_seconds = 6;
|
||||
}
|
||||
|
||||
message HealthCheckConfig {
|
||||
bool enabled = 1;
|
||||
string path = 2;
|
||||
uint32 interval_seconds = 3;
|
||||
uint32 timeout_seconds = 4;
|
||||
uint32 healthy_threshold = 5;
|
||||
uint32 unhealthy_threshold = 6;
|
||||
}
|
||||
|
||||
message Certificate {
|
||||
string id = 1;
|
||||
string domain = 2;
|
||||
string certificate_pem = 3;
|
||||
string private_key_pem = 4;
|
||||
int64 expires_at = 5;
|
||||
}
|
||||
|
||||
message GlobalSettings {
|
||||
map<string, string> nginx_directives = 1;
|
||||
map<string, string> env_vars = 2;
|
||||
}
|
||||
|
||||
message ConfigStatus {
|
||||
string config_id = 1;
|
||||
int64 version = 2;
|
||||
ConfigApplyStatus status = 3;
|
||||
string error_message = 4;
|
||||
int64 applied_at = 5;
|
||||
}
|
||||
|
||||
enum ConfigApplyStatus {
|
||||
CONFIG_APPLY_STATUS_UNSPECIFIED = 0;
|
||||
PENDING = 1;
|
||||
VALIDATING = 2;
|
||||
APPLYING = 3;
|
||||
SUCCESS = 4;
|
||||
FAILED = 5;
|
||||
ROLLED_BACK = 6;
|
||||
}
|
||||
|
||||
// Metrics
|
||||
message MetricsBatch {
|
||||
int64 timestamp = 1;
|
||||
repeated Metric metrics = 2;
|
||||
}
|
||||
|
||||
message Metric {
|
||||
string name = 1;
|
||||
double value = 2;
|
||||
int64 timestamp = 3;
|
||||
map<string, string> labels = 4;
|
||||
MetricType type = 5;
|
||||
}
|
||||
|
||||
enum MetricType {
|
||||
METRIC_TYPE_UNSPECIFIED = 0;
|
||||
GAUGE = 1;
|
||||
COUNTER = 2;
|
||||
HISTOGRAM = 3;
|
||||
}
|
||||
|
||||
// Logs
|
||||
message LogBatch {
|
||||
repeated LogEntry entries = 1;
|
||||
}
|
||||
|
||||
message LogEntry {
|
||||
int64 timestamp = 1;
|
||||
string level = 2;
|
||||
string message = 3;
|
||||
map<string, string> fields = 4;
|
||||
}
|
||||
|
||||
// Commands
|
||||
message Command {
|
||||
string command_id = 1;
|
||||
oneof command {
|
||||
ReloadCommand reload = 2;
|
||||
RestartCommand restart = 3;
|
||||
StopCommand stop = 4;
|
||||
GetStatusCommand get_status = 5;
|
||||
ValidateConfigCommand validate_config = 6;
|
||||
}
|
||||
}
|
||||
|
||||
message ReloadCommand {
|
||||
bool graceful = 1;
|
||||
}
|
||||
|
||||
message RestartCommand {
|
||||
bool force = 1;
|
||||
}
|
||||
|
||||
message StopCommand {
|
||||
bool graceful = 1;
|
||||
uint32 timeout_seconds = 2;
|
||||
}
|
||||
|
||||
message GetStatusCommand {}
|
||||
|
||||
message ValidateConfigCommand {
|
||||
string config_content = 1;
|
||||
}
|
||||
|
||||
// Events
|
||||
message Event {
|
||||
string event_id = 1;
|
||||
string event_type = 2;
|
||||
int64 timestamp = 3;
|
||||
map<string, string> data = 4;
|
||||
}
|
||||
|
||||
// Common messages
|
||||
message Ack {
|
||||
string message_id = 1;
|
||||
bool success = 2;
|
||||
string error_message = 3;
|
||||
}
|
||||
|
||||
message Error {
|
||||
string code = 1;
|
||||
string message = 2;
|
||||
map<string, string> details = 3;
|
||||
}
|
||||
9
crates/nxmesh-proto/src/lib.rs
Normal file
9
crates/nxmesh-proto/src/lib.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
//! NxMesh Protocol Buffers
|
||||
//!
|
||||
//! This crate contains the gRPC protocol definitions for master-agent communication.
|
||||
|
||||
pub mod agent {
|
||||
tonic::include_proto!("nxmesh.agent.v1");
|
||||
}
|
||||
|
||||
pub use agent::*;
|
||||
Reference in New Issue
Block a user