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
14 changes: 14 additions & 0 deletions .mise.toml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ description = "Run the WebSocket server"
dir = "services/ws-server"
run = "cargo run"

[tasks.ws-server.env]
OTLP_AUTH_PASSWORD = "1234"
OTLP_AUTH_USERNAME = "root@example.com"

[tasks.build-ws-wasm-agent]
description = "Build the WebSocket WASM client"
dir = "services/ws-wasm-agent"
Expand Down Expand Up @@ -189,3 +193,13 @@ run = "env CHROMEDRIVER=\"$(mise where chromedriver)/bin/chromedriver\" wasm-pac
[tasks.ws-e2e-chrome]
depends = ["test-ws-wasm-agent-chrome", "ws-server"]
description = "Run both the ws-server and ws-wasm-agent using Chrome"

[tasks.openobserve]
alias = "o2"
run = "docker run --rm -it --name openobserve -p 5080:5080 --env-file config/o2.env openobserve/openobserve:v0.70.3"

[tasks.demo]
depends = ["openobserve", "ws-server"]

[tasks.open-o2]
run = "open http://localhost:5080/"
10 changes: 10 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,23 @@ members = [
"services/ws-modules/video1",
"services/ws-server",
"services/ws-wasm-agent",
"utilities/onnx",
]
resolver = "2"

[workspace.dependencies]
base64 = "0.22.1"
chrono = { version = "0.4", features = ["serde"] }
clap = { version = "4.4", features = ["derive"] }
et-web = { path = "libs/web" }
log = "0.4"
onnx-extractor = "0.3"
rstest = "0.26"
secrecy = { version = "0.10.3", features = ["serde"] }
serde = { version = "1", features = ["derive"] }
serde-env = "0.3"
serde-inline-default = "1.0"
serde_default = "0.2"
serde_json = "1"
tracing = "0.1"
uuid = { version = "1", features = ["v4", "v7"] }
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ and save it as `services/ws-server/static/models/human_activity_recognition.onnx

### Build WASM and run the WS server

In a separate terminal start OpenObserve (o2) and leave it running.

```bash
mise run o2
```

Then start the server

```bash
mise run build-wasm
mise run ws-server
Expand All @@ -45,6 +53,14 @@ The module list is dynamically populated from the modules in [services/ws-module

Note: The WASM build disables WebAssembly reference types, so it can still load on older browsers such as Chrome 95.

In a separate terminal, open the OpenObserve UX using:

```bash
mise run open-o2
```

The server logs appear in the Logs section.

## Grant

This repository is part of a grant managed by the School of EECMS, Curtin University.
Expand Down
3 changes: 3 additions & 0 deletions config/o2.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
RUST_LOG=warn
ZO_ROOT_USER_EMAIL=root@example.com
ZO_ROOT_USER_PASSWORD=1234
7 changes: 7 additions & 0 deletions libs/edge-toolkit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,12 @@ license.workspace = true
repository.workspace = true

[dependencies]
base64.workspace = true
secrecy.workspace = true
serde.workspace = true
serde-env.workspace = true
serde_default.workspace = true
serde_json.workspace = true

[dev-dependencies]
rstest.workspace = true
18 changes: 18 additions & 0 deletions libs/edge-toolkit/src/args.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/// Return the executable name that was invoked.
///
/// Removes `.exe` suffix.
///
/// # Panics
/// This function will panic if there is no command line argument 0
/// which may happen if the invoking environment is not similar to a "std" environment.
#[must_use]
pub fn executable_name() -> String {
executable_name_inner(std::env::args().collect())
}

#[expect(clippy::unwrap_used)]
pub fn executable_name_inner(args: Vec<String>) -> String {
let path = args.first().unwrap();
let path = std::path::PathBuf::from(path);
path.file_stem().unwrap().to_string_lossy().to_string()
}
30 changes: 30 additions & 0 deletions libs/edge-toolkit/src/auth.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use base64::{Engine, engine::general_purpose::STANDARD as b64standard};
use secrecy::{ExposeSecret, SecretString};
use serde::Deserialize;

#[derive(Clone, Debug, Deserialize)]
/// Basic Authentication config.
pub struct BasicAuth {
/// Username.
pub username: String,
/// Password.
pub password: SecretString,
}

impl BasicAuth {
/// Create a new `BasicAuth` instance.
#[must_use]
pub fn new(username: String, password: SecretString) -> Self {
Self { username, password }
}

/// Add authorisation header to HashMap.
pub fn add_basic_auth_header(&self, headers: &mut std::collections::HashMap<String, String>) {
let mut buf = String::default();
b64standard.encode_string(
format!("{}:{}", self.username, self.password.expose_secret()).as_bytes(),
&mut buf,
);
headers.insert("authorization".to_string(), format!("Basic {buf}"));
}
}
60 changes: 60 additions & 0 deletions libs/edge-toolkit/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use serde::Deserialize;
use serde_default::DefaultFromSerde;

use crate::args::executable_name;
use crate::auth::BasicAuth;
use crate::ports::Services;

/// Localhost address 127.0.0.1 .
pub const LOCALHOST: &str = "127.0.0.1";

/// Default port for the otlp http collector.
#[must_use]
const fn default_otlp_collector_port() -> u16 {
Services::OtlpCollector.port()
}

/// Default url for the otlp collector. This is the tracing endpoint path for OpenObserve trace collection.
#[must_use]
pub fn default_otlp_collector_url() -> String {
format!("http://{LOCALHOST}:{}/api/default/v1", default_otlp_collector_port())
}

/// Default service label name for use in OpenTelemetry.
///
/// Removes "-server" suffix from the invoked executable name if present,
/// such as binary name `et-ws-server`.
#[must_use]
pub fn default_trace_service_label() -> String {
executable_name().replace("-server", "")
}

/// OTLP message data protocol.
///
/// Binary is more compact and efficient, while JSON is more human-readable and easier to debug.
#[expect(clippy::exhaustive_enums)]
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq)]
pub enum OtlpProtocol {
/// Binary messages.
#[default]
Binary,
/// JSON messages.
JSON,
}

/// OpenTelemetry service config.
#[derive(Clone, Debug, DefaultFromSerde, Deserialize)]
#[non_exhaustive]
pub struct OtlpConfig {
/// OpenTelemetry collector URL.
#[serde(default = "default_otlp_collector_url")]
pub collector_url: String,
/// OpenTelemetry protocol.
#[serde(default)]
pub protocol: OtlpProtocol,
/// OpenTelemetry service label.
#[serde(default = "default_trace_service_label")]
pub service_label: String,
/// OpenTelemetry HTTP basic auth.
pub auth: Option<BasicAuth>,
}
4 changes: 4 additions & 0 deletions libs/edge-toolkit/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
pub mod args;
pub mod auth;
pub mod config;
pub mod ports;
pub mod ws;
30 changes: 30 additions & 0 deletions libs/edge-toolkit/src/ports.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//! Port allocation.

/// Define each services port.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[non_exhaustive]
pub enum Services {
/// OTLP collector.
OtlpCollector,

/// Insecure WebSocket server.
InsecureWebSocketServer,
/// Secure WebSocket server.
SecureWebSocketServer,
}

use Services::{InsecureWebSocketServer, OtlpCollector, SecureWebSocketServer};

impl Services {
/// Get the allocation port for the service.
#[must_use]
pub const fn port(&self) -> u16 {
match self {
// OpenObserve specific http port
OtlpCollector => 5080,

InsecureWebSocketServer => 8080,
SecureWebSocketServer => 8443,
}
}
}
20 changes: 20 additions & 0 deletions libs/edge-toolkit/tests/args.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#![cfg(test)]

#[rstest::rstest]
#[case::normal(vec!["et-ws-server"])]
#[case::unix_path(vec!["/path/to/et-ws-server"])]
#[case::windows_exe(vec!["et-ws-server.exe"])]
fn executable_name(#[case] args: Vec<&str>) {
let args: Vec<String> = args.into_iter().map(String::from).collect();
assert_eq!(
edge_toolkit::args::executable_name_inner(args),
"et-ws-server".to_string()
);
}

#[cfg(windows)]
#[rstest::rstest]
#[case::windows_path(vec!["C:\\path\\to\\et-ws-server"])]
fn executable_name_windows(#[case] args: Vec<&str>) {
executable_name(args);
}
41 changes: 30 additions & 11 deletions services/ws-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,47 @@ repository.workspace = true
[dependencies]
actix = "0.13"
actix-files = "0.6"
actix-rt = "2"
actix-web = { version = "4", features = ["rustls-0_23"] }
actix-web-actors = "4"
chrono.workspace = true
clap.workspace = true
edge-toolkit = { path = "../../libs/edge-toolkit" }
# mime = "0.3"
futures-util = "0.3"
hostname = "0.4"
local-ip-address = "0.6"
log.workspace = true
onnx-extractor.workspace = true
opentelemetry = "0.31"
opentelemetry-otlp = { version = "0.31", default-features = false, features = ["logs", "metrics", "trace"] }
opentelemetry-appender-tracing = "0.31"
opentelemetry-otlp = { version = "0.31", default-features = false, features = [
"http-json",
"http-proto",
"logs",
"metrics",
"reqwest-blocking-client",
"trace",
] }
opentelemetry_sdk = "0.31"
qr2term = "0.3"
rcgen = "0.14"
regex = { version = "1.12", default-features = false }
rustls = "0.23"
secrecy.workspace = true
serde.workspace = true
serde-env.workspace = true
serde-inline-default.workspace = true
serde_default.workspace = true
serde_json.workspace = true
serde_yaml = "0.9"
tokio = { version = "1", features = ["full"] }
tracing.workspace = true
tracing-actix-web = { version = "0.7", default-features = false, features = [
"emit_event_on_error",
"opentelemetry_0_31",
"uuid_v7",
] }
tracing-log = "0.2"
tracing-opentelemetry = "0.32"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
#opentelemetry-actix-web = "0.10"
chrono.workspace = true
clap = { version = "4.4", features = ["derive"] }
futures-util = "0.3"
local-ip-address = "0.6"
qr2term = "0.3"
uuid.workspace = true

[dependencies.actix-rt]
version = "2"
11 changes: 11 additions & 0 deletions services/ws-server/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use edge_toolkit::config::OtlpConfig;
use serde::Deserialize;
use serde_default::DefaultFromSerde;

/// Application environment variables and config.
#[derive(Clone, Debug, DefaultFromSerde, Deserialize)]
pub struct Config {
/// OpenTelemetry config.
#[serde(default)]
pub otlp: Option<OtlpConfig>,
}
Loading
Loading