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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ target/
services/ws-wasm-agent/pkg/
services/ws-server/static/models/
.zig-cache/
*.pem
30 changes: 18 additions & 12 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 0 additions & 42 deletions libs/edge-toolkit/src/ws_server.rs
Original file line number Diff line number Diff line change
@@ -1,52 +1,10 @@
use std::collections::BTreeMap;
use std::path::PathBuf;
use std::sync::{Arc, Mutex};

use serde::{Deserialize, Serialize};
use serde_default::DefaultFromSerde;
use serde_inline_default::serde_inline_default;

use crate::config::{OtlpConfig, default_modules_folders};
use crate::ws::{AgentConnectionState, AgentSummary, ConnectStatus};

/// Default storage directory.
#[must_use]
pub fn default_storage_folder() -> PathBuf {
let project_root = crate::config::get_project_root();
project_root.join("services/ws-server/storage")
}

/// Modules config.
#[serde_inline_default]
#[derive(Clone, Debug, DefaultFromSerde, Deserialize)]
pub struct ModulesConfig {
#[serde(default = "default_modules_folders")]
pub paths: Vec<PathBuf>,
#[serde_inline_default(String::from("et-ws-server-static"))]
pub root: String,
}

/// Storage config.
#[derive(Clone, Debug, DefaultFromSerde, Deserialize)]
pub struct StorageConfig {
#[serde(default = "default_storage_folder")]
pub path: PathBuf,
}

/// Application config shared across ws-server services.
#[derive(Clone, Debug, DefaultFromSerde, Deserialize)]
pub struct Config {
/// OpenTelemetry config.
#[serde(default)]
pub otlp: Option<OtlpConfig>,
/// Modules config.
#[serde(default)]
pub modules: ModulesConfig,
/// Storage config.
#[serde(default)]
pub storage: StorageConfig,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PendingDirectMessage {
pub message_id: String,
Expand Down
3 changes: 3 additions & 0 deletions services/modules/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ repository.workspace = true
actix-files = "0.6"
actix-web = "4"
edge-toolkit = { path = "../../libs/edge-toolkit" }
serde.workspace = true
serde-inline-default.workspace = true
serde_default.workspace = true
serde_json.workspace = true
tracing.workspace = true

Expand Down
30 changes: 20 additions & 10 deletions services/modules/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,20 @@ use std::path::PathBuf;

use actix_files::Files;
use actix_web::{HttpResponse, web};
use edge_toolkit::ws_server::{Config, ModulesConfig};
use edge_toolkit::config::default_modules_folders;
use serde::Deserialize;
use serde_default::DefaultFromSerde;
use serde_inline_default::serde_inline_default;

/// Modules config.
#[serde_inline_default]
#[derive(Clone, Debug, DefaultFromSerde, Deserialize)]
pub struct ModulesConfig {
#[serde(default = "default_modules_folders")]
pub paths: Vec<PathBuf>,
#[serde_inline_default(String::from("et-ws-server-static"))]
pub root: String,
}

fn read_package_name(package_json: &std::path::Path) -> Option<String> {
let content = std::fs::read_to_string(package_json).ok()?;
Expand Down Expand Up @@ -56,24 +69,21 @@ pub fn list_modules(config: &ModulesConfig) -> Vec<(String, PathBuf)> {
modules
}

async fn list_modules_handler(config: web::Data<Config>) -> HttpResponse {
let names: Vec<String> = list_modules(&config.modules)
.into_iter()
.map(|(name, _)| name)
.collect();
async fn list_modules_handler(config: web::Data<ModulesConfig>) -> HttpResponse {
let names: Vec<String> = list_modules(&config).into_iter().map(|(name, _)| name).collect();
HttpResponse::Ok().json(names)
}

/// Register `GET /modules/` (JSON list), `GET /modules/{name}/...` (static files),
/// and `GET /` (root module).
pub fn configure(cfg: &mut web::ServiceConfig, config: &Config) {
let modules = list_modules(&config.modules);
pub fn configure(cfg: &mut web::ServiceConfig, config: &ModulesConfig) {
let modules = list_modules(config);

let root_module_dir = modules
.iter()
.find(|(name, _)| name == &config.modules.root)
.find(|(name, _)| name == &config.root)
.map(|(_, path)| path.clone())
.unwrap_or_else(|| panic!("Root module '{}' not found", config.modules.root));
.unwrap_or_else(|| panic!("Root module '{}' not found", config.root));

cfg.route("/modules/", web::get().to(list_modules_handler));
for (name, pkg_dir) in &modules {
Expand Down
6 changes: 3 additions & 3 deletions services/modules/tests/api_modules.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use actix_web::{App, test, web};
use edge_toolkit::ws_server::{AgentRegistry, Config};
use et_modules_service::configure;
use edge_toolkit::ws_server::AgentRegistry;
use et_modules_service::{ModulesConfig, configure};

#[actix_rt::test]
async fn list_modules_api() {
let config = Config::default();
let config = ModulesConfig::default();
let app = test::init_service(
App::new()
.app_data(web::Data::new(AgentRegistry::<()>::default()))
Expand Down
3 changes: 3 additions & 0 deletions services/storage/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,8 @@ actix-files = "0.6"
actix-web = "4"
edge-toolkit = { path = "../../libs/edge-toolkit" }
futures-util = "0.3"
serde.workspace = true
serde-inline-default.workspace = true
serde_default.workspace = true
tokio = { version = "1", features = ["full"] }
tracing.workspace = true
26 changes: 21 additions & 5 deletions services/storage/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,31 @@ use std::path::PathBuf;

use actix_files::Files;
use actix_web::{Error, HttpRequest, HttpResponse, web};
use edge_toolkit::ws_server::{AgentRegistry, Config};
use edge_toolkit::ws_server::AgentRegistry;
use futures_util::StreamExt;
use serde::Deserialize;
use serde_default::DefaultFromSerde;
use tracing::info;

/// Default storage directory.
#[must_use]
pub fn default_storage_folder() -> PathBuf {
let project_root = edge_toolkit::config::get_project_root();
project_root.join("services/ws-server/storage")
}

/// Storage config.
#[derive(Clone, Debug, DefaultFromSerde, Deserialize)]
pub struct StorageConfig {
#[serde(default = "default_storage_folder")]
pub path: PathBuf,
}

pub async fn agent_put_file<S: Clone + Send + 'static>(
req: HttpRequest,
mut payload: web::Payload,
registry: web::Data<AgentRegistry<S>>,
config: web::Data<Config>,
config: web::Data<StorageConfig>,
) -> Result<HttpResponse, Error> {
let agent_id: String = req.match_info().query("agent_id").parse().unwrap();
let filename: PathBuf = req
Expand All @@ -30,7 +46,7 @@ pub async fn agent_put_file<S: Clone + Send + 'static>(
return Err(actix_web::error::ErrorBadRequest("invalid filename"));
}

let storage_dir = &config.storage.path;
let storage_dir = &config.path;
let agent_dir = storage_dir.join(&agent_id);
std::fs::create_dir_all(&agent_dir)?;

Expand All @@ -47,8 +63,8 @@ pub async fn agent_put_file<S: Clone + Send + 'static>(
}

/// Register `PUT /storage/{agent_id}/{filename}` and `GET /storage/...` (static file serving).
pub fn configure<S: Clone + Send + 'static>(cfg: &mut web::ServiceConfig, config: &Config) {
let storage_dir = config.storage.path.clone();
pub fn configure<S: Clone + Send + 'static>(cfg: &mut web::ServiceConfig, config: &StorageConfig) {
let storage_dir = config.path.clone();
cfg.route("/storage/{agent_id}/{filename}", web::put().to(agent_put_file::<S>))
.service(
Files::new("/storage", storage_dir)
Expand Down
36 changes: 35 additions & 1 deletion services/ws-server/src/config.rs
Original file line number Diff line number Diff line change
@@ -1 +1,35 @@
pub use edge_toolkit::ws_server::{Config, ModulesConfig, StorageConfig};
use std::path::PathBuf;

use edge_toolkit::config::OtlpConfig;
pub use et_modules_service::ModulesConfig;
pub use et_storage_service::StorageConfig;
use serde::Deserialize;
use serde_default::DefaultFromSerde;
use serde_inline_default::serde_inline_default;

/// TLS certificate and key paths.
#[serde_inline_default]
#[derive(Clone, Debug, DefaultFromSerde, Deserialize)]
pub struct TlsConfig {
#[serde_inline_default(PathBuf::from("cert.pem"))]
pub cert_file: PathBuf,
#[serde_inline_default(PathBuf::from("key.pem"))]
pub key_file: PathBuf,
}

/// Application config shared across ws-server services.
#[derive(Clone, Debug, DefaultFromSerde, Deserialize)]
pub struct Config {
/// OpenTelemetry config.
#[serde(default)]
pub otlp: Option<OtlpConfig>,
/// Modules config.
#[serde(default)]
pub modules: ModulesConfig,
/// Storage config.
#[serde(default)]
pub storage: StorageConfig,
/// TLS config.
#[serde(default)]
pub tls: TlsConfig,
}
20 changes: 8 additions & 12 deletions services/ws-server/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
pub mod config;

use std::path::{Path, PathBuf};

use actix_web::{HttpResponse, web};
pub use et_ws_service::{WebSocketActor, WsAgentRegistry};

use crate::config::Config;
pub mod config;

pub fn browser_static_dir() -> PathBuf {
Path::new(".").join("static")
}
use crate::config::Config;

pub async fn no_content() -> HttpResponse {
HttpResponse::NoContent().finish()
Expand All @@ -22,14 +16,16 @@ pub async fn health() -> HttpResponse {
}))
}

pub fn configure_app(cfg: &mut web::ServiceConfig, agent_registry: web::Data<WsAgentRegistry>, config: Config) {
pub fn configure_app(cfg: &mut web::ServiceConfig, agent_registry: web::Data<WsAgentRegistry>, config: &Config) {
cfg.app_data(agent_registry)
.app_data(web::Data::new(config.clone()))
.app_data(web::Data::new(config.modules.clone()))
.app_data(web::Data::new(config.storage.clone()))
.route("/favicon.ico", web::get().to(no_content))
.route("/health", web::get().to(health));

et_ws_service::configure(cfg, &config);
et_storage_service::configure::<actix::Addr<WebSocketActor>>(cfg, &config);
et_ws_service::configure(cfg);
et_storage_service::configure::<actix::Addr<WebSocketActor>>(cfg, &config.storage);
// Must be last: registers a catch-all Files::new("/", ...) for the root module.
et_modules_service::configure(cfg, &config);
et_modules_service::configure(cfg, &config.modules);
}
Loading
Loading