Skip to content

Conversation

@JanZachmann
Copy link
Contributor

@JanZachmann JanZachmann commented Nov 16, 2025

Summary

This PR introduces the Crux Core/Shell architecture infrastructure to omnect-ui, providing the foundation for separating business logic (Core) from platform-specific UI code (Shell). The refactor builds on top of the workspace restructuring from #68.

Learn more about Crux:

Key Changes

Crux Core Architecture (src/app/)

  • New Cargo package omnect-ui-core with platform-agnostic business logic framework
  • Domain-based module organization: auth, device, websocket, ui
  • Custom Centrifugo capability with Command API for WebSocket management
  • HTTP capability migrated to Command API pattern
  • WASM compilation target for web integration
  • See src/app/README.md for architecture details

TypeScript Integration (src/shared_types/)

  • TypeGen-based type generation from Rust to TypeScript
  • Automatic bincode serialization/deserialization bindings
  • Type-safe communication between Core and Shell

Frontend Shell (src/ui/)

  • Vue 3 composable (useCore.ts) bridging WASM Core and UI
  • Effect handlers for HTTP requests and Centrifugo WebSocket
  • Migrated DeviceInfo component to use Core (DeviceInfoCore.vue)
  • Frontend assets embedded in backend binary via static-files crate

Backend Updates (src/backend/)

  • Frontend assets embedded in binary (no separate static file serving)
  • Improved build script with WASM compilation stage
  • New setup-password utility binary for development
    • CLI tool for creating/updating password files during local development
    • Integrated with VS Code launch tasks for automated setup
    • Usage: cargo run --bin setup-password --features=mock -- <password>

Build & Deployment

  • Consolidated build scripts with multi-architecture support
  • Docker workflow improvements with WASM build stage
  • Enhanced deployment script with registry push capabilities

Architecture Notes

The Crux pattern separates concerns into three layers:

  1. Core (Rust, compiled to WASM): All business logic, state management, type definitions
  2. Shell (Vue 3 TypeScript): UI rendering and effect execution (HTTP, WebSocket)
  3. Communication: Events (Shell → Core), Effects (Core → Shell), ViewModel (Core → Shell)

Benefits:

  • Platform-agnostic business logic testable without UI
  • Type-safe frontend with auto-generated TypeScript bindings
  • Clear separation of concerns between logic and presentation
  • Potential for future multi-platform support (native apps, CLI, etc.)

Event Flow:

  1. User action in Vue component
  2. Component calls sendEvent() or convenience method (e.g., login())
  3. Event is serialized (bincode) and sent to WASM Core
  4. Core processes event, updates Model, returns Effects
  5. Shell processes Effects (HTTP request, render update, WebSocket subscription)
  6. ViewModel is updated and Vue reactively re-renders

Current Status

What This PR Delivers

Complete Crux Core Infrastructure:

  • Working WASM compilation pipeline with wasm-pack
  • TypeScript type generation from Rust types
  • HTTP and Centrifugo capabilities with Command API
  • Domain-based module organization (auth, device, websocket, ui)
  • Effect processing in Vue shell (HTTP, WebSocket, render)

Component Migration:

  • DeviceInfo successfully migrated to Core architecture
  • Demonstrates full Core/Shell pattern with real functionality
  • useCore() composable for easy Core integration

Build Pipeline:

  • Docker multi-stage build with WASM compilation
  • Frontend assets embedded in backend binary
  • Multi-architecture support (amd64, arm64)

Developer Experience:

  • setup-password CLI utility for local development
  • VS Code tasks for automated pre-launch setup
  • Improved build scripts and documentation

Future Work (Separate PRs)

⚠️ 5 Vue components still use direct API calls and need migration to use the Core. See detailed migration plan in src/app/README.md.

Components to Migrate (5 remaining):

  1. DeviceActions.vue - Reboot and factory reset actions
  2. DeviceNetworks.vue - Network list and status
  3. NetworkSettings.vue - Network configuration
  4. UpdateFileUpload.vue - Firmware update upload
  5. UserMenu.vue - User authentication actions

Additional Tasks:

  • Remove useCentrifuge composable once all components migrated
  • Comprehensive integration testing
  • Performance optimization and bundle size analysis

This PR establishes the architectural foundation and demonstrates successful component migration. Remaining component migrations will proceed incrementally in follow-up PRs for easier review and testing.

Replace hyper-util with reqwest for all HTTP/Unix socket communication,
remove Arc wrappers, and eliminate the socket_client module to simplify
the codebase.

Changes:
- Remove Arc wrappers from Api struct fields (OmnectDeviceServiceClient
  and SingleSignOnProvider are already cheap to clone via internal pools)
- Replace hyper-util Client with reqwest for Unix socket communication
  using reqwest 0.12.23's native unix_socket() support
- Remove socket_client.rs module entirely (~109 lines removed)
- Inline HTTP request logic directly in OmnectDeviceServiceClient with
  cleaner helper methods: get(), post(), post_json()
- Convert keycloak_client from blocking to async reqwest
- Update certificate.rs to use reqwest::Client directly

Dependencies:
- Remove: http-body-util, hyper, hyperlocal, hyper-util
- Update: reqwest from 0.12 to 0.12.23 (minimum for Unix socket support)
- Remove: blocking feature from reqwest (no longer needed)

Benefits:
- Single HTTP client library throughout codebase
- Simpler, more maintainable code with direct reqwest usage
- Better error messages and logging
- Removed unnecessary abstraction layers
- Zero runtime overhead (no Arc, reqwest uses internal Arc)

Signed-off-by: Jan Zachmann <[email protected]>
This commit implements comprehensive refactorings across the codebase:

Performance Optimizations:
- Cache Keycloak HTTP client using OnceLock to avoid repeated instantiation
- Consolidate HTTP client usage patterns

Code Quality Improvements:
- Add HTTP response handling helper to reduce duplication
- Consolidate path macros for consistency
- Add API error handling helper with standardized logging
- Use endpoint constants instead of string literals
- Remove unnecessary .map(|_| ()) patterns
- Standardize error logging format to {e:#}
- Standardize all error messages to use "failed to..." format

Maintainability:
- Replace magic string "omnect-ui" with env!("CARGO_PKG_NAME")
- Improve string interpolation to use direct variable references
- Optimize imports (remove wildcard imports, use explicit imports)
- Fix inconsistent crate imports in main.rs

Signed-off-by: Jan Zachmann <[email protected]>
Problems addressed:
- Inconsistent cleanup across different shutdown paths (ctrl-c, SIGTERM, server crash, centrifugo crash)
- Race conditions between service client shutdown and centrifugo termination
- Mixed error handling (panics vs logging)
- No guaranteed cleanup order

Solution:
- Unified shutdown sequence that runs regardless of exit reason
- Consistent cleanup order: service client → server → centrifugo
- Graceful error handling throughout (no panics)
- Clear debug logging at each step

Benefits:
- Predictable shutdown behavior in all scenarios
- Proper resource cleanup guaranteed
- Better observability with consistent logging
- Safer error handling

Signed-off-by: Jan Zachmann <[email protected]>
This commit introduces new infrastructure modules without changing existing
code, preparing for gradual migration in future PRs.

New Modules:
- config.rs: Centralized configuration management with validation
  - AppConfig struct with all environment variable configuration
  - Explicit validation at startup instead of lazy initialization
  - IoT Edge configuration detection
  - Path configuration helpers

- errors.rs: Structured error types with HTTP status mapping
  - OmnectUiError enum with specific error categories
  - Automatic HTTP status code mapping via ResponseError trait
  - ErrorContext trait for adding typed error context
  - Better error diagnostics for API responses

Benefits:
- Foundation for removing global OnceLock state in future PRs
- Type-safe configuration with clear documentation
- Structured error handling improves debugging and API responses
- No breaking changes to existing code

Future Work:
- PR #2: Consolidate HTTP clients using config
- PR #3: Migrate authentication and testing to use new infrastructure
This commit introduces HttpClientFactory to centralize HTTP client creation,
eliminating code duplication and ensuring consistent configuration.

New Module: http_client.rs
- HttpClientFactory with three client types:
  - https_client(): Cached HTTPS client for external APIs
  - unix_socket_client(): For local Unix socket communication
  - workload_client(): For IoT Edge workload API

Testing:
- Unit tests: 8 tests covering all three client factory methods
  - test_https_client_is_cached
  - test_https_client_returns_valid_client
  - test_https_client_multiple_calls_same_instance
  - test_workload_client_parses_uri
  - test_workload_client_rejects_invalid_scheme
  - test_unix_socket_client_creates_client
  - test_unix_socket_client_with_relative_path
  - test_unix_socket_client_with_empty_path

- Integration tests: 14 tests in tests/http_client.rs
  - 6 tests for https_client (using httpbin.org and example.com)
  - 4 tests for unix_socket_client (using mock Unix socket servers)
  - 4 tests for workload_client (using mock IoT Edge workload API)

- Test configuration: Added .cargo/config.toml
  - Set RUST_TEST_THREADS=1 to prevent rate-limiting issues
  - Ensures reliable test execution for external API tests

- Fixed dead_code warning for workload_client when using mock feature
  by adding #[cfg_attr(feature = "mock", allow(dead_code))]

Benefits:
- Eliminates 3 separate OnceLock instances across modules
- Single source of truth for HTTP client configuration
- Shared connection pools improve performance
- Easier to add timeout/retry configuration in future
- Better testability with centralized mocking point
- Comprehensive test coverage for all client types

Changes by Module:
- keycloak_client.rs: Use HttpClientFactory::https_client()
  - Removed local OnceLock<Client>
  - 4 lines removed, cleaner code

- omnect_device_service_client.rs: Use HttpClientFactory::unix_socket_client()
  - Simplified client creation
  - 3 lines removed

- certificate.rs: Use HttpClientFactory::workload_client()
  - Removed manual URI parsing
  - 8 lines removed, moved to factory

Signed-off-by: Jan Zachmann <[email protected]>
This commit introduces a centralized TokenManager for handling JWT token
creation and verification, eliminating scattered token logic.

New Module: auth/token.rs
- TokenManager: Centralized token management
  - create_token(): Generate JWT tokens with configured expiration
  - verify_token(): Validate tokens with proper verification options
  - Configurable expiration, subject, and secret

Benefits:
- Single source of truth for token handling
- Eliminates duplicate token creation/verification logic
- Consistent verification options (expiration, subject, tolerance)
- Better testability with 4 comprehensive tests
- Easier to change token strategy in future

Changes by Module:
- middleware.rs: Use TokenManager for verify_token()
  - Removed inline HS256Key creation
  - Removed manual VerificationOptions setup
  - 15 lines simplified to 1-line call

- api.rs: Use TokenManager for session_token()
  - Removed inline HS256Key and Claims creation
  - Cleaner error handling
  - 8 lines simplified to 6 lines

Signed-off-by: Jan Zachmann <[email protected]>
Add network configuration management UI and backend:

Backend:
- Add network_config module with support for static IP and DHCP configuration
- Implement network rollback mechanism with 90-second timeout
- Add API endpoint for setting network configuration with validation
- Refactor certificate management to use DeviceServiceClient reference
- Move certificate creation to server startup after service client initialization
- Remove certificate regeneration from network rollback flow
- Add server restart channel for handling network changes
- Cancel pending rollback on successful authentication

Frontend:
- Add Network page with network settings UI
- Add NetworkSettings component for editing network configuration
- Add NetworkActions component for network-related actions
- Add useWaitForNewIp composable for handling IP changes
- Update DeviceNetworks component to show network details
- Refactor DeviceActions to use composable pattern
- Update Vuetify and Biome configurations
- Add route for /network page

Dependencies:
- Add rust-ini for network config file management
- Add serde_valid for request validation
- Add trait-variant for async trait support
- Add actix-cors for CORS support
- Bump version to 1.1.0

Signed-off-by: Jan Zachmann <[email protected]>
…e common.rs

- Introduce AppConfig with OnceLock for thread-safe singleton configuration
- Replace all direct environment variable access with AppConfig::get()
- All config fields now used (centrifugo, iot_edge, paths)
- Add test-mode support with conditional validation and defaults

- Remove common.rs by moving functions to appropriate modules:
  - validate_password → auth/mod.rs
  - create_frontend_config_file → keycloak_client.rs
  - handle_http_response → http_client.rs
- Consolidate HTTP response handling across all clients
- Remove unnecessary wrapper functions (cert_path, key_path, keycloak_url macro)
- Remove duplicate CentrifugoConfig from common.rs

- main.rs: Use AppConfig for UI_PORT, config paths
- api.rs: Use AppConfig for tenant, paths
- keycloak_client.rs: Remove keycloak_url! macro, use AppConfig directly
- omnect_device_service_client.rs: Use AppConfig for socket_path, centrifugo config
- certificate.rs: Use AppConfig for cert/key paths, iot_edge config
- middleware.rs: Use AppConfig for centrifugo client_token in tests

- Fix test isolation issues with cached AppConfig
- Update middleware tests to work with cached config paths
- Add test helper function create_password_file()
- All 69 tests passing with zero warnings

- Make iot_edge mandatory (no longer Option)
- Add cfg_attr for mock feature dead_code suppression
- Remove unnecessary pub from test modules
- Direct calls to handle_http_response (remove wrapper methods)
- Conditional bail import for test vs non-test builds

Signed-off-by: Jan Zachmann <[email protected]>
This commit refactors the codebase to separate business logic from HTTP
concerns by introducing a dedicated services layer.

Introduces a new `services/` module that encapsulates all business logic:

- **AuthorizationService**: Token validation and role-based access control
  (FleetAdministrator, FleetOperator) with tenant/fleet authorization
- **TokenManager**: Session token creation and validation with configurable
  expiration and HMAC signing
- **CertificateService**: Module certificate creation via IoT Edge workload
  API with conditional compilation for mock/production
- **FirmwareService**: Update file persistence and data folder management
- **NetworkConfigService**: Network configuration with automatic rollback,
  server restart coordination, and certificate renewal
- **PasswordService**: Password hashing (Argon2id) and secure storage

- Clear separation of concerns between HTTP handlers and business logic
- Improved testability through dependency injection
- Services accept client traits, enabling easy mocking
- Stateless service structs with static methods
- Reusable functions across different contexts

- Moves `certificate.rs` to `services/certificate.rs`
- Moves `network.rs` to `services/network.rs` (also introduces NetworkConfigService pattern)
- Moves `auth/` to `services/auth/` with new authorization module
- API handlers (`api.rs`) now delegate to services
- Backward compatibility maintained via re-exports in `lib.rs`

- Separates public HTTP handlers from private helper methods
- Reduces `Api` struct responsibility to HTTP concerns only
- Consistent error handling and logging patterns
- Session management delegated to auth service

This refactoring improves maintainability, testability, and code clarity
while maintaining all existing functionality.

Signed-off-by: Jan Zachmann <[email protected]>
Signed-off-by: Jan Zachmann <[email protected]>
Fixed network configuration rollback implementation:
- Corrected rollback processing logic to properly handle pending rollbacks
- Fixed rollback file structure and serialization/deserialization
- Improved rollback timing and cancellation handling
- Added proper error context throughout network service

Reverted device service client builder pattern:
- Removed OmnectDeviceServiceClientBuilder in favor of simpler initialization
- Moved certificate creation and endpoint registration to main startup flow
- Made startup sequence more explicit and easier to follow
- Changed has_publish_endpoint to public field for direct access

Optimized application startup:
- Reorganized startup sequence for better clarity
- Service client now created once and reused across restarts
- Certificate and centrifugo setup moved to run_until_shutdown
- Added warning for unexpected pending rollback on startup

Improved error handling in main.rs:
- Replaced all .expect() calls with proper anyhow::Result and .context()
- Added top-level error handler in main() with proper logging
- All functions now return Result with descriptive error context
- Error chains display full context with {e:#} formatting
- Standardized error messages: lowercase, concise, no trailing periods

Additional improvements:
- Added complete function documentation for network service
- Fixed shutdown sequence to only unregister service client on full shutdown
- Removed redundant "backup:" prefixes from log messages
- Fixed typo: "publish} endpoint" -> "publish endpoint"
- Added centrifugo log level configuration support

Signed-off-by: Jan Zachmann <[email protected]>
…d and frontend

- Created Cargo workspace structure with backend in src/backend/
- Moved Vue 3 frontend from vue/ to src/frontend/
- Updated build artifacts location from src/backend/static/ to dist/
- Removed redundant root biome.json (frontend has its own)
- Updated all Docker paths and build scripts for new structure
- Updated documentation to reflect new project layout

This prepares the codebase for future Crux framework integration.

Signed-off-by: Jan Zachmann <[email protected]>
- Rename src/frontend/ to src/ui/ to reflect its new role as UI shell
- Create src/app/ as Crux Core workspace member with:
  - Model/ViewModel pattern for state management
  - Event-driven architecture with Command API (Crux 0.16)
  - Consolidated shared types (SystemInfo, NetworkStatus, etc.)
  - Custom Centrifugo capability for WebSocket
  - HTTP capability for REST API calls
- Create src/shared_types/ for TypeGen to generate TypeScript types
- Update Dockerfile and .dockerignore for new structure
- Fix formatting issues in backend services
- All 31 tests pass, clippy clean

This lays the foundation for migrating frontend state management from
Vue to Crux Core, enabling type-safe cross-platform logic sharing.

Signed-off-by: Jan Zachmann <[email protected]>
- Add WASM FFI bindings using crux_core::bridge::Bridge for serialization
- Create useCore.ts Vue composable for Crux Core integration
- Integrate generated TypeScript types from shared_types for type safety
- Implement event serialization and effect processing pipeline
- Add complete ViewModel deserialization from bincode format
- Support for Render, HTTP, and Centrifugo effects (HTTP/Centrifugo not yet implemented)
- Build WASM module with wasm-pack (586KB optimized)
- Add WASM package output directory to .gitignore

The Vue bridge provides:
- Reactive viewModel state synchronized with Crux Core
- Event constructors for type-safe communication
- Convenience methods for common operations (login, logout, reboot, etc.)
- Automatic WASM module loading with fallback mode

Signed-off-by: Jan Zachmann <[email protected]>
- Integrate actix-web-static-files to embed Vue dist at compile time
- Update build.rs to generate embedded static resources
- Serve embedded index.html and static assets from memory
- Implement complete Centrifugo capability in Core (Connect, Subscribe, History, etc.)
- Add shell handler for Centrifugo WebSocket operations
- Fix import mismatch (useCentrifuge vs useCentrifugo)
- Configure workspace to exclude shared_types from default-members
- Add lints.rust config to allow facet_typegen cfg from crux_macros
- Remove deprecated filesystem-based static file serving

Signed-off-by: Jan Zachmann <[email protected]>
Migrated Centrifugo WebSocket capability from deprecated Capabilities API
to new Command API pattern. The old capability remains in Capabilities struct
for Effect enum generation, while actual usage now goes through the Command-based
API using RequestBuilder pattern.

Also updated README future work items to reflect completed milestones.

Signed-off-by: Jan Zachmann <[email protected]>
Migrated all HTTP handlers from deprecated Capabilities API to new Command API
pattern. HTTP requests now use HttpCmd type alias with RequestBuilder pattern,
returning Commands with Command::all() to combine render() and HTTP effects.

Updated documentation to clarify that deprecated capabilities are kept only for
Effect enum generation via #[derive(Effect)] macro.

Signed-off-by: Jan Zachmann <[email protected]>
- Replace build-arm64-image.sh with build-and-deploy-image.sh
  - Full CLI with --help, --deploy, --push, --arch, --host, --port, --tag
  - Configurable architecture (default: arm64), tag (default: whoami)
  - Pre-flight directory validation on target device
  - Optional push to registry and deploy to device
- Update build-and-run-image.sh to use main build script internally
  - Auto-detect host architecture
  - Consistent image naming
- Restructure Dockerfile for correct build order
  - Build Rust first to generate TypeScript types
  - Vue build uses generated types from Rust build
  - Add dependency caching with dummy source files
  - Skip frontend embedding warning with SKIP_EMBED_FRONTEND
- Move Centrifugo to tools/ directory with setup script
  - tools/setup-centrifugo.sh downloads binary for local development
  - Backend looks in multiple locations (Docker, tools/, legacy)
- Improve README with Development section and script documentation

Signed-off-by: Jan Zachmann <[email protected]>
- Extract Model and ViewModel into separate model.rs
- Extract Event enum into events.rs
- Split update logic into domain handlers (auth, device, websocket, ui)
- Mock centrifugo binary path in test/mock mode to fix test failures
- Suppress deprecated warnings in centrifugo capability module

Signed-off-by: Jan Zachmann <[email protected]>
- Update key files section with domain-based update modules
- Document new auth, device, websocket, and ui handlers
- Mark completed tasks in Future Work section
- Clarify deprecated capability usage

Signed-off-by: Jan Zachmann <[email protected]>
- Remove username field from Event::Login (only password required)
- Remove username field from LoginCredentials type
- Update auth handler and test to use password-only
- Update README example to reflect password-only API

Signed-off-by: Jan Zachmann <[email protected]>
- Add deployment prerequisite warning to build-and-deploy-image.sh
- Update README.md to document deployment requirements

Signed-off-by: Jan Zachmann <[email protected]>
Remove the hack that called reload_network twice with a 5 second delay.
The issue where ods reports same networks after reload has been fixed.

Signed-off-by: Jan Zachmann <[email protected]>
- Make --host parameter required for deployment to prevent accidental deployments
- Remove hardcoded default device host IP for safety
- Optimize build.rs to reduce unnecessary rebuilds
- Fix reboot error handling to treat connection drops as success
  (device successfully reboots but HTTP connection drops before response)

Signed-off-by: Jan Zachmann <[email protected]>
- Add wasm-build stage to Dockerfile to build Crux core WASM module
- Install wasm-pack and build WASM package before Vue build
- Copy WASM pkg to Vue build stage to resolve import errors
- Remove reboot fetch error workaround in DeviceActions component

Signed-off-by: Jan Zachmann <[email protected]>
@JanZachmann JanZachmann force-pushed the refactor-frontend-to-use-crux branch from 35d9bd3 to 4f84674 Compare November 25, 2025 11:29
@JanZachmann JanZachmann changed the title WIP: Refactor frontend to use Crux architecture Refactor frontend to use Crux architecture Nov 25, 2025
@JanZachmann JanZachmann marked this pull request as ready for review November 25, 2025 14:26
The old DeviceInfo component used direct Centrifuge subscriptions.
DeviceInfoCore uses the Crux Core architecture for state management.

Both components provide identical functionality, but DeviceInfoCore
follows the Core/Shell pattern with proper separation of concerns.

Signed-off-by: Jan Zachmann <[email protected]>
@JanZachmann JanZachmann force-pushed the refactor-frontend-to-use-crux branch from 543b4da to c4da2bb Compare November 25, 2025 17:09
DeviceInfo.vue has been successfully replaced with DeviceInfoCore.vue.
This reduces the remaining component migration count from 6 to 5.

Also fixes markdown linting issues (MD022 - blank lines around headings).

Signed-off-by: Jan Zachmann <[email protected]>
@JanZachmann JanZachmann force-pushed the refactor-frontend-to-use-crux branch from c4da2bb to 9a70029 Compare November 25, 2025 17:10
Add three new macros to streamline repetitive patterns:

- handle_response!: Standardizes HTTP response handling with automatic
  loading state management, error handling, and rendering (12 handlers)
- update_field!: Simplifies single field updates with auto-render
  (10 events across websocket and UI handlers)
- parse_channel_data!: Reduces WebSocket JSON parsing boilerplate
  (6 channel types)

Benefits:
- 68% reduction in handler boilerplate (~221 lines → ~70 lines)
- Centralized patterns improve consistency and maintainability
- Zero functional changes, all tests pass

Files modified:
- src/app/src/macros.rs (new): 180 lines of reusable macro definitions
- src/app/src/lib.rs: Added macros module
- src/app/src/update/auth.rs: 5 handlers optimized (60% reduction)
- src/app/src/update/device.rs: 7 handlers optimized (71% reduction)
- src/app/src/update/websocket.rs: 8 events + parsing (72% reduction)
- src/app/src/update/ui.rs: 2 handlers optimized (75% reduction)

Signed-off-by: Jan Zachmann <[email protected]>
Replace separate ViewModel struct with Model itself by:
- Using #[serde(skip_serializing)] on auth_token field in Model
- Updating App trait to use Model as ViewModel type
- Removing the manual view() function that copied fields
- Removing duplicate ViewModel struct definition

Benefits:
- Eliminates ~30 lines of duplicate code
- Removes manual field-by-field copying in view()
- Simplifies architecture with single source of truth
- Auth token automatically excluded from serialization
- Zero functional changes, all tests pass

Files modified:
- src/app/src/model.rs: Removed ViewModel, added skip_serializing
- src/app/src/lib.rs: Use Model as ViewModel, simplified view()
- src/app/src/update/mod.rs: Removed manual view() function

Signed-off-by: Jan Zachmann <[email protected]>
@JanZachmann JanZachmann force-pushed the refactor-frontend-to-use-crux branch from 424daa5 to 3957347 Compare November 25, 2025 22:05
This commit introduces three additional optimizations to reduce code
repetition in the Crux Core update handlers:

1. auth_post! macro - Standardizes authenticated POST requests with
   three patterns (simple, JSON body, string body). Reduces 9 handler
   implementations from ~234 lines to ~35 lines (85% reduction).

2. Request struct consolidation - Moves 5 inline request struct
   definitions to types.rs for better organization and discoverability:
   - SetPasswordRequest
   - UpdatePasswordRequest
   - FactoryResetRequest
   - LoadUpdateRequest
   - RunUpdateRequest

3. http_error helper - Standardizes HTTP error message formatting
   across all authenticated requests.

Changes:
- src/app/src/macros.rs: Add auth_post! macro and http_error helper
- src/app/src/types.rs: Add 5 request type definitions
- src/app/src/update/auth.rs: Apply auth_post! to Logout and UpdatePassword
- src/app/src/update/device.rs: Apply auth_post! to 6 device actions

All tests pass, no clippy warnings, zero functional changes.

Signed-off-by: Jan Zachmann <[email protected]>
This commit introduces the unauth_post! macro to handle unauthenticated
HTTP requests (login, password setup, and status checks). Completes the
authentication handler optimization by applying consistent patterns to
all POST and GET requests.

The macro supports three patterns:
1. POST with JSON body expecting JSON response (Login)
2. POST with JSON body expecting status only (SetPassword)
3. GET expecting JSON response (CheckRequiresPasswordSet)

Changes:
- src/app/src/macros.rs: Add unauth_post! macro with 3 patterns (99 lines)
- src/app/src/update/auth.rs: Apply macro to Login, SetPassword, and
  CheckRequiresPasswordSet handlers (reduced from 125 to 86 lines)

Net reduction: 53 lines eliminated (39 lines of boilerplate removed)
All tests pass, no clippy warnings, zero functional changes.

Signed-off-by: Jan Zachmann <[email protected]>
Copy link
Contributor

@empwilli empwilli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some issues here and there that I'd like to discuss but overall LGTM

Comment on lines 7 to 61
{
"type": "lldb",
"request": "launch",
"name": "Debug executable 'omnect-ui'",
"cargo": {
"args": [
"build",
"--bin=omnect-ui",
"--package=omnect-ui",
"--features=mock"
],
"filter": {
"name": "omnect-ui",
"kind": "bin"
}
},
"args": [],
"cwd": "${workspaceFolder}",
"preLaunchTask": "pre_launch_setup",
"env": {
"RUST_LOG": "omnect_ui=debug",
"SOCKET_PATH": "/tmp/api.sock",
"CERT_PATH": "temp/device_id_cert.pem",
"KEY_PATH": "temp/device_id_cert_key.pem",
"CENTRIFUGO_CLIENT_ALLOWED_ORIGINS": "https://${env:hostname}:1977 https://localhost:1977",
"CENTRIFUGO_CHANNEL_WITHOUT_NAMESPACE_ALLOW_SUBSCRIBE_FOR_CLIENT": "true",
"CENTRIFUGO_CHANNEL_WITHOUT_NAMESPACE_ALLOW_HISTORY_FOR_CLIENT": "true",
"CENTRIFUGO_CHANNEL_WITHOUT_NAMESPACE_HISTORY_SIZE": "1",
"CENTRIFUGO_CHANNEL_WITHOUT_NAMESPACE_HISTORY_TTL": "720h",
"CENTRIFUGO_HTTP_SERVER_TLS_ENABLED": "true",
"CENTRIFUGO_ADMIN_ENABLED": "true",
"CENTRIFUGO_ADMIN_PASSWORD": "123",
"CENTRIFUGO_ADMIN_SECRET": "123",
"UI_PORT": "1977"
}
},
{
"type": "lldb",
"request": "launch",
"name": "Debug unit tests in executable 'omnect-ui'",
"cargo": {
"args": [
"test",
"--no-run",
"--bin=omnect-ui",
"--package=omnect-ui"
],
"filter": {
"name": "omnect-ui",
"kind": "bin"
}
},
"args": [],
"cwd": "${workspaceFolder}"
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider extracting this into a test setup script or container to make things easier for non VSCode development environments :)

Comment on lines 3 to 47
"tasks": [
{
"label": "killall_centrifugo",
"command": "killall centrifugo || true",
"type": "shell",
"problemMatcher": []
},
{
"label": "setup_test_password",
"type": "shell",
"command": "cargo",
"args": [
"run",
"--bin",
"setup-password",
"--features=mock",
"--",
"123"
],
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": []
},
{
"label": "check_ods_running",
"type": "shell",
"command": "bash",
"args": [
"-c",
"if [ ! -S /tmp/api.sock ]; then echo '❌ ERROR: omnect-device-service is not running!' && echo 'Please start it first from /home/jzac/projects/omnect-device-service' && echo 'See: https://github.com/omnect/omnect-device-service' && exit 1; else echo '✓ omnect-device-service is running'; fi"
],
"problemMatcher": []
},
{
"label": "pre_launch_setup",
"dependsOn": [
"check_ods_running",
"killall_centrifugo",
"setup_test_password"
],
"dependsOrder": "sequence",
"problemMatcher": []
}
]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potentially extract to dev script, as well :)

Consolidates the two field update macros into one that handles both
single and multiple field updates. This simplifies the API while
maintaining the same functionality.

The merged macro uses Rust's pattern matching to distinguish between:
- Single field: update_field!(model.field, value)
- Multiple fields: update_field!(field1, val1; field2, val2)

Changes:
- src/app/src/macros.rs: Merge update_fields! into update_field! macro

Net reduction: 9 lines eliminated
All tests pass, no clippy warnings, zero functional changes.

Signed-off-by: Jan Zachmann <[email protected]>
Aligns the Core Cargo.toml with the backend's dependency style by
explicitly setting default-features=false for all dependencies. This:
- Reduces binary size by excluding unnecessary default features
- Makes feature requirements explicit
- Matches the pattern used in src/backend/Cargo.toml
- Improves build reproducibility

All dependencies now explicitly specify default-features=false:
- crux_core
- crux_http
- serde
- serde_json
- wasm-bindgen
- lazy_static

Build-dependencies updated similarly for consistency.

All tests pass, no functional changes.

Signed-off-by: Jan Zachmann <[email protected]>
Address all review findings from PR omnect#69:
- Remove VS Code default comments from launch.json
- Extract dev setup to scripts/dev-setup.sh for non-VS Code environments
- Simplify tasks.json to use dev-setup.sh script
- Revert version from 1.1.1 to 1.1.0 (architectural change only)
- Add SPA mention to README architecture section
- Fix src/app/Cargo.toml readme path to local README
- Remove API_BASE_URL and macro expansion comments
- Extract tests from lib.rs to separate tests.rs module
- Remove "Example component" language from DeviceInfoCore.vue
- Create static_files() function alias for better naming
- Make build.rs error when dist folder is missing
- Remove test-core auto-import from main.ts (load manually in dev)
- Add --clean flag to build-and-deploy-image.sh for cache-free builds

Fix Docker build issue:
- Change ViewModel import to Model in useCore.ts (ViewModel was removed
  in architecture refactoring commit 3957347)

Deferred to future PRs:
- Split types.rs into domain modules (types/{auth,device,network}.rs)
- Refactor macros to extract logic into helper functions

Signed-off-by: Jan Zachmann <[email protected]>
@JanZachmann JanZachmann requested a review from empwilli November 26, 2025 14:57
JanZachmann added a commit to JanZachmann/omnect-ui that referenced this pull request Nov 26, 2025
Split the monolithic src/app/src/types.rs file into domain-based modules
for better organization and maintainability:

- types/auth.rs - Authentication types (LoginCredentials, AuthToken, etc.)
- types/device.rs - Device information types (SystemInfo, HealthcheckInfo, etc.)
- types/network.rs - Network configuration types (NetworkStatus, OnlineStatus, etc.)
- types/factory_reset.rs - Factory reset types (FactoryReset, FactoryResetStatus, etc.)
- types/update.rs - Update validation types (UpdateValidationStatus, etc.)
- types/common.rs - Common shared types (Duration, Timeouts)
- types/mod.rs - Module re-exports

This addresses PR omnect#69 review finding:
omnect#69 (comment)

Additionally fixed pre-existing bug where TypeGen-generated TypeScript types
were using CommonJS format but Vite requires ESM modules. Added script to
automate proper type generation:
- scripts/generate-types.sh - Generates types and converts to ESM
- Updated CLAUDE.md with new type generation instructions

Fixed useCore.ts import to use Model instead of ViewModel (from previous
refactoring that eliminated the ViewModel struct).

Signed-off-by: Jan Zachmann <[email protected]>
Fixed two critical bugs preventing frontend build:

1. **TypeScript Type Generation (CommonJS/ESM)**
   - TypeGen generates CommonJS by default but Vite requires ESM
   - Created scripts/generate-types.sh to automate proper generation
   - Script modifies tsconfig.json to output ESM and recompiles TypeScript

2. **useCore.ts Import**
   - Fixed import to use Model instead of ViewModel
   - ViewModel was removed in previous refactoring (commit 3957347)

These fixes resolve the frontend build error where Rollup couldn't import
CommonJS modules from TypeScript files.

Signed-off-by: Jan Zachmann <[email protected]>
JanZachmann added a commit to JanZachmann/omnect-ui that referenced this pull request Nov 26, 2025
Split the monolithic src/app/src/types.rs file into domain-based modules
for better organization and maintainability:

- types/auth.rs - Authentication types (LoginCredentials, AuthToken, etc.)
- types/device.rs - Device information types (SystemInfo, HealthcheckInfo, etc.)
- types/network.rs - Network configuration types (NetworkStatus, OnlineStatus, etc.)
- types/factory_reset.rs - Factory reset types (FactoryReset, FactoryResetStatus, etc.)
- types/update.rs - Update validation types (UpdateValidationStatus, etc.)
- types/common.rs - Common shared types (Duration, Timeouts)
- types/mod.rs - Module re-exports

This addresses PR omnect#69 review finding:
omnect#69 (comment)

Additionally fixed pre-existing bug where TypeGen-generated TypeScript types
were using CommonJS format but Vite requires ESM modules. Added script to
automate proper type generation:
- scripts/generate-types.sh - Generates types and converts to ESM
- Updated CLAUDE.md with new type generation instructions

Fixed useCore.ts import to use Model instead of ViewModel (from previous
refactoring that eliminated the ViewModel struct).

Signed-off-by: Jan Zachmann <[email protected]>
JanZachmann added a commit to JanZachmann/omnect-ui that referenced this pull request Nov 26, 2025
…functions

Extract repetitive HTTP response handling logic from macros into reusable helper functions to improve maintainability and reduce code duplication.

Changes:
- Add handle_json_response() helper for extracting JSON response bodies
- Add handle_status_response() helper for checking HTTP status codes
- Update unauth_post! macro to use new helpers (3 patterns)
- Update auth_post! macro to use new helpers (3 patterns)
- Reduce macro complexity by moving logic to testable functions

This refactoring:
- Reduces code duplication across macro patterns
- Improves debuggability by moving logic to named functions
- Makes response handling more testable and maintainable
- Maintains existing functionality and API

Addresses PR omnect#69 finding r2563954064

Signed-off-by: Jan Zachmann <[email protected]>
JanZachmann added a commit to JanZachmann/omnect-ui that referenced this pull request Nov 26, 2025
…functions

Extract repetitive HTTP response handling logic from macros into reusable helper functions to improve maintainability and reduce code duplication.

Changes:
- Add build_api_url() helper for URL construction
- Add handle_json_response() helper for extracting JSON response bodies
- Add handle_status_response() helper for checking HTTP status codes
- Update unauth_post! macro to use new helpers (3 patterns)
- Update auth_post! macro to use new helpers (3 patterns)
- Reduce macro complexity by moving logic to testable functions

This refactoring:
- Reduces code duplication across macro patterns
- Improves debuggability by moving logic to named functions
- Makes response handling more testable and maintainable
- Centralizes URL construction logic
- Maintains existing functionality and API

Addresses PR omnect#69 finding r2563954064

Signed-off-by: Jan Zachmann <[email protected]>
JanZachmann added a commit to JanZachmann/omnect-ui that referenced this pull request Nov 26, 2025
…functions

Extract repetitive HTTP response handling logic from macros into reusable helper functions and a helper macro to improve maintainability and reduce code duplication.

Changes:
- Add build_api_url() helper for URL construction
- Add handle_json_response() helper for extracting JSON response bodies
- Add handle_status_response() helper for checking HTTP status codes
- Add with_loading! helper macro for loading state management
- Update unauth_post! macro to use new helpers (3 patterns)
- Update auth_post! macro to use new helpers (3 patterns)
- Reduce macro complexity by moving logic to testable functions

This refactoring:
- Reduces code duplication across macro patterns
- Centralizes loading state and render command logic
- Improves debuggability by moving logic to named functions
- Makes response handling more testable and maintainable
- Centralizes URL construction logic
- Maintains existing functionality and API

Addresses PR omnect#69 finding r2563954064

Signed-off-by: Jan Zachmann <[email protected]>
JanZachmann added a commit to JanZachmann/omnect-ui that referenced this pull request Nov 26, 2025
…functions

Extract repetitive HTTP response handling logic from macros into reusable helper functions and helper macros to improve maintainability and reduce code duplication.

Changes:
- Add build_api_url() helper for URL construction
- Add handle_json_response() helper for extracting JSON response bodies
- Add handle_status_response() helper for checking HTTP status codes
- Add with_loading! helper macro for loading state management
- Add handle_result! helper macro for result matching with error handling
- Update unauth_post! macro to use new helpers (3 patterns)
- Update auth_post! macro to use new helpers (3 patterns)
- Update handle_response! macro to use handle_result! helper (4 patterns)
- Reduce macro complexity by moving logic to testable functions

This refactoring:
- Eliminates repetitive boilerplate across all macro patterns
- Centralizes loading state, error handling, and render command logic
- Improves debuggability by moving logic to named functions
- Makes response handling more testable and maintainable
- Centralizes URL construction logic
- Maintains existing functionality and API

Addresses PR omnect#69 finding r2563954064

Signed-off-by: Jan Zachmann <[email protected]>
Copy link
Contributor

@empwilli empwilli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@JanZachmann JanZachmann merged commit 675f60c into omnect:main Nov 28, 2025
1 of 2 checks passed
JanZachmann added a commit to JanZachmann/omnect-ui that referenced this pull request Dec 2, 2025
Document todos for splitting types.rs into domain modules and
refactoring macros.rs for better maintainability.

Signed-off-by: Jan Zachmann <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants