This repository implements a web frontend and backend to provide omnect specific features in a local environment, where the device might not be connected to the azure cloud.
- Before committing any changes that affect the project structure, the "Project Structure" section in this file must be updated.
The project uses a Cargo workspace structure and implements the Crux framework's Core/Shell architecture for the frontend.
The application follows the Crux pattern where:
- Core (
src/app/): Contains all business logic, state management, and type definitions. Compiled to WASM for the web shell. - Shell (
src/ui/): Vue 3 UI that renders the Core's view model and processes effects (HTTP, WebSocket). - Shared Types (
src/shared_types/): Generates TypeScript bindings from Rust types using TypeGen.
- Shell sends Events to Core (serialized via bincode)
- Core processes events, updates Model, returns Commands
- Shell processes Commands (render UI, make HTTP requests, manage WebSocket)
- Shell reads ViewModel from Core for rendering
- Role: Web Service / API providing access to omnect device features
- Frameworks: Rust, Actix-web
- Contains frontend as static ressource
- Role: Business Logic, State Management (Platform-agnostic)
- Frameworks: Rust, Crux (compiled to WASM)
- Role: User Interface (Single Page Application)
- Frameworks: Vue 3, TypeScript, Vite
- Role: Type definitions shared between Backend/Core and Frontend
- Frameworks: TypeGen (generates TypeScript bindings)
# build all frontend artefacts
# This script performs:
# 1. Builds WASM module with wasm-pack
# 2. Generates TypeScript types from Rust (cargo build -p shared_types)
# 3. Removes .js files to force Vite to use .ts sources
# 4. Installs dependencies and builds UI with bun
scripts/build-frontend.sh# Build and Deploy ARM64 image
./scripts/build-and-deploy-image.sh --arch arm64 --deploy --host <device-ip> --password <ssh-pwd># Build and Deploy ARM64 image
./scripts/build-and-run-image.sh# Run tests
cargo test --features mock# Requires nightly toolchain (rustfmt.toml uses unstable options)
cargo +nightly fmt# Run all e2e tests
./scripts/run-e2e-tests.sh
# Run a single e2e test
./scripts/run-e2e-tests.sh -g 'my-test'
# Run all e2e tests located in a file
./scripts/run-e2e-tests.sh my-tests.spec.ts- Every page wraps content in
<v-sheet :border="true" rounded class="ma-4">. - No page-level padding beyond
ma-4on the sheet; inner spacing is handled per component.
| Level | Class | Usage |
|---|---|---|
| Page section header | text-h4 text-secondary border-b |
Top-level section titles ("Common Info", "Network") |
| Sub-section header | text-h5 text-secondary font-weight-bold border-b pb-2 |
Secondary sections ("Commands", "Update Details") |
| Category label | text-subtitle-2 text-medium-emphasis mb-1 |
Column/group labels ("Connectivity", "Version", "Provider") |
- Read-only data uses plain
<dt>/<dd>pairs or theKeyValuePaircomponent — no form fields, no icons. - Form fields that become read-only (e.g., DHCP-managed network fields) switch to
variant="plain"to visually distinguish them from editable fields (variant="outlined").
- Do not add decorative icons to form field labels or data displays. Labels are self-explanatory.
- Icons are reserved for functional actions (copy-to-clipboard, status indicators, navigation).
| Role | Variant | Color |
|---|---|---|
| Primary action | variant="flat" |
color="primary" |
| Destructive confirm | variant="flat" |
color="error" |
| Secondary / Cancel | variant="text" |
color="primary" |
- Confirmation dialogs use
v-cardwithv-card-title(text-h5),v-card-text, andv-card-actions. - Actions are right-aligned (
<v-spacer>before buttons). - Cancel is always
variant="text", confirm usesvariant="flat".
- Do not display the same information twice in different components. Choose the most informative presentation and remove the redundant one.
omnect-ui/
├── Cargo.toml # Workspace root
├── Dockerfile # Multi-stage Docker build
├── scripts/ # Build and test scripts
│ ├── build-frontend.sh # Build WASM + Types + UI
│ ├── build-and-deploy-image.sh # Docker build and deploy
│ ├── build-and-run-image.sh # Local docker development
│ └── run-e2e-tests.sh # Playwright test runner
├── src/
│ ├── app/ # Crux Core (business logic)
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── lib.rs # App struct, Effect enum, re-exports
│ │ ├── model.rs # Model struct (application state)
│ │ ├── events.rs # Event enum
│ │ ├── wasm.rs # WASM FFI bindings
│ │ ├── macros.rs # URL and log macros
│ │ ├── http_helpers.rs # HTTP request utilities
│ │ ├── wifi_psk.rs # WiFi PSK utilities
│ │ ├── commands/ # Custom side-effect commands
│ │ │ ├── mod.rs
│ │ │ └── websocket.rs # WebSocket commands
│ │ ├── types/ # Domain types
│ │ │ ├── mod.rs
│ │ │ ├── auth.rs # Authentication types
│ │ │ ├── common.rs # Common shared types
│ │ │ ├── device.rs # Device information types
│ │ │ ├── network.rs # Network configuration types
│ │ │ ├── wifi.rs # WiFi types
│ │ │ ├── ods.rs # ODS-specific DTOs
│ │ │ ├── settings.rs # Timeout settings types
│ │ │ ├── websocket.rs # WebSocket channel enum
│ │ │ ├── factory_reset.rs
│ │ │ └── update.rs # Update validation types
│ │ └── update/ # Domain-based event handlers
│ │ ├── mod.rs # Main dispatcher
│ │ ├── auth.rs # Auth event handlers
│ │ ├── ui.rs # UI state handlers
│ │ ├── wifi.rs # WiFi event handlers
│ │ ├── websocket.rs # WebSocket state handlers
│ │ └── device/ # Device domain handlers
│ │ ├── mod.rs
│ │ ├── operations.rs # Reboot/Factory Reset logic
│ │ ├── reconnection.rs # Device reconnection polling
│ │ └── network/ # Network configuration logic
│ ├── backend/ # Rust backend (Actix-web)
│ │ ├── Cargo.toml
│ │ ├── src/
│ │ │ ├── main.rs # Application entry point
│ │ │ ├── lib.rs # Library entry point
│ │ │ ├── api.rs # API route handlers
│ │ │ ├── build.rs # Static resource generation
│ │ │ ├── middleware.rs # Auth middleware
│ │ │ ├── config.rs # Configuration loading
│ │ │ ├── http_client.rs # Internal HTTP client
│ │ │ ├── keycloak_client.rs
│ │ │ ├── omnect_device_service_client.rs
│ │ │ ├── wifi_commissioning_client.rs
│ │ │ └── services/ # Business logic services
│ │ │ ├── mod.rs
│ │ │ ├── certificate.rs
│ │ │ ├── firmware.rs
│ │ │ ├── network.rs
│ │ │ ├── marker.rs
│ │ │ ├── settings.rs # Timeout settings service
│ │ │ ├── websocket.rs # WebSocket handlers
│ │ │ └── auth/ # Auth logic
│ │ │ ├── mod.rs
│ │ │ ├── authorization.rs # JWT/SSO validation
│ │ │ ├── password.rs # Password hashing/storage
│ │ │ ├── session_key.rs # Session key management
│ │ │ └── token.rs # JWT generation
│ │ └── tests/ # Integration tests
│ ├── shared_types/ # TypeGen for TypeScript bindings
│ │ ├── Cargo.toml
│ │ ├── build.rs # TypeGen build script
│ │ └── generated/ # Generated TypeScript types
│ └── ui/ # Vue 3 Shell
│ ├── package.json
│ ├── playwright.config.ts # E2E test configuration
│ ├── vite.config.ts # Build configuration
│ ├── src/
│ │ ├── App.vue # Root component
│ │ ├── main.ts # UI entry point
│ │ ├── components/ # UI components
│ │ ├── composables/ # Logic & WASM bridge
│ │ │ ├── useCore.ts # Main bridge + effect handlers
│ │ │ ├── useWebSocket.ts # Native WebSocket client
│ │ │ └── core/ # Modular Core integration
│ │ │ ├── index.ts # Main entry point
│ │ │ ├── state.ts # Singleton reactive state
│ │ │ ├── types.ts # TypeScript type conversions
│ │ │ ├── effects.ts # Effect processing
│ │ │ ├── http.ts # HTTP capability
│ │ │ ├── websocket.ts # WebSocket capability
│ │ │ ├── time.ts # Time/timer capability (crux_time)
│ │ │ └── sync.ts # ViewModel synchronization and navigation side-effects
│ │ ├── pages/ # Route components
│ │ │ ├── DeviceOverview.vue
│ │ │ ├── DeviceUpdate.vue
│ │ │ ├── Network.vue
│ │ │ ├── Settings.vue
│ │ │ ├── Login.vue
│ │ │ ├── SetPassword.vue
│ │ │ ├── UpdatePassword.vue
│ │ │ └── Callback.vue
│ │ ├── plugins/ # Router, Vuetify
│ │ └── types/ # UI-specific types
│ └── tests/ # Playwright E2E tests
│ ├── auth.spec.ts
│ ├── device.spec.ts
│ ├── error-handling.spec.ts
│ ├── factory-reset.spec.ts
│ ├── network-configuration.spec.ts
│ ├── network-multi-adapter.spec.ts
│ ├── reboot.spec.ts
│ ├── settings.spec.ts
│ ├── smoke.spec.ts
│ ├── update.spec.ts
│ ├── version-mismatch.spec.ts
│ ├── wifi.spec.ts
│ └── fixtures/
│ ├── mock-api.ts
│ ├── network-test-harness.ts
│ ├── test-setup.ts
│ └── websocket.ts # WebSocket test mocks
└── project-context.md # This file
Frontend (Shell):
src/ui/src/composables/useCore.ts- Core WASM bridge + effect handlerssrc/ui/src/composables/useWebSocket.ts- WebSocket client integrationsrc/ui/src/pages/DeviceOverview.vue- Main device dashboard page
Core:
src/app/src/lib.rs- App struct, Effect enum, and re-exportssrc/app/src/model.rs- Model struct (application state)src/app/src/events.rs- Event enum definitionssrc/app/src/types/- Domain types organized by domainsrc/app/src/update/- Domain-based event handlerssrc/app/src/commands/websocket.rs- Custom WebSocket commands
Backend:
src/backend/src/main.rs- Application entry pointsrc/backend/src/api.rs- API route handlerssrc/backend/src/services/websocket.rs- Native WebSocket implementation
Scripts:
scripts/build-frontend.sh- Build complete frontend (WASM + TypeScript types + UI)scripts/build-and-deploy-image.sh- Docker build and deploy script