diff --git a/.cargo/config.toml b/.cargo/config.toml index 2548b156..7e62bd5c 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -9,6 +9,14 @@ rustflags = ["-C", "link-arg=-fuse-ld=mold"] [target.'cfg(all(windows, target_env = "msvc"))'] rustflags = ["-C", "target-feature=+crt-static"] +[target.wasm32-unknown-unknown] +rustflags = [ + "-C", + "opt-level=z", + "--cfg", + 'getrandom_backend="wasm_js"', +] + # region # region CI diff --git a/.github/actions/prepare-build/action.yml b/.github/actions/prepare-build/action.yml index 7b11ee93..115dc941 100644 --- a/.github/actions/prepare-build/action.yml +++ b/.github/actions/prepare-build/action.yml @@ -50,6 +50,7 @@ runs: if: ${{ inputs.pnpm == 'true' }} uses: ./.github/actions/prepare-pnpm with: + token: ${{ inputs.token }} build-filter: ${{ inputs.pnpm-build-filter }} - name: Install GUI dependencies (Linux) diff --git a/.github/actions/prepare-pnpm/action.yml b/.github/actions/prepare-pnpm/action.yml index 8bc756a9..4cebfec6 100644 --- a/.github/actions/prepare-pnpm/action.yml +++ b/.github/actions/prepare-pnpm/action.yml @@ -3,6 +3,9 @@ author: Luna description: 'Setup Node.js, pnpm, and install dependencies' inputs: + token: + description: 'GitHub token, used by setup-protoc action' + required: false build-filter: description: 'The filter argument for pnpm build (e.g. ./easytier-web/*)' required: false @@ -11,6 +14,22 @@ inputs: runs: using: "composite" steps: + - uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + toolchain: 1.95 + target: wasm32-unknown-unknown + cache: false + rustflags: '' + + - uses: taiki-e/install-action@v2 + with: + tool: wasm-pack + + - uses: arduino/setup-protoc@v3 + with: + version: '35.1' + repo-token: ${{ inputs.token }} + - name: Setup Node.js uses: actions/setup-node@v5 with: @@ -46,3 +65,11 @@ runs: echo "No build filter provided, building all packages" pnpm -r --workspace-concurrency=1 build fi + + - name: Bundle config generator with web frontend + shell: bash + run: | + if [ -f easytier-web/frontend/dist/index.html ] && [ -f easytier-web/config-generator/dist/index.html ]; then + mkdir -p easytier-web/frontend/dist/config-generator + cp -R easytier-web/config-generator/dist/. easytier-web/frontend/dist/config-generator/ + fi diff --git a/.github/workflows/core.yml b/.github/workflows/core.yml index 185fd2cd..7e8f4ee3 100644 --- a/.github/workflows/core.yml +++ b/.github/workflows/core.yml @@ -47,6 +47,7 @@ jobs: - name: Setup Frontend Environment uses: ./.github/actions/prepare-pnpm with: + token: ${{ github.token }} build-filter: './easytier-web/*' - name: Archive artifact diff --git a/.gitignore b/.gitignore index b17d8c5e..6a270837 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,8 @@ easytier-panic.log # web node_modules easytier-web/frontend-lib/src/generated/ +easytier-web/config-generator/dist/ +easytier-web/config-generator/src/generated/ .vite diff --git a/Cargo.lock b/Cargo.lock index 41d9dfd2..5f73a262 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2434,6 +2434,8 @@ dependencies = [ "derive_builder", "easytier-proto", "futures", + "getrandom 0.2.15", + "getrandom 0.3.2", "guarden 0.2.0", "hmac", "http-body-util", @@ -2468,6 +2470,7 @@ dependencies = [ "tracing", "url", "uuid", + "wasm-bindgen", "webpki-roots 0.26.3", "wildmatch", "x25519-dalek", diff --git a/easytier-core/Cargo.toml b/easytier-core/Cargo.toml index b923dc54..c67d412a 100644 --- a/easytier-core/Cargo.toml +++ b/easytier-core/Cargo.toml @@ -14,6 +14,9 @@ license-file = "../LICENSE" [lib] crate-type = ["rlib", "cdylib"] +[package.metadata.wasm-pack.profile.release] +wasm-opt = ["-Oz", "--enable-bulk-memory", "--enable-nontrapping-float-to-int"] + [dependencies] anyhow = "1.0" ariadne = { version = "0.5", optional = true } @@ -55,7 +58,6 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1" sha2 = "0.10.8" smoltcp = { git = "https://github.com/smoltcp-rs/smoltcp.git", rev = "0a926767a68bc88d5512afefa7529c5ecdade4ea", optional = true, default-features = false } -snow = "0.10.0" stun_codec = "0.3.4" thiserror = "1.0" tracing = "0.1" @@ -81,9 +83,20 @@ aes-gcm = { version = "0.10.3", optional = true } chacha20poly1305 = { version = "0.10.1", optional = true } openssl = { version = "0.10", optional = true, features = ["vendored"] } +[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies] +getrandom-02 = { package = "getrandom", version = "0.2.15", features = ["js"] } +getrandom-03 = { package = "getrandom", version = "0.3.2", features = ["wasm_js"] } +snow = { version = "0.10.0", default-features = false, features = ["default-resolver", "default-resolver-crypto"] } +uuid = { version = "1.5.0", features = ["js"] } +wasm-bindgen = "0.2" + +[target.'cfg(not(all(target_arch = "wasm32", target_os = "unknown")))'.dependencies] +snow = "0.10.0" + [features] default = ["aes-gcm", "endpoint-discovery", "extended-services", "management", "tcp-hole-punch"] aes-gcm = ["dep:aes-gcm"] +browser-config = ["config-write", "easytier-proto/api", "easytier-proto/json-rpc"] chacha20 = ["dep:chacha20poly1305"] openssl-crypto = ["dep:openssl"] ring-crypto = ["dep:ring"] diff --git a/easytier-core/src/config/browser.rs b/easytier-core/src/config/browser.rs new file mode 100644 index 00000000..2913ee83 --- /dev/null +++ b/easytier-core/src/config/browser.rs @@ -0,0 +1,27 @@ +use wasm_bindgen::prelude::*; + +use super::{ + api_input::{NetworkConfig, NetworkConfigExt}, + toml::{ConfigLoader, TomlConfig}, +}; + +fn js_error(error: impl std::fmt::Debug) -> JsValue { + JsValue::from_str(&format!("{error:?}")) +} + +#[wasm_bindgen] +pub fn generate_config(config_json: &str) -> Result { + let config: NetworkConfig = serde_json::from_str(config_json).map_err(js_error)?; + config + .gen_config() + .map(|config| config.dump()) + .map_err(js_error) +} + +#[wasm_bindgen] +pub fn parse_config(toml_config: &str) -> Result { + let config = TomlConfig::new_from_str(toml_config) + .and_then(|config| NetworkConfig::new_from_config(&config)) + .map_err(js_error)?; + serde_json::to_string(&config).map_err(js_error) +} diff --git a/easytier-core/src/config/mod.rs b/easytier-core/src/config/mod.rs index fc48a73a..88314f41 100644 --- a/easytier-core/src/config/mod.rs +++ b/easytier-core/src/config/mod.rs @@ -2,8 +2,14 @@ #[cfg(feature = "management")] pub mod api; -#[cfg(feature = "management")] +#[cfg(any(feature = "management", feature = "browser-config"))] pub mod api_input; +#[cfg(all( + feature = "browser-config", + target_arch = "wasm32", + target_os = "unknown" +))] +mod browser; mod encryption; pub mod gateway; pub mod peers; diff --git a/easytier-web/config-generator/index.html b/easytier-web/config-generator/index.html new file mode 100644 index 00000000..a31f93e2 --- /dev/null +++ b/easytier-web/config-generator/index.html @@ -0,0 +1,12 @@ + + + + + + EasyTier Config Generator + + +
+ + + diff --git a/easytier-web/config-generator/package.json b/easytier-web/config-generator/package.json new file mode 100644 index 00000000..e7cb3a66 --- /dev/null +++ b/easytier-web/config-generator/package.json @@ -0,0 +1,24 @@ +{ + "name": "easytier-config-generator", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "build:config-wasm": "wasm-pack build ../../easytier-core --target web --out-dir ../easytier-web/config-generator/src/generated/config-wasm --out-name easytier_config --release --no-pack -- --no-default-features --features browser-config", + "dev": "pnpm build:config-wasm && pnpm --dir ../frontend-lib build && vite", + "build": "pnpm build:config-wasm && pnpm --dir ../frontend-lib build && vue-tsc --noEmit && vite build", + "preview": "vite preview" + }, + "dependencies": { + "@primeuix/themes": "^1.2.3", + "easytier-frontend-lib": "workspace:*", + "primevue": "^4.3.9", + "vue": "^3.5.12" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.1.4", + "typescript": "~5.6.2", + "vite": "^5.4.21", + "vue-tsc": "^2.1.10" + } +} diff --git a/easytier-web/config-generator/src/App.vue b/easytier-web/config-generator/src/App.vue new file mode 100644 index 00000000..747e8598 --- /dev/null +++ b/easytier-web/config-generator/src/App.vue @@ -0,0 +1,151 @@ + + + diff --git a/easytier-web/frontend/src/main.ts b/easytier-web/frontend/src/main.ts index 13414677..1e08d12e 100644 --- a/easytier-web/frontend/src/main.ts +++ b/easytier-web/frontend/src/main.ts @@ -16,7 +16,6 @@ import DeviceManagement from './components/DeviceManagement.vue' import Dashboard from './components/Dashboard.vue' import DialogService from 'primevue/dialogservice'; import ToastService from 'primevue/toastservice'; -import ConfigGenerator from './components/ConfigGenerator.vue' const routes = [ { @@ -67,10 +66,6 @@ const routes = [ return { name: 'login' } } } - }, - { - path: '/config_generator', - component: ConfigGenerator, } ] diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e3ad8a56..18672c14 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -145,6 +145,34 @@ importers: specifier: ^2.1.10 version: 2.2.12(typescript@5.6.3) + easytier-web/config-generator: + dependencies: + '@primeuix/themes': + specifier: ^1.2.3 + version: 1.2.3 + easytier-frontend-lib: + specifier: workspace:* + version: link:../frontend-lib + primevue: + specifier: ^4.3.9 + version: 4.3.9(vue@3.5.21(typescript@5.6.3)) + vue: + specifier: ^3.5.12 + version: 3.5.21(typescript@5.6.3) + devDependencies: + '@vitejs/plugin-vue': + specifier: ^5.1.4 + version: 5.2.4(vite@5.4.21(@types/node@22.18.1))(vue@3.5.21(typescript@5.6.3)) + typescript: + specifier: ~5.6.2 + version: 5.6.3 + vite: + specifier: ^5.4.21 + version: 5.4.21(@types/node@22.18.1) + vue-tsc: + specifier: ^2.1.10 + version: 2.2.12(typescript@5.6.3) + easytier-web/frontend: dependencies: '@modyfi/vite-plugin-yaml': diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 25d3909b..66fefd5f 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,4 +1,5 @@ packages: + - 'easytier-web/config-generator' - 'easytier-web/frontend' - 'easytier-web/frontend-lib' - 'easytier-gui'