diff --git a/.github/workflows/action.yml b/.github/workflows/action.yml index 6288ad6..709d35b 100644 --- a/.github/workflows/action.yml +++ b/.github/workflows/action.yml @@ -23,6 +23,11 @@ jobs: # Note: Required by vergen (https://crates.io/crates/vergen) run: git config --global --add safe.directory $GITHUB_WORKSPACE + - name: install dependencies (desktop-app) + run: | + sudo apt-get update + sudo apt-get install -y libwebkit2gtk-4.0-dev libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf + - name: Install build dependencies - Rustup run: | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain stable -y @@ -92,3 +97,77 @@ jobs: tag: ${{ github.ref }} prerelease: ${{ !startsWith(github.ref, 'refs/tags/') }} overwrite: true + + build-tauri: + needs: quick-tests + permissions: + contents: write + strategy: + fail-fast: false + matrix: + include: + - platform: 'macos-latest' + args: '--target aarch64-apple-darwin' + label: 'Mac-M1' + - platform: 'macos-latest' + args: '--target x86_64-apple-darwin' + label: 'Mac-x86' + - platform: 'ubuntu-22.04' + args: '' + label: 'Linux' + - platform: 'windows-latest' + args: '' + label: 'Windows' + + runs-on: ${{ matrix.platform }} + steps: + - uses: actions/checkout@v4 + + - name: install dependencies (ubuntu only) + if: matrix.platform == 'ubuntu-22.04' # This must match the platform value defined above. + run: | + sudo apt-get update + sudo apt-get install -y libwebkit2gtk-4.0-dev libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf + # webkitgtk 4.0 is for Tauri v1 - webkitgtk 4.1 is for Tauri v2. + + - name: setup node + uses: actions/setup-node@v4 + with: + node-version: lts/* + + - name: Setup Bun + uses: oven-sh/setup-bun@v1 + with: + bun-version: latest + + - name: install Rust stable + uses: dtolnay/rust-toolchain@stable + with: + # Those targets are only used on macos runners so it's in an `if` to slightly speed up windows and linux builds. + targets: ${{ matrix.platform == 'macos-latest' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }} + + - name: Rust cache + uses: swatinem/rust-cache@v2 + with: + workspaces: './src-tauri -> target' + + - name: Install dependencies + run: | + cd ping-viewer-next-desktop + bun install + + - name: Build the app + id: tauri-build + uses: tauri-apps/tauri-action@v0.5.13 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + projectPath: './ping-viewer-next-desktop' + args: ${{ matrix.args }} + + - name: Upload artifacts + uses: actions/upload-artifact@v4.4.0 + with: + name: 'ping-viewer_${{steps.tauri-build.outputs.appVersion}}_${{ matrix.label }}' + path: "${{ join(fromJSON(steps.tauri-build.outputs.artifactPaths), '\n') }}" + retention-days: 5 \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 2de692f..e845313 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,9 @@ validator = "0.18.1" thiserror = "1.0.63" shellexpand = "3.1" +tauri = { version = "1.7.2", optional = true, features = ["shell-open"] } + + [build-dependencies] vergen-gix = { version = "1.0.1", default-features = false, features = ["build", "cargo"] } @@ -44,3 +47,7 @@ path = "src/lib.rs" [[bin]] name = "ping-viewer-next" path = "src/main.rs" + +[features] +default = [] +desktop-app = ["tauri"] diff --git a/ping-viewer-next-desktop/.gitignore b/ping-viewer-next-desktop/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/ping-viewer-next-desktop/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/ping-viewer-next-desktop/README.md b/ping-viewer-next-desktop/README.md new file mode 100644 index 0000000..4b0342d --- /dev/null +++ b/ping-viewer-next-desktop/README.md @@ -0,0 +1,17 @@ +# Ping Viewer Next DesktopApp + +Ping Viewer Next Desktop is a standalone version of the **Ping Viewer Next** core server. This application allows you to interface with **ping devices** attached directly to your computer or connected to a remotely one. + +## Key Features + +- **Local and Remote Connections**: Connect to ping devices on your local network or to remote Ping Viewer Next servers. +- **Cross-Platform Support**: Available for macOS, Linux, and Windows. +- **Standalone App**: Just install and use directly on your favorite OS. + +### Download + +You can download the latest version from the [releases page](https://github.com/bluerobotics/ping-viewer-next/releases). + +## How It Works + +Ping Viewer Next Desktop integrates the core Ping Viewer Next server with an embedded GUI, using Tauri to create a native experience across platforms. diff --git a/ping-viewer-next-desktop/bun.lockb b/ping-viewer-next-desktop/bun.lockb new file mode 100755 index 0000000..14f3822 Binary files /dev/null and b/ping-viewer-next-desktop/bun.lockb differ diff --git a/ping-viewer-next-desktop/index.html b/ping-viewer-next-desktop/index.html new file mode 100644 index 0000000..99f203f --- /dev/null +++ b/ping-viewer-next-desktop/index.html @@ -0,0 +1,14 @@ + + + + + + + Tauri + Vue + Typescript App + + + +
+ + + diff --git a/ping-viewer-next-desktop/package.json b/ping-viewer-next-desktop/package.json new file mode 100644 index 0000000..2891872 --- /dev/null +++ b/ping-viewer-next-desktop/package.json @@ -0,0 +1,23 @@ +{ + "name": "ping-viewer-next-desktop", + "private": true, + "version": "0.1.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vue-tsc --noEmit && vite build", + "preview": "vite preview", + "tauri": "tauri" + }, + "dependencies": { + "vue": "^3.3.4", + "@tauri-apps/api": "^1" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.0.5", + "typescript": "^5.2.2", + "vite": "^5.3.1", + "vue-tsc": "^2.0.22", + "@tauri-apps/cli": "^1" + } +} diff --git a/ping-viewer-next-desktop/public/tauri.svg b/ping-viewer-next-desktop/public/tauri.svg new file mode 100644 index 0000000..31b62c9 --- /dev/null +++ b/ping-viewer-next-desktop/public/tauri.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/ping-viewer-next-desktop/public/vite.svg b/ping-viewer-next-desktop/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/ping-viewer-next-desktop/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ping-viewer-next-desktop/src-tauri/.gitignore b/ping-viewer-next-desktop/src-tauri/.gitignore new file mode 100644 index 0000000..16936f9 --- /dev/null +++ b/ping-viewer-next-desktop/src-tauri/.gitignore @@ -0,0 +1,9 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ + +# Generated by Tauri +# will have schema files for capabilities auto-completion +/gen/schemas + +/Cargo.lock diff --git a/ping-viewer-next-desktop/src-tauri/Cargo.toml b/ping-viewer-next-desktop/src-tauri/Cargo.toml new file mode 100644 index 0000000..dcd3087 --- /dev/null +++ b/ping-viewer-next-desktop/src-tauri/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "ping-viewer-next-desktop" +version = "0.1.0" +description = "A Tauri App" +authors = ["you"] +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[build-dependencies] +tauri-build = { version = "1.5.4", features = [] } + +[dependencies] +tauri = { version = "1.7.2", features = ["shell-open"] } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +ping-viewer-next = { path = "./../../", features = ["desktop-app"] } +tokio = { version = "1.40.0", features = ["full"] } +actix-web = "4.6.0" + +[features] +# This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!! +custom-protocol = ["tauri/custom-protocol"] diff --git a/ping-viewer-next-desktop/src-tauri/build.rs b/ping-viewer-next-desktop/src-tauri/build.rs new file mode 100644 index 0000000..d860e1e --- /dev/null +++ b/ping-viewer-next-desktop/src-tauri/build.rs @@ -0,0 +1,3 @@ +fn main() { + tauri_build::build() +} diff --git a/ping-viewer-next-desktop/src-tauri/icons/128x128.png b/ping-viewer-next-desktop/src-tauri/icons/128x128.png new file mode 100644 index 0000000..6be5e50 Binary files /dev/null and b/ping-viewer-next-desktop/src-tauri/icons/128x128.png differ diff --git a/ping-viewer-next-desktop/src-tauri/icons/128x128@2x.png b/ping-viewer-next-desktop/src-tauri/icons/128x128@2x.png new file mode 100644 index 0000000..e81bece Binary files /dev/null and b/ping-viewer-next-desktop/src-tauri/icons/128x128@2x.png differ diff --git a/ping-viewer-next-desktop/src-tauri/icons/32x32.png b/ping-viewer-next-desktop/src-tauri/icons/32x32.png new file mode 100644 index 0000000..a437dd5 Binary files /dev/null and b/ping-viewer-next-desktop/src-tauri/icons/32x32.png differ diff --git a/ping-viewer-next-desktop/src-tauri/icons/Square107x107Logo.png b/ping-viewer-next-desktop/src-tauri/icons/Square107x107Logo.png new file mode 100644 index 0000000..0ca4f27 Binary files /dev/null and b/ping-viewer-next-desktop/src-tauri/icons/Square107x107Logo.png differ diff --git a/ping-viewer-next-desktop/src-tauri/icons/Square142x142Logo.png b/ping-viewer-next-desktop/src-tauri/icons/Square142x142Logo.png new file mode 100644 index 0000000..b81f820 Binary files /dev/null and b/ping-viewer-next-desktop/src-tauri/icons/Square142x142Logo.png differ diff --git a/ping-viewer-next-desktop/src-tauri/icons/Square150x150Logo.png b/ping-viewer-next-desktop/src-tauri/icons/Square150x150Logo.png new file mode 100644 index 0000000..624c7bf Binary files /dev/null and b/ping-viewer-next-desktop/src-tauri/icons/Square150x150Logo.png differ diff --git a/ping-viewer-next-desktop/src-tauri/icons/Square284x284Logo.png b/ping-viewer-next-desktop/src-tauri/icons/Square284x284Logo.png new file mode 100644 index 0000000..c021d2b Binary files /dev/null and b/ping-viewer-next-desktop/src-tauri/icons/Square284x284Logo.png differ diff --git a/ping-viewer-next-desktop/src-tauri/icons/Square30x30Logo.png b/ping-viewer-next-desktop/src-tauri/icons/Square30x30Logo.png new file mode 100644 index 0000000..6219700 Binary files /dev/null and b/ping-viewer-next-desktop/src-tauri/icons/Square30x30Logo.png differ diff --git a/ping-viewer-next-desktop/src-tauri/icons/Square310x310Logo.png b/ping-viewer-next-desktop/src-tauri/icons/Square310x310Logo.png new file mode 100644 index 0000000..f9bc048 Binary files /dev/null and b/ping-viewer-next-desktop/src-tauri/icons/Square310x310Logo.png differ diff --git a/ping-viewer-next-desktop/src-tauri/icons/Square44x44Logo.png b/ping-viewer-next-desktop/src-tauri/icons/Square44x44Logo.png new file mode 100644 index 0000000..d5fbfb2 Binary files /dev/null and b/ping-viewer-next-desktop/src-tauri/icons/Square44x44Logo.png differ diff --git a/ping-viewer-next-desktop/src-tauri/icons/Square71x71Logo.png b/ping-viewer-next-desktop/src-tauri/icons/Square71x71Logo.png new file mode 100644 index 0000000..63440d7 Binary files /dev/null and b/ping-viewer-next-desktop/src-tauri/icons/Square71x71Logo.png differ diff --git a/ping-viewer-next-desktop/src-tauri/icons/Square89x89Logo.png b/ping-viewer-next-desktop/src-tauri/icons/Square89x89Logo.png new file mode 100644 index 0000000..f3f705a Binary files /dev/null and b/ping-viewer-next-desktop/src-tauri/icons/Square89x89Logo.png differ diff --git a/ping-viewer-next-desktop/src-tauri/icons/StoreLogo.png b/ping-viewer-next-desktop/src-tauri/icons/StoreLogo.png new file mode 100644 index 0000000..4556388 Binary files /dev/null and b/ping-viewer-next-desktop/src-tauri/icons/StoreLogo.png differ diff --git a/ping-viewer-next-desktop/src-tauri/icons/icon.icns b/ping-viewer-next-desktop/src-tauri/icons/icon.icns new file mode 100644 index 0000000..12a5bce Binary files /dev/null and b/ping-viewer-next-desktop/src-tauri/icons/icon.icns differ diff --git a/ping-viewer-next-desktop/src-tauri/icons/icon.ico b/ping-viewer-next-desktop/src-tauri/icons/icon.ico new file mode 100644 index 0000000..b3636e4 Binary files /dev/null and b/ping-viewer-next-desktop/src-tauri/icons/icon.ico differ diff --git a/ping-viewer-next-desktop/src-tauri/icons/icon.png b/ping-viewer-next-desktop/src-tauri/icons/icon.png new file mode 100644 index 0000000..e1cd261 Binary files /dev/null and b/ping-viewer-next-desktop/src-tauri/icons/icon.png differ diff --git a/ping-viewer-next-desktop/src-tauri/src/main.rs b/ping-viewer-next-desktop/src-tauri/src/main.rs new file mode 100644 index 0000000..fdd29d1 --- /dev/null +++ b/ping-viewer-next-desktop/src-tauri/src/main.rs @@ -0,0 +1,46 @@ +// Prevents additional console window on Windows in release, DO NOT REMOVE!! +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +use ping_viewer_next::{cli, device, logger, server}; +use tauri::Manager; + +#[tokio::main] +async fn main() { + cli::manager::init(); + + logger::manager::init(); + + let (manager, handler) = device::manager::DeviceManager::new(10); + + tokio::spawn(async move { manager.run().await }); + + run_tauri_app(handler).await; +} + +async fn run_tauri_app(handler: device::manager::ManagerActorHandler) { + tauri::Builder::default() + .setup(|app: &mut tauri::App| { + let window = app.get_window("main").unwrap(); + + std::thread::spawn(move || { + run_from_tauri(&cli::manager::server_address(), handler).unwrap(); + }); + + std::thread::spawn(move || { + std::thread::sleep(std::time::Duration::from_secs(6)); + window.eval("window.location.replace('http://0.0.0.0:8080')").unwrap(); + }); + + Ok(()) + }) + .run(tauri::generate_context!()) + .expect("error while running tauri application"); +} + +#[actix_web::main] +pub async fn run_from_tauri( + server_address: &str, + handler: device::manager::ManagerActorHandler, +) -> std::io::Result<()> { + server::manager::run(server_address, handler).await +} diff --git a/ping-viewer-next-desktop/src-tauri/tauri.conf.json b/ping-viewer-next-desktop/src-tauri/tauri.conf.json new file mode 100644 index 0000000..1fa0e4d --- /dev/null +++ b/ping-viewer-next-desktop/src-tauri/tauri.conf.json @@ -0,0 +1,43 @@ +{ + "build": { + "beforeDevCommand": "bun run dev", + "beforeBuildCommand": "bun run build", + "devPath": "http://localhost:1420", + "distDir": "../dist" + }, + "package": { + "productName": "ping-viewer-next-desktop", + "version": "0.1.0" + }, + "tauri": { + "allowlist": { + "all": false, + "shell": { + "all": false, + "open": true + } + }, + "windows": [ + { + "title": "ping-viewer-next-desktop", + "width": 800, + "height": 600 + } + ], + "security": { + "csp": null + }, + "bundle": { + "active": true, + "targets": "all", + "identifier": "com.ping-viewer-next-desktop.app", + "icon": [ + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.icns", + "icons/icon.ico" + ] + } + } +} diff --git a/ping-viewer-next-desktop/src/App.vue b/ping-viewer-next-desktop/src/App.vue new file mode 100644 index 0000000..c3fe08c --- /dev/null +++ b/ping-viewer-next-desktop/src/App.vue @@ -0,0 +1,57 @@ + + + + + diff --git a/ping-viewer-next-desktop/src/assets/vue.svg b/ping-viewer-next-desktop/src/assets/vue.svg new file mode 100644 index 0000000..770e9d3 --- /dev/null +++ b/ping-viewer-next-desktop/src/assets/vue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ping-viewer-next-desktop/src/components/Greet.vue b/ping-viewer-next-desktop/src/components/Greet.vue new file mode 100644 index 0000000..5bee007 --- /dev/null +++ b/ping-viewer-next-desktop/src/components/Greet.vue @@ -0,0 +1,21 @@ + + + diff --git a/ping-viewer-next-desktop/src/main.ts b/ping-viewer-next-desktop/src/main.ts new file mode 100644 index 0000000..b670de8 --- /dev/null +++ b/ping-viewer-next-desktop/src/main.ts @@ -0,0 +1,4 @@ +import { createApp } from "vue"; +import App from "./App.vue"; + +createApp(App).mount("#app"); diff --git a/ping-viewer-next-desktop/src/vite-env.d.ts b/ping-viewer-next-desktop/src/vite-env.d.ts new file mode 100644 index 0000000..fc81239 --- /dev/null +++ b/ping-viewer-next-desktop/src/vite-env.d.ts @@ -0,0 +1,7 @@ +/// + +declare module "*.vue" { + import type { DefineComponent } from "vue"; + const component: DefineComponent<{}, {}, any>; + export default component; +} diff --git a/ping-viewer-next-desktop/tsconfig.json b/ping-viewer-next-desktop/tsconfig.json new file mode 100644 index 0000000..f82888f --- /dev/null +++ b/ping-viewer-next-desktop/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "preserve", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/ping-viewer-next-desktop/tsconfig.node.json b/ping-viewer-next-desktop/tsconfig.node.json new file mode 100644 index 0000000..42872c5 --- /dev/null +++ b/ping-viewer-next-desktop/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/ping-viewer-next-desktop/vite.config.ts b/ping-viewer-next-desktop/vite.config.ts new file mode 100644 index 0000000..ce8e371 --- /dev/null +++ b/ping-viewer-next-desktop/vite.config.ts @@ -0,0 +1,21 @@ +import { defineConfig } from "vite"; +import vue from "@vitejs/plugin-vue"; + +// https://vitejs.dev/config/ +export default defineConfig(async () => ({ + plugins: [vue()], + + // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` + // + // 1. prevent vite from obscuring rust errors + clearScreen: false, + // 2. tauri expects a fixed port, fail if that port is not available + server: { + port: 1420, + strictPort: true, + watch: { + // 3. tell vite to ignore watching `src-tauri` + ignored: ["**/src-tauri/**"], + }, + }, +})); diff --git a/src/logger/manager.rs b/src/logger/manager.rs index 7a4db2d..dbeaf62 100644 --- a/src/logger/manager.rs +++ b/src/logger/manager.rs @@ -1,4 +1,4 @@ -use std::str::FromStr; +use std::{path::PathBuf, str::FromStr}; use crate::cli; @@ -39,13 +39,15 @@ pub fn init() { EnvFilter::new(LevelFilter::DEBUG.to_string()) }; - let dir = cli::manager::log_path(); + let dir = get_app_log_dir(); + let file_appender = custom_rolling_appender( dir, tracing_appender::rolling::Rotation::HOURLY, "ping-viewer", "log", ); + let file_layer = fmt::Layer::new() .with_writer(file_appender) .with_ansi(false) @@ -136,3 +138,41 @@ fn custom_rolling_appender>( .build(dir) .expect("failed to initialize rolling file appender") } + +#[allow(unused)] +#[cfg(feature = "desktop-app")] +static APP_DIR: &str = "Ping-Viewer-Next"; + +#[cfg(feature = "desktop-app")] +pub fn get_app_home_dir() -> PathBuf { + #[cfg(target_os = "windows")] + if let Err(e) = std::env::current_dir() { + error!("Failed to get app home dir. Errmsg: {}", e); + std::process::exit(-1); + } else { + return std::env::current_dir().unwrap(); + } + + #[cfg(not(target_os = "windows"))] + match tauri::api::path::home_dir() { + None => { + error!("Failed to get app home dir"); + std::process::exit(-1); + } + Some(path) => { + return path.join(APP_DIR); + } + } +} + +pub fn get_app_log_dir() -> PathBuf { + #[cfg(feature = "desktop-app")] + { + get_app_home_dir().join("logs") + } + + #[cfg(not(feature = "desktop-app"))] + { + PathBuf::from(cli::manager::log_path()) + } +}