From bb43cd724922c0d9a38e909f0caae9adb2a460d3 Mon Sep 17 00:00:00 2001 From: Henrique Cavarsan Date: Fri, 16 Aug 2024 21:14:40 -0400 Subject: [PATCH] feat: add job to deploy kftray-tui artifacts (#285) * feat: add job to deploy kftray-tui artifacts * feat: add job to deploy kftray-tui artifacts * feat: add job to deploy kftray-tui artifacts * feat: add job to deploy kftray-tui artifacts * feat: add job to deploy kftray-tui artifacts * feat: add job to deploy kftray-tui artifacts * feat: add job to deploy kftray-tui artifacts * feat: add job to deploy kftray-tui artifacts * feat: add job to deploy kftray-tui artifacts * feat: add job to deploy kftray-tui artifacts * feat: add job to deploy kftray-tui artifacts * feat: add job to deploy kftray-tui artifacts * feat: add job to deploy kftray-tui artifacts * feat: add job to deploy kftray-tui artifacts * feat: add job to deploy kftray-tui artifacts * feat: add job to deploy kftray-tui artifacts --- .github/workflows/main.yml | 415 ++--- Cargo.lock | 1352 ++++++++++------- Cargo.toml | 3 +- README.md | 275 +--- crates/kftray-commons/.gitignore | 5 + crates/kftray-commons/Cargo.toml | 42 + crates/kftray-commons/src/lib.rs | 5 + .../src/models/config_model.rs} | 0 .../src/models/config_state_model.rs | 11 + crates/kftray-commons/src/models/mod.rs | 4 + .../src/models/response.rs | 0 .../src/models/window.rs | 14 +- crates/kftray-commons/src/utils/config.rs | 249 +++ .../src/utils/config_dir.rs | 21 +- .../kftray-commons/src/utils/config_state.rs | 68 + .../src => kftray-commons/src/utils}/db.rs | 178 ++- crates/kftray-commons/src/utils/github.rs | 35 + .../src/utils}/logging.rs | 3 +- crates/kftray-commons/src/utils/migration.rs | 179 +++ crates/kftray-commons/src/utils/mod.rs | 8 + .../src/utils/validate_configs.rs | 2 - crates/kftray-portforward/.gitignore | 5 + crates/kftray-portforward/Cargo.toml | 41 + crates/kftray-portforward/src/.gitignore | 5 + crates/kftray-portforward/src/client.rs | 75 + .../src/core.rs} | 227 +-- crates/kftray-portforward/src/lib.rs | 12 + crates/kftray-portforward/src/mod.rs | 7 + crates/kftray-portforward/src/models/kube.rs | 232 +++ crates/kftray-portforward/src/models/mod.rs | 1 + .../src}/pod_finder.rs | 2 +- .../src}/port_forward.rs | 56 +- crates/kftray-server/Cargo.toml | 4 +- crates/kftray-server/Dockerfile | 2 +- crates/kftray-tauri/Cargo.toml | 20 +- crates/kftray-tauri/src/commands/config.rs | 72 + .../kftray-tauri/src/commands/config_state.rs | 10 + crates/kftray-tauri/src/commands/github.rs | 154 ++ .../src/{commands.rs => commands/httplogs.rs} | 242 +-- .../{kubeforward => commands}/kubecontext.rs | 119 +- crates/kftray-tauri/src/commands/mod.rs | 7 + .../kftray-tauri/src/commands/portforward.rs | 135 ++ .../kftray-tauri/src/commands/window_state.rs | 25 + crates/kftray-tauri/src/config.rs | 473 ------ crates/kftray-tauri/src/keychain.rs | 166 -- crates/kftray-tauri/src/kubeforward/mod.rs | 101 -- .../src/kubeforward/pod_selection.rs | 37 - crates/kftray-tauri/src/lib.rs | 5 + crates/kftray-tauri/src/logging.rs | 4 +- crates/kftray-tauri/src/main.rs | 118 +- crates/kftray-tauri/src/models/kube.rs | 124 -- crates/kftray-tauri/src/models/mod.rs | 4 - crates/kftray-tauri/src/remote_config.rs | 59 - crates/kftray-tauri/src/tray.rs | 20 +- crates/kftray-tauri/src/utils/mod.rs | 2 - crates/kftray-tauri/src/window.rs | 53 +- crates/kftray-tauri/tauri.conf.json | 16 +- crates/kftui/.gitignore | 5 + crates/kftui/Cargo.toml | 66 + crates/kftui/build.rs | 3 + crates/kftui/entitlements.plist | 16 + crates/kftui/src/.gitignore | 81 + crates/kftui/src/core/logging.rs | 68 + crates/kftui/src/core/mod.rs | 6 + crates/kftui/src/core/port_forward.rs | 116 ++ crates/kftui/src/main.rs | 13 + crates/kftui/src/tui/app.rs | 78 + crates/kftui/src/tui/input/file_explorer.rs | 247 +++ crates/kftui/src/tui/input/mod.rs | 450 ++++++ crates/kftui/src/tui/input/navigation.rs | 16 + crates/kftui/src/tui/input/popup.rs | 36 + crates/kftui/src/tui/mod.rs | 5 + crates/kftui/src/tui/ui/draw.rs | 161 ++ crates/kftui/src/tui/ui/header.rs | 1 + crates/kftui/src/tui/ui/logo.rs | 55 + crates/kftui/src/tui/ui/mod.rs | 44 + crates/kftui/src/tui/ui/popup.rs | 339 +++++ crates/kftui/src/tui/ui/render.rs | 165 ++ crates/kftui/src/tui/ui/table.rs | 294 ++++ crates/kftui/src/utils/config.rs | 40 + crates/kftui/src/utils/file.rs | 15 + crates/kftui/src/utils/mod.rs | 2 + docs/ARCH.md | 35 + docs/kftray/BUILD.md | 115 ++ docs/kftray/INSTALL.md | 58 + docs/kftray/USAGE.md | 113 ++ docs/kftui/BUILD.md | 55 + docs/kftui/INSTALL.md | 66 + docs/kftui/USAGE.md | 82 + examples/configs.json | 29 +- frontend/package.json | 2 +- .../Footer/BulkDeleteButton/index.tsx | 17 +- .../Footer/SyncConfigsButton/index.tsx | 2 - frontend/src/components/Footer/index.tsx | 4 - .../src/components/GitSyncModal/index.tsx | 3 - frontend/src/components/Main/index.tsx | 412 ++--- .../PortForwardRow/index.tsx | 24 +- .../ContextsAccordion/index.tsx | 4 - .../src/components/PortForwardTable/index.tsx | 17 +- frontend/src/types/index.ts | 27 +- hacks/kftray-utils/Cargo.lock | 1292 ++++++++++++++++ {crates => hacks}/kftray-utils/Cargo.toml | 3 + .../kftray-utils/src/bump_version.rs | 56 +- .../kftray-utils/src/generate_icons.rs | 0 hacks/kftui_installer.ps1 | 66 + hacks/kftui_installer.sh | 142 ++ icon.png | Bin 0 -> 122541 bytes img/ss.png | Bin 0 -> 329947 bytes img/sstui.png | Bin 0 -> 96853 bytes pnpm-lock.yaml | 649 ++++---- 110 files changed, 7761 insertions(+), 3338 deletions(-) create mode 100644 crates/kftray-commons/.gitignore create mode 100644 crates/kftray-commons/Cargo.toml create mode 100644 crates/kftray-commons/src/lib.rs rename crates/{kftray-tauri/src/models/config.rs => kftray-commons/src/models/config_model.rs} (100%) create mode 100644 crates/kftray-commons/src/models/config_state_model.rs create mode 100644 crates/kftray-commons/src/models/mod.rs rename crates/{kftray-tauri => kftray-commons}/src/models/response.rs (100%) rename crates/{kftray-tauri => kftray-commons}/src/models/window.rs (62%) create mode 100644 crates/kftray-commons/src/utils/config.rs rename crates/{kftray-tauri => kftray-commons}/src/utils/config_dir.rs (86%) create mode 100644 crates/kftray-commons/src/utils/config_state.rs rename crates/{kftray-tauri/src => kftray-commons/src/utils}/db.rs (50%) create mode 100644 crates/kftray-commons/src/utils/github.rs rename crates/{kftray-tauri/src/kubeforward => kftray-commons/src/utils}/logging.rs (99%) create mode 100644 crates/kftray-commons/src/utils/migration.rs create mode 100644 crates/kftray-commons/src/utils/mod.rs rename crates/{kftray-tauri => kftray-commons}/src/utils/validate_configs.rs (98%) create mode 100644 crates/kftray-portforward/.gitignore create mode 100644 crates/kftray-portforward/Cargo.toml create mode 100644 crates/kftray-portforward/src/.gitignore create mode 100644 crates/kftray-portforward/src/client.rs rename crates/{kftray-tauri/src/kubeforward/commands.rs => kftray-portforward/src/core.rs} (82%) create mode 100644 crates/kftray-portforward/src/lib.rs create mode 100644 crates/kftray-portforward/src/mod.rs create mode 100644 crates/kftray-portforward/src/models/kube.rs create mode 100644 crates/kftray-portforward/src/models/mod.rs rename crates/{kftray-tauri/src/kubeforward => kftray-portforward/src}/pod_finder.rs (97%) rename crates/{kftray-tauri/src/kubeforward => kftray-portforward/src}/port_forward.rs (93%) create mode 100644 crates/kftray-tauri/src/commands/config.rs create mode 100644 crates/kftray-tauri/src/commands/config_state.rs create mode 100644 crates/kftray-tauri/src/commands/github.rs rename crates/kftray-tauri/src/{commands.rs => commands/httplogs.rs} (63%) rename crates/kftray-tauri/src/{kubeforward => commands}/kubecontext.rs (72%) create mode 100644 crates/kftray-tauri/src/commands/mod.rs create mode 100644 crates/kftray-tauri/src/commands/portforward.rs create mode 100644 crates/kftray-tauri/src/commands/window_state.rs delete mode 100644 crates/kftray-tauri/src/config.rs delete mode 100644 crates/kftray-tauri/src/keychain.rs delete mode 100644 crates/kftray-tauri/src/kubeforward/mod.rs delete mode 100644 crates/kftray-tauri/src/kubeforward/pod_selection.rs create mode 100644 crates/kftray-tauri/src/lib.rs delete mode 100644 crates/kftray-tauri/src/models/kube.rs delete mode 100644 crates/kftray-tauri/src/models/mod.rs delete mode 100644 crates/kftray-tauri/src/remote_config.rs delete mode 100644 crates/kftray-tauri/src/utils/mod.rs create mode 100644 crates/kftui/.gitignore create mode 100644 crates/kftui/Cargo.toml create mode 100644 crates/kftui/build.rs create mode 100644 crates/kftui/entitlements.plist create mode 100644 crates/kftui/src/.gitignore create mode 100644 crates/kftui/src/core/logging.rs create mode 100644 crates/kftui/src/core/mod.rs create mode 100644 crates/kftui/src/core/port_forward.rs create mode 100644 crates/kftui/src/main.rs create mode 100644 crates/kftui/src/tui/app.rs create mode 100644 crates/kftui/src/tui/input/file_explorer.rs create mode 100644 crates/kftui/src/tui/input/mod.rs create mode 100644 crates/kftui/src/tui/input/navigation.rs create mode 100644 crates/kftui/src/tui/input/popup.rs create mode 100644 crates/kftui/src/tui/mod.rs create mode 100644 crates/kftui/src/tui/ui/draw.rs create mode 100644 crates/kftui/src/tui/ui/header.rs create mode 100644 crates/kftui/src/tui/ui/logo.rs create mode 100644 crates/kftui/src/tui/ui/mod.rs create mode 100644 crates/kftui/src/tui/ui/popup.rs create mode 100644 crates/kftui/src/tui/ui/render.rs create mode 100644 crates/kftui/src/tui/ui/table.rs create mode 100644 crates/kftui/src/utils/config.rs create mode 100644 crates/kftui/src/utils/file.rs create mode 100644 crates/kftui/src/utils/mod.rs create mode 100644 docs/ARCH.md create mode 100644 docs/kftray/BUILD.md create mode 100644 docs/kftray/INSTALL.md create mode 100644 docs/kftray/USAGE.md create mode 100644 docs/kftui/BUILD.md create mode 100644 docs/kftui/INSTALL.md create mode 100644 docs/kftui/USAGE.md create mode 100644 hacks/kftray-utils/Cargo.lock rename {crates => hacks}/kftray-utils/Cargo.toml (89%) rename {crates => hacks}/kftray-utils/src/bump_version.rs (83%) rename {crates => hacks}/kftray-utils/src/generate_icons.rs (100%) create mode 100644 hacks/kftui_installer.ps1 create mode 100755 hacks/kftui_installer.sh create mode 100644 icon.png create mode 100644 img/ss.png create mode 100644 img/sstui.png diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5685831f..ab520de4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -7,156 +7,70 @@ on: env: TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} - VITE_ENV: 'production' - TAURI_DEBUG: 'false' + VITE_ENV: "production" + TAURI_DEBUG: "false" jobs: - macos-universal: - permissions: - contents: write - runs-on: macos-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Reconfigure for MacOS Universal - run: rustup target add aarch64-apple-darwin && rustup target add x86_64-apple-darwin - - - name: Rust setup - uses: dtolnay/rust-toolchain@stable - - - name: Rust cache - uses: swatinem/rust-cache@v2 - - - name: Install pnpm - uses: pnpm/action-setup@v3 - with: - version: 9.1.2 - - - name: Sync node version and setup cache - uses: actions/setup-node@v4 - with: - node-version: "lts/*" - cache: "pnpm" - - - name: Cache pnpm modules - uses: actions/cache@v4 - with: - path: | - $(pnpm store path --silent) - **/node_modules - key: ${{ runner.OS }}-pnpm-${{ runner.ARCH }}-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.OS }}-pnpm-${{ runner.ARCH }}- - - - name: Install dependencies - run: pnpm install --no-frozen-lockfile - - - name: Build the app for MacOS Universal - uses: tauri-apps/tauri-action@v0 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NODE_OPTIONS: --max-old-space-size=6000 - # MacOS Signing: - ENABLE_CODE_SIGNING: ${{ secrets.APPLE_CERTIFICATE }} - APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} - APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} - APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} - APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} - APPLE_ID: ${{ secrets.APPLE_ID }} - APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }} - with: - tagName: ${{ github.ref_name }} - releaseName: "KFtray - ${{ github.ref_name }}" - releaseBody: "See the assets to download this version and install." - releaseDraft: true - prerelease: false - updaterJsonKeepUniversal: true - args: --target universal-apple-darwin --bundles dmg,updater --verbose - - - ubuntu-amd64: + create-release-draft: permissions: contents: write runs-on: ubuntu-latest - steps: - name: Checkout repository uses: actions/checkout@v4 - - - name: Install dependencies for Linux + - name: Create release draft run: | - sudo apt-get update - sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libayatana-appindicator3-dev librsvg2-dev libssl-dev - - - name: Rust setup - uses: dtolnay/rust-toolchain@stable - - - name: Rust cache - uses: swatinem/rust-cache@v2 - - - name: Install pnpm - uses: pnpm/action-setup@v3 - with: - version: 9.1.2 - - - name: Sync node version and setup cache - uses: actions/setup-node@v4 - with: - node-version: "lts/*" - cache: "pnpm" - - - name: Cache pnpm modules - uses: actions/cache@v4 - with: - path: | - $(pnpm store path --silent) - **/node_modules - key: ${{ runner.OS }}-pnpm-${{ runner.ARCH }}-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.OS }}-pnpm-${{ runner.ARCH }}- - - - name: Install dependencies - run: pnpm install --no-frozen-lockfile - - - name: Build the app for Linux amd64 - uses: tauri-apps/tauri-action@v0 + echo "Creating release draft for tag: ${{ github.ref_name }}" + gh release create ${{ github.ref_name }} --draft --title "KFtray - ${{ github.ref_name }}" --notes "See the assets to download this version and install." env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NODE_OPTIONS: --max-old-space-size=6000 - with: - tagName: ${{ github.ref_name }} - releaseName: "KFtray - ${{ github.ref_name }}" - releaseBody: "See the assets to download this version and install." - releaseDraft: true - prerelease: false - updaterJsonKeepUniversal: true - args: --target x86_64-unknown-linux-gnu --bundles appimage,updater --verbose - ubuntu-arm64: + kftray-tauri: + needs: create-release-draft permissions: contents: write - runs-on: ubicloud-standard-4-arm - + strategy: + matrix: + include: + - os: ubuntu-latest + arch: arm64 + runner: ubicloud-standard-4-arm + rust_target: aarch64-unknown-linux-gnu + tauri_args: "--target aarch64-unknown-linux-gnu --bundles appimage,updater --verbose" + - os: ubuntu-latest + arch: amd64 + rust_target: x86_64-unknown-linux-gnu + tauri_args: "--target x86_64-unknown-linux-gnu --bundles appimage,updater --verbose" + - os: macos-latest + arch: universal + rust_target: universal-apple-darwin + tauri_args: "--target universal-apple-darwin --bundles dmg,updater --verbose" + - os: windows-latest + arch: x86 + rust_target: i686-pc-windows-msvc + tauri_args: "--target i686-pc-windows-msvc --bundles nsis,updater --verbose" + msvc_arch: x86 + - os: windows-latest + arch: x86_64 + rust_target: x86_64-pc-windows-msvc + tauri_args: "--target x86_64-pc-windows-msvc --bundles nsis,updater --verbose" + msvc_arch: x64 + - os: windows-latest + arch: arm64 + rust_target: aarch64-pc-windows-msvc + tauri_args: "--target aarch64-pc-windows-msvc --bundles nsis,updater --verbose" + + runs-on: ${{ matrix.runner || matrix.os }} steps: - name: Checkout repository uses: actions/checkout@v4 - - name: Install dependencies for Linux - run: | - sudo apt-get update - sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libayatana-appindicator3-dev librsvg2-dev libssl-dev perl - - - name: Rust setup + - name: Setup Rust uses: dtolnay/rust-toolchain@stable - - name: Rust cache + - name: Cache Rust uses: swatinem/rust-cache@v2 - - name: Reconfigure for Linux ARM64 - run: rustup target add aarch64-unknown-linux-gnu - - name: Install pnpm uses: pnpm/action-setup@v3 with: @@ -181,67 +95,38 @@ jobs: - name: Install dependencies run: pnpm install --no-frozen-lockfile - - name: Build the appimage for Linux ARM - uses: tauri-apps/tauri-action@v0 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NODE_OPTIONS: --max-old-space-size=6000 - with: - tagName: ${{ github.ref_name }} - releaseName: "KFtray - ${{ github.ref_name }}" - releaseBody: "See the assets to download this version and install." - releaseDraft: true - prerelease: false - updaterJsonKeepUniversal: true - args: --target aarch64-unknown-linux-gnu --bundles appimage,updater --verbose - - windows-x86_64: - permissions: - contents: write - runs-on: windows-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Rust setup - uses: dtolnay/rust-toolchain@stable - - - name: Rust cache - uses: swatinem/rust-cache@v2 - - - name: Install pnpm - uses: pnpm/action-setup@v3 - with: - version: 9.1.2 - - - name: Sync node version and setup cache - uses: actions/setup-node@v4 - with: - node-version: "lts/*" - cache: "pnpm" - - - name: Cache pnpm modules - uses: actions/cache@v4 + - name: Set up Visual Studio shell + if: matrix.os == 'windows-latest' && matrix.arch == 'arm64' + uses: TheMrMilchmann/setup-msvc-dev@v3 with: - path: | - $(pnpm store path --silent) - **/node_modules - key: ${{ runner.OS }}-pnpm-${{ runner.ARCH }}-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.OS }}-pnpm-${{ runner.ARCH }}- - - - name: Install dependencies - run: pnpm install --no-frozen-lockfile + arch: amd64_arm64 - - name: Reconfigure for Windows x86_64 - run: rustup target add x86_64-pc-windows-msvc + - name: Setup platform-specific dependencies + if: matrix.os == 'ubuntu-latest' + run: | + sudo apt-get update + sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libayatana-appindicator3-dev librsvg2-dev libssl-dev - - name: Build the app for Windows x86_64 + - name: Reconfigure Rust targets + run: | + if [ "${{ matrix.os }}" == "macos-latest" ]; then + rustup target add aarch64-apple-darwin x86_64-apple-darwin + else + rustup target add ${{ matrix.rust_target }} + fi + shell: bash + - name: Build kftray-tauri Desktop App uses: tauri-apps/tauri-action@v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NODE_OPTIONS: --max-old-space-size=6000 + ENABLE_CODE_SIGNING: ${{ secrets.APPLE_CERTIFICATE }} + APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} + APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} + APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} + APPLE_ID: ${{ secrets.APPLE_ID }} + APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }} with: tagName: ${{ github.ref_name }} releaseName: "KFtray - ${{ github.ref_name }}" @@ -249,24 +134,50 @@ jobs: releaseDraft: true prerelease: false updaterJsonKeepUniversal: true - args: --target x86_64-pc-windows-msvc --bundles nsis,updater --verbose + args: ${{ matrix.tauri_args }} - windows-x86: + kftui: + needs: create-release-draft permissions: contents: write - runs-on: windows-latest + strategy: + matrix: + include: + - os: ubuntu-latest + arch: arm64 + runner: ubicloud-standard-4-arm + rust_target: aarch64-unknown-linux-gnu + - os: ubuntu-latest + arch: amd64 + rust_target: x86_64-unknown-linux-gnu + - os: macos-latest + arch: universal + rust_target: universal-apple-darwin + - os: windows-latest + arch: x86 + rust_target: i686-pc-windows-msvc + msvc_arch: x86 + - os: windows-latest + arch: x86_64 + rust_target: x86_64-pc-windows-msvc + msvc_arch: x64 + + runs-on: ${{ matrix.runner || matrix.os }} + + outputs: + os: ${{ matrix.os }} + arch: ${{ matrix.arch }} steps: - name: Checkout repository uses: actions/checkout@v4 - - name: Rust setup + - name: Setup Rust uses: dtolnay/rust-toolchain@stable - - name: Rust cache + - name: Cache Rust uses: swatinem/rust-cache@v2 - - name: Install pnpm uses: pnpm/action-setup@v3 with: @@ -291,85 +202,65 @@ jobs: - name: Install dependencies run: pnpm install --no-frozen-lockfile - - name: Reconfigure for Windows x86 - run: rustup target add i686-pc-windows-msvc - - - name: Build the app for Windows x86 - uses: tauri-apps/tauri-action@v0 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NODE_OPTIONS: --max-old-space-size=6000 - with: - tagName: ${{ github.ref_name }} - releaseName: "KFtray - ${{ github.ref_name }}" - releaseBody: "See the assets to download this version and install." - releaseDraft: true - prerelease: false - updaterJsonKeepUniversal: true - args: --target i686-pc-windows-msvc --bundles nsis,updater --verbose - - windows-arm64: - permissions: - contents: write - runs-on: windows-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Rust setup - uses: dtolnay/rust-toolchain@stable - - - name: Rust cache - uses: swatinem/rust-cache@v2 - - - name: Install pnpm - uses: pnpm/action-setup@v3 - with: - version: 9.1.2 - - - name: Sync node version and setup cache - uses: actions/setup-node@v4 - with: - node-version: "lts/*" - cache: "pnpm" - - - name: Cache pnpm modules - uses: actions/cache@v4 - with: - path: | - $(pnpm store path --silent) - **/node_modules - key: ${{ runner.OS }}-pnpm-${{ runner.ARCH }}-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.OS }}-pnpm-${{ runner.ARCH }}- - - - name: Install dependencies - run: pnpm install --no-frozen-lockfile + - name: Setup platform-specific dependencies + if: matrix.os == 'ubuntu-latest' + run: | + sudo apt-get update + sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libayatana-appindicator3-dev librsvg2-dev libssl-dev - - name: Install dependencies for Windows on ARM64 - uses: TheMrMilchmann/setup-msvc-dev@v3 + - name: Reconfigure Rust targets + run: | + if [ "${{ matrix.os }}" == "macos-latest" ]; then + rustup target add aarch64-apple-darwin x86_64-apple-darwin + else + rustup target add ${{ matrix.rust_target }} + fi + shell: bash + + - name: Build kftui + run: | + if [ "${{ matrix.os }}" == "macos-latest" ]; then + cargo build --release --bin kftui --target aarch64-apple-darwin + cargo build --release --bin kftui --target x86_64-apple-darwin + lipo -create -output ./target/release/kftui ./target/x86_64-apple-darwin/release/kftui ./target/aarch64-apple-darwin/release/kftui + else + cargo build --release --bin kftui --target ${{ matrix.rust_target }} + fi + shell: bash + + - name: Sign and notarize the release build + if: matrix.os == 'macos-latest' + uses: toitlang/action-macos-sign-notarize@v1.2.0 with: - arch: amd64_arm64 - - - name: Reconfigure for Windows on ARM64 - run: rustup target add aarch64-pc-windows-msvc - - - name: Build the app for Windows on ARM64 - uses: tauri-apps/tauri-action@v0 + certificate: ${{ secrets.APPLE_CERTIFICATE }} + certificate-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} + username: ${{ secrets.APPLE_ID }} + password: ${{ secrets.APPLE_PASSWORD }} + apple-team-id: 6M376JWU73 + app-path: target/release/kftui + entitlements-path: crates/kftui/entitlements.plist + + - name: Rename and upload release asset + run: | + if [ "${{ matrix.os }}" == "macos-latest" ]; then + mv ./target/release/kftui ./target/release/kftui_macos_universal + gh release upload ${{ github.ref_name }} ./target/release/kftui_macos_universal --clobber + elif [ "${{ matrix.os }}" == "windows-latest" ]; then + mv ./target/${{ matrix.rust_target }}/release/kftui.exe ./target/release/kftui_${{ matrix.arch }}.exe + chmod +x ./target/release/kftui_${{ matrix.arch }}.exe + gh release upload ${{ github.ref_name }} ./target/release/kftui_${{ matrix.arch }}.exe --clobber + else + mv ./target/${{ matrix.rust_target }}/release/kftui ./target/release/kftui_${{ matrix.arch }} + chmod +x ./target/release/kftui_${{ matrix.arch }} + gh release upload ${{ github.ref_name }} ./target/release/kftui_${{ matrix.arch }} --clobber + fi env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NODE_OPTIONS: --max-old-space-size=6000 - with: - tagName: ${{ github.ref_name }} - releaseName: "KFtray - ${{ github.ref_name }}" - releaseBody: "See the assets to download this version and install." - releaseDraft: true - prerelease: false - updaterJsonKeepUniversal: true - args: --target aarch64-pc-windows-msvc --bundles nsis,updater --verbose + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + shell: bash - build-and-push-docker: + kftray-server: permissions: contents: write packages: write diff --git a/Cargo.lock b/Cargo.lock index 7465c885..ecfa40e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,12 +39,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "aligned-vec" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" - [[package]] name = "alloc-no-stdlib" version = "2.0.4" @@ -136,12 +130,6 @@ version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" -[[package]] -name = "arbitrary" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" - [[package]] name = "arboard" version = "3.4.0" @@ -161,29 +149,6 @@ dependencies = [ "x11rb", ] -[[package]] -name = "arg_enum_proc_macro" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.74", -] - -[[package]] -name = "arrayvec" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" - -[[package]] -name = "ascii" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" - [[package]] name = "async-broadcast" version = "0.7.1" @@ -374,6 +339,15 @@ dependencies = [ "system-deps 6.2.2", ] +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -386,29 +360,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" -[[package]] -name = "av1-grain" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6678909d8c5d46a42abcf571271e15fdbc0a225e3646cf23762cd415046c78bf" -dependencies = [ - "anyhow", - "arrayvec", - "log", - "nom", - "num-rational", - "v_frame", -] - -[[package]] -name = "avif-serialize" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876c75a42f6364451a033496a14c44bffe41f5f4a8236f697391f11024e596d2" -dependencies = [ - "arrayvec", -] - [[package]] name = "backoff" version = "0.4.0" @@ -454,10 +405,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] -name = "bit_field" -version = "0.10.2" +name = "base64ct" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "bitflags" @@ -470,12 +421,9 @@ name = "bitflags" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" - -[[package]] -name = "bitstream-io" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dcde5f311c85b8ca30c2e4198d4326bc342c76541590106f5fa4a50946ea499" +dependencies = [ + "serde", +] [[package]] name = "block" @@ -618,14 +566,28 @@ dependencies = [ "toml 0.7.8", ] +[[package]] +name = "cassowary" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" + +[[package]] +name = "castaway" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5" +dependencies = [ + "rustversion", +] + [[package]] name = "cc" -version = "1.1.10" +version = "1.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9e8aabfac534be767c909e0690571677d49f41bd8465ae876fe043d52ba5292" +checksum = "5fb8dd288a69fc53a1996d7ecfbf4a20d59065bff137ce7e56bbd620de191189" dependencies = [ - "jobserver", - "libc", + "shlex", ] [[package]] @@ -720,22 +682,6 @@ dependencies = [ "objc", ] -[[package]] -name = "cocoa" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" -dependencies = [ - "bitflags 1.3.2", - "block", - "cocoa-foundation", - "core-foundation", - "core-graphics 0.23.2", - "foreign-types 0.5.0", - "libc", - "objc", -] - [[package]] name = "cocoa-foundation" version = "0.1.2" @@ -772,6 +718,19 @@ dependencies = [ "memchr", ] +[[package]] +name = "compact_str" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f" +dependencies = [ + "castaway", + "cfg-if", + "itoa 1.0.11", + "ryu", + "static_assertions", +] + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -781,6 +740,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "convert_case" version = "0.4.0" @@ -842,13 +807,28 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" dependencies = [ "libc", ] +[[package]] +name = "crc" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + [[package]] name = "crc32fast" version = "1.4.2" @@ -886,6 +866,15 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.20" @@ -893,10 +882,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] -name = "crunchy" -version = "0.2.2" +name = "crossterm" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" +dependencies = [ + "bitflags 2.6.0", + "crossterm_winapi", + "libc", + "mio 0.8.11", + "parking_lot", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] [[package]] name = "crypto-common" @@ -947,12 +955,12 @@ dependencies = [ [[package]] name = "ctrlc" -version = "3.4.4" +version = "3.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "672465ae37dc1bc6380a6547a8883d5dd397b0f1faaad4f265726cc7042a5345" +checksum = "90eeab0aa92f3f9b4e87f258c72b139c207d251f9cbc1080a0086b86a8870dd3" dependencies = [ - "nix 0.28.0", - "windows-sys 0.52.0", + "nix 0.29.0", + "windows-sys 0.59.0", ] [[package]] @@ -1010,6 +1018,17 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + [[package]] name = "deranged" version = "0.3.11" @@ -1062,7 +1081,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", + "const-oid", "crypto-common", + "subtle", ] [[package]] @@ -1122,6 +1143,12 @@ dependencies = [ "libloading 0.8.5", ] +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + [[package]] name = "downcast" version = "0.11.0" @@ -1160,6 +1187,9 @@ name = "either" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +dependencies = [ + "serde", +] [[package]] name = "embed-resource" @@ -1262,6 +1292,17 @@ version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0474425d51df81997e2f90a21591180b38eccf27292d755f3e30750225c175b" +[[package]] +name = "etcetera" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +dependencies = [ + "cfg-if", + "home", + "windows-sys 0.48.0", +] + [[package]] name = "event-listener" version = "5.3.1" @@ -1283,34 +1324,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "exr" -version = "1.72.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4" -dependencies = [ - "bit_field", - "flume", - "half", - "lebe", - "miniz_oxide", - "rayon-core", - "smallvec", - "zune-inflate", -] - -[[package]] -name = "fallible-iterator" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" - -[[package]] -name = "fallible-streaming-iterator" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" - [[package]] name = "fastrand" version = "2.1.0" @@ -1388,6 +1401,8 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" dependencies = [ + "futures-core", + "futures-sink", "spin", ] @@ -1506,6 +1521,17 @@ dependencies = [ "futures-util", ] +[[package]] +name = "futures-intrusive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot", +] + [[package]] name = "futures-io" version = "0.3.30" @@ -1716,16 +1742,6 @@ dependencies = [ "wasi 0.11.0+wasi-snapshot-preview1", ] -[[package]] -name = "gif" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" -dependencies = [ - "color_quant", - "weezl", -] - [[package]] name = "gimli" version = "0.29.0" @@ -1904,7 +1920,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.3.0", + "indexmap 2.4.0", "slab", "tokio", "tokio-util", @@ -1923,23 +1939,13 @@ dependencies = [ "futures-core", "futures-sink", "http 1.1.0", - "indexmap 2.3.0", + "indexmap 2.4.0", "slab", "tokio", "tokio-util", "tracing", ] -[[package]] -name = "half" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" -dependencies = [ - "cfg-if", - "crunchy", -] - [[package]] name = "hashbrown" version = "0.12.3" @@ -2028,6 +2034,24 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "home" version = "0.5.9" @@ -2369,37 +2393,11 @@ checksum = "99314c8a2152b8ddb211f924cdae532d8c5e4c8bb54728e12fff1b0cd5963a10" dependencies = [ "bytemuck", "byteorder-lite", - "color_quant", - "exr", - "gif", - "image-webp", "num-traits", "png", - "qoi", - "ravif", - "rayon", - "rgb", "tiff", - "zune-core", - "zune-jpeg", -] - -[[package]] -name = "image-webp" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f79afb8cbee2ef20f59ccd477a218c12a93943d075b492015ecb1bb81f8ee904" -dependencies = [ - "byteorder-lite", - "quick-error", ] -[[package]] -name = "imgref" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44feda355f4159a7c757171a77de25daf6411e217b4cabd03bd6650690468126" - [[package]] name = "indexmap" version = "1.9.3" @@ -2413,9 +2411,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" +checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" dependencies = [ "equivalent", "hashbrown 0.14.5", @@ -2449,17 +2447,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "interpolate_name" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.74", -] - [[package]] name = "ipnet" version = "2.9.0" @@ -2493,18 +2480,18 @@ checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itertools" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" dependencies = [ "either", ] [[package]] name = "itertools" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] @@ -2564,15 +2551,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" -[[package]] -name = "jobserver" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" -dependencies = [ - "libc", -] - [[package]] name = "jpeg-decoder" version = "0.3.1" @@ -2581,9 +2559,9 @@ checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" dependencies = [ "wasm-bindgen", ] @@ -2663,54 +2641,62 @@ dependencies = [ ] [[package]] -name = "kftray" +name = "kftray-commons" version = "0.12.2" dependencies = [ "anyhow", - "base64 0.22.1", "bytes", "dashmap", "dirs", - "env_logger", - "fix-path-env", "flate2", - "futures", - "h2 0.4.5", "hostsfile", "httparse", - "hyper 1.4.1", - "hyper-util", "k8s-openapi", - "keyring", - "kube", - "kube-runtime", "lazy_static", "log", - "native-dialog", - "open 5.3.0", - "openssl", - "openssl-sys", "rand 0.8.5", - "reqwest 0.12.5", - "rusqlite", "serde", "serde_json", + "sqlx", "tauri", - "tauri-build", - "tauri-plugin-positioner", - "tempfile", + "tokio", + "tracing", + "uuid", +] + +[[package]] +name = "kftray-portforward" +version = "0.12.2" +dependencies = [ + "anyhow", + "async-trait", + "bytes", + "dashmap", + "flate2", + "futures", + "hostsfile", + "httparse", + "hyper-util", + "k8s-openapi", + "kftray-commons", + "kube", + "kube-runtime", + "lazy_static", + "log", + "rand 0.8.5", + "serde", + "serde_json", "tokio", "tokio-stream", "tower", "tracing", - "tracing-subscriber", "uuid", "whoami", ] [[package]] name = "kftray-server" -version = "0.12.2" +version = "0.13.0" dependencies = [ "byteorder", "ctrlc", @@ -2721,15 +2707,83 @@ dependencies = [ ] [[package]] -name = "kftray-utils" -version = "0.0.1" +name = "kftray-tauri" +version = "0.13.0" dependencies = [ - "ico", - "image 0.25.2", + "anyhow", + "base64 0.22.1", + "dirs", + "env_logger", + "fix-path-env", + "futures", + "h2 0.4.5", + "hostsfile", + "hyper 1.4.1", + "hyper-util", + "k8s-openapi", + "keyring", + "kftray-commons", + "kftray-portforward", + "kube", + "kube-runtime", + "lazy_static", "log", - "regex", + "open 5.3.0", + "openssl", + "openssl-sys", + "rand 0.8.5", + "reqwest 0.12.5", + "serde", "serde_json", - "thiserror", + "sqlx", + "tauri", + "tauri-build", + "tauri-plugin-positioner", + "tempfile", + "tokio", + "tokio-stream", + "tower", + "tracing", + "tracing-subscriber", + "whoami", +] + +[[package]] +name = "kftui" +version = "0.13.0" +dependencies = [ + "anyhow", + "base64 0.22.1", + "built", + "crossterm", + "dirs", + "futures", + "h2 0.4.5", + "hostsfile", + "hyper 1.4.1", + "hyper-util", + "k8s-openapi", + "kftray-commons", + "kftray-portforward", + "kube", + "kube-runtime", + "lazy_static", + "libc", + "log", + "once_cell", + "rand 0.8.5", + "ratatui", + "ratatui-explorer", + "serde", + "serde_json", + "sqlx", + "tauri", + "tempfile", + "tokio", + "tokio-stream", + "tower", + "tracing", + "whoami", ] [[package]] @@ -2845,12 +2899,9 @@ name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "lebe" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" +dependencies = [ + "spin", +] [[package]] name = "libappindicator" @@ -2882,17 +2933,6 @@ version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" -[[package]] -name = "libfuzzer-sys" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" -dependencies = [ - "arbitrary", - "cc", - "once_cell", -] - [[package]] name = "libloading" version = "0.7.4" @@ -2913,6 +2953,12 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + [[package]] name = "libredox" version = "0.1.3" @@ -2926,9 +2972,9 @@ dependencies = [ [[package]] name = "libsqlite3-sys" -version = "0.30.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f" dependencies = [ "cc", "pkg-config", @@ -2983,12 +3029,12 @@ dependencies = [ ] [[package]] -name = "loop9" -version = "0.1.5" +name = "lru" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" dependencies = [ - "imgref", + "hashbrown 0.14.5", ] [[package]] @@ -3049,12 +3095,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] -name = "maybe-rayon" -version = "0.1.1" +name = "md-5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" dependencies = [ "cfg-if", + "digest", ] [[package]] @@ -3111,9 +3158,21 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.1" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.48.0", +] + +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ "hermit-abi 0.3.9", "libc", @@ -3147,29 +3206,6 @@ dependencies = [ "syn 2.0.74", ] -[[package]] -name = "native-dialog" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84e7038885d2aeab236bd60da9e159a5967b47cde3292da3b15ff1bec27c039f" -dependencies = [ - "ascii", - "block", - "cocoa 0.25.0", - "core-foundation", - "dirs-next", - "objc", - "objc-foundation", - "objc_id", - "once_cell", - "raw-window-handle", - "thiserror", - "versions", - "wfd", - "which", - "winapi", -] - [[package]] name = "native-tls" version = "0.2.12" @@ -3274,12 +3310,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "noop_proc_macro" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" - [[package]] name = "notify-rust" version = "4.11.1" @@ -3304,13 +3334,20 @@ dependencies = [ ] [[package]] -name = "num-bigint" -version = "0.4.6" +name = "num-bigint-dig" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" dependencies = [ + "byteorder", + "lazy_static", + "libm", "num-integer", + "num-iter", "num-traits", + "rand 0.8.5", + "smallvec", + "zeroize", ] [[package]] @@ -3319,17 +3356,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" -[[package]] -name = "num-derive" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.74", -] - [[package]] name = "num-integer" version = "0.1.46" @@ -3340,12 +3366,12 @@ dependencies = [ ] [[package]] -name = "num-rational" -version = "0.4.2" +name = "num-iter" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ - "num-bigint", + "autocfg", "num-integer", "num-traits", ] @@ -3357,6 +3383,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -3736,6 +3763,15 @@ dependencies = [ "serde", ] +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -3794,7 +3830,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.3.0", + "indexmap 2.4.0", ] [[package]] @@ -3974,6 +4010,27 @@ dependencies = [ "futures-io", ] +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "pkg-config" version = "0.3.30" @@ -3987,7 +4044,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016" dependencies = [ "base64 0.22.1", - "indexmap 2.3.0", + "indexmap 2.4.0", "quick-xml 0.32.0", "serde", "time", @@ -4126,40 +4183,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "profiling" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58" -dependencies = [ - "profiling-procmacros", -] - -[[package]] -name = "profiling-procmacros" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd" -dependencies = [ - "quote", - "syn 2.0.74", -] - -[[package]] -name = "qoi" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "quick-error" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" - [[package]] name = "quick-xml" version = "0.31.0" @@ -4278,52 +4301,34 @@ dependencies = [ ] [[package]] -name = "rav1e" -version = "0.7.1" +name = "ratatui" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" +checksum = "f44c9e68fd46eda15c646fbb85e1040b657a58cdc8c98db1d97a55930d991eef" dependencies = [ - "arbitrary", - "arg_enum_proc_macro", - "arrayvec", - "av1-grain", - "bitstream-io", - "built", - "cfg-if", - "interpolate_name", + "bitflags 2.6.0", + "cassowary", + "compact_str", + "crossterm", "itertools 0.12.1", - "libc", - "libfuzzer-sys", - "log", - "maybe-rayon", - "new_debug_unreachable", - "noop_proc_macro", - "num-derive", - "num-traits", - "once_cell", + "lru", "paste", - "profiling", - "rand 0.8.5", - "rand_chacha 0.3.1", - "simd_helpers", - "system-deps 6.2.2", - "thiserror", - "v_frame", - "wasm-bindgen", + "stability", + "strum", + "unicode-segmentation", + "unicode-truncate", + "unicode-width", ] [[package]] -name = "ravif" -version = "0.11.10" +name = "ratatui-explorer" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f0bfd976333248de2078d350bfdf182ff96e168a24d23d2436cef320dd4bdd" +checksum = "a93dbe2398d7478db2f2ce5cd7ba1566792effda5361fadedd706b3ce325b511" dependencies = [ - "avif-serialize", - "imgref", - "loop9", - "quick-error", - "rav1e", - "rgb", + "crossterm", + "derivative", + "ratatui", ] [[package]] @@ -4332,26 +4337,6 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" -[[package]] -name = "rayon" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - [[package]] name = "redox_syscall" version = "0.4.1" @@ -4534,15 +4519,6 @@ dependencies = [ "windows 0.37.0", ] -[[package]] -name = "rgb" -version = "0.8.48" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f86ae463694029097b846d8f99fd5536740602ae00022c0c50c5600720b2f71" -dependencies = [ - "bytemuck", -] - [[package]] name = "ring" version = "0.17.8" @@ -4559,17 +4535,23 @@ dependencies = [ ] [[package]] -name = "rusqlite" -version = "0.32.1" +name = "rsa" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" +checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" dependencies = [ - "bitflags 2.6.0", - "fallible-iterator", - "fallible-streaming-iterator", - "hashlink", - "libsqlite3-sys", - "smallvec", + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core 0.6.4", + "signature", + "spki", + "subtle", + "zeroize", ] [[package]] @@ -4770,9 +4752,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.206" +version = "1.0.207" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b3e4cd94123dd520a128bcd11e34d9e9e423e7e3e50425cb1b4b1e3549d0284" +checksum = "5665e14a49a4ea1b91029ba7d3bca9f299e1f7cfa194388ccc20f14743e784f2" dependencies = [ "serde_derive", ] @@ -4789,9 +4771,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.206" +version = "1.0.207" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabfb6138d2383ea8208cf98ccf69cdfb1aff4088460681d84189aa259762f97" +checksum = "6aea2634c86b0e8ef2cfdc0c340baede54ec27b1e46febd7f80dffb2aa44a00e" dependencies = [ "proc-macro2", "quote", @@ -4804,7 +4786,7 @@ version = "1.0.124" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66ad62847a56b3dba58cc891acd13884b9c61138d330c0d7b6181713d4fce38d" dependencies = [ - "indexmap 2.3.0", + "indexmap 2.4.0", "itoa 1.0.11", "memchr", "ryu", @@ -4853,7 +4835,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.3.0", + "indexmap 2.4.0", "serde", "serde_derive", "serde_json", @@ -4879,7 +4861,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.3.0", + "indexmap 2.4.0", "itoa 1.0.11", "ryu", "serde", @@ -4959,96 +4941,352 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" +dependencies = [ + "libc", + "mio 0.8.11", + "signal-hook", +] + [[package]] name = "signal-hook-registry" version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +dependencies = [ + "serde", +] + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "soup2" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b4d76501d8ba387cf0fefbe055c3e0a59891d09f0f995ae4e4b16f6b60f3c0" +dependencies = [ + "bitflags 1.3.2", + "gio", + "glib", + "libc", + "once_cell", + "soup2-sys", +] + +[[package]] +name = "soup2-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "009ef427103fcb17f802871647a7fa6c60cbb654b4c4e4c0ac60a31c5f6dc9cf" +dependencies = [ + "bitflags 1.3.2", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps 5.0.0", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ - "libc", + "base64ct", + "der", ] [[package]] -name = "simd-adler32" -version = "0.3.7" +name = "sqlformat" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +checksum = "f895e3734318cc55f1fe66258926c9b910c124d47520339efecbb6c59cec7c1f" +dependencies = [ + "nom", + "unicode_categories", +] [[package]] -name = "simd_helpers" -version = "0.1.0" +name = "sqlx" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +checksum = "27144619c6e5802f1380337a209d2ac1c431002dd74c6e60aebff3c506dc4f0c" dependencies = [ - "quote", + "sqlx-core", + "sqlx-macros", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", ] [[package]] -name = "siphasher" -version = "0.3.11" +name = "sqlx-core" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +checksum = "a999083c1af5b5d6c071d34a708a19ba3e02106ad82ef7bbd69f5e48266b613b" +dependencies = [ + "atoi", + "byteorder", + "bytes", + "crc", + "crossbeam-queue", + "either", + "event-listener", + "futures-channel", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashbrown 0.14.5", + "hashlink", + "hex", + "indexmap 2.4.0", + "log", + "memchr", + "native-tls", + "once_cell", + "paste", + "percent-encoding", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlformat", + "thiserror", + "tokio", + "tokio-stream", + "tracing", + "url", +] [[package]] -name = "slab" -version = "0.4.9" +name = "sqlx-macros" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +checksum = "a23217eb7d86c584b8cbe0337b9eacf12ab76fe7673c513141ec42565698bb88" dependencies = [ - "autocfg", + "proc-macro2", + "quote", + "sqlx-core", + "sqlx-macros-core", + "syn 2.0.74", ] [[package]] -name = "smallvec" -version = "1.13.2" +name = "sqlx-macros-core" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "1a099220ae541c5db479c6424bdf1b200987934033c2584f79a0e1693601e776" +dependencies = [ + "dotenvy", + "either", + "heck 0.5.0", + "hex", + "once_cell", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2", + "sqlx-core", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", + "syn 2.0.74", + "tempfile", + "tokio", + "url", +] [[package]] -name = "socket2" -version = "0.5.7" +name = "sqlx-mysql" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "5afe4c38a9b417b6a9a5eeffe7235d0a106716495536e7727d1c7f4b1ff3eba6" dependencies = [ - "libc", - "windows-sys 0.52.0", + "atoi", + "base64 0.22.1", + "bitflags 2.6.0", + "byteorder", + "bytes", + "crc", + "digest", + "dotenvy", + "either", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "generic-array", + "hex", + "hkdf", + "hmac", + "itoa 1.0.11", + "log", + "md-5", + "memchr", + "once_cell", + "percent-encoding", + "rand 0.8.5", + "rsa", + "serde", + "sha1", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "tracing", + "whoami", ] [[package]] -name = "soup2" -version = "0.2.1" +name = "sqlx-postgres" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2b4d76501d8ba387cf0fefbe055c3e0a59891d09f0f995ae4e4b16f6b60f3c0" +checksum = "b1dbb157e65f10dbe01f729339c06d239120221c9ad9fa0ba8408c4cc18ecf21" dependencies = [ - "bitflags 1.3.2", - "gio", - "glib", - "libc", + "atoi", + "base64 0.22.1", + "bitflags 2.6.0", + "byteorder", + "crc", + "dotenvy", + "etcetera", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "hex", + "hkdf", + "hmac", + "home", + "itoa 1.0.11", + "log", + "md-5", + "memchr", "once_cell", - "soup2-sys", + "rand 0.8.5", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "tracing", + "whoami", ] [[package]] -name = "soup2-sys" -version = "0.2.0" +name = "sqlx-sqlite" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "009ef427103fcb17f802871647a7fa6c60cbb654b4c4e4c0ac60a31c5f6dc9cf" +checksum = "9b2cdd83c008a622d94499c0006d8ee5f821f36c89b7d625c900e5dc30b5c5ee" dependencies = [ - "bitflags 1.3.2", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "system-deps 5.0.0", + "atoi", + "flume", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "serde_urlencoded", + "sqlx-core", + "tracing", + "url", ] [[package]] -name = "spin" -version = "0.9.8" +name = "stability" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +checksum = "d904e7009df136af5297832a3ace3370cd14ff1546a232f4f185036c2736fcac" dependencies = [ - "lock_api", + "quote", + "syn 2.0.74", ] [[package]] @@ -5098,6 +5336,17 @@ dependencies = [ "quote", ] +[[package]] +name = "stringprep" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", + "unicode-properties", +] + [[package]] name = "strip-ansi-escapes" version = "0.2.0" @@ -5113,6 +5362,28 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.74", +] + [[package]] name = "subtle" version = "2.6.1" @@ -5222,7 +5493,7 @@ dependencies = [ "bitflags 1.3.2", "cairo-rs", "cc", - "cocoa 0.24.1", + "cocoa", "core-foundation", "core-graphics 0.22.3", "crossbeam-channel", @@ -5299,7 +5570,7 @@ dependencies = [ "anyhow", "base64 0.21.7", "bytes", - "cocoa 0.24.1", + "cocoa", "dirs-next", "dunce", "embed_plist", @@ -5456,7 +5727,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4c3db170233096aa30330feadcd895bf9317be97e624458560a20e814db7955" dependencies = [ "arboard", - "cocoa 0.24.1", + "cocoa", "gtk", "percent-encoding", "rand 0.8.5", @@ -5653,7 +5924,7 @@ dependencies = [ "backtrace", "bytes", "libc", - "mio", + "mio 1.0.2", "parking_lot", "pin-project-lite", "signal-hook-registry", @@ -5779,7 +6050,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.3.0", + "indexmap 2.4.0", "serde", "serde_spanned", "toml_datetime", @@ -5792,7 +6063,7 @@ version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ - "indexmap 2.3.0", + "indexmap 2.4.0", "toml_datetime", "winnow 0.5.40", ] @@ -5803,7 +6074,7 @@ version = "0.22.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" dependencies = [ - "indexmap 2.3.0", + "indexmap 2.4.0", "serde", "serde_spanned", "toml_datetime", @@ -5848,15 +6119,15 @@ dependencies = [ [[package]] name = "tower-layer" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" @@ -6002,12 +6273,41 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-properties" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4259d9d4425d9f0661581b804cb85fe66a4c631cadd8f490d1c13a35d5d9291" + [[package]] name = "unicode-segmentation" version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +[[package]] +name = "unicode-truncate" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" +dependencies = [ + "itertools 0.13.0", + "unicode-segmentation", + "unicode-width", +] + +[[package]] +name = "unicode-width" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" + +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + [[package]] name = "unsafe-libyaml" version = "0.2.11" @@ -6053,17 +6353,6 @@ dependencies = [ "getrandom 0.2.15", ] -[[package]] -name = "v_frame" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b" -dependencies = [ - "aligned-vec", - "num-traits", - "wasm-bindgen", -] - [[package]] name = "valuable" version = "0.1.0" @@ -6094,16 +6383,6 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" -[[package]] -name = "versions" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c73a36bc44e3039f51fbee93e39f41225f6b17b380eb70cc2aab942df06b34dd" -dependencies = [ - "itertools 0.11.0", - "nom", -] - [[package]] name = "vswhom" version = "0.1.0" @@ -6183,19 +6462,20 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" dependencies = [ "bumpalo", "log", @@ -6208,9 +6488,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.42" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" dependencies = [ "cfg-if", "js-sys", @@ -6220,9 +6500,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -6230,9 +6510,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", @@ -6243,9 +6523,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" [[package]] name = "wasm-streams" @@ -6335,9 +6615,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.69" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" dependencies = [ "js-sys", "wasm-bindgen", @@ -6434,28 +6714,6 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" -[[package]] -name = "wfd" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e713040b67aae5bf1a0ae3e1ebba8cc29ab2b90da9aa1bff6e09031a8a41d7a8" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix", -] - [[package]] name = "whoami" version = "1.5.1" @@ -6992,7 +7250,7 @@ checksum = "00711278ed357350d44c749c286786ecac644e044e4da410d466212152383b45" dependencies = [ "base64 0.13.1", "block", - "cocoa 0.24.1", + "cocoa", "core-graphics 0.22.3", "crossbeam-channel", "dunce", @@ -7181,30 +7439,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "zune-core" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" - -[[package]] -name = "zune-inflate" -version = "0.2.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "zune-jpeg" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16099418600b4d8f028622f73ff6e3deaabdff330fb9a2a131dea781ee8b0768" -dependencies = [ - "zune-core", -] - [[package]] name = "zvariant" version = "4.2.0" diff --git a/Cargo.toml b/Cargo.toml index 95289a93..61b02bf1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,9 @@ [workspace] members = [ "crates/kftray-server", - "crates/kftray-utils", "crates/kftray-tauri", + "crates/kftui", + "crates/kftray-portforward", ] resolver = "2" diff --git a/README.md b/README.md index 2476259c..a35ce902 100644 --- a/README.md +++ b/README.md @@ -1,276 +1,87 @@
-
- -

kftray

-

- A cross-platform system tray application for managing multiple kubectl port-forward commands, with support for UDP and proxy connections through k8s clusters. - -

-

- kftray.app » -

- kftray - -

- -

Download latest release

-
- - Download for macOS - - - Download for Windows ARM64 - -
- - Download for Windows x64 - - - Download for Windows x86 - -
- - Download for Linux AMD64 - - - Download for Linux AARCH64 - -
- -
-

Overview

-

kftray is a cross-platform system tray app made with Tauri (Rust and TypeScript) for Kubernetes users. It simplifies setting up multiple kubectl port forward configurations through a user-friendly interface. Easily store and manage all configurations from local files or GitHub repositories.

-

Learn More: Blog Post - Kubernetes Debugging with KFtray

- - - - - - - - - - - - -
Kftray OverviewDemo: GitHub SyncDemo: Adding a New Configuration
- - Kftray Overview - - - - Kftray Demo: Github Sync - - - - Kftray Demo: Adding a new configuration - -

-
- -## Table of Contents - -- [Features](#-features) -- [Installation](#-installation) -- [Usage](#-usage) -- [Architecture](#-architecture) -- [Contributing](#-contributing) -- [License](#-license) - -## 🚀 Features - -- **Resilient Port Forwarding Connection:** Ensures continuous service even if a pod dies, by reconnecting to another running pod automatically. -- **One-Click Multiple Port Forwards:** Allows for the setup of several port forwarding instances at the same time with a single click. -- **Independent of Kubectl:** Directly interfaces with the Kubernetes API, eliminating the need for `kubectl`. -- **Multi-Protocol Support:** Enables access to internal or external servers through a Proxy Relay server deployed in a Kubernetes cluster, including TCP and UDP port forwarding. -- **HTTP Logs Tracing**: Enable or disable HTTP logs for specific configurations to save the requests and responses in a local log file. - -## 📦 Installation - -KFtray is available for macOS and Linux users via Homebrew, and directly from the GitHub releases page for other systems. Here's how you can get started: - -**For macOS** - -```bash -brew tap hcavarsan/kftray -brew install --HEAD kftray -``` - -**For Linux** - -```bash -brew tap hcavarsan/kftray -brew install kftray-linux -``` - -_Please check the caveats section for global app creation instructions after installation._ - -Linux Note: due to GTK limitations, it is necessary to install and enable the GNOME Shell extension for AppIndicator support to kftray works. See here: - -For other systems, visit the [GitHub releases page](https://github.com/hcavarsan/kftray/releases) for downloadable binaries. - -## 🧭 Usage + KFtray Logo +

+ Visit kftray.app » +

+ -## 🎛 Configuring Your First Port Forward +

+ KFtray and KFtui are independent, cross-platform applications. They help you set up and manage multiple port-forwarding settings easily. Both apps are part of the same open-source project and aim to make working with Kubernetes easier. KFtray has a desktop interface, while KFtui has a terminal interface, so you can choose the one that suits you best. +

-In a few simple steps, you can configure your first port forward: -1. **Launch the application** -2. **Open the configuration panel from the tray icon** -3. **Add a new configuration:** +

KFtray

KFtray Screenshot
- - Give it a unique alias and set if you want to set the alias as domain to your forward \*1 - - Indicate if the configuration is for a port forward for a service (common use) or a proxy (port forward to an endpoint via a Kubernetes cluster). - - Specify the Kubernetes context - - Define the namespace housing your service - - Enter the service name - - Choose TCP or UDP - - Set the local and remote port numbers - - Configure a custom local IP address (optional) -4. **Activate Your Configuration**: With your configuration saved, simply click on the switch button in the main menu to start the port forward in a single por forward or in Start All to start all configurations at the same time +

KFtui

KFtui Screenshot
-> Note: To use the alias feature with a local domain name, you must enable write permissions in the hosts file. This method is not secure. We are addressing this in the following issue: [https://github.com/hcavarsan/kftray/issues/171](https://github.com/hcavarsan/kftray/issues/171). -> Follow these steps to allow write access: -> -> For Windows: -> -> ```bash -> icacls "C:\Windows\System32\drivers\etc\hosts" /grant Everyone:(R,W) -> ``` -> -> For MacOS and Linux: -> -> ```bash -> sudo chmod ugo+rw /etc/hosts -> ``` -## Export configurations to a JSON file +## Features -1. Open the main menu in the footer -2. Select the `Export Local File` option -3. Choose a file name and location to save the JSON file -4. The JSON file will contain all your current configurations +- **Resilient Port Forwarding Connection:** Ensures continuous service even if a pod dies by reconnecting to another running pod automatically. +- **One-Click Multiple Port Forwards:** Allows for the setup of several port forwarding instances simultaneously with a single click. +- **Independent of Kubectl:** Directly interfaces with the Kubernetes API, eliminating the need for `kubectl`. +- **Multi-Protocol Support:** Enables access to internal or external servers through a Proxy Relay server deployed in a Kubernetes cluster, including TCP and UDP port forwarding. +- **HTTP Logs Tracing:** Enable or disable HTTP logs for specific configurations to save the requests and responses in a local log file. _(Currently available only in the KFtray desktop app)_ - [Blog Post](https://kftray.app/blog/posts/6-debug-http-traffics-kftray) +- **GitHub Sync:** Keep your configurations saved on GitHub and share or synchronize them in a GitOps style. _(Currently available only in the KFtray desktop app)_ -You can then import this JSON file at any time to restore your configurations. -Example Json configuration File: -```json -[ - { - "service": "argocd-server", - "namespace": "argocd", - "local_port": 8888, - "remote_port": 8080, - "context": "test-cluster", - "workload_type": "service", - "protocol": "tcp", - "remote_address": "", - "local_address": "127.0.0.1", - "alias": "argocd", - "domain_enabled": true - } -] -``` +## Features Matrix -## Sharing the configurations through Git +
-now, with the local json saved, you can share your configurations with your team members by committing the JSON file to a GitHub repository. This allows for easy collaboration and synchronization of KFtray configurations across your team. +| Feature | KFtray (Desktop App) | KFtui (Terminal UI) | +|----------------------------------------------|----------------------|---------------------| +| Resilient Port Forwarding Connection | ✔️ | ✔️ | +| One-Click Multiple Port Forwards | ✔️ | ✔️ | +| Independent of Kubectl | ✔️ | ✔️ | +| Multi-Protocol Support (TCP/UDP) | ✔️ | ✔️ | +| HTTP Logs Tracing | ✔️ | ❌ (Coming Soon) | +| GitHub Sync | ✔️ | ❌ (Coming Soon) | +| Local JSON File Configuration | ✔️ | ✔️ | -To import and sync your GitHub configs in kftray: +
-1. Open the application's main menu -2. Select the button with GitHub icon in the footer menu -4. Enter the URL of your Git repository and path containing the JSON file -5. If your GitHub repository is private, you will need to enter the private token. Credentials are securely saved in the SO keyring (Keychain on macOS). Kftray does not store or save credentials in any local file; they are only stored in the local keyring. -6. Select the polling time for when Kftray will synchronize configurations and retrieve them from GitHub. +## Installation -6. KFtray will now sync with the Git repository to automatically import any new configurations or changes committed to the JSON file. +- **KFtray Desktop App:** Check [INSTALL.md](https://github.com/hcavarsan/kftray/tree/main/docs/kftray/INSTALL.md). +- **KFtui:** Check [INSTALL.md](https://github.com/hcavarsan/kftray/tree/main/docs/kftui/INSTALL.md). -This allows you to quickly deploy any port forward changes to all team members. And if someone on your team adds a new configuration, it will be automatically synced to everyone else's KFtray. +--- ## Building from Source -### Requirements - -- Node.js and pnpm or yarn for building the frontend. -- Rust for building the backend. - -To compile `kftray`, these steps should be followed: +- **KFtray Desktop App:** Check [BUILD.md](https://github.com/hcavarsan/kftray/tree/main/docs/kftray/BUILD.md). +- **KFtui:** Check [BUILD.md](https://github.com/hcavarsan/kftray/tree/main/docs/kftui/BUILD.md). -1. Clone the repository: - ```bash - git clone https://github.com/hcavarsan/kftray.git - ``` - -2. Navigate to the cloned directory: - - ```bash - cd kftray - ``` - -3. Install dependencies: - - ```bash - pnpm install - ``` - -4. Launch the application in development mode: - - ```bash - pnpm run tauri dev - ``` ## 🏗 Architecture -### Server +For an overall architectural review, check [ARCH.md](https://github.com/hcavarsan/kftray/tree/main/docs/ARCH.md). -KFtray Server is a Rust application that relays UDP/TCP traffic to an upstream server. Check the source code [here](https://github.com/hcavarsan/kftray/tree/main/crates/kftray-server). -### Forwarding Flows -- **TCP Forwarding:** A local TCP socket, similar to kubectl, can be used to communicate with a Kubernetes pod. This approach offers parallel execution and improved resilience. +## 👥 Contributing -```mermaid -sequenceDiagram -Application->>Kubernetes Pod: Opens TCP socket, starts port-forwarding -Kubernetes Pod-->>Application: Responds with TCP Packet -``` +- **Pull Requests:** Feel free to create pull requests for bug fixes, new features, or improvements. +- **Issues:** Report bugs, suggest new features, or ask questions. +- **Feedback:** Your feedback helps improve kftray. -- **Proxy TCP Forwarding:** The local TCP connects to the kftray-server pod, which then sends TCP packet to the upstream server. -```mermaid -sequenceDiagram -Application->>Kubernetes Pod: Socket to kftray-server, facilitates TCP relay -Kubernetes Pod->>Remote Service: Relays TCP Packet -Remote Service-->>Kubernetes Pod: Responds -Kubernetes Pod-->>Application: Returns TCP Packet -``` -- **UDP Forwarding:** The KFtray client opens a local UDP socket and connects a local TCP socket to the kftray-server pod. The TCP socket sends UDP packets over TCP, which are then forwarded to the upstream server. +## 📄 License -```mermaid -sequenceDiagram -Application->>Kubernetes Pod: UDP socket, TCP port-forward to kftray-server -Kubernetes Pod->>Service/Remote: Converts to UDP, sends packet -Service/Remote-->>Kubernetes Pod: Responds with UDP Packet -Kubernetes Pod-->>Application: Relays as TCP -``` +KFtray is available under the [MIT License](LICENSE.md). See the LICENSE file for full details. -## 👥 Contributing -- 🛠 **Pull Requests**: Feel free to create pull requests for bug fixes, new features, or improvements. -- 📝 **Issues**: Report bugs, suggest new features, or ask questions. -- 💡 **Feedback**: Your feedback helps improve kftray. -## 📄 License +This layout uses horizontal rules to separate sections, centers important elements, and maintains a clean, minimalist aesthetic. -KFtray is available under the [MIT License](LICENSE.md), which is included in the repository. See the LICENSE file for full details. ## Star History diff --git a/crates/kftray-commons/.gitignore b/crates/kftray-commons/.gitignore new file mode 100644 index 00000000..9bc2171e --- /dev/null +++ b/crates/kftray-commons/.gitignore @@ -0,0 +1,5 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ + + diff --git a/crates/kftray-commons/Cargo.toml b/crates/kftray-commons/Cargo.toml new file mode 100644 index 00000000..82a05ac3 --- /dev/null +++ b/crates/kftray-commons/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "kftray-commons" +version = "0.12.2" +description = "KFtray commons" +authors = [ + "Henrique Cavarsan ", +] +license = "MIT" +homepage = "https://kftray.app" +repository = "https://github.com/hcavarsan/kftray" +edition = "2021" + +[dependencies] +anyhow = "1.0.86" +bytes = "1.6.0" +dashmap = "6.0.0" +flate2 = "1.0" +httparse = "1.9.4" +k8s-openapi = { version = "0.22.0", default-features = false, features = ["latest"] } +lazy_static = "1.5.0" +log = "0.4" +rand = "0.8.5" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0.120" +tokio = { version = "1.39.1", features = ["rt-multi-thread", "macros", "full"] } +tracing = "0.1.40" +uuid = { version = "1.10.0", features = ["v4"] } +dirs = "5.0.1" +tauri = { version = "1.6", default-features = false, features = [ + "updater", + "api-all", + "macos-private-api", + "system-tray", + "icon-png", + "devtools", +] } +sqlx = { version = "0.8.0", features = ["sqlite", "runtime-tokio-native-tls"] } +hostsfile = { git = "https://github.com/tonarino/innernet", branch = "main" } + +[lib] +name = "kftray_commons" +path = "src/lib.rs" diff --git a/crates/kftray-commons/src/lib.rs b/crates/kftray-commons/src/lib.rs new file mode 100644 index 00000000..7da0e0b0 --- /dev/null +++ b/crates/kftray-commons/src/lib.rs @@ -0,0 +1,5 @@ +pub mod models; +pub mod utils; + +pub use models::*; +pub use utils::*; diff --git a/crates/kftray-tauri/src/models/config.rs b/crates/kftray-commons/src/models/config_model.rs similarity index 100% rename from crates/kftray-tauri/src/models/config.rs rename to crates/kftray-commons/src/models/config_model.rs diff --git a/crates/kftray-commons/src/models/config_state_model.rs b/crates/kftray-commons/src/models/config_state_model.rs new file mode 100644 index 00000000..4003557e --- /dev/null +++ b/crates/kftray-commons/src/models/config_state_model.rs @@ -0,0 +1,11 @@ +use serde::{ + Deserialize, + Serialize, +}; + +#[derive(Clone, Deserialize, PartialEq, Serialize, Debug)] +pub struct ConfigState { + pub id: Option, + pub config_id: i64, + pub is_running: bool, +} diff --git a/crates/kftray-commons/src/models/mod.rs b/crates/kftray-commons/src/models/mod.rs new file mode 100644 index 00000000..bf6931f8 --- /dev/null +++ b/crates/kftray-commons/src/models/mod.rs @@ -0,0 +1,4 @@ +pub mod config_model; +pub mod config_state_model; +pub mod response; +pub mod window; diff --git a/crates/kftray-tauri/src/models/response.rs b/crates/kftray-commons/src/models/response.rs similarity index 100% rename from crates/kftray-tauri/src/models/response.rs rename to crates/kftray-commons/src/models/response.rs diff --git a/crates/kftray-tauri/src/models/window.rs b/crates/kftray-commons/src/models/window.rs similarity index 62% rename from crates/kftray-tauri/src/models/window.rs rename to crates/kftray-commons/src/models/window.rs index 1a9c938d..3cd58560 100644 --- a/crates/kftray-tauri/src/models/window.rs +++ b/crates/kftray-commons/src/models/window.rs @@ -1,16 +1,19 @@ use std::sync::atomic::AtomicBool; +use std::sync::{ + Arc, + Mutex, +}; use serde::{ Deserialize, Serialize, }; +use tokio::runtime::Runtime; -// state for the save dialog pub struct SaveDialogState { pub is_open: AtomicBool, } -// default implementation for the SaveDialogState impl Default for SaveDialogState { fn default() -> Self { SaveDialogState { @@ -24,3 +27,10 @@ pub struct WindowPosition { pub x: i32, pub y: i32, } + +pub struct AppState { + pub is_moving: Arc>, + pub is_plugin_moving: Arc, + pub is_pinned: Arc, + pub runtime: Arc, +} diff --git a/crates/kftray-commons/src/utils/config.rs b/crates/kftray-commons/src/utils/config.rs new file mode 100644 index 00000000..5c9c1015 --- /dev/null +++ b/crates/kftray-commons/src/utils/config.rs @@ -0,0 +1,249 @@ +use hostsfile::HostsBuilder; +use serde_json::{ + json, + Value as JsonValue, +}; +use sqlx::Row; + +use crate::db::get_db_pool; +use crate::migration::migrate_configs; +use crate::models::config_model::Config; + +pub async fn delete_config(id: i64) -> Result<(), String> { + let pool = get_db_pool().await.map_err(|e| e.to_string())?; + let mut conn = pool.acquire().await.map_err(|e| e.to_string())?; + + sqlx::query("DELETE FROM configs WHERE id = ?1") + .bind(id) + .execute(&mut *conn) + .await + .map_err(|e| format!("Failed to delete config: {}", e))?; + + Ok(()) +} + +pub async fn delete_configs(ids: Vec) -> Result<(), String> { + let pool = get_db_pool().await.map_err(|e| e.to_string())?; + let mut transaction = pool.begin().await.map_err(|e| e.to_string())?; + + for id in ids { + sqlx::query("DELETE FROM configs WHERE id = ?1") + .bind(id) + .execute(&mut *transaction) + .await + .map_err(|e| format!("Failed to delete config with id {}: {}", id, e))?; + } + + transaction.commit().await.map_err(|e| e.to_string())?; + + Ok(()) +} + +pub async fn delete_all_configs() -> Result<(), String> { + let pool = get_db_pool().await.map_err(|e| e.to_string())?; + let mut conn = pool.acquire().await.map_err(|e| e.to_string())?; + + sqlx::query("DELETE FROM configs") + .execute(&mut *conn) + .await + .map_err(|e| format!("Failed to delete all configs: {}", e))?; + + Ok(()) +} + +pub async fn insert_config(config: Config) -> Result<(), String> { + let pool = get_db_pool().await.map_err(|e| e.to_string())?; + let mut conn = pool.acquire().await.map_err(|e| e.to_string())?; + + sqlx::query( + "CREATE TABLE IF NOT EXISTS configs ( + id INTEGER PRIMARY KEY, + data TEXT NOT NULL + )", + ) + .execute(&mut *conn) + .await + .map_err(|e| e.to_string())?; + + let data = json!(config).to_string(); + + sqlx::query("INSERT INTO configs (data) VALUES (?1)") + .bind(data) + .execute(&mut *conn) + .await + .map_err(|e| e.to_string())?; + + Ok(()) +} + +pub async fn read_configs() -> Result, String> { + let pool = get_db_pool().await.map_err(|e| e.to_string())?; + let mut conn = pool.acquire().await.map_err(|e| e.to_string())?; + + let rows = sqlx::query("SELECT id, data FROM configs") + .fetch_all(&mut *conn) + .await + .map_err(|e| e.to_string())?; + + let mut configs = Vec::new(); + + for row in rows { + let id: i64 = row.try_get("id").map_err(|e| e.to_string())?; + let data: String = row.try_get("data").map_err(|e| e.to_string())?; + let mut config: Config = + serde_json::from_str(&data).map_err(|_| "Failed to decode config".to_string())?; + config.id = Some(id); + configs.push(config); + } + + Ok(configs) +} + +pub async fn clean_all_custom_hosts_entries() -> Result<(), String> { + let configs = read_configs().await.map_err(|e| e.to_string())?; + + for config in configs { + let hostfile_comment = format!( + "kftray custom host for {} - {}", + config.service.unwrap_or_default(), + config.id.unwrap_or_default() + ); + + let hosts_builder = HostsBuilder::new(&hostfile_comment); + + hosts_builder.write().map_err(|e| { + format!( + "Failed to write to the hostfile for {}: {}", + hostfile_comment, e + ) + })?; + } + + Ok(()) +} + +pub async fn get_configs() -> Result, String> { + read_configs().await +} + +pub async fn get_config(id: i64) -> Result { + let pool = get_db_pool().await.map_err(|e| e.to_string())?; + let mut conn = pool.acquire().await.map_err(|e| e.to_string())?; + + let row = sqlx::query("SELECT id, data FROM configs WHERE id = ?1") + .bind(id) + .fetch_optional(&mut *conn) + .await + .map_err(|e| e.to_string())?; + + match row { + Some(row) => { + let id: i64 = row.try_get("id").map_err(|e| e.to_string())?; + let data: String = row.try_get("data").map_err(|e| e.to_string())?; + let mut config: Config = serde_json::from_str(&data) + .map_err(|e| format!("Failed to parse config: {}", e))?; + config.id = Some(id); + Ok(config) + } + None => Err(format!("No config found with id: {}", id)), + } +} + +pub async fn update_config(config: Config) -> Result<(), String> { + let pool = get_db_pool().await.map_err(|e| e.to_string())?; + let mut conn = pool.acquire().await.map_err(|e| e.to_string())?; + + let data = json!(config).to_string(); + + sqlx::query("UPDATE configs SET data = ?1 WHERE id = ?2") + .bind(data) + .bind(config.id.unwrap()) + .execute(&mut *conn) + .await + .map_err(|e| e.to_string())?; + + Ok(()) +} + +pub async fn export_configs() -> Result { + let mut configs = read_configs().await.map_err(|e| e.to_string())?; + + for config in &mut configs { + config.id = None; + } + + let mut json_config = serde_json::to_value(configs).map_err(|e| e.to_string())?; + let default_config = serde_json::to_value(Config::default()).map_err(|e| e.to_string())?; + remove_blank_or_default_fields(&mut json_config, &default_config); + + let json = serde_json::to_string(&json_config).map_err(|e| e.to_string())?; + + Ok(json) +} + +pub async fn import_configs(json: String) -> Result<(), String> { + match serde_json::from_str::>(&json) { + Ok(configs) => { + for config in configs { + insert_config(config) + .await + .map_err(|e| format!("Failed to insert config: {}", e))?; + } + } + Err(_) => { + let config = serde_json::from_str::(&json) + .map_err(|e| format!("Failed to parse config: {}", e))?; + insert_config(config) + .await + .map_err(|e| format!("Failed to insert config: {}", e))?; + } + } + + if let Err(e) = migrate_configs().await { + return Err(format!("Error migrating configs: {}", e)); + } + + Ok(()) +} + +fn is_value_blank(value: &JsonValue) -> bool { + match value { + JsonValue::String(s) => s.trim().is_empty(), + _ => false, + } +} + +fn is_value_default(value: &serde_json::Value, default_config: &serde_json::Value) -> bool { + *value == *default_config +} + +fn remove_blank_or_default_fields(value: &mut JsonValue, default_config: &JsonValue) { + match value { + JsonValue::Object(map) => { + let keys_to_remove: Vec = map + .iter() + .filter(|(k, v)| { + let default_v = &default_config[k]; + is_value_blank(v) + || (default_v != &JsonValue::Array(vec![]) + && is_value_default(v, default_v)) + }) + .map(|(k, _)| k.clone()) + .collect(); + + for key in keys_to_remove { + map.remove(&key); + } + + for value in map.values_mut() { + remove_blank_or_default_fields(value, default_config); + } + } + JsonValue::Array(arr) => { + for value in arr { + remove_blank_or_default_fields(value, default_config); + } + } + _ => (), + } +} diff --git a/crates/kftray-tauri/src/utils/config_dir.rs b/crates/kftray-commons/src/utils/config_dir.rs similarity index 86% rename from crates/kftray-tauri/src/utils/config_dir.rs rename to crates/kftray-commons/src/utils/config_dir.rs index b7698a19..a6867786 100644 --- a/crates/kftray-tauri/src/utils/config_dir.rs +++ b/crates/kftray-commons/src/utils/config_dir.rs @@ -5,8 +5,6 @@ use std::{ use anyhow::Result; -/// Determines and returns the configuration directory path. -/// Tries environment variables and defaults to home directory if needed. pub fn get_config_dir() -> Result { if let Ok(config_dir) = env::var("KFTRAY_CONFIG") { return Ok(PathBuf::from(config_dir)); @@ -27,35 +25,30 @@ pub fn get_config_dir() -> Result { Err("Unable to determine the configuration directory".to_string()) } -/// Gets the log folder path within the configuration directory. pub fn get_log_folder_path() -> Result { let mut config_path = get_config_dir()?; config_path.push("http_logs"); Ok(config_path) } -/// Gets the database file path within the configuration directory. pub fn get_db_file_path() -> Result { let mut config_path = get_config_dir()?; config_path.push("configs.db"); Ok(config_path) } -/// Returns the path to the pod manifest file within the home directory. pub fn get_pod_manifest_path() -> Result { let mut config_path = get_config_dir()?; config_path.push("proxy_manifest.json"); Ok(config_path) } -/// Gets the path to the application log file within the home directory. pub fn get_app_log_path() -> Result { let mut config_path = get_config_dir()?; config_path.push("app.log"); Ok(config_path) } -/// Gets the path to the window state file within the configuration directory. pub fn get_window_state_path() -> Result { let mut config_path = get_config_dir()?; config_path.push("window_position.json"); @@ -63,17 +56,13 @@ pub fn get_window_state_path() -> Result { } pub fn get_default_kubeconfig_path() -> Result { - // Check the KUBECONFIG environment variable if let Ok(kubeconfig_path) = env::var("KUBECONFIG") { Ok(PathBuf::from(kubeconfig_path)) + } else if let Some(mut config_path) = dirs::home_dir() { + config_path.push(".kube/config"); + Ok(config_path) } else { - // Fallback to the default home directory path - if let Some(mut config_path) = dirs::home_dir() { - config_path.push(".kube/config"); - Ok(config_path) - } else { - Err(anyhow::anyhow!("Unable to determine home directory")) - } + Err(anyhow::anyhow!("Unable to determine home directory")) } } @@ -83,7 +72,6 @@ mod tests { use super::*; - // Utility function to preserve environment variables during tests. fn preserve_env_vars(keys: &[&str]) -> Vec<(String, Option)> { keys.iter() .map(|&key| { @@ -93,7 +81,6 @@ mod tests { .collect() } - // Utility function to restore environment variables after tests. fn restore_env_vars(vars: Vec<(String, Option)>) { for (key, value) in vars { match value { diff --git a/crates/kftray-commons/src/utils/config_state.rs b/crates/kftray-commons/src/utils/config_state.rs new file mode 100644 index 00000000..b2c5c16e --- /dev/null +++ b/crates/kftray-commons/src/utils/config_state.rs @@ -0,0 +1,68 @@ +use log::error; +use sqlx::Row; + +use crate::db::get_db_pool; +use crate::models::config_state_model::ConfigState; + +pub async fn update_config_state(config_state: &ConfigState) -> Result<(), String> { + let pool = get_db_pool().await.map_err(|e| e.to_string())?; + let mut conn = pool.acquire().await.map_err(|e| e.to_string())?; + + sqlx::query("UPDATE config_state SET is_running = ?1 WHERE config_id = ?2") + .bind(config_state.is_running) + .bind(config_state.config_id) + .execute(&mut *conn) + .await + .map_err(|e| e.to_string())?; + + Ok(()) +} + +pub async fn read_config_states() -> Result, sqlx::Error> { + let pool = get_db_pool().await.map_err(|e| { + error!("Failed to get database pool: {}", e); + sqlx::Error::Configuration(e.into()) + })?; + + let mut conn = pool.acquire().await.map_err(|e| { + error!("Failed to acquire database connection: {}", e); + e + })?; + + let rows = sqlx::query("SELECT id, config_id, is_running FROM config_state") + .fetch_all(&mut *conn) + .await + .map_err(|e| { + error!("Failed to fetch config states: {}", e); + e + })?; + + let config_states = rows + .into_iter() + .map(|row| { + let id: Option = row.try_get("id").ok(); + let config_id: i64 = row.try_get("config_id").map_err(|e| { + error!("Failed to get config_id: {}", e); + e + })?; + let is_running: bool = row.try_get("is_running").map_err(|e| { + error!("Failed to get is_running: {}", e); + e + })?; + Ok(ConfigState { + id, + config_id, + is_running, + }) + }) + .collect::, sqlx::Error>>()?; + + Ok(config_states) +} + +pub async fn get_configs_state() -> Result, String> { + read_config_states().await.map_err(|e| { + error!("Failed to get config states: {}", e); + e.to_string() + }) +} diff --git a/crates/kftray-tauri/src/db.rs b/crates/kftray-commons/src/utils/db.rs similarity index 50% rename from crates/kftray-tauri/src/db.rs rename to crates/kftray-commons/src/utils/db.rs index 9567a78d..06223915 100644 --- a/crates/kftray-tauri/src/db.rs +++ b/crates/kftray-commons/src/utils/db.rs @@ -1,3 +1,4 @@ +use std::sync::Arc; use std::{ fs::{ self, @@ -7,22 +8,20 @@ use std::{ path::Path, }; -use rusqlite::{ - params, - Connection, - Result, +use log::{ + error, + info, }; use serde_json::json; +use sqlx::SqlitePool; +use tokio::sync::OnceCell; use crate::utils::config_dir::{ get_db_file_path, get_pod_manifest_path, }; -/// Initializes the application by ensuring that both the database file and the -/// server configuration manifest file exist. - -pub fn init() -> Result<(), Box> { +pub async fn init() -> Result<(), Box> { if !db_file_exists() { create_db_file()?; } @@ -31,26 +30,110 @@ pub fn init() -> Result<(), Box> { create_server_config_manifest()?; } - create_db_table()?; + create_db_table().await?; Ok(()) } -fn create_db_table() -> Result<(), rusqlite::Error> { - let db_dir = get_db_file_path().map_err(|e| { - rusqlite::Error::InvalidPath(format!("Failed to get DB path: {}", e).into()) +static DB_POOL: OnceCell> = OnceCell::const_new(); + +pub async fn get_db_pool() -> Result, String> { + DB_POOL + .get_or_try_init(|| async { + let db_dir = get_db_file_path().map_err(|e| { + error!("Failed to get DB file path: {}", e); + e.to_string() + })?; + let db_dir_str = db_dir.to_str().ok_or("Invalid DB path")?; + info!("Database file path: {}", db_dir_str); + let pool = SqlitePool::connect(db_dir_str).await.map_err(|e| { + error!("Failed to connect to DB: {}", e); + e.to_string() + })?; + Ok(Arc::new(pool)) + }) + .await + .map(Arc::clone) +} + +async fn create_db_table() -> Result<(), sqlx::Error> { + info!("Creating database tables and triggers."); + let pool = get_db_pool().await.map_err(|e| { + error!("Failed to get DB pool: {}", e); + sqlx::Error::Configuration(e.into()) + })?; + let mut conn = pool.acquire().await.map_err(|e| { + error!("Failed to acquire connection: {}", e); + e })?; - let conn = Connection::open(db_dir)?; + sqlx::query("PRAGMA foreign_keys = ON;") + .execute(&mut *conn) + .await + .map_err(|e| { + error!("Failed to set PRAGMA foreign_keys: {}", e); + e + })?; - conn.execute( + sqlx::query( "CREATE TABLE IF NOT EXISTS configs ( id INTEGER PRIMARY KEY, data TEXT NOT NULL )", - params![], - )?; + ) + .execute(&mut *conn) + .await + .map_err(|e| { + error!("Failed to create configs table: {}", e); + e + })?; + + sqlx::query( + "CREATE TABLE IF NOT EXISTS config_state ( + id INTEGER PRIMARY KEY, + config_id INTEGER NOT NULL, + is_running BOOLEAN NOT NULL DEFAULT false, + FOREIGN KEY(config_id) REFERENCES configs(id) ON DELETE CASCADE + )", + ) + .execute(&mut *conn) + .await + .map_err(|e| { + error!("Failed to create config_state table: {}", e); + e + })?; + + sqlx::query( + "CREATE TRIGGER IF NOT EXISTS after_insert_config + AFTER INSERT ON configs + FOR EACH ROW + BEGIN + INSERT INTO config_state (config_id, is_running) VALUES (NEW.id, false); + END;", + ) + .execute(&mut *conn) + .await + .map_err(|e| { + error!("Failed to create after_insert_config trigger: {}", e); + e + })?; + + sqlx::query( + "CREATE TRIGGER IF NOT EXISTS after_delete_config + AFTER DELETE ON configs + FOR EACH ROW + BEGIN + DELETE FROM config_state WHERE config_id = OLD.id; + END;", + ) + .execute(&mut *conn) + .await + .map_err(|e| { + error!("Failed to create after_delete_config trigger: {}", e); + e + })?; + info!("Database tables and triggers created successfully."); Ok(()) } @@ -62,7 +145,6 @@ fn pod_manifest_file_exists() -> bool { } } -/// Creates the server configuration manifest file with placeholders. fn create_server_config_manifest() -> Result<(), std::io::Error> { let manifest_path = get_pod_manifest_path().map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; @@ -118,9 +200,13 @@ fn create_server_config_manifest() -> Result<(), std::io::Error> { File::create(&manifest_path)?.write_all(manifest_json.as_bytes()) } -/// Checks if the pod manifest file already exists. - -/// Creates a new database file if it doesn't exist already. +fn db_file_exists() -> bool { + if let Ok(db_dir) = get_db_file_path() { + Path::new(&db_dir).exists() + } else { + false + } +} fn create_db_file() -> Result<(), std::io::Error> { let db_path = @@ -138,55 +224,3 @@ fn create_db_file() -> Result<(), std::io::Error> { Ok(()) } - -fn db_file_exists() -> bool { - if let Ok(db_dir) = get_db_file_path() { - Path::new(&db_dir).exists() - } else { - false - } -} - -#[cfg(test)] - -mod tests { - - use tempfile::TempDir; - - use super::*; - - /// Sets up a temporary test environment and overrides the home directory. - - fn setup_test_environment() -> TempDir { - let temp = tempfile::tempdir().expect("Failed to create a temp dir"); - - std::env::set_var("HOME", temp.path()); - - temp - } - - /// Tests if the initialization creates the required database and manifest - /// files. - #[test] - - fn test_initialization_creates_files() { - let _temp_dir = setup_test_environment(); - - init().expect("Initialization failed"); - - assert!(db_file_exists()); - - assert!(pod_manifest_file_exists()); - } - - /// Confirms that the database file gets created successfully. - #[test] - - fn test_db_file_creation() { - let _temp_dir = setup_test_environment(); - - create_db_file().expect("Failed to create db file"); - - assert!(db_file_exists()); - } -} diff --git a/crates/kftray-commons/src/utils/github.rs b/crates/kftray-commons/src/utils/github.rs new file mode 100644 index 00000000..15b055d0 --- /dev/null +++ b/crates/kftray-commons/src/utils/github.rs @@ -0,0 +1,35 @@ +use crate::db::get_db_pool; + +pub async fn clear_existing_configs() -> Result<(), sqlx::Error> { + let pool = get_db_pool() + .await + .map_err(|e| sqlx::Error::Configuration(format!("DB Pool error: {}", e).into()))?; + let mut conn = pool.acquire().await?; + + sqlx::query("DELETE FROM configs") + .execute(&mut *conn) + .await?; + + Ok(()) +} + +pub fn build_github_api_url(repo_url: &str, config_path: &str) -> Result { + let base_api_url = "https://api.github.com/repos"; + + let url_parts: Vec<&str> = repo_url + .split('/') + .filter(|&x| !x.is_empty() && x != "https:" && x != "github.com") + .collect(); + + if url_parts.len() < 2 { + return Err("Invalid GitHub repository URL".to_string()); + } + + let owner = url_parts[0]; + let repo = url_parts[1]; + + Ok(format!( + "{}/{}/{}/contents/{}", + base_api_url, owner, repo, config_path + )) +} diff --git a/crates/kftray-tauri/src/kubeforward/logging.rs b/crates/kftray-commons/src/utils/logging.rs similarity index 99% rename from crates/kftray-tauri/src/kubeforward/logging.rs rename to crates/kftray-commons/src/utils/logging.rs index 7f38fb65..8db2d2fa 100644 --- a/crates/kftray-tauri/src/kubeforward/logging.rs +++ b/crates/kftray-commons/src/utils/logging.rs @@ -36,7 +36,7 @@ use uuid::Uuid; use crate::utils::config_dir::get_log_folder_path; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Logger { log_sender: Sender, trace_map: TraceMap, @@ -44,6 +44,7 @@ pub struct Logger { type TraceMap = Arc>; +#[derive(Clone, Debug)] struct TraceInfo { trace_id: String, timestamp: DateTime, diff --git a/crates/kftray-commons/src/utils/migration.rs b/crates/kftray-commons/src/utils/migration.rs new file mode 100644 index 00000000..f97bc3bc --- /dev/null +++ b/crates/kftray-commons/src/utils/migration.rs @@ -0,0 +1,179 @@ +use log::{ + error, + info, +}; +use serde_json::Value as JsonValue; +use sqlx::{ + Acquire, + Row, + Sqlite, + SqliteConnection, + Transaction, +}; + +use crate::db::get_db_pool; +use crate::models::config_model::Config; + +pub async fn migrate_configs() -> Result<(), String> { + info!("Starting configuration migration."); + let pool = get_db_pool().await.map_err(|e| { + error!("Failed to get DB pool: {}", e); + e.to_string() + })?; + let mut conn = pool.acquire().await.map_err(|e| { + error!("Failed to acquire connection: {}", e); + e.to_string() + })?; + let mut transaction = conn.begin().await.map_err(|e| { + error!("Failed to begin transaction: {}", e); + e.to_string() + })?; + + let rows = sqlx::query("SELECT id, data FROM configs") + .fetch_all(&mut *transaction) + .await + .map_err(|e| { + error!("Failed to fetch configs: {}", e); + e.to_string() + })?; + + for row in rows { + let id: i64 = row.try_get("id").map_err(|e| { + error!("Failed to get id: {}", e); + e.to_string() + })?; + let data: String = row.try_get("data").map_err(|e| { + error!("Failed to get data: {}", e); + e.to_string() + })?; + let config_json: JsonValue = serde_json::from_str(&data).map_err(|e| { + error!("Failed to parse JSON: {}", e); + e.to_string() + })?; + let default_config_json = serde_json::to_value(Config::default()).map_err(|e| { + error!("Failed to serialize default config: {}", e); + e.to_string() + })?; + let merged_config_json = merge_json_values(default_config_json, config_json); + let updated_data = serde_json::to_string(&merged_config_json).map_err(|e| { + error!("Failed to serialize merged config: {}", e); + e.to_string() + })?; + + sqlx::query("UPDATE configs SET data = ?1 WHERE id = ?2") + .bind(updated_data) + .bind(id) + .execute(&mut *transaction) + .await + .map_err(|e| { + error!("Failed to update config: {}", e); + e.to_string() + })?; + } + + drop_triggers(&mut transaction).await.map_err(|e| { + error!("Failed to drop triggers: {}", e); + e.to_string() + })?; + + sqlx::query( + "INSERT INTO config_state (id, config_id, is_running) + SELECT c.id, c.id, false + FROM configs c + LEFT JOIN config_state cs ON c.id = cs.config_id + WHERE cs.config_id IS NULL", + ) + .execute(&mut *transaction) + .await + .map_err(|e| { + error!("Failed to insert into config_state: {}", e); + e.to_string() + })?; + + transaction.commit().await.map_err(|e| { + error!("Failed to commit transaction: {}", e); + e.to_string() + })?; + + let mut conn = pool.acquire().await.map_err(|e| { + error!("Failed to acquire connection: {}", e); + e.to_string() + })?; + create_triggers(&mut conn).await.map_err(|e| { + error!("Failed to create triggers: {}", e); + e.to_string() + })?; + + info!("Configuration migration completed successfully."); + Ok(()) +} + +async fn drop_triggers(transaction: &mut Transaction<'_, Sqlite>) -> Result<(), sqlx::Error> { + info!("Dropping triggers."); + sqlx::query("DROP TRIGGER IF EXISTS after_insert_config;") + .execute(&mut **transaction) + .await + .map_err(|e| { + error!("Failed to drop after_insert_config trigger: {}", e); + e + })?; + + sqlx::query("DROP TRIGGER IF EXISTS after_delete_config;") + .execute(&mut **transaction) + .await + .map_err(|e| { + error!("Failed to drop after_delete_config trigger: {}", e); + e + })?; + + info!("Triggers dropped successfully."); + Ok(()) +} + +async fn create_triggers(conn: &mut SqliteConnection) -> Result<(), sqlx::Error> { + info!("Creating triggers."); + sqlx::query( + "CREATE TRIGGER IF NOT EXISTS after_insert_config + AFTER INSERT ON configs + FOR EACH ROW + BEGIN + INSERT INTO config_state (config_id, is_running) VALUES (NEW.id, false); + END;", + ) + .execute(&mut *conn) + .await + .map_err(|e| { + error!("Failed to create after_insert_config trigger: {}", e); + e + })?; + + sqlx::query( + "CREATE TRIGGER IF NOT EXISTS after_delete_config + AFTER DELETE ON configs + FOR EACH ROW + BEGIN + DELETE FROM config_state WHERE config_id = OLD.id; + END;", + ) + .execute(&mut *conn) + .await + .map_err(|e| { + error!("Failed to create after_delete_config trigger: {}", e); + e + })?; + + info!("Triggers created successfully."); + Ok(()) +} +pub fn merge_json_values(default: JsonValue, custom: JsonValue) -> JsonValue { + match (default, custom) { + (JsonValue::Object(mut default_map), JsonValue::Object(custom_map)) => { + for (key, custom_value) in custom_map { + let default_value = default_map.entry(key.clone()).or_insert(JsonValue::Null); + *default_value = merge_json_values(default_value.take(), custom_value); + } + JsonValue::Object(default_map) + } + (_, custom) => custom, + } +} diff --git a/crates/kftray-commons/src/utils/mod.rs b/crates/kftray-commons/src/utils/mod.rs new file mode 100644 index 00000000..9a97d051 --- /dev/null +++ b/crates/kftray-commons/src/utils/mod.rs @@ -0,0 +1,8 @@ +pub mod config; +pub mod config_dir; +pub mod config_state; +pub mod db; +pub mod github; +pub mod logging; +pub mod migration; +pub mod validate_configs; diff --git a/crates/kftray-tauri/src/utils/validate_configs.rs b/crates/kftray-commons/src/utils/validate_configs.rs similarity index 98% rename from crates/kftray-tauri/src/utils/validate_configs.rs rename to crates/kftray-commons/src/utils/validate_configs.rs index 75c08091..6e1f0bb6 100644 --- a/crates/kftray-tauri/src/utils/validate_configs.rs +++ b/crates/kftray-commons/src/utils/validate_configs.rs @@ -102,8 +102,6 @@ async fn show_alert_dialog( active_config_msg, dirs::home_dir().map_or("".to_string(), |p| p.join(".kftray").display().to_string()) ); - - // Use the app handle to trigger the dialog on the main thread spawn_blocking(move || { let _ = app_handle.run_on_main_thread(move || { MessageDialogBuilder::new("Multiple Configuration Directories Detected", full_message) diff --git a/crates/kftray-portforward/.gitignore b/crates/kftray-portforward/.gitignore new file mode 100644 index 00000000..9bc2171e --- /dev/null +++ b/crates/kftray-portforward/.gitignore @@ -0,0 +1,5 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ + + diff --git a/crates/kftray-portforward/Cargo.toml b/crates/kftray-portforward/Cargo.toml new file mode 100644 index 00000000..a5d49cb0 --- /dev/null +++ b/crates/kftray-portforward/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "kftray-portforward" +version = "0.12.2" +description = "KFtray library with port forwarding logic" +authors = [ + "Henrique Cavarsan ", +] +license = "MIT" +homepage = "https://kftray.app" +repository = "https://github.com/hcavarsan/kftray" +edition = "2021" + +[dependencies] +anyhow = "1.0.86" +async-trait = "0.1" +bytes = "1.6.0" +dashmap = "6.0.0" +flate2 = "1.0" +futures = "0.3.30" +httparse = "1.9.4" +k8s-openapi = { version = "0.22.0", default-features = false, features = ["latest"] } +kube = { version = "0.93.1", features = ["client", "config", "rustls-tls", "ws"] } +kube-runtime = "0.93.1" +lazy_static = "1.5.0" +log = "0.4" +rand = "0.8.5" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0.120" +tokio = { version = "1.39.1", features = ["rt-multi-thread", "macros", "full"] } +tokio-stream = { version = "0.1.15", features = ["net"] } +tracing = "0.1.40" +uuid = { version = "1.10.0", features = ["v4"] } +whoami = "1.5.1" +hostsfile = { git = "https://github.com/tonarino/innernet", branch = "main" } +kftray-commons = { path = "../kftray-commons" } +tower = "0.4.13" +hyper-util = "0.1.7" + +[lib] +name = "kftray_portforward" +path = "src/lib.rs" diff --git a/crates/kftray-portforward/src/.gitignore b/crates/kftray-portforward/src/.gitignore new file mode 100644 index 00000000..9bc2171e --- /dev/null +++ b/crates/kftray-portforward/src/.gitignore @@ -0,0 +1,5 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ + + diff --git a/crates/kftray-portforward/src/client.rs b/crates/kftray-portforward/src/client.rs new file mode 100644 index 00000000..85b5fea3 --- /dev/null +++ b/crates/kftray-portforward/src/client.rs @@ -0,0 +1,75 @@ +use anyhow::{ + Context, + Result, +}; +use hyper_util::rt::TokioExecutor; +use kftray_commons::config_dir::get_default_kubeconfig_path; +use kube::{ + client::ConfigExt, + config::{ + Config, + KubeConfigOptions, + Kubeconfig, + }, + Client, +}; +use log::info; +use tower::ServiceBuilder; + +pub async fn create_client_with_specific_context( + kubeconfig: Option, context_name: &str, +) -> Result { + let kubeconfig = if let Some(path) = kubeconfig { + if path == "default" { + let default_path = get_default_kubeconfig_path()?; + + info!( + "Reading kubeconfig from default location: {:?}", + default_path + ); + + Kubeconfig::read_from(default_path) + .context("Failed to read kubeconfig from default location")? + } else { + info!("Reading kubeconfig from specified path: {}", path); + + Kubeconfig::read_from(path).context("Failed to read kubeconfig from specified path")? + } + } else { + let default_path = get_default_kubeconfig_path()?; + + info!( + "Reading kubeconfig from default location: {:?}", + default_path + ); + + Kubeconfig::read_from(default_path) + .context("Failed to read kubeconfig from default location")? + }; + + let config = Config::from_custom_kubeconfig( + kubeconfig, + &KubeConfigOptions { + context: Some(context_name.to_owned()), + ..Default::default() + }, + ) + .await + .context("Failed to create configuration from kubeconfig")?; + + let https_connector = config + .rustls_https_connector() + .context("Failed to create Rustls HTTPS connector")?; + + let service = ServiceBuilder::new() + .layer(config.base_uri_layer()) + .option_layer(config.auth_layer()?) + .service( + hyper_util::client::legacy::Client::builder(TokioExecutor::new()) + .build(https_connector), + ); + + let client = Client::new(service, config.default_namespace); + + Ok(client) +} diff --git a/crates/kftray-tauri/src/kubeforward/commands.rs b/crates/kftray-portforward/src/core.rs similarity index 82% rename from crates/kftray-tauri/src/kubeforward/commands.rs rename to crates/kftray-portforward/src/core.rs index 9ec558ae..a1bed47c 100644 --- a/crates/kftray-tauri/src/kubeforward/commands.rs +++ b/crates/kftray-portforward/src/core.rs @@ -1,18 +1,21 @@ use std::collections::HashMap; -use std::sync::{ - Arc, - Mutex, -}; -use std::{ - fs::File, - io::Read, - time::{ - SystemTime, - UNIX_EPOCH, - }, +use std::fs::File; +use std::io::Read; +use std::sync::Arc; +use std::time::{ + SystemTime, + UNIX_EPOCH, }; use hostsfile::HostsBuilder; +use k8s_openapi::api::core::v1::Pod; +use kftray_commons::models::{ + config_model::Config, + config_state_model::ConfigState, + response::CustomResponse, +}; +use kftray_commons::utils::config_dir::get_pod_manifest_path; +use kftray_commons::utils::config_state::update_config_state; use kube::{ api::{ Api, @@ -22,52 +25,28 @@ use kube::{ Client, }; use kube_runtime::wait::conditions; -use lazy_static::lazy_static; +use log::{ + error, + info, +}; use rand::{ distributions::Alphanumeric, Rng, }; -use tokio::sync::Notify; -use tokio::task::JoinHandle; - -use crate::kubeforward::kubecontext::create_client_with_specific_context; -use crate::utils::config_dir::get_pod_manifest_path; -use crate::{ - config, - kubeforward::vx::Pod, - models::{ - config::Config, - kube::{ - HttpLogState, - Port, - PortForward, - Target, - TargetSelector, - }, - response::CustomResponse, - }, -}; -lazy_static! { - pub static ref CHILD_PROCESSES: Arc>>> = - Arc::new(Mutex::new(HashMap::new())); - pub static ref CANCEL_NOTIFIER: Arc = Arc::new(Notify::new()); -} - -pub async fn start_port_forward_udp( - configs: Vec, http_log_state: tauri::State<'_, HttpLogState>, -) -> Result, String> { - start_port_forward(configs, "udp", http_log_state).await -} -#[tauri::command] -pub async fn start_port_forward_tcp( - configs: Vec, http_log_state: tauri::State<'_, HttpLogState>, -) -> Result, String> { - start_port_forward(configs, "tcp", http_log_state).await -} +use crate::client::create_client_with_specific_context; +use crate::models::kube::{ + HttpLogState, + Port, + PortForward, + Target, + TargetSelector, +}; +use crate::port_forward::CANCEL_NOTIFIER; +use crate::port_forward::CHILD_PROCESSES; -async fn start_port_forward( - configs: Vec, protocol: &str, http_log_state: tauri::State<'_, HttpLogState>, +pub async fn start_port_forward( + configs: Vec, protocol: &str, http_log_state: Arc, ) -> Result, String> { let mut responses = Vec::new(); let mut errors = Vec::new(); @@ -113,8 +92,13 @@ async fn start_port_forward( match port_forward_result { Ok(port_forward) => { let forward_result = match protocol { - "udp" => port_forward.port_forward_udp().await, - "tcp" => port_forward.port_forward_tcp(http_log_state.clone()).await, + "udp" => port_forward.clone().port_forward_udp().await, + "tcp" => { + port_forward + .clone() + .port_forward_tcp(http_log_state.clone()) + .await + } _ => Err(anyhow::anyhow!("Unsupported protocol")), }; @@ -132,6 +116,9 @@ async fn start_port_forward( &config.service ); + error!("Port forwarding details: {:?}", port_forward); + error!("Actual local port: {:?}", actual_local_port); + let handle_key = format!( "{}_{}", config.id.unwrap(), @@ -191,6 +178,15 @@ async fn start_port_forward( } } + let config_state = ConfigState { + id: None, + config_id: config.id.unwrap(), + is_running: true, + }; + if let Err(e) = update_config_state(&config_state).await { + log::error!("Failed to update config state: {}", e); + } + responses.push(CustomResponse { id: config.id, service: config.service.clone().unwrap(), @@ -262,7 +258,7 @@ async fn start_port_forward( Ok(responses) } -#[tauri::command] + pub async fn stop_all_port_forward() -> Result, String> { log::info!("Attempting to stop all port forwards"); @@ -273,15 +269,16 @@ pub async fn stop_all_port_forward() -> Result, String> { e.to_string() })?; - // Notify all port forwarding tasks to cancel CANCEL_NOTIFIER.notify_waiters(); let handle_map: HashMap> = CHILD_PROCESSES.lock().unwrap().drain().collect(); - let configs_result = config::get_configs().await; + let configs_result = kftray_commons::utils::config::get_configs().await; if let Err(e) = configs_result { - return Err(format!("Failed to retrieve configs: {}", e)); + let error_message = format!("Failed to retrieve configs: {}", e); + log::error!("{}", error_message); + return Err(error_message); } let configs = configs_result.unwrap(); @@ -324,6 +321,14 @@ pub async fn stop_all_port_forward() -> Result, String> { stderr: e.to_string(), status: 1, }); + let config_state = ConfigState { + id: None, + config_id: config_id_parsed, + is_running: false, + }; + if let Err(e) = update_config_state(&config_state).await { + log::error!("Failed to update config state: {}", e); + } continue; } } @@ -336,7 +341,6 @@ pub async fn stop_all_port_forward() -> Result, String> { config_id_str ); - // Abort the handle, which should naturally be cancelled now handle.abort(); let client_clone = client.clone(); @@ -391,6 +395,15 @@ pub async fn stop_all_port_forward() -> Result, String> { pod_deletion_tasks.push(pod_deletion_task); + let config_state = ConfigState { + id: None, + config_id: config_id_parsed, + is_running: false, + }; + if let Err(e) = update_config_state(&config_state).await { + log::error!("Failed to update config state: {}", e); + } + responses.push(CustomResponse { id: Some(config_id_parsed), service: service_id.to_string(), @@ -415,14 +428,10 @@ pub async fn stop_all_port_forward() -> Result, String> { Ok(responses) } -#[tauri::command] -pub async fn stop_port_forward( - _service_name: String, config_id: String, -) -> Result { +pub async fn stop_port_forward(config_id: String) -> Result { let cancellation_notifier = CANCEL_NOTIFIER.clone(); cancellation_notifier.notify_waiters(); - // Retrieve composite key representing the child process let composite_key = { let child_processes = CHILD_PROCESSES.lock().unwrap(); child_processes @@ -432,23 +441,21 @@ pub async fn stop_port_forward( }; if let Some(composite_key) = composite_key { - // Remove and retrieve child process handle let join_handle = { let mut child_processes = CHILD_PROCESSES.lock().unwrap(); - println!("child_processes: {:?}", child_processes); + info!("child_processes: {:?}", child_processes); child_processes.remove(&composite_key) }; if let Some(join_handle) = join_handle { - println!("Join handle: {:?}", join_handle); + info!("Join handle: {:?}", join_handle); join_handle.abort(); } - // Split the composite key to get config_id and service_name let (config_id_str, service_name) = composite_key.split_once('_').unwrap_or(("", "")); let config_id_parsed = config_id_str.parse::().unwrap_or_default(); - match config::get_configs().await { + match kftray_commons::config::get_configs().await { Ok(configs) => { if let Some(config) = configs .iter() @@ -468,6 +475,15 @@ pub async fn stop_port_forward( service_name, e ); + + let config_state = ConfigState { + id: None, + config_id: config_id_parsed, + is_running: false, + }; + if let Err(e) = update_config_state(&config_state).await { + log::error!("Failed to update config state: {}", e); + } return Err(e.to_string()); } } @@ -475,6 +491,15 @@ pub async fn stop_port_forward( log::warn!("Config with id '{}' not found.", config_id_str); } + let config_state = ConfigState { + id: None, + config_id: config_id_parsed, + is_running: false, + }; + if let Err(e) = update_config_state(&config_state).await { + log::error!("Failed to update config state: {}", e); + } + Ok(CustomResponse { id: None, service: service_name.to_string(), @@ -488,9 +513,29 @@ pub async fn stop_port_forward( status: 0, }) } - Err(e) => Err(format!("Failed to retrieve configs: {}", e)), + Err(e) => { + let config_id_parsed = config_id.parse::().unwrap_or_default(); + let config_state = ConfigState { + id: None, + config_id: config_id_parsed, + is_running: false, + }; + if let Err(e) = update_config_state(&config_state).await { + log::error!("Failed to update config state: {}", e); + } + Err(format!("Failed to retrieve configs: {}", e)) + } } } else { + let config_id_parsed = config_id.parse::().unwrap_or_default(); + let config_state = ConfigState { + id: None, + config_id: config_id_parsed, + is_running: false, + }; + if let Err(e) = update_config_state(&config_state).await { + log::error!("Failed to update config state: {}", e); + } Err(format!( "No port forwarding process found for config_id '{}'", config_id @@ -508,9 +553,8 @@ fn render_json_template(template: &str, values: &HashMap<&str, String>) -> Strin rendered_template } -#[tauri::command] pub async fn deploy_and_forward_pod( - configs: Vec, http_log_state: tauri::State<'_, HttpLogState>, + configs: Vec, http_log_state: Arc, ) -> Result, String> { let mut responses: Vec = Vec::new(); @@ -540,7 +584,7 @@ pub async fn deploy_and_forward_pod( let username = whoami::username().to_lowercase(); let clean_username: String = username.chars().filter(|c| c.is_alphanumeric()).collect(); - println!("Cleaned username: {}", clean_username); + info!("Cleaned username: {}", clean_username); let protocol = config.protocol.to_string().to_lowercase(); @@ -606,10 +650,12 @@ pub async fn deploy_and_forward_pod( let start_response = match protocol.as_str() { "udp" => { - start_port_forward_udp(vec![config.clone()], http_log_state.clone()).await + start_port_forward(vec![config.clone()], "udp", http_log_state.clone()) + .await } "tcp" => { - start_port_forward_tcp(vec![config.clone()], http_log_state.clone()).await + start_port_forward(vec![config.clone()], "tcp", http_log_state.clone()) + .await } _ => { let _ = pods @@ -641,7 +687,6 @@ pub async fn deploy_and_forward_pod( Ok(responses) } -#[tauri::command] pub async fn stop_proxy_forward( config_id: String, namespace: &str, service_name: String, ) -> Result { @@ -698,34 +743,16 @@ pub async fn stop_proxy_forward( log::info!("Stopping port forward for service: {}", service_name); - let stop_result = stop_port_forward(service_name.clone(), config_id) - .await - .map_err(|e| { - log::error!( - "Failed to stop port forwarding for service '{}': {}", - service_name, - e - ); + let stop_result = stop_port_forward(config_id.clone()).await.map_err(|e| { + log::error!( + "Failed to stop port forwarding for service '{}': {}", + service_name, e - })?; + ); + e + })?; log::info!("Proxy forward stopped for service: {}", service_name); Ok(stop_result) } - -#[tauri::command] -pub async fn set_http_logs( - state: tauri::State<'_, HttpLogState>, config_id: i64, enable: bool, -) -> Result<(), String> { - state.set_http_logs(config_id, enable).await; - Ok(()) -} - -#[tauri::command] -pub async fn get_http_logs( - state: tauri::State<'_, HttpLogState>, config_id: i64, -) -> Result { - let current_state = state.get_http_logs(config_id).await; - Ok(current_state) -} diff --git a/crates/kftray-portforward/src/lib.rs b/crates/kftray-portforward/src/lib.rs new file mode 100644 index 00000000..a2ce022a --- /dev/null +++ b/crates/kftray-portforward/src/lib.rs @@ -0,0 +1,12 @@ +pub mod client; +pub mod core; +pub mod models; +pub mod pod_finder; +pub mod port_forward; + +pub use core::*; + +pub use client::*; +pub use models::*; +pub use pod_finder::*; +pub use port_forward::*; diff --git a/crates/kftray-portforward/src/mod.rs b/crates/kftray-portforward/src/mod.rs new file mode 100644 index 00000000..fb14a2ce --- /dev/null +++ b/crates/kftray-portforward/src/mod.rs @@ -0,0 +1,7 @@ + +use anyhow::Context; +pub use k8s_openapi::api::core::v1 as vx; +use k8s_openapi::apimachinery::pkg::util::intstr::IntOrString; +use kube::ResourceExt; +use vx::Pod; + diff --git a/crates/kftray-portforward/src/models/kube.rs b/crates/kftray-portforward/src/models/kube.rs new file mode 100644 index 00000000..c02aaacf --- /dev/null +++ b/crates/kftray-portforward/src/models/kube.rs @@ -0,0 +1,232 @@ +use std::collections::HashMap; +use std::sync::atomic::{ + AtomicBool, + Ordering, +}; +use std::sync::Arc; + +use anyhow::Context; +use k8s_openapi::api::core::v1::{ + Pod, + Service, +}; +use k8s_openapi::apimachinery::pkg::util::intstr::IntOrString; +use kube::api::Api; +use serde::{ + Deserialize, + Serialize, +}; +use tokio::sync::Mutex; +use tracing::debug; + +impl NameSpace { + pub fn name_any(&self) -> String { + self.0.clone().unwrap_or_else(|| "default".to_string()) + } +} + +impl From for Port { + fn from(port: i32) -> Self { + Self::Number(port) + } +} + +impl From<&str> for Port { + fn from(port: &str) -> Self { + Self::Name(port.to_string()) + } +} + +impl From for Port { + fn from(port: IntOrString) -> Self { + match port { + IntOrString::Int(port) => Self::Number(port), + IntOrString::String(port) => Self::Name(port), + } + } +} + +impl TargetPod { + pub fn new(pod_name: String, port_number: i32) -> anyhow::Result { + let port_number = u16::try_from(port_number).context("Port not valid")?; + + Ok(Self { + pod_name, + port_number, + }) + } + + pub fn into_parts(self) -> (String, u16) { + (self.pod_name, self.port_number) + } +} +impl Target { + pub fn new>, T: Into, P: Into>( + selector: TargetSelector, port: P, namespace: I, + ) -> Self { + Self { + selector, + port: port.into(), + namespace: NameSpace(namespace.into().map(Into::into)), + } + } + + pub fn find(&self, pod: &Pod, port: Option) -> anyhow::Result { + let port = port.as_ref().unwrap_or(&self.port); + + let pod_name = pod.metadata.name.clone().context("Pod Name is None")?; + + let port_number = match port { + Port::Number(port) => *port, + Port::Name(name) => { + let spec = pod.spec.as_ref().context("Pod Spec is None")?; + let containers = &spec.containers; + + // Find the port by name within the container ports + containers + .iter() + .flat_map(|c| c.ports.as_ref().map_or(Vec::new(), |v| v.clone())) + .find(|p| p.name.as_ref() == Some(name)) + .context("Port not found")? + .container_port + } + }; + + TargetPod::new(pod_name, port_number) + } +} + +fn is_pod_ready(pod: &&Pod) -> bool { + let conditions = pod.status.as_ref().and_then(|s| s.conditions.as_ref()); + + let is_ready = conditions + .map(|c| c.iter().any(|c| c.type_ == "Ready" && c.status == "True")) + .unwrap_or(false); + + debug!( + "Pod: {}, is_ready: {}", + pod.metadata.name.clone().unwrap_or_default(), + is_ready + ); + is_ready +} + +#[derive(Serialize, Deserialize)] +pub struct KubeContextInfo { + pub name: String, +} + +#[derive(Serialize)] +pub struct KubeNamespaceInfo { + pub name: String, +} + +#[derive(Serialize)] +pub struct KubeServiceInfo { + pub name: String, +} + +#[derive(Serialize)] +pub struct KubeServicePortInfo { + pub name: Option, + pub port: Option, +} + +#[derive(Serialize, Debug)] +pub struct PodInfo { + pub labels_str: String, +} + +#[derive(Clone, Debug)] +#[allow(dead_code)] +pub struct PortForward { + pub target: Target, + pub local_port: Option, + pub local_address: Option, + pub pod_api: Api, + pub svc_api: Api, + pub context_name: Option, + pub config_id: i64, + pub workload_type: String, + pub connection: Arc>>, +} + +#[derive(Clone, Debug)] +pub enum TargetSelector { + ServiceName(String), + PodLabel(String), +} + +#[derive(Clone, Debug)] +pub enum Port { + Number(i32), + Name(String), +} + +#[derive(Clone, Debug)] +pub struct Target { + pub selector: TargetSelector, + pub port: Port, + pub namespace: NameSpace, +} + +#[derive(Clone, Debug)] +pub struct NameSpace(pub Option); + +#[derive(Clone, Debug)] +pub struct TargetPod { + pub pod_name: String, + pub port_number: u16, +} + +pub trait PodSelection { + fn select<'p>(&self, pods: &'p [Pod], selector: &str) -> anyhow::Result<&'p Pod>; +} + +pub struct AnyReady {} + +#[derive(Clone, Debug)] +pub struct HttpLogState { + pub enable_http_logs: Arc>>, +} + +impl HttpLogState { + pub fn new() -> Self { + HttpLogState { + enable_http_logs: Arc::new(Mutex::new(HashMap::new())), + } + } + + pub async fn set_http_logs(&self, config_id: i64, enable: bool) { + let mut logs = self.enable_http_logs.lock().await; + logs.entry(config_id) + .or_insert_with(|| AtomicBool::new(enable)) + .store(enable, Ordering::SeqCst); + } + + pub async fn get_http_logs(&self, config_id: i64) -> bool { + let logs = self.enable_http_logs.lock().await; + if let Some(state) = logs.get(&config_id) { + state.load(Ordering::SeqCst) + } else { + false + } + } +} + +impl Default for HttpLogState { + fn default() -> Self { + Self::new() + } +} + +impl PodSelection for AnyReady { + fn select<'p>(&self, pods: &'p [Pod], selector: &str) -> anyhow::Result<&'p Pod> { + let pod = pods.iter().find(is_pod_ready).context(anyhow::anyhow!( + "No ready pods found matching the selector '{}'", + selector + ))?; + + Ok(pod) + } +} diff --git a/crates/kftray-portforward/src/models/mod.rs b/crates/kftray-portforward/src/models/mod.rs new file mode 100644 index 00000000..0f886aa4 --- /dev/null +++ b/crates/kftray-portforward/src/models/mod.rs @@ -0,0 +1 @@ +pub mod kube; diff --git a/crates/kftray-tauri/src/kubeforward/pod_finder.rs b/crates/kftray-portforward/src/pod_finder.rs similarity index 97% rename from crates/kftray-tauri/src/kubeforward/pod_finder.rs rename to crates/kftray-portforward/src/pod_finder.rs index 00a27fab..c62c95bc 100644 --- a/crates/kftray-tauri/src/kubeforward/pod_finder.rs +++ b/crates/kftray-portforward/src/pod_finder.rs @@ -57,13 +57,13 @@ impl<'a> TargetPodFinder<'a> { ); let pod = ready_pod.select(&pods.items, &label_selector_str)?; + debug!("Pod found for service '{}': {:?}", name, pod); target.find(pod, None) } else { Err(anyhow::anyhow!("No selector found for service '{}'", name)) } } Err(kube::Error::Api(kube::error::ErrorResponse { code: 404, .. })) => { - // Fallback using name as label selector directly let label_selector_str = format!("app={}", name); debug!( diff --git a/crates/kftray-tauri/src/kubeforward/port_forward.rs b/crates/kftray-portforward/src/port_forward.rs similarity index 93% rename from crates/kftray-tauri/src/kubeforward/port_forward.rs rename to crates/kftray-portforward/src/port_forward.rs index 49b0cf47..962434fb 100644 --- a/crates/kftray-tauri/src/kubeforward/port_forward.rs +++ b/crates/kftray-portforward/src/port_forward.rs @@ -1,13 +1,20 @@ +use std::collections::HashMap; use std::net::SocketAddr; use std::sync::Arc; +use std::sync::Mutex as StdMutex; use std::time::Duration; use anyhow::Context; use futures::TryStreamExt; +use kftray_commons::logging::{ + create_log_file_path, + Logger, +}; use kube::{ api::Api, Client, }; +use lazy_static::lazy_static; use tokio::net::TcpStream; use tokio::net::UdpSocket as TokioUdpSocket; use tokio::sync::Mutex; @@ -29,17 +36,18 @@ use tracing::{ trace, }; -use crate::kubeforward::commands::CANCEL_NOTIFIER; -use crate::kubeforward::logging::{ - create_log_file_path, - Logger, -}; -use crate::kubeforward::pod_finder::TargetPodFinder; use crate::models::kube::HttpLogState; use crate::models::kube::{ PortForward, Target, }; +use crate::pod_finder::TargetPodFinder; + +lazy_static! { + pub static ref CHILD_PROCESSES: Arc>>> = + Arc::new(StdMutex::new(HashMap::new())); + pub static ref CANCEL_NOTIFIER: Arc = Arc::new(Notify::new()); +} const BUFFER_SIZE: usize = 131072; @@ -50,11 +58,7 @@ impl PortForward { kubeconfig: Option, config_id: i64, workload_type: String, ) -> anyhow::Result { let client = if let Some(ref context_name) = context_name { - crate::kubeforward::kubecontext::create_client_with_specific_context( - kubeconfig, - context_name, - ) - .await? + crate::client::create_client_with_specific_context(kubeconfig, context_name).await? } else { Client::try_default().await? }; @@ -83,7 +87,7 @@ impl PortForward { } pub async fn port_forward_tcp( - self, http_log_state: tauri::State<'_, HttpLogState>, + self, http_log_state: Arc, ) -> anyhow::Result<(u16, tokio::task::JoinHandle<()>)> { let local_addr = self .local_address() @@ -101,7 +105,7 @@ impl PortForward { let server = { let cancel_notifier = CANCEL_NOTIFIER.clone(); - let http_log_state = http_log_state.inner().clone(); + let http_log_state = http_log_state.clone(); TcpListenerStream::new(bind).try_for_each(move |client_conn| { let pf = self.clone(); let client_conn = Arc::new(Mutex::new(client_conn)); @@ -112,7 +116,6 @@ impl PortForward { trace!(%peer_addr, "new connection"); } - // Set TCP_NODELAY for the client connection { let conn = client_conn.lock().await; conn.set_nodelay(true)?; @@ -122,11 +125,7 @@ impl PortForward { tokio::spawn(async move { if let Err(e) = pf - .forward_connection( - client_conn, - Arc::new(http_log_state), - cancel_notifier_clone, - ) + .forward_connection(client_conn, http_log_state, cancel_notifier_clone) .await { error!( @@ -164,17 +163,28 @@ impl PortForward { ) -> anyhow::Result<()> { let target = self.finder().find(&self.target).await?; + debug!("Forwarding connection to target pod"); + debug!("Target pod: {:?}", target); + let (pod_name, pod_port) = target.into_parts(); + debug!("Pod name: {}", pod_name); + debug!("Pod port: {}", pod_port); + let mut forwarder = self.pod_api.portforward(&pod_name, &[pod_port]).await?; + debug!("Forwarder created"); + let upstream_conn = forwarder .take_stream(pod_port) .context("port not found in forwarder")?; let local_port = self.local_port(); + debug!("Local port: {}", local_port); let config_id = self.config_id; + debug!("Config ID: {}", config_id); let workload_type = self.workload_type.clone(); + debug!("Workload type: {}", workload_type); trace!(local_port, pod_port, pod_name = %pod_name, "forwarding connections"); @@ -186,8 +196,14 @@ impl PortForward { None }; + debug!("Logger created"); + debug!("Logger: {:?}", logger); + let request_id = Arc::new(Mutex::new(None)); + debug!("Request ID created"); + debug!("Request ID: {:?}", request_id); + let mut client_conn_guard = client_conn.lock().await; client_conn_guard.set_nodelay(true)?; let (mut client_reader, mut client_writer) = tokio::io::split(&mut *client_conn_guard); @@ -533,7 +549,6 @@ impl PortForward { let mut len_bytes = [0u8; 4]; if tcp_read.read_exact(&mut len_bytes).await.is_err() { - // If there's an error reading (which includes EOF), return None return Ok(None); } @@ -542,7 +557,6 @@ impl PortForward { let mut packet = vec![0u8; len]; if tcp_read.read_exact(&mut packet).await.is_err() { - // If there's an error reading the packet (which includes EOF), return None return Ok(None); } diff --git a/crates/kftray-server/Cargo.toml b/crates/kftray-server/Cargo.toml index 03929bff..ad07314c 100644 --- a/crates/kftray-server/Cargo.toml +++ b/crates/kftray-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kftray-server" -version = "0.12.2" +version = "0.13.0" description = "KFtray Server is a Rust application that relays UDP/TCP traffic to an upstream server" authors = [ "Henrique Cavarsan ", @@ -18,4 +18,4 @@ byteorder = "1.4" tokio = { version = "1", features = ["full"] } [dev-dependencies] -mockall = "0.13" +mockall = "0.13" \ No newline at end of file diff --git a/crates/kftray-server/Dockerfile b/crates/kftray-server/Dockerfile index c8bb9511..7dc95c12 100644 --- a/crates/kftray-server/Dockerfile +++ b/crates/kftray-server/Dockerfile @@ -1,4 +1,4 @@ -FROM rust:1.80.1-alpine3.19 AS builder +FROM rust:1.80.0-alpine3.19 AS builder RUN apk add --no-cache musl-dev diff --git a/crates/kftray-tauri/Cargo.toml b/crates/kftray-tauri/Cargo.toml index 0f28aab7..955d2c3f 100644 --- a/crates/kftray-tauri/Cargo.toml +++ b/crates/kftray-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "kftray" -version = "0.12.2" +name = "kftray-tauri" +version = "0.13.0" description = "A cross-platform system tray app for Kubernetes port-forward management" authors = ["Henrique Cavarsan "] license = "MIT" @@ -8,6 +8,10 @@ homepage = "https://kftray.app" repository = "https://github.com/hcavarsan/kftray" edition = "2021" +[lib] +name = "kftray_tauri" +path = "src/lib.rs" + [build-dependencies] tauri-build = { version = "1.5", features = [] } @@ -23,7 +27,7 @@ tauri = { version = "1.6", default-features = false, features = [ ] } tauri-plugin-positioner = { version = "1.0.5", features = ["system-tray"] } tokio = { version = "1.39.1", features = ["rt-multi-thread", "macros", "full"] } -rusqlite = { version = "0.32.0", features = ["bundled"] } +sqlx = { version = "0.8.0", features = ["sqlite", "runtime-tokio-native-tls"] } dirs = "5.0.1" reqwest = "0.12.5" base64 = "0.22.1" @@ -58,13 +62,9 @@ tempfile = "3.9" h2 = { optional = true, version = "0.4.5" } fix-path-env = { git = "https://github.com/tauri-apps/fix-path-env-rs" } open = "5.3.0" -flate2 = "1.0" -httparse = "1.9.2" -uuid = { version = "1.10.0", features = ["v4"] } -bytes = "1.6.0" -tracing-subscriber = "0.3.18" -dashmap = "6.0.0" -native-dialog = "0.7.0" + +kftray-portforward = { path = "../kftray-portforward" } +kftray-commons = { path = "../kftray-commons" } [dev-dependencies] tempfile = "3.9" diff --git a/crates/kftray-tauri/src/commands/config.rs b/crates/kftray-tauri/src/commands/config.rs new file mode 100644 index 00000000..b318d96d --- /dev/null +++ b/crates/kftray-tauri/src/commands/config.rs @@ -0,0 +1,72 @@ +use kftray_commons::config::{ + delete_all_configs, + delete_config, + delete_configs, + export_configs, + get_config, + get_configs, + import_configs, + insert_config, + update_config, +}; +use kftray_commons::models::config_model::Config; +use log::{ + error, + info, +}; + +#[tauri::command] +pub async fn delete_config_cmd(id: i64) -> Result<(), String> { + info!("Deleting config with id: {}", id); + delete_config(id).await +} + +#[tauri::command] +pub async fn delete_configs_cmd(ids: Vec) -> Result<(), String> { + info!("Deleting configs with ids: {:?}", ids); + delete_configs(ids).await +} + +#[tauri::command] +pub async fn delete_all_configs_cmd() -> Result<(), String> { + info!("Deleting all configs"); + delete_all_configs().await +} + +#[tauri::command] +pub async fn insert_config_cmd(config: Config) -> Result<(), String> { + insert_config(config).await +} + +#[tauri::command] +pub async fn get_configs_cmd() -> Result, String> { + info!("get_configs called"); + let configs = get_configs().await?; + info!("{:?}", configs); + Ok(configs) +} + +#[tauri::command] +pub async fn get_config_cmd(id: i64) -> Result { + info!("get_config called with id: {}", id); + get_config(id).await +} + +#[tauri::command] +pub async fn update_config_cmd(config: Config) -> Result<(), String> { + update_config(config).await +} + +#[tauri::command] +pub async fn export_configs_cmd() -> Result { + export_configs().await +} + +#[tauri::command] +pub async fn import_configs_cmd(json: String) -> Result<(), String> { + if let Err(e) = import_configs(json).await { + error!("Error migrating configs: {}. Please check if the configurations are valid and compatible with the current system/version.", e); + return Err(format!("Error migrating configs: {}", e)); + } + Ok(()) +} diff --git a/crates/kftray-tauri/src/commands/config_state.rs b/crates/kftray-tauri/src/commands/config_state.rs new file mode 100644 index 00000000..11754d52 --- /dev/null +++ b/crates/kftray-tauri/src/commands/config_state.rs @@ -0,0 +1,10 @@ +use kftray_commons::config_state::get_configs_state; +use kftray_commons::models::config_state_model::ConfigState; + +#[tauri::command] +pub async fn get_config_states() -> Result, String> { + log::info!("get_configs state called"); + let configs = get_configs_state().await?; + log::info!("{:?}", configs); + Ok(configs) +} diff --git a/crates/kftray-tauri/src/commands/github.rs b/crates/kftray-tauri/src/commands/github.rs new file mode 100644 index 00000000..11746992 --- /dev/null +++ b/crates/kftray-tauri/src/commands/github.rs @@ -0,0 +1,154 @@ +use base64::{ + engine::general_purpose, + Engine as _, +}; +use keyring::{ + Entry, + Error as KeyringError, +}; +use kftray_commons::{ + models::config_model::Config, + utils::config::import_configs, + utils::github::{ + build_github_api_url, + clear_existing_configs, + }, + utils::migration::migrate_configs, +}; +use log::{ + error, + info, +}; +use reqwest::header::{ + AUTHORIZATION, + USER_AGENT, +}; +use serde_json::Value; +use tauri::{ + Error as TauriError, + InvokeError, +}; + +#[derive(Debug)] +pub enum CustomError { + Keyring(KeyringError), + Tauri(TauriError), +} + +impl From for CustomError { + fn from(error: KeyringError) -> Self { + CustomError::Keyring(error) + } +} + +impl From for CustomError { + fn from(error: TauriError) -> Self { + CustomError::Tauri(error) + } +} + +impl From for InvokeError { + fn from(error: CustomError) -> Self { + match error { + CustomError::Keyring(err) => InvokeError::from(err.to_string()), + CustomError::Tauri(err) => InvokeError::from(err.to_string()), + } + } +} + +#[tauri::command] +pub fn store_key( + service: &str, name: &str, password: &str, +) -> std::result::Result<(), CustomError> { + let entry = Entry::new(service, name).map_err(CustomError::from)?; + + entry.set_password(password).map_err(CustomError::from)?; + + Ok(()) +} + +#[tauri::command] +pub fn get_key(service: &str, name: &str) -> std::result::Result { + let entry = Entry::new(service, name).map_err(CustomError::from)?; + + let password = entry.get_password().map_err(CustomError::from)?; + + Ok(password) +} + +#[tauri::command] +pub fn delete_key(service: &str, name: &str) -> std::result::Result<(), CustomError> { + let entry = Entry::new(service, name).map_err(CustomError::from)?; + + entry.delete_credential().map_err(CustomError::from)?; + + Ok(()) +} + +#[tauri::command] +pub async fn import_configs_from_github( + repo_url: String, config_path: String, is_private: bool, flush: bool, token: Option, +) -> Result<(), String> { + let client = reqwest::Client::new(); + + let url = build_github_api_url(&repo_url, &config_path) + .map_err(|e| format!("Failed to build GitHub API URL: {}", e))?; + + let mut request_builder = client.get(&url); + + if is_private { + let token = token.ok_or("Token is required for private repositories")?; + request_builder = request_builder.header(AUTHORIZATION, format!("token {}", token)); + } + + let response = request_builder + .header(USER_AGENT, "request") + .send() + .await + .map_err(|e| format!("Failed to send request: {}", e))? + .error_for_status() + .map_err(|e| format!("Request failed: {}", e))?; + + let json_content = response.text().await.map_err(|e| e.to_string())?; + + let json_obj: Value = serde_json::from_str(&json_content) + .map_err(|e| format!("Failed to parse response: {}", e))?; + + let base64_content = json_obj["content"] + .as_str() + .ok_or("Failed to extract content from response")? + .trim(); + + info!("base64_content: {}", base64_content); + + let base64_content_cleaned = base64_content.replace(['\n', '\r'], ""); + + let decoded_content = general_purpose::STANDARD + .decode(&base64_content_cleaned) + .map_err(|e| format!("Failed to decode base64 content: {}", e))?; + + let decoded_str = String::from_utf8(decoded_content) + .map_err(|e| format!("Failed to convert decoded content to string: {}", e))?; + + info!("decoded_str: {}", decoded_str); + + let configs: Vec = serde_json::from_str(&decoded_str) + .map_err(|e| format!("Failed to parse configs: {}", e))?; + + if flush { + clear_existing_configs().await.map_err(|e| e.to_string())?; + } + + for config in configs { + let config_json = serde_json::to_string(&config) + .map_err(|e| format!("Failed to serialize config: {}", e))?; + + import_configs(config_json).await?; + } + + if let Err(e) = migrate_configs().await { + error!("Error migrating configs: {}. Please check if the configurations are valid and compatible with the current system/version.", e); + } + + Ok(()) +} diff --git a/crates/kftray-tauri/src/commands.rs b/crates/kftray-tauri/src/commands/httplogs.rs similarity index 63% rename from crates/kftray-tauri/src/commands.rs rename to crates/kftray-tauri/src/commands/httplogs.rs index f18f268d..fb3ee9b2 100644 --- a/crates/kftray-tauri/src/commands.rs +++ b/crates/kftray-tauri/src/commands/httplogs.rs @@ -1,113 +1,97 @@ -use std::sync::atomic::Ordering; +use kftray_commons::utils::config_dir::get_log_folder_path; +use kftray_portforward::models::kube::HttpLogState; +use log::error; +use log::info; -use base64::{ - engine::general_purpose, - Engine as _, -}; -use reqwest::header::{ - AUTHORIZATION, - USER_AGENT, -}; -use tauri::State; - -use crate::utils::config_dir::get_log_folder_path; -use crate::{ - config::{ - import_configs, - migrate_configs, - }, - models::{ - config::Config, - window::SaveDialogState, - }, - remote_config::{ - build_github_api_url, - clear_existing_configs, - }, -}; - -// command to save the dialog state when is open #[tauri::command] - -pub fn open_save_dialog(state: State) { - state.is_open.store(true, Ordering::SeqCst); +pub async fn set_http_logs_cmd( + state: tauri::State<'_, HttpLogState>, config_id: i64, enable: bool, +) -> Result<(), String> { + state.set_http_logs(config_id, enable).await; + Ok(()) } -// command to save the dialog state when is closed #[tauri::command] - -pub fn close_save_dialog(state: State) { - state.is_open.store(false, Ordering::SeqCst); +pub async fn get_http_logs_cmd( + state: tauri::State<'_, HttpLogState>, config_id: i64, +) -> Result { + let current_state = state.get_http_logs(config_id).await; + Ok(current_state) } - -// command to import configs from github #[tauri::command] +pub async fn clear_http_logs() -> Result<(), String> { + use std::fs; + use std::path::PathBuf; -pub async fn import_configs_from_github( - repo_url: String, config_path: String, is_private: bool, flush: bool, token: Option, -) -> Result<(), String> { - let client = reqwest::Client::new(); - - let url = build_github_api_url(&repo_url, &config_path); - - let mut request_builder = client.get(url); - - if is_private { - let token = token.ok_or("Token is required for private repositories")?; + fn delete_files_in_folder(path: &PathBuf) -> Result<(), String> { + if path.is_dir() { + for entry in + fs::read_dir(path).map_err(|e| format!("Failed to read directory: {}", e))? + { + let entry = entry.map_err(|e| format!("Failed to read directory entry: {}", e))?; + let path = entry.path(); + if path.is_file() { + fs::remove_file(&path).map_err(|e| format!("Failed to delete file: {}", e))?; + } + } + } else { + return Err(format!("Path is not a directory: {}", path.display())); + } - request_builder = request_builder.header(AUTHORIZATION, format!("token {}", token)); + Ok(()) } - let response = request_builder - .header(USER_AGENT, "request") - .send() - .await - .map_err(|e| format!("Failed to send request: {}", e))? - .error_for_status() - .map_err(|e| format!("Request failed: {}", e))?; - - let json_content = response.text().await.map_err(|e| e.to_string())?; - - let json_obj: serde_json::Value = serde_json::from_str(&json_content) - .map_err(|e| format!("Failed to parse response: {}", e))?; - - let base64_content = json_obj["content"] - .as_str() - .ok_or("Failed to extract content from response")? - .trim(); - - println!("base64_content: {}", base64_content); + let log_folder_path = get_log_folder_path()?; - let base64_content_cleaned = base64_content.replace(['\n', '\r'], ""); + if !log_folder_path.exists() { + return Err(format!( + "Log folder does not exist: {}", + log_folder_path.display() + )); + } - let decoded_content = general_purpose::STANDARD - .decode(&base64_content_cleaned) - .map_err(|e| format!("Failed to decode base64 content: {}", e))?; + delete_files_in_folder(&log_folder_path) +} - let decoded_str = String::from_utf8(decoded_content) - .map_err(|e| format!("Failed to convert decoded content to string: {}", e))?; +#[tauri::command] +pub async fn get_http_log_size() -> Result { + use std::fs; + use std::path::PathBuf; - println!("decoded_str: {}", decoded_str); + fn calculate_folder_size(path: &PathBuf) -> Result { + let mut size = 0; - let configs: Vec = serde_json::from_str(&decoded_str) - .map_err(|e| format!("Failed to parse configs: {}", e))?; + if path.is_dir() { + for entry in + fs::read_dir(path).map_err(|e| format!("Failed to read directory: {}", e))? + { + let entry = entry.map_err(|e| format!("Failed to read directory entry: {}", e))?; + let path = entry.path(); + if path.is_file() { + size += fs::metadata(&path) + .map_err(|e| format!("Failed to get file metadata: {}", e))? + .len(); + } else if path.is_dir() { + size += calculate_folder_size(&path)?; + } + } + } else { + return Err(format!("Path is not a directory: {}", path.display())); + } - if flush { - clear_existing_configs().map_err(|e| e.to_string())?; + Ok(size) } - for config in configs { - let config_json = serde_json::to_string(&config) - .map_err(|e| format!("Failed to serialize config: {}", e))?; - - import_configs(config_json).await?; - } + let log_folder_path = get_log_folder_path()?; - if let Err(e) = migrate_configs() { - eprintln!("Error migrating configs: {}. Please check if the configurations are valid and compatible with the current system/version.", e); + if !log_folder_path.exists() { + return Err(format!( + "Log folder does not exist: {}", + log_folder_path.display() + )); } - Ok(()) + calculate_folder_size(&log_folder_path) } #[tauri::command] @@ -127,7 +111,7 @@ pub async fn open_log_file(log_file_name: String) -> Result<(), String> { )); } - println!("Opening log file: {}", log_file_path.display()); + info!("Opening log file: {}", log_file_path.display()); if fs::metadata(&log_file_path).is_err() { return Err(format!( @@ -141,7 +125,7 @@ pub async fn open_log_file(log_file_name: String) -> Result<(), String> { match try_open_with_editor(log_file_path.to_str().unwrap(), &editor) { Ok(_) => Ok(()), Err(err) => { - println!( + error!( "Error opening with editor '{}': {}. Trying default method...", editor, err ); @@ -149,7 +133,7 @@ pub async fn open_log_file(log_file_name: String) -> Result<(), String> { match that_in_background(&log_file_path).join() { Ok(Ok(_)) => Ok(()), Ok(Err(err)) => { - println!("Error opening log file with default method: {}. Trying fallback methods...", err); + error!("Error opening log file with default method: {}. Trying fallback methods...", err); fallback_methods(log_file_path.to_str().unwrap()) } Err(err) => Err(format!("Failed to join thread: {:?}", err)), @@ -210,79 +194,3 @@ fn fallback_methods(log_file_path: &str) -> Result<(), String> { .or_else(|_| try_open_with_editor(log_file_path, "vim")) } } - -#[tauri::command] -pub async fn clear_http_logs() -> Result<(), String> { - use std::fs; - use std::path::PathBuf; - - fn delete_files_in_folder(path: &PathBuf) -> Result<(), String> { - if path.is_dir() { - for entry in - fs::read_dir(path).map_err(|e| format!("Failed to read directory: {}", e))? - { - let entry = entry.map_err(|e| format!("Failed to read directory entry: {}", e))?; - let path = entry.path(); - if path.is_file() { - fs::remove_file(&path).map_err(|e| format!("Failed to delete file: {}", e))?; - } - } - } else { - return Err(format!("Path is not a directory: {}", path.display())); - } - - Ok(()) - } - - let log_folder_path = get_log_folder_path()?; - - if !log_folder_path.exists() { - return Err(format!( - "Log folder does not exist: {}", - log_folder_path.display() - )); - } - - delete_files_in_folder(&log_folder_path) -} - -#[tauri::command] -pub async fn get_http_log_size() -> Result { - use std::fs; - use std::path::PathBuf; - - fn calculate_folder_size(path: &PathBuf) -> Result { - let mut size = 0; - - if path.is_dir() { - for entry in - fs::read_dir(path).map_err(|e| format!("Failed to read directory: {}", e))? - { - let entry = entry.map_err(|e| format!("Failed to read directory entry: {}", e))?; - let path = entry.path(); - if path.is_file() { - size += fs::metadata(&path) - .map_err(|e| format!("Failed to get file metadata: {}", e))? - .len(); - } else if path.is_dir() { - size += calculate_folder_size(&path)?; - } - } - } else { - return Err(format!("Path is not a directory: {}", path.display())); - } - - Ok(size) - } - - let log_folder_path = get_log_folder_path()?; - - if !log_folder_path.exists() { - return Err(format!( - "Log folder does not exist: {}", - log_folder_path.display() - )); - } - - calculate_folder_size(&log_folder_path) -} diff --git a/crates/kftray-tauri/src/kubeforward/kubecontext.rs b/crates/kftray-tauri/src/commands/kubecontext.rs similarity index 72% rename from crates/kftray-tauri/src/kubeforward/kubecontext.rs rename to crates/kftray-tauri/src/commands/kubecontext.rs index 7bf2247f..fcd983c5 100644 --- a/crates/kftray-tauri/src/kubeforward/kubecontext.rs +++ b/crates/kftray-tauri/src/commands/kubecontext.rs @@ -4,7 +4,7 @@ use anyhow::{ Context, Result, }; -use hyper_util::rt::TokioExecutor; +use k8s_openapi::api::core::v1::Pod; use k8s_openapi::{ api::core::v1::{ Namespace, @@ -12,103 +12,25 @@ use k8s_openapi::{ }, apimachinery::pkg::util::intstr::IntOrString, }; +use kftray_commons::utils::config_dir::get_default_kubeconfig_path; +use kftray_portforward::client::create_client_with_specific_context; +use kftray_portforward::models::kube::{ + KubeContextInfo, + KubeNamespaceInfo, + KubeServiceInfo, + KubeServicePortInfo, + PodInfo, +}; use kube::Resource; use kube::{ api::{ Api, ListParams, }, - client::ConfigExt, - config::{ - Config, - KubeConfigOptions, - Kubeconfig, - }, - Client, + config::Kubeconfig, ResourceExt, }; -use tower::ServiceBuilder; - -use crate::utils::config_dir::get_default_kubeconfig_path; -use crate::{ - kubeforward::vx::Pod, - models::kube::{ - KubeContextInfo, - KubeNamespaceInfo, - KubeServiceInfo, - KubeServicePortInfo, - PodInfo, - }, -}; -pub async fn create_client_with_specific_context( - kubeconfig: Option, context_name: &str, -) -> Result { - println!( - "create_client_with_specific_context {}", - kubeconfig.as_deref().unwrap_or("") - ); - - println!("create_client_with_specific_context {}", context_name); - - // Determine the kubeconfig based on the input - let kubeconfig = if let Some(path) = kubeconfig { - if path == "default" { - let default_path = get_default_kubeconfig_path()?; - - println!( - "Reading kubeconfig from default location: {:?}", - default_path - ); - - Kubeconfig::read_from(default_path) - .context("Failed to read kubeconfig from default location")? - } else { - // Otherwise, try to read the kubeconfig from the specified path - println!("Reading kubeconfig from specified path: {}", path); - - Kubeconfig::read_from(path).context("Failed to read kubeconfig from specified path")? - } - } else { - // If no kubeconfig is specified, read the default kubeconfig - let default_path = get_default_kubeconfig_path()?; - - println!( - "Reading kubeconfig from default location: {:?}", - default_path - ); - - Kubeconfig::read_from(default_path) - .context("Failed to read kubeconfig from default location")? - }; - - println!("create_client_with_specific_context2 {:?}", kubeconfig); - - let config = Config::from_custom_kubeconfig( - kubeconfig, - &KubeConfigOptions { - context: Some(context_name.to_owned()), - ..Default::default() - }, - ) - .await - .context("Failed to create configuration from kubeconfig")?; - - let https_connector = config - .rustls_https_connector() - .context("Failed to create Rustls HTTPS connector")?; - - let service = ServiceBuilder::new() - .layer(config.base_uri_layer()) - .option_layer(config.auth_layer()?) - .service( - hyper_util::client::legacy::Client::builder(TokioExecutor::new()) - .build(https_connector), - ); - - let client = Client::new(service, config.default_namespace); - - Ok(client) -} +use log::info; #[tauri::command] pub async fn list_pods( @@ -158,7 +80,7 @@ pub async fn list_pods( pub async fn list_kube_contexts( kubeconfig: Option, ) -> Result, String> { - println!("list_kube_contexts {}", kubeconfig.as_deref().unwrap_or("")); + info!("list_kube_contexts {}", kubeconfig.as_deref().unwrap_or("")); let kubeconfig = if let Some(path) = &kubeconfig { if path == "default" { @@ -166,7 +88,7 @@ pub async fn list_kube_contexts( .context("Couldn't get default kubeconfig path") .map_err(|err| err.to_string())?; - println!( + info!( "Reading kubeconfig from default location: {:?}", default_path ); @@ -175,7 +97,7 @@ pub async fn list_kube_contexts( .context("Failed to read kubeconfig from default path") .map_err(|err| err.to_string())? } else { - println!("Reading kubeconfig from specified path: {}", path); + info!("Reading kubeconfig from specified path: {}", path); Kubeconfig::read_from(path) .context("Failed to read kubeconfig from specified path") @@ -186,7 +108,7 @@ pub async fn list_kube_contexts( .context("Couldn't get default kubeconfig path") .map_err(|err| err.to_string())?; - println!( + info!( "Reading kubeconfig from default location: {:?}", default_path ); @@ -281,7 +203,6 @@ pub async fn list_ports( let api_svc: Api = Api::namespaced(client.clone(), namespace); let api_pod: Api = Api::namespaced(client.clone(), namespace); - // Try to get the service first match api_svc.get(service_name).await { Ok(service) => { let mut service_port_infos = Vec::new(); @@ -290,7 +211,6 @@ pub async fn list_ports( if let Some(service_ports) = spec.ports { for sp in service_ports { if let Some(IntOrString::String(ref name)) = sp.target_port { - // Construct a selector string from the pod's labels, if available. let selector_string = spec.selector.as_ref().map_or_else(String::new, |s| { s.iter() @@ -299,21 +219,16 @@ pub async fn list_ports( .join(", ") }); - // Attempt to list pods using the constructed label selector. let pods = api_pod .list(&ListParams::default().labels(selector_string.as_str())) .await .map_err(|e| format!("Failed to list pods: {}", e))?; - // Iterate through the list of pods to find a matching container port - // name. 'port_search: for pod in pods { if let Some(spec) = &pod.spec { for container in &spec.containers { if let Some(ports) = &container.ports { for cp in ports { - // Match the port name and add the port info to the - // service_port_infos vector if found. if cp.name.as_deref() == Some(name) { service_port_infos.push(KubeServicePortInfo { name: cp.name.clone(), @@ -349,8 +264,6 @@ pub async fn list_ports( } } Err(_) => { - // If service is not found, treat service_name as a label and search for - // matching pods let pods = api_pod .list(&ListParams::default().labels(service_name)) .await diff --git a/crates/kftray-tauri/src/commands/mod.rs b/crates/kftray-tauri/src/commands/mod.rs new file mode 100644 index 00000000..6244bab1 --- /dev/null +++ b/crates/kftray-tauri/src/commands/mod.rs @@ -0,0 +1,7 @@ +pub mod config; +pub mod config_state; +pub mod github; +pub mod httplogs; +pub mod kubecontext; +pub mod portforward; +pub mod window_state; diff --git a/crates/kftray-tauri/src/commands/portforward.rs b/crates/kftray-tauri/src/commands/portforward.rs new file mode 100644 index 00000000..a7cd2475 --- /dev/null +++ b/crates/kftray-tauri/src/commands/portforward.rs @@ -0,0 +1,135 @@ +use std::sync::Arc; + +use kftray_commons::config::get_configs; +use kftray_commons::models::config_model::Config; +use kftray_commons::models::response::CustomResponse; +use kftray_commons::utils::config_state::get_configs_state; +use kftray_portforward::core::{ + deploy_and_forward_pod, + start_port_forward, + stop_all_port_forward, + stop_port_forward, + stop_proxy_forward, +}; +use kftray_portforward::models::kube::HttpLogState; +use log::error; +use tauri::AppHandle; +use tauri::Manager; +use tokio::sync::Mutex; +use tokio::time::{ + interval, + Duration, +}; + +pub async fn check_and_emit_changes(app_handle: AppHandle) { + let mut interval = interval(Duration::from_millis(500)); + let previous_config_states = Arc::new(Mutex::new(Vec::new())); + let previous_configs = Arc::new(Mutex::new(Vec::new())); + + loop { + interval.tick().await; + + let current_config_states = match get_configs_state().await { + Ok(states) => states, + Err(e) => { + error!("Failed to get config states: {}", e); + continue; + } + }; + + let current_configs = match get_configs().await { + Ok(configs) => configs, + Err(e) => { + error!("Failed to get configs: {}", e); + continue; + } + }; + + let mut prev_states = previous_config_states.lock().await; + let mut prev_configs = previous_configs.lock().await; + + if !config_compare_changes(&prev_states, ¤t_config_states) + || !config_compare_changes(&prev_configs, ¤t_configs) + { + app_handle + .emit_all("config_state_changed", &Vec::::new()) + .unwrap_or_else(|e| { + error!("Failed to emit configs changed event: {}", e); + }); + + log::info!("Configs changed event emitted"); + + *prev_states = current_config_states; + *prev_configs = current_configs; + } + } +} + +fn config_compare_changes(prev: &[T], current: &[T]) -> bool { + if prev.len() != current.len() { + return false; + } + + for (prev_item, current_item) in prev.iter().zip(current.iter()) { + if prev_item != current_item { + return false; + } + } + + true +} + +#[tauri::command] +pub async fn start_port_forward_udp_cmd( + configs: Vec, http_log_state: tauri::State<'_, HttpLogState>, + _app_handle: tauri::AppHandle, +) -> Result, String> { + start_port_forward( + configs.clone(), + "udp", + Arc::new(http_log_state.inner().clone()), + ) + .await +} + +#[tauri::command] +pub async fn start_port_forward_tcp_cmd( + configs: Vec, http_log_state: tauri::State<'_, HttpLogState>, + _app_handle: tauri::AppHandle, +) -> Result, String> { + start_port_forward( + configs.clone(), + "tcp", + Arc::new(http_log_state.inner().clone()), + ) + .await +} + +#[tauri::command] +pub async fn stop_all_port_forward_cmd( + _app_handle: tauri::AppHandle, +) -> Result, String> { + stop_all_port_forward().await +} + +#[tauri::command] +pub async fn stop_port_forward_cmd( + config_id: String, _app_handle: tauri::AppHandle, +) -> Result { + stop_port_forward(config_id.clone()).await +} + +#[tauri::command] +pub async fn deploy_and_forward_pod_cmd( + configs: Vec, http_log_state: tauri::State<'_, HttpLogState>, + _app_handle: tauri::AppHandle, +) -> Result, String> { + deploy_and_forward_pod(configs.clone(), Arc::new(http_log_state.inner().clone())).await +} + +#[tauri::command] +pub async fn stop_proxy_forward_cmd( + config_id: String, namespace: &str, service_name: String, _app_handle: tauri::AppHandle, +) -> Result { + stop_proxy_forward(config_id.clone(), namespace, service_name).await +} diff --git a/crates/kftray-tauri/src/commands/window_state.rs b/crates/kftray-tauri/src/commands/window_state.rs new file mode 100644 index 00000000..6b7d674d --- /dev/null +++ b/crates/kftray-tauri/src/commands/window_state.rs @@ -0,0 +1,25 @@ +use std::sync::atomic::Ordering; + +use kftray_commons::models::window::AppState; +use kftray_commons::models::window::SaveDialogState; +use tauri::State; + +#[tauri::command] +pub fn open_save_dialog(state: State) { + state.is_open.store(true, Ordering::SeqCst); +} + +#[tauri::command] +pub fn close_save_dialog(state: State) { + state.is_open.store(false, Ordering::SeqCst); +} + +#[tauri::command] +pub fn toggle_pin_state(app_state: tauri::State, window: tauri::Window) { + let is_pinned = app_state.is_pinned.load(Ordering::SeqCst); + app_state.is_pinned.store(!is_pinned, Ordering::SeqCst); + + if let Err(e) = window.set_always_on_top(!is_pinned) { + eprintln!("Failed to toggle pin state: {:?}", e); + } +} diff --git a/crates/kftray-tauri/src/config.rs b/crates/kftray-tauri/src/config.rs deleted file mode 100644 index b871d098..00000000 --- a/crates/kftray-tauri/src/config.rs +++ /dev/null @@ -1,473 +0,0 @@ -use hostsfile::HostsBuilder; -use rusqlite::{ - params, - types::ToSql, - Connection, - Result, -}; -use serde_json::{ - json, - Value as JsonValue, -}; - -use crate::models::config::Config; -use crate::utils::config_dir::get_db_file_path; - -fn is_value_blank(value: &JsonValue) -> bool { - match value { - JsonValue::String(s) => s.trim().is_empty(), - _ => false, - } -} - -fn is_value_default(value: &serde_json::Value, default_config: &serde_json::Value) -> bool { - *value == *default_config -} - -fn remove_blank_or_default_fields(value: &mut JsonValue, default_config: &JsonValue) { - match value { - JsonValue::Object(map) => { - let keys_to_remove: Vec = map - .iter() - .filter(|(k, v)| { - let default_v = &default_config[k]; - is_value_blank(v) - || (default_v != &JsonValue::Array(vec![]) - && is_value_default(v, default_v)) - }) - .map(|(k, _)| k.clone()) - .collect(); - - for key in keys_to_remove { - map.remove(&key); - } - - for value in map.values_mut() { - remove_blank_or_default_fields(value, default_config); - } - } - JsonValue::Array(arr) => { - for value in arr { - remove_blank_or_default_fields(value, default_config); - } - } - _ => (), - } -} - -// function to delete a config from the database -#[tauri::command] - -pub async fn delete_config(id: i64) -> Result<(), String> { - println!("Deleting config with id: {}", id); - - let db_dir = get_db_file_path()?; - - let conn = match Connection::open(db_dir) { - Ok(conn) => conn, - Err(e) => return Err(format!("Failed to open database: {}", e)), - }; - - match conn.execute("DELETE FROM configs WHERE id=?1", params![id]) { - Ok(_) => Ok(()), - Err(e) => Err(format!("Failed to delete config: {}", e)), - } -} - -// function to delete multiple configs from the database -#[tauri::command] - -pub async fn delete_configs(ids: Vec) -> Result<(), String> { - println!("Deleting configs with ids: {:?}", ids); - - let db_dir = get_db_file_path()?; - - let mut conn = Connection::open(db_dir).map_err(|e| e.to_string())?; - - let transaction = conn.transaction().map_err(|e| e.to_string())?; - - for id in ids { - transaction - .execute("DELETE FROM configs WHERE id = ?1", params![id]) - .map_err(|e| format!("Failed to delete config with id {}: {}", id, e))?; - } - - transaction.commit().map_err(|e| e.to_string())?; - - Ok(()) -} - -// function to delete all configs from the database -#[tauri::command] - -pub async fn delete_all_configs() -> Result<(), String> { - println!("Deleting all configs"); - - let db_dir = get_db_file_path()?; - - let conn = Connection::open(db_dir).map_err(|e| e.to_string())?; - - match conn.execute("DELETE FROM configs", params![]) { - Ok(_) => Ok(()), - Err(e) => Err(format!("Failed to delete all configs: {}", e)), - } -} - -// function to insert a config into the database -#[tauri::command] - -pub fn insert_config(config: Config) -> Result<(), String> { - let db_dir = get_db_file_path()?; - - let conn = Connection::open(db_dir).map_err(|e| e.to_string())?; - - conn.execute( - "CREATE TABLE IF NOT EXISTS configs ( - id INTEGER PRIMARY KEY, - data TEXT NOT NULL - )", - params![], - ) - .map_err(|e| e.to_string())?; - - let data = json!(config).to_string(); - - conn.execute("INSERT INTO configs (data) VALUES (?1)", params![data]) - .map_err(|e| e.to_string())?; - - Ok(()) -} - -// function to read configs from the database -fn read_configs() -> Result, rusqlite::Error> { - let db_dir = get_db_file_path().map_err(|e| rusqlite::Error::InvalidPath(e.into()))?; - - let conn = Connection::open(db_dir)?; - - let mut stmt = conn.prepare("SELECT id, data FROM configs")?; - - let rows = stmt.query_map(params![], |row| { - let id: i64 = row.get(0)?; - - let data: String = row.get(1)?; - - let mut config: Config = - serde_json::from_str(&data).map_err(|_| rusqlite::Error::QueryReturnedNoRows)?; - - config.id = Some(id); - - Ok(config) - })?; - - let mut configs = Vec::new(); - - for row in rows { - configs.push(row?); - } - - println!("Reading configs {:?}", configs); - - Ok(configs) -} - -pub fn clean_all_custom_hosts_entries() -> Result<(), String> { - let configs = read_configs().map_err(|e| e.to_string())?; - - for config in configs { - let hostfile_comment = format!( - "kftray custom host for {} - {}", - config.service.unwrap_or_default(), - config.id.unwrap_or_default() - ); - - let hosts_builder = HostsBuilder::new(&hostfile_comment); - - hosts_builder.write().map_err(|e| { - format!( - "Failed to write to the hostfile for {}: {}", - hostfile_comment, e - ) - })?; - } - - Ok(()) -} - -// function to get all configs from the database -#[tauri::command] - -pub async fn get_configs() -> Result, String> { - println!("get_configs called"); - - let configs = read_configs().map_err(|e| e.to_string())?; - - println!("{:?}", configs); - - Ok(configs) -} - -// function to get a config from the database -#[tauri::command] - -pub async fn get_config(id: i64) -> Result { - println!("get_config called with id: {}", id); - - let db_dir = get_db_file_path()?; - - let conn = Connection::open(db_dir).map_err(|e| e.to_string())?; - - let mut stmt = conn - .prepare("SELECT id, data FROM configs WHERE id = ?1") - .map_err(|e| e.to_string())?; - - let mut rows = stmt - .query_map(params![id], |row| { - // For `row.get`, we directly use `rusqlite::Result` with `?`. - let _id: i64 = row.get(0)?; - - let data: String = row.get(1)?; - - // The error from `serde_json` is converted to a `rusqlite::Error` before using - // `?`. - let config: Config = serde_json::from_str(&data) - .map_err(|_e| rusqlite::Error::ExecuteReturnedResults)?; - - Ok(config) - }) - .map_err(|e| e.to_string())?; - - match rows.next() { - Some(row_result) => { - let mut config = row_result.map_err(|e| e.to_string())?; - - config.id = Some(id); - - println!("{:?}", config); - - Ok(config) - } - None => Err(format!("No config found with id: {}", id)), - } -} - -// function to update a config in the database -#[tauri::command] - -pub fn update_config(config: Config) -> Result<(), String> { - let db_dir = get_db_file_path()?; - - let conn = Connection::open(db_dir).map_err(|e| e.to_string())?; - - let data = json!(config).to_string(); - - conn.execute( - "UPDATE configs SET data = ?1 WHERE id = ?2", - params![data, config.id.unwrap()], - ) - .map_err(|e| e.to_string())?; - - Ok(()) -} - -// function to export configs to a json file -#[tauri::command] -pub async fn export_configs() -> Result { - let mut configs = read_configs().map_err(|e| e.to_string())?; - - for config in &mut configs { - config.id = None; // Ensure that the id is None before exporting - } - - let mut json_config = serde_json::to_value(configs).map_err(|e| e.to_string())?; - let default_config = serde_json::to_value(Config::default()).map_err(|e| e.to_string())?; - remove_blank_or_default_fields(&mut json_config, &default_config); - - let json = serde_json::to_string(&json_config).map_err(|e| e.to_string())?; - - Ok(json) -} - -// function to import configs from a json file -#[tauri::command] - -pub async fn import_configs(json: String) -> Result<(), String> { - match serde_json::from_str::>(&json) { - Ok(configs) => { - for config in configs { - insert_config(config).map_err(|e| format!("Failed to insert config: {}", e))?; - } - } - Err(_) => { - let config = serde_json::from_str::(&json) - .map_err(|e| format!("Failed to parse config: {}", e))?; - - insert_config(config).map_err(|e| format!("Failed to insert config: {}", e))?; - } - } - - if let Err(e) = migrate_configs() { - eprintln!("Error migrating configs: {}. Please check if the configurations are valid and compatible with the current system/version.", e); - - return Err(format!("Error migrating configs: {}", e)); - } - - Ok(()) -} - -pub fn migrate_configs() -> Result<(), String> { - let db_dir = get_db_file_path()?; - - let mut conn = Connection::open(db_dir).map_err(|e| e.to_string())?; - - let transaction = conn.transaction().map_err(|e| e.to_string())?; - - { - let mut stmt = transaction - .prepare("SELECT id, data FROM configs") - .map_err(|e| e.to_string())?; - - let rows = stmt - .query_map([], |row| { - Ok((row.get::<_, i64>(0)?, row.get::<_, String>(1)?)) - }) - .map_err(|e| e.to_string())? - .collect::, _>>() - .map_err(|e| e.to_string())?; - - for (id, data) in rows { - let config_json: JsonValue = serde_json::from_str(&data).map_err(|e| e.to_string())?; - - let default_config_json = - serde_json::to_value(Config::default()).map_err(|e| e.to_string())?; - - let merged_config_json = merge_json_values(default_config_json, config_json); - - let updated_data = - serde_json::to_string(&merged_config_json).map_err(|e| e.to_string())?; - - transaction - .execute( - "UPDATE configs SET data = ?1 WHERE id = ?2", - [&updated_data as &dyn ToSql, &id], - ) - .map_err(|e| e.to_string())?; - } - } - - transaction.commit().map_err(|e| e.to_string())?; - - Ok(()) -} - -fn merge_json_values(default: JsonValue, mut config: JsonValue) -> JsonValue { - match (&default, &mut config) { - (JsonValue::Object(default_map), JsonValue::Object(config_map)) => { - for (key, default_value) in default_map { - #[allow(clippy::redundant_pattern_matching)] - let should_replace = matches!(config_map.get(key), None); - - if should_replace { - config_map.insert(key.clone(), default_value.clone()); - - continue; - } - - config_map - .entry(key.clone()) - .and_modify(|e| *e = merge_json_values(default_value.clone(), e.clone())); - } - } - (JsonValue::Null, _) => return default, - _ => (), - } - - config -} - -#[cfg(test)] - -mod tests { - - use super::*; - - #[test] - - fn test_is_value_blank() { - assert!(is_value_blank(&json!(""))); - - assert!(!is_value_blank(&json!("not blank"))); - - assert!(!is_value_blank(&json!(0))); - - assert!(!is_value_blank(&json!(false))); - } - - // Test `remove_blank_fields` function - #[test] - fn test_remove_blank_or_default_fields() { - let mut obj = json!({ - "name": "Test", - "empty_string": " ", - "nested": { - "blank": "", - "non_blank": "value" - }, - "array": [ - { - "blank_field": " " - } - ] - }); - - let default_config = json!({ - "name": "", - "empty_string": "", - "nested": { - "blank": "", - "non_blank": "" - }, - "array": [ - { - "blank_field": "" - } - ] - }); - - remove_blank_or_default_fields(&mut obj, &default_config); - - assert!(obj.get("empty_string").is_none()); - - assert!(obj.get("nested").unwrap().get("blank").is_none()); - - assert_eq!( - obj.get("nested").unwrap().get("non_blank"), - Some(&json!("value")) - ); - - assert!(obj.get("array").unwrap()[0].get("blank_field").is_none()); - } - #[test] - - fn test_merge_json_values() { - let default = json!({ - "field1": "default value", - "field2": null, - "nested": { - "nested_field1": "nested default" - } - }); - - let to_merge = json!({ - "field2": "overridden value", - "nested": {} - }); - - let merged = merge_json_values(default, to_merge); - - assert_eq!(merged["field1"], "default value"); - - assert_eq!(merged["field2"], "overridden value"); - - assert_eq!(merged["nested"]["nested_field1"], "nested default"); - } -} diff --git a/crates/kftray-tauri/src/keychain.rs b/crates/kftray-tauri/src/keychain.rs deleted file mode 100644 index 73ac3a50..00000000 --- a/crates/kftray-tauri/src/keychain.rs +++ /dev/null @@ -1,166 +0,0 @@ -use keyring::{ - Entry, - Error as KeyringError, -}; -use tauri::{ - Error as TauriError, - InvokeError, -}; - -#[derive(Debug)] -pub enum CustomError { - Keyring(KeyringError), - Tauri(TauriError), -} - -// Define a custom error type that encapsulates errors from different sources. -impl From for CustomError { - fn from(error: KeyringError) -> Self { - CustomError::Keyring(error) - } -} - -// Implement conversion from `KeyringError` to `CustomError`. -impl From for CustomError { - fn from(error: TauriError) -> Self { - CustomError::Tauri(error) - } -} - -impl From for InvokeError { - fn from(error: CustomError) -> Self { - match error { - CustomError::Keyring(err) => InvokeError::from(err.to_string()), - CustomError::Tauri(err) => InvokeError::from(err.to_string()), - } - } -} - -/// Stores a key using the `keyring` crate. -#[tauri::command] -pub fn store_key( - service: &str, name: &str, password: &str, -) -> std::result::Result<(), CustomError> { - let entry = Entry::new(service, name).map_err(CustomError::from)?; - - entry.set_password(password).map_err(CustomError::from)?; - - Ok(()) -} - -/// Retrieves a key using the `keyring` crate. -#[tauri::command] -pub fn get_key(service: &str, name: &str) -> std::result::Result { - let entry = Entry::new(service, name).map_err(CustomError::from)?; - - let password = entry.get_password().map_err(CustomError::from)?; - - Ok(password) -} - -/// Deletes a key using the `keyring` crate. -#[tauri::command] -pub fn delete_key(service: &str, name: &str) -> std::result::Result<(), CustomError> { - let entry = Entry::new(service, name).map_err(CustomError::from)?; - - entry.delete_credential().map_err(CustomError::from)?; - - Ok(()) -} - -#[cfg(test)] - -mod tests { - - use super::*; - - const SERVICE: &str = "test_service"; - - const ACCOUNT: &str = "test_account"; - - const PASSWORD: &str = "test_password"; - - #[test] - - fn test_store_key_success() { - println!("Starting test_store_key_success"); - - let res = store_key(SERVICE, ACCOUNT, PASSWORD); - - assert!(res.is_ok()); - - println!("store_key succeeded"); - - let entry = Entry::new(SERVICE, ACCOUNT).unwrap(); - - let delete_result = entry.delete_credential(); - - println!("Tried to delete password: {:?}", delete_result); - - assert!(delete_result.is_ok()); - - println!("Password deleted successfully"); - } - - #[test] - - fn test_get_key_success() { - let entry = Entry::new(SERVICE, ACCOUNT).unwrap(); - - let _ = entry.set_password(PASSWORD); - - let res = get_key(SERVICE, ACCOUNT); - - assert!(res.is_ok()); - - assert_eq!(res.unwrap(), PASSWORD); - - let _ = entry.delete_credential(); - } - - #[test] - - fn test_delete_key_success() { - let entry = Entry::new(SERVICE, ACCOUNT).unwrap(); - - let _ = entry.set_password(PASSWORD); - - let res = delete_key(SERVICE, ACCOUNT); - - assert!(res.is_ok()); - - let res_after_deletion = entry.get_password(); - - assert!(res_after_deletion.is_err()); - } - - #[test] - - fn test_store_key_error() { - let invalid_service = ""; - - let res = store_key(invalid_service, ACCOUNT, PASSWORD); - - assert!(res.is_err()); - } - - #[test] - - fn test_get_key_error() { - let invalid_service = ""; - - let res = get_key(invalid_service, ACCOUNT); - - assert!(res.is_err()); - } - - #[test] - - fn test_delete_key_error() { - let invalid_service = ""; - - let res = delete_key(invalid_service, ACCOUNT); - - assert!(res.is_err()); - } -} diff --git a/crates/kftray-tauri/src/kubeforward/mod.rs b/crates/kftray-tauri/src/kubeforward/mod.rs deleted file mode 100644 index 901669e1..00000000 --- a/crates/kftray-tauri/src/kubeforward/mod.rs +++ /dev/null @@ -1,101 +0,0 @@ -pub mod commands; -pub mod kubecontext; -pub mod logging; -pub mod pod_finder; -pub mod pod_selection; -pub mod port_forward; - -use anyhow::Context; -pub use k8s_openapi::api::core::v1 as vx; -use k8s_openapi::apimachinery::pkg::util::intstr::IntOrString; -use kube::ResourceExt; -use vx::Pod; - -use crate::models::kube::{ - NameSpace, - Port, - Target, - TargetPod, - TargetSelector, -}; - -impl NameSpace { - /// Returns the configured namespace or the default. - - pub fn name_any(&self) -> String { - self.0.clone().unwrap_or_else(|| "default".to_string()) - } -} - -impl From for Port { - fn from(port: i32) -> Self { - Self::Number(port) - } -} - -impl From<&str> for Port { - fn from(port: &str) -> Self { - Self::Name(port.to_string()) - } -} - -impl From for Port { - fn from(port: IntOrString) -> Self { - match port { - IntOrString::Int(port) => Self::Number(port), - IntOrString::String(port) => Self::Name(port), - } - } -} - -impl TargetPod { - pub fn new(pod_name: String, port_number: i32) -> anyhow::Result { - let port_number = u16::try_from(port_number).context("Port not valid")?; - - Ok(Self { - pod_name, - port_number, - }) - } - - pub fn into_parts(self) -> (String, u16) { - (self.pod_name, self.port_number) - } -} - -impl Target { - pub fn new>, T: Into, P: Into>( - selector: TargetSelector, port: P, namespace: I, - ) -> Self { - Self { - selector, - port: port.into(), - namespace: NameSpace(namespace.into().map(Into::into)), - } - } - - pub fn find(&self, pod: &Pod, port: Option) -> anyhow::Result { - let port = match &port { - None => &self.port, - Some(port) => port, - }; - - TargetPod::new( - pod.name_any(), - match port { - Port::Number(port) => *port, - Port::Name(name) => { - let spec = pod.spec.as_ref().context("Pod Spec is None")?; - - let containers = &spec.containers; - - let mut ports = containers.iter().filter_map(|c| c.ports.as_ref()).flatten(); - - let port = ports.find(|p| p.name.as_ref() == Some(name)); - - port.context("Port not found")?.container_port - } - }, - ) - } -} diff --git a/crates/kftray-tauri/src/kubeforward/pod_selection.rs b/crates/kftray-tauri/src/kubeforward/pod_selection.rs deleted file mode 100644 index 56f504c6..00000000 --- a/crates/kftray-tauri/src/kubeforward/pod_selection.rs +++ /dev/null @@ -1,37 +0,0 @@ -use anyhow::Context; -use tracing::debug; - -use crate::{ - kubeforward::vx::Pod, - models::kube::{ - AnyReady, - PodSelection, - }, -}; - -impl PodSelection for AnyReady { - fn select<'p>(&self, pods: &'p [Pod], selector: &str) -> anyhow::Result<&'p Pod> { - // todo: randomly select from the ready pods - let pod = pods.iter().find(is_pod_ready).context(anyhow::anyhow!( - "No ready pods found matching the selector '{}'", - selector - ))?; - - Ok(pod) - } -} - -fn is_pod_ready(pod: &&Pod) -> bool { - let conditions = pod.status.as_ref().and_then(|s| s.conditions.as_ref()); - - let is_ready = conditions - .map(|c| c.iter().any(|c| c.type_ == "Ready" && c.status == "True")) - .unwrap_or(false); - - debug!( - "Pod: {}, is_ready: {}", - pod.metadata.name.clone().unwrap_or_default(), - is_ready - ); - is_ready -} diff --git a/crates/kftray-tauri/src/lib.rs b/crates/kftray-tauri/src/lib.rs new file mode 100644 index 00000000..6bdfd113 --- /dev/null +++ b/crates/kftray-tauri/src/lib.rs @@ -0,0 +1,5 @@ +pub mod commands; +pub use commands::*; +pub mod logging; +pub mod tray; +pub mod window; diff --git a/crates/kftray-tauri/src/logging.rs b/crates/kftray-tauri/src/logging.rs index 915f64b9..8f3ce96d 100644 --- a/crates/kftray-tauri/src/logging.rs +++ b/crates/kftray-tauri/src/logging.rs @@ -4,7 +4,7 @@ use std::{ io, }; -use crate::utils::config_dir::get_app_log_path; +use kftray_commons::utils::config_dir::get_app_log_path; pub fn setup_logging() -> Result<(), Box> { let log_filter = match env::var("RUST_LOG") { @@ -12,7 +12,6 @@ pub fn setup_logging() -> Result<(), Box> { Err(_) => log::LevelFilter::Off, }; - // If the `KFTRAY_DEBUG` environment variable is set, set up file-based logging. if env::var("KFTRAY_DEBUG").is_ok() { let log_path = get_app_log_path().map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; @@ -23,7 +22,6 @@ pub fn setup_logging() -> Result<(), Box> { std::fs::create_dir_all(log_dir) .map_err(|_| io::Error::new(io::ErrorKind::Other, "Could not create log directory"))?; - // Open the log file for appending, or return an error if it fails. let log_file = OpenOptions::new() .create(true) .append(true) diff --git a/crates/kftray-tauri/src/main.rs b/crates/kftray-tauri/src/main.rs index 0c9cd4aa..6d0e937f 100644 --- a/crates/kftray-tauri/src/main.rs +++ b/crates/kftray-tauri/src/main.rs @@ -8,30 +8,26 @@ use std::sync::{ Mutex, }; -use crate::utils::validate_configs::alert_multiple_configs; +use kftray_commons::utils::validate_configs::alert_multiple_configs; +use log::error; mod commands; -mod config; -mod db; -mod keychain; -mod kubeforward; mod logging; -mod models; -mod remote_config; mod tray; -mod utils; mod window; use std::sync::atomic::AtomicBool; use std::sync::atomic::Ordering; +use kftray_commons::models::window::AppState; +use kftray_commons::models::window::SaveDialogState; +use kftray_portforward::models::kube::HttpLogState; use tauri::{ GlobalShortcutManager, Manager, }; use tokio::runtime::Runtime; -use crate::models::kube::HttpLogState; -use crate::models::window::SaveDialogState; +use crate::commands::portforward::check_and_emit_changes; use crate::tray::{ create_tray_menu, handle_run_event, @@ -40,19 +36,11 @@ use crate::tray::{ }; use crate::window::toggle_window_visibility; -pub struct AppState { - pub is_moving: Arc>, - pub is_plugin_moving: Arc, - pub is_pinned: Arc, - pub runtime: Arc, -} - fn main() { let _ = logging::setup_logging(); let _ = fix_path_env::fix(); - // configure tray menu let system_tray = create_tray_menu(); let is_moving = Arc::new(Mutex::new(false)); let is_plugin_moving = Arc::new(AtomicBool::new(false)); @@ -72,16 +60,31 @@ fn main() { .setup(move |app| { let app_handle = app.app_handle(); + let app_handle_clone = app_handle.clone(); tauri::async_runtime::spawn(async move { - alert_multiple_configs(app_handle).await; + alert_multiple_configs(app_handle_clone).await; }); - let _ = config::clean_all_custom_hosts_entries(); - let _ = db::init(); + tauri::async_runtime::spawn(async move { + if let Err(e) = + kftray_commons::utils::config::clean_all_custom_hosts_entries().await + { + error!("Failed to clean custom hosts entries: {}", e); + } + + if let Err(e) = kftray_commons::utils::db::init().await { + error!("Failed to initialize database: {}", e); + } + + if let Err(e) = kftray_commons::utils::migration::migrate_configs().await { + error!("Failed to migrate configs: {}", e); + } + }); - if let Err(e) = config::migrate_configs() { - eprintln!("Failed to migrate configs: {}", e); - } + let app_handle_clone = app_handle.clone(); + tauri::async_runtime::spawn(async move { + check_and_emit_changes(app_handle_clone).await; + }); #[cfg(target_os = "macos")] { @@ -97,14 +100,13 @@ fn main() { window.set_always_on_top(true).unwrap(); } - // register global shortcut to open the app let mut shortcut = app.global_shortcut_manager(); shortcut .register("CmdOrCtrl+Shift+F1", move || { toggle_window_visibility(&window); }) - .unwrap_or_else(|err| println!("{:?}", err)); + .unwrap_or_else(|err| error!("{:?}", err)); Ok(()) }) @@ -113,37 +115,39 @@ fn main() { .on_system_tray_event(handle_system_tray_event) .on_window_event(handle_window_event) .invoke_handler(tauri::generate_handler![ - kubeforward::commands::start_port_forward_tcp, - kubeforward::commands::stop_port_forward, - kubeforward::commands::stop_all_port_forward, - kubeforward::kubecontext::list_kube_contexts, - kubeforward::kubecontext::list_namespaces, - kubeforward::kubecontext::list_services, - kubeforward::kubecontext::list_pods, - kubeforward::kubecontext::list_ports, - kubeforward::commands::deploy_and_forward_pod, - kubeforward::commands::stop_proxy_forward, - kubeforward::commands::set_http_logs, - kubeforward::commands::get_http_logs, - config::get_configs, - config::insert_config, - config::delete_config, - config::get_config, - config::update_config, - config::export_configs, - config::import_configs, - config::delete_configs, - config::delete_all_configs, - commands::open_save_dialog, - commands::close_save_dialog, - commands::import_configs_from_github, - commands::open_log_file, - commands::clear_http_logs, - commands::get_http_log_size, - keychain::store_key, - keychain::get_key, - keychain::delete_key, - window::toggle_pin_state + commands::portforward::start_port_forward_tcp_cmd, + commands::portforward::start_port_forward_udp_cmd, + commands::portforward::stop_port_forward_cmd, + commands::portforward::stop_all_port_forward_cmd, + commands::kubecontext::list_kube_contexts, + commands::kubecontext::list_namespaces, + commands::kubecontext::list_services, + commands::kubecontext::list_pods, + commands::kubecontext::list_ports, + commands::portforward::deploy_and_forward_pod_cmd, + commands::portforward::stop_proxy_forward_cmd, + commands::httplogs::set_http_logs_cmd, + commands::httplogs::get_http_logs_cmd, + commands::config::get_configs_cmd, + commands::config::insert_config_cmd, + commands::config::delete_config_cmd, + commands::config::get_config_cmd, + commands::config::update_config_cmd, + commands::config::export_configs_cmd, + commands::config::import_configs_cmd, + commands::config::delete_configs_cmd, + commands::config::delete_all_configs_cmd, + commands::window_state::open_save_dialog, + commands::window_state::close_save_dialog, + commands::github::import_configs_from_github, + commands::httplogs::open_log_file, + commands::httplogs::clear_http_logs, + commands::httplogs::get_http_log_size, + commands::github::store_key, + commands::github::get_key, + commands::github::delete_key, + commands::window_state::toggle_pin_state, + commands::config_state::get_config_states, ]) .build(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/crates/kftray-tauri/src/models/kube.rs b/crates/kftray-tauri/src/models/kube.rs deleted file mode 100644 index c9c1d929..00000000 --- a/crates/kftray-tauri/src/models/kube.rs +++ /dev/null @@ -1,124 +0,0 @@ -use std::collections::HashMap; -use std::sync::atomic::{ - AtomicBool, - Ordering, -}; -use std::sync::Arc; - -use k8s_openapi::apimachinery::pkg::util::intstr::IntOrString; -use kube::api::Api; -use serde::{ - Deserialize, - Serialize, -}; -use tokio::sync::Mutex; - -use crate::kubeforward::vx::{ - Pod, - Service, -}; - -#[derive(Serialize, Deserialize)] -pub struct KubeContextInfo { - pub name: String, -} - -#[derive(Serialize)] -pub struct KubeNamespaceInfo { - pub name: String, -} - -#[derive(Serialize)] -pub struct KubeServiceInfo { - pub name: String, -} - -#[derive(Serialize)] -pub struct KubeServicePortInfo { - pub name: Option, - pub port: Option, -} - -#[derive(Serialize, Debug)] -pub struct PodInfo { - pub labels_str: String, -} - -#[derive(Clone)] -#[allow(dead_code)] -pub struct PortForward { - pub target: Target, - pub local_port: Option, - pub local_address: Option, - pub pod_api: Api, - pub svc_api: Api, - pub context_name: Option, - pub config_id: i64, - pub workload_type: String, - pub connection: Arc>>, -} - -#[derive(Clone)] -pub enum TargetSelector { - ServiceName(String), - PodLabel(String), -} - -#[derive(Clone)] -pub enum Port { - Number(i32), - Name(String), -} - -#[derive(Clone)] -pub struct Target { - pub selector: TargetSelector, - pub port: Port, - pub namespace: NameSpace, -} - -#[derive(Clone)] -pub struct NameSpace(pub Option); - -#[derive(Clone)] -pub struct TargetPod { - pub pod_name: String, - pub port_number: u16, -} - -/// Pod selection according to impl specific criteria. -pub(crate) trait PodSelection { - fn select<'p>(&self, pods: &'p [Pod], selector: &str) -> anyhow::Result<&'p Pod>; -} - -/// Selects any pod so long as it's ready. -pub(crate) struct AnyReady {} - -#[derive(Clone, Debug)] -pub struct HttpLogState { - pub enable_http_logs: Arc>>, -} - -impl HttpLogState { - pub fn new() -> Self { - HttpLogState { - enable_http_logs: Arc::new(Mutex::new(HashMap::new())), - } - } - - pub async fn set_http_logs(&self, config_id: i64, enable: bool) { - let mut logs = self.enable_http_logs.lock().await; - logs.entry(config_id) - .or_insert_with(|| AtomicBool::new(enable)) - .store(enable, Ordering::SeqCst); - } - - pub async fn get_http_logs(&self, config_id: i64) -> bool { - let logs = self.enable_http_logs.lock().await; - if let Some(state) = logs.get(&config_id) { - state.load(Ordering::SeqCst) - } else { - false - } - } -} diff --git a/crates/kftray-tauri/src/models/mod.rs b/crates/kftray-tauri/src/models/mod.rs deleted file mode 100644 index be1aa69d..00000000 --- a/crates/kftray-tauri/src/models/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod config; -pub mod kube; -pub mod response; -pub mod window; diff --git a/crates/kftray-tauri/src/remote_config.rs b/crates/kftray-tauri/src/remote_config.rs deleted file mode 100644 index b0ca4012..00000000 --- a/crates/kftray-tauri/src/remote_config.rs +++ /dev/null @@ -1,59 +0,0 @@ -use rusqlite::Connection; - -use crate::utils::config_dir::get_db_file_path; - -// function to clear existing configs from the database -pub fn clear_existing_configs() -> Result<(), rusqlite::Error> { - let db_dir = get_db_file_path().map_err(|e| { - rusqlite::Error::InvalidPath(format!("Failed to get DB path: {}", e).into()) - })?; - - let conn = Connection::open(db_dir)?; - - conn.execute("DELETE FROM configs", ())?; - - Ok(()) -} - -// function to build the github api url -pub fn build_github_api_url(repo_url: &str, config_path: &str) -> String { - let base_api_url = "https://api.github.com/repos"; - - let url_parts: Vec<&str> = repo_url - .split('/') - .filter(|&x| !x.is_empty() && x != "https:" && x != "github.com") - .collect(); - - if url_parts.len() < 2 { - return "".to_string(); - } - - let owner = url_parts[0]; - - let repo = url_parts[1]; - - format!( - "{}/{}/{}/contents/{}", - base_api_url, owner, repo, config_path - ) -} - -#[cfg(test)] - -mod tests { - - use super::*; - - #[test] - - fn test_build_github_api_url() { - let repo_url = "https://github.com/exampleUser/exampleRepo"; - - let config_path = "path/to/config.json"; - - let expected_url = - "https://api.github.com/repos/exampleUser/exampleRepo/contents/path/to/config.json"; - - assert_eq!(build_github_api_url(repo_url, config_path), expected_url); - } -} diff --git a/crates/kftray-tauri/src/tray.rs b/crates/kftray-tauri/src/tray.rs index 051fbe7c..2400fccf 100644 --- a/crates/kftray-tauri/src/tray.rs +++ b/crates/kftray-tauri/src/tray.rs @@ -1,6 +1,13 @@ use std::sync::atomic::Ordering; use std::time::Duration; +use kftray_commons::models::window::AppState; +use kftray_commons::models::window::SaveDialogState; +use kftray_portforward::core; +use log::{ + error, + info, +}; use tauri::{ CustomMenuItem, GlobalWindowEvent, @@ -15,8 +22,6 @@ use tauri_plugin_positioner::Position; use tokio::runtime::Runtime; use tokio::time::sleep; -use crate::kubeforward::commands; -use crate::models::window::SaveDialogState; use crate::window::{ adjust_window_size_and_position, reset_window_position, @@ -25,7 +30,6 @@ use crate::window::{ set_window_position, toggle_window_visibility, }; -use crate::AppState; pub fn create_tray_menu() -> SystemTray { let quit = CustomMenuItem::new("quit".to_string(), "Quit").accelerator("CmdOrCtrl+Shift+Q"); @@ -88,7 +92,7 @@ pub fn handle_window_event(event: GlobalWindowEvent) { return; } - println!("event: {:?}", event.event()); + info!("event: {:?}", event.event()); let app_state = event.window().state::(); let mut is_moving = app_state.is_moving.lock().unwrap(); @@ -139,7 +143,7 @@ pub fn handle_window_event(event: GlobalWindowEvent) { }); if !*is_moving && !app_state.is_plugin_moving.load(Ordering::SeqCst) { - println!( + info!( "is_plugin_moving: {}", app_state.is_plugin_moving.load(Ordering::SeqCst) ); @@ -179,12 +183,12 @@ pub fn stop_all_port_forwards_and_exit(app_handle: &tauri::AppHandle) { let runtime = Runtime::new().expect("Failed to create a Tokio runtime"); runtime.block_on(async { - match commands::stop_all_port_forward().await { + match core::stop_all_port_forward().await { Ok(_) => { - println!("Successfully stopped all port forwards."); + info!("Successfully stopped all port forwards."); } Err(err) => { - eprintln!("Failed to stop port forwards: {}", err); + error!("Failed to stop port forwards: {}", err); } } }); diff --git a/crates/kftray-tauri/src/utils/mod.rs b/crates/kftray-tauri/src/utils/mod.rs deleted file mode 100644 index e8eaf902..00000000 --- a/crates/kftray-tauri/src/utils/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod config_dir; -pub mod validate_configs; diff --git a/crates/kftray-tauri/src/window.rs b/crates/kftray-tauri/src/window.rs index 20cbb434..23663822 100644 --- a/crates/kftray-tauri/src/window.rs +++ b/crates/kftray-tauri/src/window.rs @@ -3,6 +3,13 @@ use std::path::Path; use std::sync::atomic::Ordering; use std::time::Duration; +use kftray_commons::models::window::AppState; +use kftray_commons::models::window::WindowPosition; +use kftray_commons::utils::config_dir::get_window_state_path; +use log::{ + info, + warn, +}; use tauri::{ Manager, Window, @@ -13,11 +20,6 @@ use tauri_plugin_positioner::{ }; use tokio::time::sleep; -use crate::models::window::WindowPosition; -use crate::utils::config_dir::get_window_state_path; -use crate::AppState; - -/// Saves the current window position to the configuration file. pub fn save_window_position(window: &Window) { if let Ok(position) = window.outer_position() { let position_data = WindowPosition { @@ -30,21 +32,21 @@ pub fn save_window_position(window: &Window) { Ok(path) => { if let Some(parent_dir) = path.parent() { if let Err(e) = fs::create_dir_all(parent_dir) { - println!("Failed to create config directory: {}", e); + info!("Failed to create config directory: {}", e); return; } } if fs::write(&path, position_json).is_ok() { - println!("Window position saved: {:?}", position_data); + info!("Window position saved: {:?}", position_data); } else { - println!("Failed to save window position."); + info!("Failed to save window position."); } } - Err(err) => println!("Failed to get window state path: {}", err), + Err(err) => info!("Failed to get window state path: {}", err), } } else { - println!("Failed to get window position."); + info!("Failed to get window position."); } } @@ -54,7 +56,7 @@ pub fn load_window_position() -> Option { match fs::read_to_string(&home_path) { Ok(position_json) => match serde_json::from_str(&position_json) { Ok(position) => { - println!( + info!( "Window position loaded from home directory: {:?}", home_path ); @@ -71,19 +73,19 @@ pub fn load_window_position() -> Option { } } } else { - println!("No window position file found."); + info!("No window position file found."); None } } else { - println!("Could not determine window state path."); + info!("Could not determine window state path."); None } } fn handle_corrupted_file(path: &Path, error: impl std::fmt::Display) { - eprintln!("Failed to parse window position JSON: {}", error); + warn!("Failed to parse window position JSON: {}", error); if let Err(delete_err) = fs::remove_file(path) { - eprintln!( + warn!( "Failed to delete corrupted window position file: {}", delete_err ); @@ -151,13 +153,13 @@ pub fn reset_to_default_position(window: &Window) { #[cfg(target_os = "linux")] { if let Err(e) = window.move_window(Position::Center) { - eprintln!("Failed to move window to center: {}", e); + warn!("Failed to move window to center: {}", e); } } #[cfg(not(target_os = "linux"))] { if let Err(e) = window.move_window(Position::TrayCenter) { - eprintln!("Failed to move window to tray center: {}", e); + warn!("Failed to move window to tray center: {}", e); } } @@ -177,21 +179,21 @@ fn remove_position_file() { if let Ok(path) = get_window_state_path() { if path.exists() { if let Err(e) = fs::remove_file(&path) { - eprintln!("Failed to delete window position file: {}", e); + warn!("Failed to delete window position file: {}", e); } else { - println!("Window position file deleted successfully."); + info!("Window position file deleted successfully."); } } else { - println!("No window position file found to delete."); + info!("No window position file found to delete."); } } else { - println!("Could not determine window state path."); + info!("Could not determine window state path."); } } pub fn set_window_position(window: &Window, position: Position) { if let Err(e) = window.move_window(position) { - eprintln!("Failed to move window: {}", e); + warn!("Failed to move window: {}", e); } } @@ -220,10 +222,3 @@ pub fn adjust_window_size_and_position( reset_plugin_moving_state_after_delay(&app_state); } - -#[tauri::command] -pub fn toggle_pin_state(app_state: tauri::State, window: tauri::Window) { - let is_pinned = app_state.is_pinned.load(Ordering::SeqCst); - app_state.is_pinned.store(!is_pinned, Ordering::SeqCst); - window.set_always_on_top(!is_pinned).unwrap(); -} diff --git a/crates/kftray-tauri/tauri.conf.json b/crates/kftray-tauri/tauri.conf.json index a4ec7aeb..b9240647 100644 --- a/crates/kftray-tauri/tauri.conf.json +++ b/crates/kftray-tauri/tauri.conf.json @@ -8,7 +8,7 @@ }, "package": { "productName": "kftray", - "version": "0.12.2" + "version": "0.13.0" }, "tauri": { "allowlist": { @@ -33,6 +33,13 @@ "icons/icon.ico" ], "identifier": "com.hcavarsan.kftray", + "macOS": { + "entitlements": "./entitlements.plist", + "exceptionDomain": "", + "frameworks": [], + "providerShortName": null, + "signingIdentity": null + }, "resources": [], "shortDescription": "", "targets": [ @@ -48,13 +55,6 @@ "certificateThumbprint": null, "digestAlgorithm": "sha256", "timestampUrl": "" - }, - "macOS": { - "entitlements": "./entitlements.plist", - "exceptionDomain": "", - "frameworks": [], - "providerShortName": null, - "signingIdentity": null } }, "macOSPrivateApi": true, diff --git a/crates/kftui/.gitignore b/crates/kftui/.gitignore new file mode 100644 index 00000000..9bc2171e --- /dev/null +++ b/crates/kftui/.gitignore @@ -0,0 +1,5 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ + + diff --git a/crates/kftui/Cargo.toml b/crates/kftui/Cargo.toml new file mode 100644 index 00000000..4c77a6db --- /dev/null +++ b/crates/kftui/Cargo.toml @@ -0,0 +1,66 @@ +[package] +name = "kftui" +version = "0.13.0" +description = "KFtray TUI is a Rust application that manage multiple kubectl port forward configurations" +authors = [ + "Henrique Cavarsan ", +] +license = "MIT" +homepage = "https://kftray.app" +repository = "https://github.com/hcavarsan/kftray" +edition = "2021" +build = "build.rs" + + +[dependencies] + +serde_json = "1.0.120" +tauri = { version = "1.6", default-features = false, features = [ + "updater", + "api-all", + "macos-private-api", + "system-tray", + "icon-png", + "devtools", +] } +tokio = { version = "1.39.1", features = ["rt-multi-thread", "macros", "full"] } +sqlx = { version = "0.8.0", features = ["sqlite", "runtime-tokio-native-tls"] } +dirs = "5.0.1" +base64 = "0.22.1" +hostsfile = { git = "https://github.com/tonarino/innernet", branch = "main" } +log = "0.4" +kube = { version = "0.93.1", features = [ + "client", + "config", + "rustls-tls", + "ws", +] } +k8s-openapi = { version = "0.22.0", default-features = false, features = [ + "latest", +] } +tokio-stream = { version = "0.1.15", features = ["net"] } +futures = "0.3.30" +anyhow = "1.0.86" +tracing = "0.1.40" +hyper = { version = "1.3.1", features = ["client", "http1", "http2"] } +hyper-util = { version = "0.1.6", features = ["client-legacy", "http1", "tokio"] } +lazy_static = "1.5.0" +serde = { version = "1.0", features = ["derive"] } +rand = "0.8.5" +kube-runtime = "0.93.1" +tower = "0.4.13" +whoami = "1.5.1" +tempfile = "3.9" +h2 = { optional = true, version = "0.4.5" } +libc = "0.2" +kftray-commons = { path = "../kftray-commons" } +kftray-portforward = { path = "../kftray-portforward" } +ratatui = { version = "0.26", features = ["unstable-widget-ref"] } +crossterm = { version = "0.27", optional = false } +ratatui-explorer = "0.1.1" +once_cell = "1.10" +built = "0.7.4" + + +[build-dependencies] +built = "0.7" diff --git a/crates/kftui/build.rs b/crates/kftui/build.rs new file mode 100644 index 00000000..4306bb78 --- /dev/null +++ b/crates/kftui/build.rs @@ -0,0 +1,3 @@ +fn main() { + built::write_built_file().expect("Failed to acquire build-time information") +} diff --git a/crates/kftui/entitlements.plist b/crates/kftui/entitlements.plist new file mode 100644 index 00000000..c863866e --- /dev/null +++ b/crates/kftui/entitlements.plist @@ -0,0 +1,16 @@ + + + + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.allow-jit + + com.apple.security.cs.disable-library-validation + + com.apple.security.hypervisor + + com.apple.security.virtualization + + + diff --git a/crates/kftui/src/.gitignore b/crates/kftui/src/.gitignore new file mode 100644 index 00000000..4302748f --- /dev/null +++ b/crates/kftui/src/.gitignore @@ -0,0 +1,81 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ + + +use std::sync::Arc; + +use kftray_commons::models::config_model::Config; +use kftray_portforward::core::{ + deploy_and_forward_pod, + start_port_forward, + stop_port_forward, + stop_proxy_forward, +}; +use kftray_portforward::models::kube::HttpLogState; +use log::error; + +use crate::tui::input::App; + +pub async fn start_port_forwarding(app: &mut App, config: Config) { + match config.workload_type.as_str() { + "proxy" => { + if let Err(e) = + deploy_and_forward_pod(vec![config.clone()], Arc::new(HttpLogState::new())).await + { + error!("Failed to start proxy forward: {:?}", e); + app.error_message = Some(format!("Failed to start proxy forward: {:?}", e)); + app.show_error_popup = true; + } + } + "service" | "pod" => match config.protocol.as_str() { + "tcp" => { + let log_state = Arc::new(HttpLogState::new()); + let result = start_port_forward(vec![config.clone()], "tcp", log_state).await; + if let Err(e) = result { + error!("Failed to start TCP port forward: {:?}", e); + app.error_message = Some(format!("Failed to start TCP port forward: {:?}", e)); + app.show_error_popup = true; + } + } + "udp" => { + let result = + deploy_and_forward_pod(vec![config.clone()], Arc::new(HttpLogState::new())) + .await; + if let Err(e) = result { + error!("Failed to start UDP port forward: {:?}", e); + app.error_message = Some(format!("Failed to start UDP port forward: {:?}", e)); + app.show_error_popup = true; + } + } + _ => {} + }, + _ => {} + } +} + +pub async fn stop_port_forwarding(app: &mut App, config: Config) { + match config.workload_type.as_str() { + "proxy" => { + if let Err(e) = stop_proxy_forward( + config.id.unwrap_or_default().to_string(), + &config.namespace, + config.service.clone().unwrap_or_default(), + ) + .await + { + error!("Failed to stop proxy forward: {:?}", e); + app.error_message = Some(format!("Failed to stop proxy forward: {:?}", e)); + app.show_error_popup = true; + } + } + "service" | "pod" => { + if let Err(e) = stop_port_forward(config.id.unwrap_or_default().to_string()).await { + error!("Failed to stop port forward: {:?}", e); + app.error_message = Some(format!("Failed to stop port forward: {:?}", e)); + app.show_error_popup = true; + } + } + _ => {} + } +} diff --git a/crates/kftui/src/core/logging.rs b/crates/kftui/src/core/logging.rs new file mode 100644 index 00000000..e6f66a87 --- /dev/null +++ b/crates/kftui/src/core/logging.rs @@ -0,0 +1,68 @@ +use std::fs::OpenOptions; +use std::io::Write; +use std::sync::{ + Arc, + Mutex, +}; + +use log::{ + Metadata, + Record, +}; +use once_cell::sync::Lazy; + +pub struct AppLogger { + pub buffer: Arc>, + pub file: Arc>, +} + +impl AppLogger { + fn new(buffer: Arc>, file: Arc>) -> Self { + AppLogger { buffer, file } + } +} + +impl log::Log for AppLogger { + fn enabled(&self, metadata: &Metadata) -> bool { + metadata.level() <= log::max_level() + } + + fn log(&self, record: &Record) { + if self.enabled(record.metadata()) { + let target = record.target(); + if target.starts_with("sqlx") { + return; + } + + let log_entry = format!("{}\n", record.args()); + + { + let mut buffer = self.buffer.lock().unwrap(); + buffer.push_str(&log_entry); + } + + { + let mut file = self.file.lock().unwrap(); + if let Err(_e) = file.write_all(log_entry.as_bytes()) {} + } + } + } + + fn flush(&self) {} +} + +pub static LOGGER: Lazy = Lazy::new(|| { + let buffer = Arc::new(Mutex::new(String::new())); + let file = Arc::new(Mutex::new( + OpenOptions::new() + .create(true) + .append(true) + .open("app.log") + .unwrap(), + )); + AppLogger::new(buffer, file) +}); + +pub fn init_logger() -> Result<(), Box> { + Ok(log::set_logger(&*LOGGER).map(|()| log::set_max_level(log::LevelFilter::Trace))?) +} diff --git a/crates/kftui/src/core/mod.rs b/crates/kftui/src/core/mod.rs new file mode 100644 index 00000000..7dd150a2 --- /dev/null +++ b/crates/kftui/src/core/mod.rs @@ -0,0 +1,6 @@ +pub mod logging; +pub mod port_forward; + +pub mod built_info { + include!(concat!(env!("OUT_DIR"), "/built.rs")); +} diff --git a/crates/kftui/src/core/port_forward.rs b/crates/kftui/src/core/port_forward.rs new file mode 100644 index 00000000..b0eb493a --- /dev/null +++ b/crates/kftui/src/core/port_forward.rs @@ -0,0 +1,116 @@ +use std::io::Write; +use std::sync::Arc; + +use crossterm::{ + cursor::Show, + execute, + terminal::{ + disable_raw_mode, + LeaveAlternateScreen, + }, +}; +use kftray_commons::models::config_model::Config; +use kftray_portforward::core::stop_all_port_forward; +use kftray_portforward::core::{ + deploy_and_forward_pod, + start_port_forward, + stop_port_forward, + stop_proxy_forward, +}; +use kftray_portforward::models::kube::HttpLogState; +use log::error; + +use crate::tui::input::{ + App, + AppState, +}; + +pub async fn start_port_forwarding(app: &mut App, config: Config) { + match config.workload_type.as_str() { + "proxy" => { + if let Err(e) = + deploy_and_forward_pod(vec![config.clone()], Arc::new(HttpLogState::new())).await + { + error!("Failed to start proxy forward: {:?}", e); + app.error_message = Some(format!("Failed to start proxy forward: {:?}", e)); + app.state = AppState::ShowErrorPopup; + } + } + "service" | "pod" => match config.protocol.as_str() { + "tcp" => { + let log_state = Arc::new(HttpLogState::new()); + let result = start_port_forward(vec![config.clone()], "tcp", log_state).await; + if let Err(e) = result { + error!("Failed to start TCP port forward: {:?}", e); + app.error_message = Some(format!("Failed to start TCP port forward: {:?}", e)); + app.state = AppState::ShowErrorPopup; + } + } + "udp" => { + let result = + deploy_and_forward_pod(vec![config.clone()], Arc::new(HttpLogState::new())) + .await; + if let Err(e) = result { + error!("Failed to start UDP port forward: {:?}", e); + app.error_message = Some(format!("Failed to start UDP port forward: {:?}", e)); + app.state = AppState::ShowErrorPopup; + } + } + _ => {} + }, + _ => {} + } +} + +pub async fn stop_port_forwarding(app: &mut App, config: Config) { + match config.workload_type.as_str() { + "proxy" => { + if let Err(e) = stop_proxy_forward( + config.id.unwrap_or_default().to_string(), + &config.namespace, + config.service.clone().unwrap_or_default(), + ) + .await + { + error!("Failed to stop proxy forward: {:?}", e); + app.error_message = Some(format!("Failed to stop proxy forward: {:?}", e)); + app.state = AppState::ShowErrorPopup; + } + } + "service" | "pod" => { + if let Err(e) = stop_port_forward(config.id.unwrap_or_default().to_string()).await { + error!("Failed to stop port forward: {:?}", e); + app.error_message = Some(format!("Failed to stop port forward: {:?}", e)); + app.state = AppState::ShowErrorPopup; + } + } + _ => {} + } +} + +pub async fn stop_all_port_forward_and_exit(app: &mut App) { + log::info!("Stopping all port forwards..."); + match stop_all_port_forward().await { + Ok(responses) => { + for response in responses { + if response.status != 0 { + error!("Error stopping port forward: {:?}", response.stderr); + } + } + } + Err(e) => { + error!("Failed to stop all port forwards: {:?}", e); + app.error_message = Some(format!("Failed to stop all port forwards: {:?}", e)); + app.state = AppState::ShowErrorPopup; + } + } + log::info!("Exiting application..."); + + // Cleanup terminal + disable_raw_mode().expect("Failed to disable raw mode"); + execute!(std::io::stdout(), LeaveAlternateScreen, Show) + .expect("Failed to leave alternate screen and show cursor"); + std::io::stdout().flush().expect("Failed to flush stdout"); + + std::process::exit(0); +} diff --git a/crates/kftui/src/main.rs b/crates/kftui/src/main.rs new file mode 100644 index 00000000..66b7e5c3 --- /dev/null +++ b/crates/kftui/src/main.rs @@ -0,0 +1,13 @@ +mod core; +mod tui; +mod utils; + +use core::logging::init_logger; + +use tui::run_tui; + +#[tokio::main] +async fn main() -> Result<(), Box> { + init_logger()?; + run_tui().await +} diff --git a/crates/kftui/src/tui/app.rs b/crates/kftui/src/tui/app.rs new file mode 100644 index 00000000..3a046ede --- /dev/null +++ b/crates/kftui/src/tui/app.rs @@ -0,0 +1,78 @@ +use std::io; + +use crossterm::{ + execute, + terminal::{ + disable_raw_mode, + enable_raw_mode, + EnterAlternateScreen, + LeaveAlternateScreen, + }, +}; +use kftray_commons::config::read_configs; +use kftray_commons::utils::config_state::read_config_states; +use kftray_commons::utils::db::init; +use kftray_commons::utils::migration::migrate_configs; +use log::{ + error, + info, +}; +use ratatui::{ + backend::CrosstermBackend, + Terminal, +}; + +use crate::tui::input::{ + handle_input, + App, +}; +use crate::tui::ui::draw_ui; + +pub async fn run_tui() -> Result<(), Box> { + init().await?; + + if let Err(e) = migrate_configs().await { + error!("Failed to migrate configs: {}", e); + } + + enable_raw_mode()?; + let mut stdout = io::stdout(); + execute!(stdout, EnterAlternateScreen)?; + let backend = CrosstermBackend::new(stdout); + let mut terminal = Terminal::new(backend)?; + + let mut app = App::new(); + + let res = run_app(&mut terminal, &mut app).await; + + disable_raw_mode()?; + execute!(terminal.backend_mut(), LeaveAlternateScreen)?; + terminal.show_cursor()?; + + if let Err(err) = res { + error!("{:?}", err); + } + + info!("run_tui completed."); + Ok(()) +} + +async fn run_app( + terminal: &mut Terminal, app: &mut App, +) -> io::Result<()> { + loop { + let configs = read_configs().await.unwrap_or_default(); + let mut config_states = read_config_states().await.unwrap_or_default(); + + app.update_configs(&configs, &config_states); + + terminal.draw(|f| { + draw_ui(f, app, &config_states); + })?; + + if handle_input(app, &mut config_states).await? { + break; + } + } + Ok(()) +} diff --git a/crates/kftui/src/tui/input/file_explorer.rs b/crates/kftui/src/tui/input/file_explorer.rs new file mode 100644 index 00000000..8883e636 --- /dev/null +++ b/crates/kftui/src/tui/input/file_explorer.rs @@ -0,0 +1,247 @@ +use std::path::Path; + +use crossterm::event::{ + Event, + KeyCode, + KeyEvent, + KeyModifiers, +}; +use ratatui_explorer::Input; + +use crate::tui::input::App; +use crate::tui::input::AppState; +use crate::utils::config::{ + export_configs_to_file, + import_configs_from_file, +}; +use crate::utils::file::get_file_content; + +async fn handle_file_selection(app: &mut App, selected_path: &Path) -> Result<(), std::io::Error> { + log::info!("Selected path: {:?}", selected_path); + + if selected_path.is_file() { + if selected_path.extension().and_then(|s| s.to_str()) == Some("json") { + match get_file_content(selected_path) { + Ok(content) => app.file_content = Some(content), + Err(e) => handle_file_error(app, e), + } + } else { + app.file_content = None; + } + } else { + app.file_content = None; + } + Ok(()) +} + +fn handle_file_error(app: &mut App, error: std::io::Error) { + let error_message = format!("Failed to read file content: {}", error); + app.file_content = None; + app.import_export_message = Some(error_message.clone()); + app.error_message = Some(error_message); + app.state = AppState::ShowErrorPopup; +} + +async fn handle_import(app: &mut App, selected_path: &Path) -> Result<(), std::io::Error> { + if selected_path.is_file() { + match import_configs_from_file(selected_path.to_str().unwrap()).await { + Ok(_) => show_confirmation_popup(app, "Import successful".to_string()), + Err(e) => show_error_popup(app, format!("Import failed: {}", e)), + } + } else { + show_error_popup(app, "Selected file is not a JSON file".to_string()); + } + Ok(()) +} + +async fn handle_export(app: &mut App, export_path: &Path) -> Result<(), std::io::Error> { + log::info!("Starting export of configs to file: {:?}", export_path); + + match export_configs_to_file(export_path.to_str().unwrap()).await { + Ok(_) => { + log::info!("Export successful"); + + show_confirmation_popup(app, format!("Export successful: {:?}", export_path)); + } + Err(e) => { + log::error!("Export failed: {}", e); + show_error_popup(app, format!("Export failed: {:?}", e)); + } + } + Ok(()) +} + +fn show_confirmation_popup(app: &mut App, message: String) { + app.import_export_message = Some(message); + app.state = AppState::ShowConfirmationPopup; + log::info!("State changed to ShowConfirmationPopup"); +} + +fn show_error_popup(app: &mut App, message: String) { + app.import_export_message = Some(message.clone()); + app.error_message = Some(message); + app.state = AppState::ShowErrorPopup; + log::info!("State changed to ShowErrorPopup"); +} + +pub async fn handle_import_file_explorer_input( + app: &mut App, key: KeyCode, +) -> Result<(), std::io::Error> { + let key_event = KeyEvent::new(key, KeyModifiers::NONE); + + if key == KeyCode::Enter { + handle_import_enter_key(app).await?; + return Ok(()); + } + + app.import_file_explorer + .handle(Input::from(&Event::Key(key_event)))?; + + match key { + KeyCode::Esc => close_import_file_explorer(app), + KeyCode::Backspace => navigate_to_parent_directory(app), + _ => handle_file_selection_key(app).await?, + } + Ok(()) +} + +async fn handle_import_enter_key(app: &mut App) -> Result<(), std::io::Error> { + if let Some(selected_path) = app + .import_file_explorer + .files() + .get(app.import_file_explorer.selected_idx()) + .map(|f| f.path().clone()) + { + if selected_path.is_dir() { + app.import_file_explorer + .set_cwd(selected_path.clone()) + .unwrap(); + } else if selected_path.extension().and_then(|s| s.to_str()) == Some("json") { + handle_import(app, &selected_path).await?; + } else { + show_error_popup(app, "Selected file is not a JSON file".to_string()); + } + } + Ok(()) +} + +fn navigate_to_parent_directory(app: &mut App) { + if let Some(parent_path) = app.import_file_explorer.cwd().parent() { + log::info!("Navigating to parent directory: {:?}", parent_path); + app.import_file_explorer + .set_cwd(parent_path.to_path_buf()) + .unwrap(); + } +} + +async fn handle_file_selection_key(app: &mut App) -> Result<(), std::io::Error> { + if let Some(selected_path) = app + .import_file_explorer + .files() + .get(app.import_file_explorer.selected_idx()) + .map(|f| f.path().clone()) + { + handle_file_selection(app, &selected_path).await?; + } + Ok(()) +} + +pub async fn handle_export_file_explorer_input( + app: &mut App, key: KeyCode, +) -> Result<(), std::io::Error> { + let key_event = KeyEvent::new(key, KeyModifiers::NONE); + + if key == KeyCode::Enter || key == KeyCode::Char(' ') { + handle_export_enter_key(app).await?; + return Ok(()); + } + + app.export_file_explorer + .handle(Input::from(&Event::Key(key_event)))?; + + match app.state { + AppState::ShowInputPrompt => handle_export_input_prompt(app, key).await?, + _ => match key { + KeyCode::Esc => close_export_file_explorer(app), + KeyCode::Backspace => navigate_to_parent_directory(app), + _ => log::info!("Unhandled key: {:?}", key), + }, + } + Ok(()) +} + +async fn handle_export_enter_key(app: &mut App) -> Result<(), std::io::Error> { + if let Some(selected_file) = app + .export_file_explorer + .files() + .get(app.export_file_explorer.selected_idx()) + { + let selected_path = selected_file.path().clone(); + log::info!("Selected path: {:?}", selected_path); + + if selected_file.is_dir() { + log::info!("Changed working directory to: {:?}", selected_path); + app.selected_file_path = Some(selected_path.clone()); + app.state = AppState::ShowInputPrompt; + } + } else { + log::warn!("No file selected"); + } + Ok(()) +} + +pub async fn handle_export_input_prompt(app: &mut App, key: KeyCode) -> Result<(), std::io::Error> { + log::info!("Handling input prompt key: {:?}", key); + + match key { + KeyCode::Enter => handle_export_enter_key_press(app).await?, + KeyCode::Char(c) => update_input_buffer(app, c), + KeyCode::Backspace => remove_last_char_from_input_buffer(app), + KeyCode::Esc => cancel_input_prompt(app), + _ => log::info!("Unhandled key in input prompt: {:?}", key), + } + Ok(()) +} + +async fn handle_export_enter_key_press(app: &mut App) -> Result<(), std::io::Error> { + if let Some(selected_file_path) = &app.selected_file_path { + let export_path = selected_file_path.join(&app.input_buffer); + log::info!("Export path: {:?}", export_path); + handle_export(app, &export_path).await?; + app.input_buffer.clear(); + } else { + log::error!("No selected file path for export"); + } + app.input_buffer.clear(); + Ok(()) +} + +fn update_input_buffer(app: &mut App, c: char) { + app.input_buffer.push(c); + log::info!("Input buffer updated: {}", app.input_buffer); +} + +fn remove_last_char_from_input_buffer(app: &mut App) { + app.input_buffer.pop(); + log::info!("Input buffer updated: {}", app.input_buffer); +} + +fn cancel_input_prompt(app: &mut App) { + app.state = AppState::Normal; + app.input_buffer.clear(); + log::info!("Input prompt canceled"); +} + +fn close_import_file_explorer(app: &mut App) { + log::info!("File explorer closed"); + app.state = AppState::Normal; + app.file_content = None; + app.selected_file_path = std::env::current_dir().ok(); +} + +fn close_export_file_explorer(app: &mut App) { + log::info!("File explorer closed"); + app.state = AppState::Normal; + app.file_content = None; + app.selected_file_path = std::env::current_dir().ok(); +} diff --git a/crates/kftui/src/tui/input/mod.rs b/crates/kftui/src/tui/input/mod.rs new file mode 100644 index 00000000..d2bd2e3b --- /dev/null +++ b/crates/kftui/src/tui/input/mod.rs @@ -0,0 +1,450 @@ +mod file_explorer; +mod navigation; +mod popup; +use std::collections::HashSet; +use std::io; +use std::sync::{ + Arc, + Mutex, +}; + +use crossterm::event::{ + self, + Event, + KeyCode, + KeyModifiers, +}; +use crossterm::terminal::size; +pub use file_explorer::*; +use kftray_commons::models::{ + config_model::Config, + config_state_model::ConfigState, +}; +pub use popup::*; +use ratatui::widgets::TableState; +use ratatui_explorer::{ + FileExplorer, + Theme, +}; + +use crate::core::logging::LOGGER; +use crate::core::port_forward::stop_all_port_forward_and_exit; +use crate::tui::input::navigation::handle_port_forward; + +#[derive(PartialEq, Clone, Copy)] +pub enum DeleteButton { + Confirm, + Close, +} + +#[derive(PartialEq, Clone, Copy)] +pub enum ActiveComponent { + Menu, + Table, +} + +#[derive(PartialEq)] +pub enum ActiveTable { + Stopped, + Running, +} + +#[derive(PartialEq)] +pub enum AppState { + Normal, + ShowErrorPopup, + ShowConfirmationPopup, + ImportFileExplorerOpen, + ExportFileExplorerOpen, + ShowInputPrompt, + ShowHelp, + ShowAbout, + ShowDeleteConfirmation, +} + +pub struct App { + pub selected_rows_stopped: HashSet, + pub selected_rows_running: HashSet, + pub import_file_explorer: FileExplorer, + pub export_file_explorer: FileExplorer, + pub state: AppState, + pub selected_row_stopped: usize, + pub selected_row_running: usize, + pub active_table: ActiveTable, + pub import_export_message: Option, + pub input_buffer: String, + pub selected_file_path: Option, + pub file_content: Option, + pub stopped_configs: Vec, + pub running_configs: Vec, + pub stdout_output: Arc>, + pub error_message: Option, + pub active_component: ActiveComponent, + pub selected_menu_item: usize, + pub delete_confirmation_message: Option, + pub selected_delete_button: DeleteButton, + pub visible_rows: usize, + pub table_state_stopped: TableState, + pub table_state_running: TableState, +} + +impl App { + pub fn new() -> Self { + let theme = Theme::default().add_default_title(); + let import_file_explorer = FileExplorer::with_theme(theme.clone()).unwrap(); + let export_file_explorer = FileExplorer::with_theme(theme).unwrap(); + let stdout_output = LOGGER.buffer.clone(); + + let mut app = Self { + import_file_explorer, + export_file_explorer, + state: AppState::Normal, + selected_row_stopped: 0, + selected_row_running: 0, + active_table: ActiveTable::Stopped, + selected_rows_stopped: HashSet::new(), + selected_rows_running: HashSet::new(), + import_export_message: None, + input_buffer: String::new(), + selected_file_path: None, + file_content: None, + stopped_configs: Vec::new(), + running_configs: Vec::new(), + stdout_output, + error_message: None, + active_component: ActiveComponent::Table, + selected_menu_item: 0, + delete_confirmation_message: None, + selected_delete_button: DeleteButton::Confirm, + visible_rows: 0, + table_state_stopped: TableState::default(), + table_state_running: TableState::default(), + }; + + if let Ok((_, height)) = size() { + app.update_visible_rows(height); + } + + app + } + + pub fn update_visible_rows(&mut self, terminal_height: u16) { + self.visible_rows = (terminal_height.saturating_sub(19)) as usize; + } + + pub fn update_configs(&mut self, configs: &[Config], config_states: &[ConfigState]) { + self.stopped_configs = configs + .iter() + .filter(|config| { + config_states + .iter() + .find(|state| state.config_id == config.id.unwrap_or_default()) + .map(|state| !state.is_running) + .unwrap_or(true) + }) + .cloned() + .collect(); + + self.running_configs = configs + .iter() + .filter(|config| { + config_states + .iter() + .find(|state| state.config_id == config.id.unwrap_or_default()) + .map(|state| state.is_running) + .unwrap_or(false) + }) + .cloned() + .collect(); + } + + pub fn scroll_up(&mut self) { + match self.active_table { + ActiveTable::Stopped => { + if let Some(selected) = self.table_state_stopped.selected() { + if selected > 0 { + self.table_state_stopped.select(Some(selected - 1)); + } + } + } + ActiveTable::Running => { + if let Some(selected) = self.table_state_running.selected() { + if selected > 0 { + self.table_state_running.select(Some(selected - 1)); + } + } + } + } + } + + pub fn scroll_down(&mut self) { + match self.active_table { + ActiveTable::Stopped => { + if let Some(selected) = self.table_state_stopped.selected() { + if selected < self.stopped_configs.len() - 1 { + self.table_state_stopped.select(Some(selected + 1)); + } + } else { + self.table_state_stopped.select(Some(0)); + } + } + ActiveTable::Running => { + if let Some(selected) = self.table_state_running.selected() { + if selected < self.running_configs.len() - 1 { + self.table_state_running.select(Some(selected + 1)); + } + } else { + self.table_state_running.select(Some(0)); + } + } + } + } + + pub fn reset_scroll(&mut self) { + self.table_state_stopped.select(Some(0)); + self.table_state_running.select(Some(0)); + } +} + +fn toggle_select_all(app: &mut App) { + let (selected_rows, configs) = match app.active_table { + ActiveTable::Stopped => (&mut app.selected_rows_stopped, &app.stopped_configs), + ActiveTable::Running => (&mut app.selected_rows_running, &app.running_configs), + }; + + if selected_rows.len() == configs.len() { + selected_rows.clear(); + } else { + selected_rows.clear(); + for i in 0..configs.len() { + selected_rows.insert(i); + } + } +} + +pub async fn handle_input(app: &mut App, _config_states: &mut [ConfigState]) -> io::Result { + if event::poll(std::time::Duration::from_millis(100))? { + if let Event::Key(key) = event::read()? { + if key.code == KeyCode::Char('c') && key.modifiers.contains(KeyModifiers::CONTROL) { + stop_all_port_forward_and_exit(app).await; + } + + match app.state { + AppState::ShowErrorPopup => { + handle_error_popup_input(app, key.code)?; + } + AppState::ShowConfirmationPopup => { + handle_confirmation_popup_input(app, key.code).await?; + } + AppState::ImportFileExplorerOpen => { + handle_import_file_explorer_input(app, key.code).await?; + } + AppState::ExportFileExplorerOpen => { + handle_export_file_explorer_input(app, key.code).await?; + } + AppState::ShowInputPrompt => { + handle_export_input_prompt(app, key.code).await?; + } + AppState::ShowHelp => { + handle_help_input(app, key.code)?; + } + AppState::ShowAbout => { + handle_about_input(app, key.code)?; + } + AppState::ShowDeleteConfirmation => { + handle_delete_confirmation_input(app, key.code).await?; + } + AppState::Normal => { + handle_normal_input(app, key.code).await?; + } + } + } else if let Event::Resize(_, height) = event::read()? { + app.update_visible_rows(height); + } + } + Ok(false) +} + +async fn handle_normal_input(app: &mut App, key: KeyCode) -> io::Result<()> { + match app.active_component { + ActiveComponent::Menu => match key { + KeyCode::Tab => app.active_component = ActiveComponent::Table, + KeyCode::Left => { + if app.selected_menu_item > 0 { + app.selected_menu_item -= 1 + } + } + KeyCode::Right => { + if app.selected_menu_item < 4 { + app.selected_menu_item += 1 + } + } + KeyCode::Enter => match app.selected_menu_item { + 0 => app.state = AppState::ShowHelp, + 1 => open_import_file_explorer(app), + 2 => open_export_file_explorer(app), + 3 => app.state = AppState::ShowAbout, + 4 => stop_all_port_forward_and_exit(app).await, + _ => {} + }, + _ => {} + }, + ActiveComponent::Table => match key { + KeyCode::Tab => app.active_component = ActiveComponent::Menu, + KeyCode::Left => switch_to_stopped_table(app), + KeyCode::Right => switch_to_running_table(app), + KeyCode::Char(' ') => toggle_row_selection(app), + KeyCode::Char('c') => clear_stdout_output(app), + KeyCode::Char('f') => handle_port_forwarding(app).await?, + KeyCode::Char('q') => app.state = AppState::ShowAbout, + KeyCode::Char('i') => open_import_file_explorer(app), + KeyCode::Char('e') => open_export_file_explorer(app), + KeyCode::Char('h') => app.state = AppState::ShowHelp, + KeyCode::Char('d') => show_delete_confirmation(app), + KeyCode::Up => app.scroll_up(), + KeyCode::Down => app.scroll_down(), + KeyCode::Char('a') => toggle_select_all(app), + _ => {} + }, + } + Ok(()) +} + +fn switch_to_stopped_table(app: &mut App) { + app.active_table = ActiveTable::Stopped; + app.reset_scroll(); + app.selected_rows_running.clear(); + app.table_state_running.select(None); +} + +fn switch_to_running_table(app: &mut App) { + app.active_table = ActiveTable::Running; + app.reset_scroll(); + app.selected_rows_stopped.clear(); + app.table_state_stopped.select(None); +} + +fn toggle_row_selection(app: &mut App) { + let selected_row = match app.active_table { + ActiveTable::Stopped => app.table_state_stopped.selected().unwrap_or(0), + ActiveTable::Running => app.table_state_running.selected().unwrap_or(0), + }; + + let selected_rows = match app.active_table { + ActiveTable::Stopped => &mut app.selected_rows_stopped, + ActiveTable::Running => &mut app.selected_rows_running, + }; + + if selected_rows.contains(&selected_row) { + selected_rows.remove(&selected_row); + } else { + selected_rows.insert(selected_row); + } +} + +fn clear_stdout_output(app: &mut App) { + let mut stdout_output = app.stdout_output.lock().unwrap(); + stdout_output.clear(); +} + +async fn handle_port_forwarding(app: &mut App) -> io::Result<()> { + let (selected_rows, configs, selected_row) = match app.active_table { + ActiveTable::Stopped => ( + &mut app.selected_rows_stopped, + &app.stopped_configs, + app.selected_row_stopped, + ), + ActiveTable::Running => ( + &mut app.selected_rows_running, + &app.running_configs, + app.selected_row_running, + ), + }; + + if selected_rows.is_empty() { + selected_rows.insert(selected_row); + } + + let selected_configs: Vec = selected_rows + .iter() + .filter_map(|&row| configs.get(row).cloned()) + .collect(); + + for config in selected_configs.clone() { + handle_port_forward(app, config).await; + } + + if app.active_table == ActiveTable::Stopped { + app.running_configs.extend(selected_configs.clone()); + app.stopped_configs + .retain(|config| !selected_configs.contains(config)); + } else { + app.stopped_configs.extend(selected_configs.clone()); + app.running_configs + .retain(|config| !selected_configs.contains(config)); + } + + match app.active_table { + ActiveTable::Stopped => app.selected_rows_stopped.clear(), + ActiveTable::Running => app.selected_rows_running.clear(), + } + + Ok(()) +} + +fn show_delete_confirmation(app: &mut App) { + if !app.selected_rows_stopped.is_empty() { + app.state = AppState::ShowDeleteConfirmation; + app.delete_confirmation_message = + Some("Are you sure you want to delete the selected configs?".to_string()); + } +} + +async fn handle_delete_confirmation_input(app: &mut App, key: KeyCode) -> io::Result<()> { + match key { + KeyCode::Left | KeyCode::Right => { + app.selected_delete_button = match app.selected_delete_button { + DeleteButton::Confirm => DeleteButton::Close, + DeleteButton::Close => DeleteButton::Confirm, + }; + } + KeyCode::Enter => { + if app.selected_delete_button == DeleteButton::Confirm { + let ids_to_delete: Vec = app + .selected_rows_stopped + .iter() + .filter_map(|&row| app.stopped_configs.get(row).and_then(|config| config.id)) + .collect(); + + match kftray_commons::utils::config::delete_configs(ids_to_delete.clone()).await { + Ok(_) => { + app.delete_confirmation_message = + Some("Configs deleted successfully.".to_string()); + app.stopped_configs.retain(|config| { + !ids_to_delete.contains(&config.id.unwrap_or_default()) + }); + } + Err(e) => { + app.delete_confirmation_message = + Some(format!("Failed to delete configs: {}", e)); + } + } + } + app.selected_rows_stopped.clear(); + app.state = AppState::Normal; + } + KeyCode::Esc => app.state = AppState::Normal, + _ => {} + } + Ok(()) +} + +fn open_import_file_explorer(app: &mut App) { + app.state = AppState::ImportFileExplorerOpen; + app.selected_file_path = std::env::current_dir().ok(); +} + +fn open_export_file_explorer(app: &mut App) { + app.state = AppState::ExportFileExplorerOpen; + app.selected_file_path = std::env::current_dir().ok(); +} diff --git a/crates/kftui/src/tui/input/navigation.rs b/crates/kftui/src/tui/input/navigation.rs new file mode 100644 index 00000000..2749e7d2 --- /dev/null +++ b/crates/kftui/src/tui/input/navigation.rs @@ -0,0 +1,16 @@ +use kftray_commons::models::config_model::Config; + +use crate::core::port_forward::{ + start_port_forwarding, + stop_port_forwarding, +}; +use crate::tui::input::ActiveTable; +use crate::tui::input::App; + +pub async fn handle_port_forward(app: &mut App, config: Config) { + if app.active_table == ActiveTable::Stopped { + start_port_forwarding(app, config).await; + } else { + stop_port_forwarding(app, config).await; + } +} diff --git a/crates/kftui/src/tui/input/popup.rs b/crates/kftui/src/tui/input/popup.rs new file mode 100644 index 00000000..0b600531 --- /dev/null +++ b/crates/kftui/src/tui/input/popup.rs @@ -0,0 +1,36 @@ +use crossterm::event::KeyCode; + +use crate::tui::input::{ + App, + AppState, +}; + +pub async fn handle_confirmation_popup_input(app: &mut App, key: KeyCode) -> std::io::Result<()> { + if key == KeyCode::Enter { + app.state = AppState::Normal; + app.import_export_message = None; + } + Ok(()) +} + +pub fn handle_help_input(app: &mut App, key: KeyCode) -> std::io::Result<()> { + if key == KeyCode::Esc || key == KeyCode::Char('h') { + app.state = AppState::Normal; + } + Ok(()) +} + +pub fn handle_about_input(app: &mut App, key: KeyCode) -> std::io::Result<()> { + if key == KeyCode::Esc || key == KeyCode::Char('q') { + app.state = AppState::Normal; + } + Ok(()) +} + +pub fn handle_error_popup_input(app: &mut App, key: KeyCode) -> std::io::Result<()> { + if key == KeyCode::Enter || key == KeyCode::Esc { + app.error_message = None; + app.state = AppState::Normal; + } + Ok(()) +} diff --git a/crates/kftui/src/tui/mod.rs b/crates/kftui/src/tui/mod.rs new file mode 100644 index 00000000..0bf349d6 --- /dev/null +++ b/crates/kftui/src/tui/mod.rs @@ -0,0 +1,5 @@ +mod app; +pub mod input; +mod ui; + +pub use app::run_tui; diff --git a/crates/kftui/src/tui/ui/draw.rs b/crates/kftui/src/tui/ui/draw.rs new file mode 100644 index 00000000..6185e21b --- /dev/null +++ b/crates/kftui/src/tui/ui/draw.rs @@ -0,0 +1,161 @@ +use kftray_commons::models::config_state_model::ConfigState; +use ratatui::{ + layout::{ + Constraint, + Direction, + Layout, + Rect, + }, + style::{ + Modifier, + Style, + }, + text::{ + Line, + Span, + }, + widgets::{ + Block, + BorderType, + Borders, + Tabs, + }, + Frame, +}; + +use crate::tui::input::{ + ActiveComponent, + App, + AppState, +}; +use crate::tui::ui::render_delete_confirmation_popup; +use crate::tui::ui::{ + centered_rect, + draw_configs_tab, + draw_file_explorer_popup, + render_about_popup, + render_background_overlay, + render_confirmation_popup, + render_error_popup, + render_help_popup, + render_input_prompt, + render_legend, + BASE, + TEXT, + YELLOW, +}; + +pub fn draw_ui(f: &mut Frame, app: &mut App, config_states: &[ConfigState]) { + let size = f.size(); + + let background = Block::default().style(Style::default().bg(BASE)); + f.render_widget(background, size); + + let chunks = Layout::default() + .direction(Direction::Vertical) + .constraints( + [ + Constraint::Length(3), + Constraint::Min(0), + Constraint::Length(3), + ] + .as_ref(), + ) + .split(size); + + if chunks.len() != 3 { + return; + } + + draw_header(f, app, chunks[0]); + draw_configs_tab(f, app, config_states, chunks[1]); + render_legend(f, chunks[2]); + + match app.state { + AppState::ShowHelp => { + let help_area = centered_rect(50, 50, size); + render_background_overlay(f, size); + render_help_popup(f, help_area); + } + AppState::ShowAbout => { + let about_area = centered_rect(30, 60, size); + render_background_overlay(f, size); + render_about_popup(f, about_area); + } + AppState::ImportFileExplorerOpen => { + let popup_area = centered_rect(90, 90, size); + render_background_overlay(f, size); + draw_file_explorer_popup(f, app, popup_area, true); + } + AppState::ExportFileExplorerOpen => { + let popup_area = centered_rect(80, 60, size); + render_background_overlay(f, size); + draw_file_explorer_popup(f, app, popup_area, false); + } + AppState::ShowInputPrompt => { + let input_area = centered_rect(40, 20, size); + render_background_overlay(f, size); + render_input_prompt(f, &app.input_buffer, input_area); + } + AppState::ShowConfirmationPopup => { + let confirmation_area = centered_rect(50, 30, size); + render_background_overlay(f, size); + render_confirmation_popup(f, &app.import_export_message, confirmation_area); + } + AppState::ShowErrorPopup => { + if let Some(error_message) = &app.error_message { + let error_area = centered_rect(60, 40, size); + render_background_overlay(f, size); + render_error_popup(f, error_message, error_area, 1); + } + } + AppState::ShowDeleteConfirmation => { + let delete_area = centered_rect(50, 30, size); + render_background_overlay(f, size); + render_delete_confirmation_popup( + f, + &app.delete_confirmation_message, + delete_area, + app.selected_delete_button, + ); + } + _ => {} + } +} + +pub fn draw_header(f: &mut Frame, app: &App, area: Rect) { + let menu_titles = ["Help", "Import", "Export", "About", "Quit"]; + let menu: Vec = menu_titles + .iter() + .enumerate() + .map(|(i, t)| { + let style = + if app.active_component == ActiveComponent::Menu && app.selected_menu_item == i { + Style::default().fg(YELLOW).add_modifier(Modifier::BOLD) + } else { + Style::default().fg(TEXT) + }; + Line::from(Span::styled(*t, style)) + }) + .collect(); + + let border_style = if app.active_component == ActiveComponent::Menu { + Style::default().fg(YELLOW) + } else { + Style::default().fg(TEXT) + }; + + let menu_titles = Tabs::new(menu) + .block( + Block::default() + .title("Menu") + .borders(Borders::ALL) + .border_type(BorderType::Rounded) + .border_style(border_style), + ) + .style(Style::default().fg(TEXT)) + .highlight_style(Style::default().fg(YELLOW)) + .divider(Span::raw(" | ")); + + f.render_widget(menu_titles, area); +} diff --git a/crates/kftui/src/tui/ui/header.rs b/crates/kftui/src/tui/ui/header.rs new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/crates/kftui/src/tui/ui/header.rs @@ -0,0 +1 @@ + diff --git a/crates/kftui/src/tui/ui/logo.rs b/crates/kftui/src/tui/ui/logo.rs new file mode 100644 index 00000000..d661e090 --- /dev/null +++ b/crates/kftui/src/tui/ui/logo.rs @@ -0,0 +1,55 @@ +pub const ASCII_LOGO: &str = r#" +............................................. +............................................. +............................................. +............................................. +...ooooo..............................ooooo.. +..oooooooooo......................oooooooooo. +..oooooooooooooo..............oooooooooooooo. +..oooooooooooooooooo......oooooooooooooooooo. +..ooooooo..ooooooooooooooooooooooo...ooooooo. +..ooooooo......ooooooooooooooo.......ooooooo. +..ooooooo..........ooooooo...........ooooooo. +..oooooooo.........................ooooooooo. +...oooooooooooo................ooooooooooooo. +....ooooooooooooooo........oooooooooooooooo.. +.......ooooooooooooooo.oooooooooooooooo...... +...........oooooooooooooooooooooooo.......... +...............oooooooooooooooo.............. +...................oooooooo.................. +............................................. +............................................. +............................................. +"#; + +pub fn resize_ascii_art(ascii_art: &str, scale: f32) -> Vec { + let lines: Vec<&str> = ascii_art.lines().collect(); + let original_height = lines.len(); + let original_width = lines.iter().map(|line| line.len()).max().unwrap_or(0); + + let new_height = (original_height as f32 * scale).round() as usize; + let new_width = (original_width as f32 * scale).round() as usize; + + let mut resized_art = Vec::new(); + + for i in 0..new_height { + let original_i = (i as f32 / scale).round() as usize; + if original_i >= original_height { + break; + } + + let mut new_line = String::new(); + for j in 0..new_width { + let original_j = (j as f32 / scale).round() as usize; + if original_j >= original_width { + break; + } + + let char = lines[original_i].chars().nth(original_j).unwrap_or(' '); + new_line.push(char); + } + resized_art.push(new_line); + } + + resized_art +} diff --git a/crates/kftui/src/tui/ui/mod.rs b/crates/kftui/src/tui/ui/mod.rs new file mode 100644 index 00000000..f48bdd25 --- /dev/null +++ b/crates/kftui/src/tui/ui/mod.rs @@ -0,0 +1,44 @@ +use ratatui::prelude::Color; + +mod draw; +mod header; +mod logo; +mod popup; +mod render; +mod table; +pub use draw::*; +pub use logo::*; +pub use popup::*; +pub use render::*; +pub use table::*; + +pub const PINK: Color = Color::Rgb(245, 194, 231); +pub const MAUVE: Color = Color::Rgb(203, 166, 247); +pub const RED: Color = Color::Rgb(243, 139, 168); + +pub const YELLOW: Color = Color::Rgb(249, 226, 175); +pub const GREEN: Color = Color::Rgb(166, 227, 161); +pub const TEAL: Color = Color::Rgb(148, 226, 213); + +pub const LAVENDER: Color = Color::Rgb(180, 190, 254); +pub const TEXT: Color = Color::Rgb(205, 214, 244); + +pub const SURFACE2: Color = Color::Rgb(88, 91, 112); +pub const SURFACE1: Color = Color::Rgb(69, 71, 90); +pub const SURFACE0: Color = Color::Rgb(49, 50, 68); +pub const BASE: Color = Color::Rgb(30, 30, 46); +pub const MANTLE: Color = Color::Rgb(24, 24, 37); +pub const CRUST: Color = Color::Rgb(17, 17, 27); + +//pub const SUBTEXT1: Color = Color::Rgb(186, 194, 222); +pub const SUBTEXT0: Color = Color::Rgb(166, 173, 200); +//pub const OVERLAY2: Color = Color::Rgb(147, 153, 178); +//pub const OVERLAY1: Color = Color::Rgb(127, 132, 156); +//pub const OVERLAY0: Color = Color::Rgb(108, 112, 134); +//pub const SKY: Color = Color::Rgb(137, 220, 235); +//pub const SAPPHIRE: Color = Color::Rgb(116, 199, 236); +//pub const BLUE: Color = Color::Rgb(137, 180, 250); +//pub const MAROON: Color = Color::Rgb(235, 160, 172); +//pub const PEACH: Color = Color::Rgb(250, 179, 135); +//pub const ROSEWATER: Color = Color::Rgb(245, 224, 220); +//pub const FLAMINGO: Color = Color::Rgb(242, 205, 205); diff --git a/crates/kftui/src/tui/ui/popup.rs b/crates/kftui/src/tui/ui/popup.rs new file mode 100644 index 00000000..b0f4c78b --- /dev/null +++ b/crates/kftui/src/tui/ui/popup.rs @@ -0,0 +1,339 @@ +use std::borrow::Cow; + +use ratatui::prelude::Modifier; +use ratatui::prelude::{ + Alignment, + Color, + Line, +}; +use ratatui::text::{ + Span, + Text, +}; +use ratatui::{ + layout::Rect, + style::Style, + widgets::{ + Block, + Borders, + Clear, + Paragraph, + }, + Frame, +}; + +use crate::core::built_info; +use crate::tui::input::DeleteButton; +use crate::tui::ui::centered_rect; +use crate::tui::ui::{ + resize_ascii_art, + ASCII_LOGO, +}; +use crate::tui::ui::{ + BASE, + CRUST, + LAVENDER, + MANTLE, + MAUVE, + PINK, + RED, + TEAL, + TEXT, + YELLOW, +}; +fn create_common_popup_style(title: &str, title_color: Color) -> Block<'_> { + Block::default() + .borders(Borders::ALL) + .title(Span::styled( + Cow::Borrowed(title), + Style::default().fg(title_color), + )) + .style(Style::default().bg(BASE).fg(TEXT)) +} + +fn create_bottom_right_shadow_layers( + area: Rect, shadow_layers: &[(Color, u16)], +) -> Vec<(Rect, Style)> { + shadow_layers + .iter() + .map(|(color, offset)| { + let shadow_area = + Rect::new(area.x + *offset, area.y + *offset, area.width, area.height); + (shadow_area, Style::default().bg(*color)) + }) + .collect() +} + +fn render_shadow_layers(f: &mut Frame, shadow_layers: Vec<(Rect, Style)>) { + for (shadow_area, style) in shadow_layers { + let shadow_block = Block::default().style(style); + f.render_widget(shadow_block, shadow_area); + } +} + +pub fn render_background_overlay(f: &mut Frame, area: Rect) { + let overlay = Block::default().style(Style::default().bg(CRUST)); + f.render_widget(overlay, area); +} + +fn render_popup( + f: &mut Frame, area: Rect, title: &str, title_color: Color, content: Text, alignment: Alignment, +) { + let popup_paragraph = Paragraph::new(content) + .block(create_common_popup_style(title, title_color)) + .style(Style::default().fg(TEXT).bg(BASE)) + .alignment(alignment); + + f.render_widget(Clear, area); + f.render_widget(popup_paragraph, area); + + let shadow_layers = [(MANTLE, 1)]; + + let popup_shadow_layers = create_bottom_right_shadow_layers(area, &shadow_layers); + render_shadow_layers(f, popup_shadow_layers); +} + +pub fn render_input_prompt(f: &mut Frame, input_buffer: &str, area: Rect) { + let input_paragraph = Text::raw(input_buffer); + render_popup( + f, + area, + "Enter file name", + PINK, + input_paragraph, + Alignment::Left, + ); +} + +pub fn render_confirmation_popup(f: &mut Frame, message: &Option, area: Rect) { + let message_text = message.as_deref().unwrap_or(""); + let message_paragraph = Text::raw(message_text); + render_popup( + f, + area, + "Confirmation", + MAUVE, + message_paragraph, + Alignment::Center, + ); + + let close_button = create_close_button(); + let button_area = Rect::new( + area.x + (area.width / 2) - 5, + area.y + area.height - 4, + 10, + 3, + ); + f.render_widget(close_button, button_area); +} + +pub fn render_help_popup(f: &mut Frame, area: Rect) { + let help_message = vec![ + Line::from(Span::styled("Ctrl+C: Quit", Style::default().fg(YELLOW))), + Line::from(Span::styled("↑/↓: Navigate", Style::default().fg(YELLOW))), + Line::from(Span::styled( + "←/→: Switch Table", + Style::default().fg(YELLOW), + )), + Line::from(Span::styled( + "f: Start/Stop Port Forward", + Style::default().fg(YELLOW), + )), + Line::from(Span::styled( + "Space: Select/Deselect", + Style::default().fg(YELLOW), + )), + Line::from(Span::styled( + "Ctrl+A: Select/Deselect All", + Style::default().fg(YELLOW), + )), + Line::from(Span::styled("h: Show Help", Style::default().fg(YELLOW))), + Line::from(Span::styled("i: Import", Style::default().fg(YELLOW))), + Line::from(Span::styled("e: Export", Style::default().fg(YELLOW))), + Line::from(Span::styled( + "d: Delete Selected", + Style::default().fg(YELLOW), + )), + Line::from(Span::styled( + "Tab: Switch Focus (Menu/Table)", + Style::default().fg(YELLOW), + )), + Line::from(Span::styled( + "Enter: Select Menu Item", + Style::default().fg(YELLOW), + )), + Line::from(Span::styled("c: Clear Output", Style::default().fg(YELLOW))), + ]; + + let help_paragraph = Paragraph::new(Text::from(help_message)) + .block( + Block::default() + .borders(Borders::ALL) + .title(Span::styled("Help", Style::default().fg(TEAL))) + .style(Style::default().bg(BASE).fg(TEXT)), + ) + .alignment(Alignment::Left) + .wrap(ratatui::widgets::Wrap { trim: true }); + + f.render_widget(Clear, area); + f.render_widget(help_paragraph, area); +} + +pub fn render_about_popup(f: &mut Frame, area: Rect) { + let resized_logo = resize_ascii_art(ASCII_LOGO, 1.0); + + let about_message = vec![ + Line::from(Span::styled( + format!("App Version: {}", built_info::PKG_VERSION), + Style::default().fg(YELLOW), + )), + Line::from(Span::styled( + format!("Author: {}", built_info::PKG_AUTHORS), + Style::default().fg(YELLOW), + )), + Line::from(Span::styled( + format!("License: {}", built_info::PKG_LICENSE), + Style::default().fg(YELLOW), + )), + ]; + + let mut combined_message = Vec::new(); + for line in resized_logo { + combined_message.push(Line::from(Span::styled(line, Style::default().fg(TEAL)))); + } + combined_message.push(Line::from("")); + combined_message.extend(about_message); + + let about_paragraph = Paragraph::new(Text::from(combined_message)) + .block( + Block::default() + .borders(Borders::ALL) + .title(Span::styled("About", Style::default().fg(TEAL))) + .style(Style::default().bg(BASE).fg(TEXT)), + ) + .alignment(Alignment::Center) + .wrap(ratatui::widgets::Wrap { trim: true }); + + let popup_area = centered_rect(80, 80, area); + f.render_widget(Clear, popup_area); + f.render_widget(about_paragraph, popup_area); +} + +pub fn render_error_popup(f: &mut Frame, error_message: &str, area: Rect, top_padding: usize) { + let max_text_width = area.width.saturating_sub(4) as usize; + let wrapped_text = wrap_text(error_message, max_text_width); + + let mut padded_text = Text::default(); + for _ in 0..top_padding { + padded_text.lines.push(Line::from("")); + } + padded_text.lines.extend(wrapped_text.lines.clone()); + + let text_height = (wrapped_text.lines.len() + top_padding) as u16 + 2; + + let popup_area = Rect::new( + area.x + (area.width / 4), + area.y + (area.height / 4), + area.width / 2, + text_height + 4, + ); + + render_popup(f, popup_area, "Error", RED, padded_text, Alignment::Center); + + let button_area = Rect::new( + popup_area.x + (popup_area.width / 2) - 5, + popup_area.y + text_height, + 10, + 3, + ); + + let close_button = create_close_button(); + + let shadow_layers = [(MANTLE, 1)]; + + let button_shadow_layers = create_bottom_right_shadow_layers(button_area, &shadow_layers); + render_shadow_layers(f, button_shadow_layers); + + f.render_widget(close_button, button_area); +} + +fn wrap_text(text: &str, max_width: usize) -> Text { + let mut wrapped_lines = Vec::new(); + for line in text.lines() { + let mut current_line = String::new(); + for word in line.split_whitespace() { + if current_line.len() + word.len() + 1 > max_width { + wrapped_lines.push(Line::from(current_line)); + current_line = String::new(); + } + if !current_line.is_empty() { + current_line.push(' '); + } + current_line.push_str(word); + } + if !current_line.is_empty() { + wrapped_lines.push(Line::from(current_line)); + } + } + Text::from(wrapped_lines) +} + +fn create_close_button() -> Paragraph<'static> { + Paragraph::new(Span::styled("", Style::default().fg(LAVENDER))) + .block( + Block::default() + .borders(Borders::ALL) + .style(Style::default().bg(BASE).fg(TEXT)), + ) + .alignment(Alignment::Center) +} + +pub fn render_delete_confirmation_popup( + f: &mut Frame, message: &Option, area: Rect, selected_button: DeleteButton, +) { + let message_text = message.as_deref().unwrap_or(""); + let message_paragraph = Text::raw(message_text); + render_popup( + f, + area, + "Delete Confirmation", + RED, + message_paragraph, + Alignment::Center, + ); + + let confirm_button = create_button("", selected_button == DeleteButton::Confirm); + let close_button = create_button("", selected_button == DeleteButton::Close); + + let confirm_button_area = Rect::new( + area.x + (area.width / 2) - 15, + area.y + area.height - 4, + 10, + 3, + ); + let close_button_area = Rect::new( + area.x + (area.width / 2) + 5, + area.y + area.height - 4, + 10, + 3, + ); + + f.render_widget(confirm_button, confirm_button_area); + f.render_widget(close_button, close_button_area); +} + +fn create_button(label: &str, is_selected: bool) -> Paragraph<'_> { + let style = if is_selected { + Style::default().fg(LAVENDER).add_modifier(Modifier::BOLD) + } else { + Style::default().fg(TEXT) + }; + + Paragraph::new(Span::styled(label, style)) + .block( + Block::default() + .borders(Borders::ALL) + .style(Style::default().bg(BASE).fg(TEXT)), + ) + .alignment(Alignment::Center) +} diff --git a/crates/kftui/src/tui/ui/render.rs b/crates/kftui/src/tui/ui/render.rs new file mode 100644 index 00000000..54da2f8a --- /dev/null +++ b/crates/kftui/src/tui/ui/render.rs @@ -0,0 +1,165 @@ +use kftray_commons::models::config_state_model::ConfigState; +use ratatui::{ + layout::{ + Constraint, + Direction, + Layout, + Rect, + }, + style::Style, + text::{ + Line, + Span, + Text, + }, + widgets::{ + Block, + Borders, + Clear, + Paragraph, + }, + Frame, +}; + +use crate::tui::input::{ + ActiveTable, + App, +}; +use crate::tui::ui::render_details; +use crate::tui::ui::{ + draw_main_tab, + BASE, + MAUVE, + TEXT, + YELLOW, +}; + +pub fn render_legend(f: &mut Frame, area: Rect) { + let legend_message = vec![Line::from(vec![ + Span::styled("CtrlC: Quit", Style::default().fg(YELLOW)), + Span::raw(" | "), + Span::styled("Tab: Toggle Menu", Style::default().fg(YELLOW)), + Span::raw(" | "), + Span::styled("h: Toggle Help", Style::default().fg(YELLOW)), + ])]; + + let legend_paragraph = Paragraph::new(Text::from(legend_message)) + .block( + Block::default() + .borders(Borders::ALL) + .title("Help") + .style(Style::default().bg(BASE).fg(TEXT)), + ) + .style(Style::default().fg(TEXT).bg(BASE)); + + f.render_widget(legend_paragraph, area); +} + +fn calculate_center_constraints(percent: u16) -> [Constraint; 3] { + [ + Constraint::Percentage((100 - percent) / 2), + Constraint::Percentage(percent), + Constraint::Percentage((100 - percent) / 2), + ] +} + +pub fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect { + let popup_layout = Layout::default() + .direction(Direction::Vertical) + .constraints(calculate_center_constraints(percent_y).as_ref()) + .split(r); + + let vertical_center = popup_layout[1]; + + let horizontal_layout = Layout::default() + .direction(Direction::Horizontal) + .constraints(calculate_center_constraints(percent_x).as_ref()) + .split(vertical_center); + + horizontal_layout[1] +} +pub fn draw_file_explorer_popup(f: &mut Frame, app: &mut App, area: Rect, is_import: bool) { + let chunks = Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Percentage(90), Constraint::Percentage(10)].as_ref()) + .split(area); + + let file_explorer_block = Block::default() + .borders(Borders::ALL) + .style(Style::default().bg(BASE).fg(TEXT)) + .title(Span::styled("File Explorer", Style::default().fg(MAUVE))); + + f.render_widget(Clear, area); + f.render_widget(file_explorer_block, area); + + let upper_chunks = Layout::default() + .direction(Direction::Horizontal) + .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref()) + .split(chunks[0]); + + let file_explorer_widget = if is_import { + app.import_file_explorer.widget() + } else { + app.export_file_explorer.widget() + }; + f.render_widget(&file_explorer_widget, upper_chunks[0]); + + f.render_widget(Clear, upper_chunks[1]); + + if let Some(content) = &app.file_content { + let file_content_widget = Paragraph::new(content.as_str()) + .block( + Block::default() + .borders(Borders::ALL) + .title(Span::styled("File Preview", Style::default().fg(MAUVE))), + ) + .style(Style::default().fg(TEXT).bg(BASE)); + f.render_widget(file_content_widget, upper_chunks[1]); + } else { + let empty_widget = Paragraph::new("") + .block( + Block::default() + .borders(Borders::ALL) + .title(Span::styled("File Preview", Style::default().fg(MAUVE))), + ) + .style(Style::default().fg(TEXT).bg(BASE)); + f.render_widget(empty_widget, upper_chunks[1]); + } + + let help_text = "↑↓: Navigate ESC: Close ENTER/SPACE: Select"; + let help_widget = Paragraph::new(help_text) + .block( + Block::default() + .borders(Borders::ALL) + .title(Span::styled("Help", Style::default().fg(MAUVE))), + ) + .style(Style::default().fg(TEXT).bg(BASE)); + + f.render_widget(help_widget, chunks[1]); +} + +pub fn draw_configs_tab(f: &mut Frame, app: &mut App, config_states: &[ConfigState], area: Rect) { + let table_height = app.visible_rows as u16; + + let chunks = Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Min(table_height), Constraint::Min(table_height)].as_ref()) + .split(area); + + draw_main_tab(f, app, config_states, chunks[0]); + + if !app.stopped_configs.is_empty() || !app.running_configs.is_empty() { + let selected_row = match app.active_table { + ActiveTable::Stopped => app.selected_row_stopped, + ActiveTable::Running => app.selected_row_running, + }; + let configs = match app.active_table { + ActiveTable::Stopped => &app.stopped_configs, + ActiveTable::Running => &app.running_configs, + }; + + if !configs.is_empty() && selected_row < configs.len() { + render_details(f, &configs[selected_row], config_states, chunks[1]); + } + } +} diff --git a/crates/kftui/src/tui/ui/table.rs b/crates/kftui/src/tui/ui/table.rs new file mode 100644 index 00000000..ab31d1b4 --- /dev/null +++ b/crates/kftui/src/tui/ui/table.rs @@ -0,0 +1,294 @@ +use std::collections::HashSet; + +use kftray_commons::models::config_model::Config; +use kftray_commons::models::config_state_model::ConfigState; +use ratatui::prelude::Alignment; +use ratatui::prelude::Color; +use ratatui::widgets::BorderType; +use ratatui::widgets::TableState; +use ratatui::{ + layout::{ + Constraint, + Direction, + Layout, + Rect, + }, + style::{ + Modifier, + Style, + }, + text::{ + Line, + Span, + Text, + }, + widgets::{ + Block, + Borders, + Cell, + Paragraph, + Row, + Scrollbar, + ScrollbarOrientation, + ScrollbarState, + Table, + }, + Frame, +}; + +use crate::tui::input::{ + ActiveTable, + App, +}; +use crate::tui::ui::{ + BASE, + GREEN, + MAUVE, + RED, + SUBTEXT0, + SURFACE0, + SURFACE1, + SURFACE2, + TEXT, + YELLOW, +}; + +pub fn draw_main_tab(f: &mut Frame, app: &mut App, config_states: &[ConfigState], area: Rect) { + let chunks = Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Percentage(100)].as_ref()) + .split(area); + + let tables_chunks = Layout::default() + .direction(Direction::Horizontal) + .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref()) + .split(chunks[0]); + + draw_configs_table( + f, + tables_chunks[0], + &app.stopped_configs, + config_states, + &mut app.table_state_stopped, + "Stopped Configs", + app.active_table == ActiveTable::Stopped, + &app.selected_rows_stopped, + ); + + draw_configs_table( + f, + tables_chunks[1], + &app.running_configs, + config_states, + &mut app.table_state_running, + "Running Configs", + app.active_table == ActiveTable::Running, + &app.selected_rows_running, + ); +} + +#[allow(clippy::too_many_arguments)] +pub fn draw_configs_table( + frame: &mut Frame, area: Rect, configs: &[Config], config_states: &[ConfigState], + state: &mut TableState, title: &str, has_focus: bool, selected_rows: &HashSet, +) { + let rows: Vec = configs + .iter() + .enumerate() + .map(|(i, config)| { + let state = config_states + .iter() + .find(|s| s.config_id == config.id.unwrap_or_default()) + .map(|s| s.is_running) + .unwrap_or(false); + + let base_style = if state { + Style::default().fg(GREEN) + } else { + Style::default().fg(RED) + }; + + let row_style = if selected_rows.contains(&i) { + base_style.bg(SURFACE0).fg(SUBTEXT0) + } else { + base_style + }; + + Row::new(vec![ + Cell::from(config.alias.clone().unwrap_or_default()), + Cell::from(config.workload_type.clone()), + Cell::from(config.local_port.to_string()), + Cell::from(config.context.clone()), + ]) + .style(row_style) + }) + .collect(); + + let focus_color = focus_color(has_focus); + + let table = Table::new( + rows, + [ + Constraint::Percentage(25), + Constraint::Percentage(25), + Constraint::Percentage(25), + Constraint::Percentage(25), + ], + ) + .header( + Row::new(vec![ + Cell::from("Alias"), + Cell::from("Workload"), + Cell::from("Local Port"), + Cell::from("Context"), + ]) + .style(style_bold().fg(MAUVE)), + ) + .block( + Block::default() + .border_type(BorderType::Rounded) + .borders(Borders::ALL) + .title_alignment(Alignment::Left) + .border_style(Style::default().fg(focus_color)) + .title(title), + ) + .highlight_style(Style::default().bg(SURFACE1).fg(TEXT)); + + frame.render_stateful_widget(table, area, state); + + let height = area.height.saturating_sub(2); + let offset_with_last_in_view = configs.len().saturating_sub(height as usize); + if let Some(selection) = state.selected() { + if selection >= offset_with_last_in_view { + *state.offset_mut() = offset_with_last_in_view; + } + } else { + *state.offset_mut() = offset_with_last_in_view; + } + + let scrollbar = Scrollbar::new(ScrollbarOrientation::VerticalRight) + .begin_symbol(None) + .track_symbol(None) + .end_symbol(None) + .style(Style::default().fg(SURFACE2).bg(BASE)); + let mut scrollbar_state = ScrollbarState::new(configs.len().saturating_sub(height as usize)) + .position(state.offset()) + .viewport_content_length(height as usize); + let scrollbar_area = Rect { + x: area.x + area.width - 1, + y: area.y.saturating_add(2), + height: area.height.saturating_sub(2), + width: 1, + }; + frame.render_stateful_widget(scrollbar, scrollbar_area, &mut scrollbar_state); +} + +pub fn render_details(f: &mut Frame, config: &Config, config_states: &[ConfigState], area: Rect) { + let state = config_states + .iter() + .find(|s| s.config_id == config.id.unwrap_or_default()) + .map(|s| s.is_running) + .unwrap_or(false); + + let details = vec![ + Line::from(vec![ + Span::styled("Context: ", Style::default().add_modifier(Modifier::BOLD)), + Span::raw(&config.context), + ]), + Line::from(vec![ + Span::styled("Alias: ", Style::default().add_modifier(Modifier::BOLD)), + Span::raw(config.alias.clone().unwrap_or_default()), + ]), + Line::from(vec![ + Span::styled("Service: ", Style::default().add_modifier(Modifier::BOLD)), + Span::raw(config.service.clone().unwrap_or_default()), + ]), + Line::from(vec![ + Span::styled("Namespace: ", Style::default().add_modifier(Modifier::BOLD)), + Span::raw(&config.namespace), + ]), + Line::from(vec![ + Span::styled( + "Local Address: ", + Style::default().add_modifier(Modifier::BOLD), + ), + Span::raw(config.local_address.clone().unwrap_or_default()), + ]), + Line::from(vec![ + Span::styled( + "Local Port: ", + Style::default().add_modifier(Modifier::BOLD), + ), + Span::raw(config.local_port.to_string()), + ]), + Line::from(vec![ + Span::styled( + "Remote Address: ", + Style::default().add_modifier(Modifier::BOLD), + ), + Span::raw(config.remote_address.clone().unwrap_or_default()), + ]), + Line::from(vec![ + Span::styled( + "Remote Port: ", + Style::default().add_modifier(Modifier::BOLD), + ), + Span::raw(config.remote_port.to_string()), + ]), + Line::from(vec![ + Span::styled("Context: ", Style::default().add_modifier(Modifier::BOLD)), + Span::raw(&config.context), + ]), + Line::from(vec![ + Span::styled( + "Workload Type: ", + Style::default().add_modifier(Modifier::BOLD), + ), + Span::raw(&config.workload_type), + ]), + Line::from(vec![ + Span::styled("Protocol: ", Style::default().add_modifier(Modifier::BOLD)), + Span::raw(&config.protocol), + ]), + Line::from(vec![ + Span::styled( + "Domain Enabled: ", + Style::default().add_modifier(Modifier::BOLD), + ), + Span::raw(config.domain_enabled.unwrap_or(false).to_string()), + ]), + Line::from(vec![ + Span::styled( + "Kubeconfig: ", + Style::default().add_modifier(Modifier::BOLD), + ), + Span::raw(config.kubeconfig.clone().unwrap_or_default()), + ]), + Line::from(vec![ + Span::styled("Target: ", Style::default().add_modifier(Modifier::BOLD)), + Span::raw(config.target.clone().unwrap_or_default()), + ]), + Line::from(vec![ + Span::styled("Status: ", Style::default().add_modifier(Modifier::BOLD)), + Span::raw(state.to_string()), + ]), + ]; + + let paragraph = Paragraph::new(Text::from(details)) + .block(Block::default().borders(Borders::ALL).title("Details")) + .style(Style::default().fg(TEXT).bg(BASE)); + + f.render_widget(paragraph, area); +} + +pub fn style_bold() -> Style { + Style::default().add_modifier(Modifier::BOLD) +} + +pub fn focus_color(has_focus: bool) -> Color { + if has_focus { + YELLOW + } else { + TEXT + } +} diff --git a/crates/kftui/src/utils/config.rs b/crates/kftui/src/utils/config.rs new file mode 100644 index 00000000..afde6f2c --- /dev/null +++ b/crates/kftui/src/utils/config.rs @@ -0,0 +1,40 @@ +use kftray_commons::config::{ + export_configs, + import_configs, +}; + +pub async fn import_configs_from_file(file_path: &str) -> Result<(), String> { + log::info!("Starting import of configs from file: {}", file_path); + let json = std::fs::read_to_string(file_path).map_err(|e| { + let err_msg = format!("Failed to read file {}: {}", file_path, e); + log::error!("{}", err_msg); + err_msg + })?; + log::debug!("File content read successfully. Size: {} bytes", json.len()); + + import_configs(json).await.map_err(|e| { + let err_msg = format!("Failed to import configs: {}", e); + log::error!("{}", err_msg); + err_msg + })?; + log::info!("Successfully imported configs from file: {}", file_path); + Ok(()) +} + +pub async fn export_configs_to_file(file_path: &str) -> Result<(), String> { + log::info!("Starting export of configs to file: {}", file_path); + let json = export_configs().await.map_err(|e| { + let err_msg = format!("Failed to export configs: {}", e); + log::error!("{}", err_msg); + err_msg + })?; + log::debug!("Configs exported successfully: {}", json); + + std::fs::write(file_path, json).map_err(|e| { + let err_msg = format!("Failed to write to file {}: {}", file_path, e); + log::error!("{}", err_msg); + err_msg + })?; + log::info!("Successfully exported configs to file: {}", file_path); + Ok(()) +} diff --git a/crates/kftui/src/utils/file.rs b/crates/kftui/src/utils/file.rs new file mode 100644 index 00000000..1d838a23 --- /dev/null +++ b/crates/kftui/src/utils/file.rs @@ -0,0 +1,15 @@ +use std::fs::read_to_string; +use std::io; +use std::path::Path; + +pub fn get_file_content(path: &Path) -> io::Result { + if !path.is_file() { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "Path is not a file", + )); + } + + let content = read_to_string(path)?; + Ok(content) +} diff --git a/crates/kftui/src/utils/mod.rs b/crates/kftui/src/utils/mod.rs new file mode 100644 index 00000000..793bf6ac --- /dev/null +++ b/crates/kftui/src/utils/mod.rs @@ -0,0 +1,2 @@ +pub mod config; +pub mod file; diff --git a/docs/ARCH.md b/docs/ARCH.md new file mode 100644 index 00000000..e3e3c3ef --- /dev/null +++ b/docs/ARCH.md @@ -0,0 +1,35 @@ +## 🏗 Architecture + +### Server + +KFtray Server is a Rust application that relays UDP/TCP traffic to an upstream server. Check the source code [here](https://github.com/hcavarsan/kftray/tree/main/crates/kftray-server). + +### Forwarding Flows + +- **TCP Forwarding:** A local TCP socket, similar to kubectl, can be used to communicate with a Kubernetes pod. This approach offers parallel execution and improved resilience. + +```mermaid +sequenceDiagram +Application->>Kubernetes Pod: Opens TCP socket, starts port-forwarding +Kubernetes Pod-->>Application: Responds with TCP Packet +``` + +- **Proxy TCP Forwarding:** The local TCP connects to the kftray-server pod, which then sends TCP packet to the upstream server. + +```mermaid +sequenceDiagram +Application->>Kubernetes Pod: Socket to kftray-server, facilitates TCP relay +Kubernetes Pod->>Remote Service: Relays TCP Packet +Remote Service-->>Kubernetes Pod: Responds +Kubernetes Pod-->>Application: Returns TCP Packet +``` + +- **UDP Forwarding:** The KFtray client opens a local UDP socket and connects a local TCP socket to the kftray-server pod. The TCP socket sends UDP packets over TCP, which are then forwarded to the upstream server. + +```mermaid +sequenceDiagram +Application->>Kubernetes Pod: UDP socket, TCP port-forward to kftray-server +Kubernetes Pod->>Service/Remote: Converts to UDP, sends packet +Service/Remote-->>Kubernetes Pod: Responds with UDP Packet +Kubernetes Pod-->>Application: Relays as TCP +``` diff --git a/docs/kftray/BUILD.md b/docs/kftray/BUILD.md new file mode 100644 index 00000000..5e2f61ba --- /dev/null +++ b/docs/kftray/BUILD.md @@ -0,0 +1,115 @@ +# Building `kftray` and `kftui` from Source + + +
+ +## `kftray` Desktop App + +### Overview + +`kftray` is a desktop application built using Tauri, which combines a Rust backend with a frontend built using React and Typescript + +### Requirements + +- **Node.js** and **pnpm** or **yarn** for building the frontend. +- **Rust** and **Cargo** for building the backend. +- **Tauri CLI** for managing the Tauri project. + +For detailed prerequisites, please refer to the [Tauri Getting Started Guide](https://tauri.app/v1/guides/getting-started/prerequisites). + +### Steps to Compile `kftray` + +1. **Clone the Repository:** + + ```bash + git clone https://github.com/hcavarsan/kftray.git + ``` + +2. **Navigate to the Cloned Directory:** + + ```bash + cd kftray + ``` + +3. **Install Dependencies:** + + ```bash + pnpm install + ``` + +4. **Install Tauri CLI Globally (if not already installed):** + + ```bash + pnpm add -g @tauri-apps/cli + ``` + +5. **Launch the Application in Development Mode:** + + ```bash + pnpm tauri dev + ``` + +6. **Build the Application for Production:** + + ```bash + pnpm tauri build + ``` + + +
+ +## `kftui` Terminal User Interface (TUI) App + +### Overview + +`kftui` is a terminal-based user interface application built using Rust and the Ratatui library. + +### Requirements + +- **Rust** and **Cargo** for building the application. +- **Git** for cloning the repository. + +### Steps to Compile `kftui` + +1. **Clone the Repository:** + + ```bash + git clone https://github.com/hcavarsan/kftray.git + ``` + +2. **Navigate to the `kftui` Directory:** + + ```bash + cd kftray/kftui + ``` + +3. **Build the Application:** + + ```bash + cargo build --release + ``` + +4. **Run the Application:** + + ```bash + ./target/release/kftui + # OR + cargo run --bin kftui + ``` + +
+ +--- + +### Additional Notes + +- Ensure you have the latest stable version of Rust installed. You can install or update Rust using [rustup](https://rustup.rs/). +- The `cargo build --release` command will create an optimized binary in the `target/release` directory. +- If you encounter any issues during the build process, ensure all dependencies are up to date by running: + + ```bash + cargo update + ``` + + + diff --git a/docs/kftray/INSTALL.md b/docs/kftray/INSTALL.md new file mode 100644 index 00000000..0381d43d --- /dev/null +++ b/docs/kftray/INSTALL.md @@ -0,0 +1,58 @@ + +# `kftray` Desktop App Installation Guide + +`kftray` is available via Homebrew for macOS and Linux. For other systems, download the appropriate release from the [GitHub releases page](https://github.com/hcavarsan/kftray/releases). + +### macOS + +Install using Homebrew: + +```bash +brew tap hcavarsan/kftray +brew install --cask kftray +``` + +### Linux + +Install using Homebrew: + +```bash +brew tap hcavarsan/kftray +brew install kftray-linux +``` + + +_Please check the caveats section for global app creation instructions after installation._ + +Linux Note: due to GTK limitations, it is necessary to install and enable the GNOME Shell extension for AppIndicator support to kftray works. See here: + + +### Direct Downloads + +Download the latest release directly from GitHub: + + + +
+
diff --git a/docs/kftray/USAGE.md b/docs/kftray/USAGE.md new file mode 100644 index 00000000..b84510c7 --- /dev/null +++ b/docs/kftray/USAGE.md @@ -0,0 +1,113 @@ +# KFtui Usage Guide + +## Configuring Your First Port Forward + +Follow these simple steps to configure your first port forward using KFtui: + +### Step 1: Launch the KFtui App + +1. Open your terminal. +2. Launch the KFtui app by typing: + + ```bash + kftui + ``` + +### Step 2: Create a Configuration File + +Currently, KFtui does not support adding configurations directly from the TUI (Text User Interface). This feature is under development. For now, you can create a JSON file and import it using the `i` hotkey. Below is the format of the JSON file: + +```json +{ + "service": "productpage", + "namespace": "bookinfo", + "local_port": 9080, + "remote_port": 9080, + "context": "kind-kind-rc-version", + "workload_type": "service", + "protocol": "tcp", + "alias": "bookinfo", + "domain_enabled": true +} +``` + +### Step 3: Import the Configuration File + +1. Save your JSON configuration file. +2. Open KFtui and press the `i` hotkey to import the saved JSON file. +3. Alternatively, you can add the configuration in the Kftray Desktop app, and it will be available in the TUI as well. + +### Step 4: Activate Your Configuration + +1. With your configuration imported, navigate to the list of configurations. +2. Select the configurations you need to start by pressing the `space` key. You can select all configurations by pressing `Ctrl + A`. +3. Start the selected configurations by pressing the `f` hotkey. +4. To stop the configurations, follow the same steps but select them in the "Stopping Configs" window. You can navigate between windows using the left and right arrow keys. + +> **Note:** To use the alias feature with a local domain name, you must enable write permissions in the hosts file. This method is not secure. We are addressing this in the following issue: [https://github.com/hcavarsan/kftray/issues/171](https://github.com/hcavarsan/kftray/issues/171). + +### Enabling Write Access to Hosts File + +#### For Windows: + +```bash +icacls "C:\Windows\System32\drivers\etc\hosts" /grant Everyone:(R,W) +``` + +#### For MacOS and Linux: + +```bash +sudo chmod ugo+rw /etc/hosts +``` + +## Exporting Configurations to a JSON File + +You can export your current configurations to a JSON file for backup or sharing purposes. + +### Steps to Export: + +1. Open KFtui. +2. Press the `e` hotkey. +3. Choose a location to save the JSON file and press `Enter`. +4. Type the file name to save the JSON file. +5. The JSON file will contain all your current configurations. + +You can import this JSON file at any time to restore your configurations. + +### Example JSON Configuration File: + +```json +[ + { + "service": "argocd-server", + "namespace": "argocd", + "local_port": 8888, + "remote_port": 8080, + "context": "test-cluster", + "workload_type": "service", + "protocol": "tcp", + "remote_address": "", + "local_address": "127.0.0.1", + "alias": "argocd", + "domain_enabled": true + } +] +``` + +## Commands + +Press the `h` hotkey to display the help section, which includes the following commands: + +- **Ctrl+C**: Quit +- **↑/↓**: Navigate +- **←/→**: Switch Table +- **f**: Start/Stop Port Forward +- **Space**: Select/Deselect +- **Ctrl+A**: Select/Deselect All +- **h**: Show Help +- **i**: Import +- **e**: Export +- **d**: Delete Selected +- **Tab**: Switch Focus (Menu/Table) +- **Enter**: Select Menu Item +- **c**: Clear Output diff --git a/docs/kftui/BUILD.md b/docs/kftui/BUILD.md new file mode 100644 index 00000000..9c0fc1aa --- /dev/null +++ b/docs/kftui/BUILD.md @@ -0,0 +1,55 @@ +## `kftui` Terminal User Interface (TUI) App + +### Overview + +`kftui` is a terminal-based user interface application built using Rust and the Ratatui library. + +### Requirements + +- **Rust** and **Cargo** for building the application. +- **Git** for cloning the repository. + +### Steps to Compile `kftui` + +1. **Clone the Repository:** + + ```bash + git clone https://github.com/hcavarsan/kftray.git + ``` + +2. **Navigate to the `kftui` Directory:** + + ```bash + cd kftray/kftui + ``` + +3. **Build the Application:** + + ```bash + cargo build --release --bin kftui + ``` + +4. **Run the Application:** + + ```bash + ./target/release/kftui + # OR + cargo run --bin kftui + ``` + +
+ +--- + +### Additional Notes + +- Ensure you have the latest stable version of Rust installed. You can install or update Rust using [rustup](https://rustup.rs/). +- The `cargo build --release` command will create an optimized binary in the `target/release` directory. +- If you encounter any issues during the build process, ensure all dependencies are up to date by running: + + ```bash + cargo update + ``` + + + diff --git a/docs/kftui/INSTALL.md b/docs/kftui/INSTALL.md new file mode 100644 index 00000000..4bd3434b --- /dev/null +++ b/docs/kftui/INSTALL.md @@ -0,0 +1,66 @@ +# `kftui` Installation Guide + +## Prerequisites + +- Ensure `curl` or `wget` is installed. +- For Windows, PowerShell is required. + +## Installation Commands + +### Unix-like Systems (Linux, macOS, WSL) + +To install `kftui`, run one of the following commands: + +Using `curl`: + +```sh +bash <(curl -s https://raw.githubusercontent.com/hcavarsan/kftray/main/hacks/kftui_installer.sh) +``` + +Using `wget`: + +```sh +bash <(wget -qO- https://raw.githubusercontent.com/hcavarsan/kftray/main/hacks/kftui_installer.sh) +``` + +### Windows (Native) + +Run the following PowerShell command: + +```powershell +Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/hcavarsan/kftray/main/hacks/kftui_installer.ps1')) +``` + +### Post-Installation Steps + +After installation, restart your terminal and verify the installation: + +```sh +kftui +``` + +--- + +## Direct Downloads for `kftui` Binaries + +Download the latest `kftui` binaries directly from GitHub: + + diff --git a/docs/kftui/USAGE.md b/docs/kftui/USAGE.md new file mode 100644 index 00000000..3b99c94c --- /dev/null +++ b/docs/kftui/USAGE.md @@ -0,0 +1,82 @@ +## 🧭 Usage + +## 🎛 Configuring Your First Port Forward + +In a few simple steps, you can configure your first port forward: + +1. **Launch the application** +2. **Open the configuration panel from the tray icon** +3. **Add a new configuration:** + + - Give it a unique alias and set if you want to set the alias as domain to your forward \*1 + - Indicate if the configuration is for a port forward for a service (common use) or a proxy (port forward to an endpoint via a Kubernetes cluster). + - Specify the Kubernetes context + - Define the namespace housing your service + - Enter the service name + - Choose TCP or UDP + - Set the local and remote port numbers + - Configure a custom local IP address (optional) + +4. **Activate Your Configuration**: With your configuration saved, simply click on the switch button in the main menu to start the port forward in a single por forward or in Start All to start all configurations at the same time + +> Note: To use the alias feature with a local domain name, you must enable write permissions in the hosts file. This method is not secure. We are addressing this in the following issue: [https://github.com/hcavarsan/kftray/issues/171](https://github.com/hcavarsan/kftray/issues/171). +> Follow these steps to allow write access: +> +> For Windows: +> +> ```bash +> icacls "C:\Windows\System32\drivers\etc\hosts" /grant Everyone:(R,W) +> ``` +> +> For MacOS and Linux: +> +> ```bash +> sudo chmod ugo+rw /etc/hosts +> ``` + +## Export configurations to a JSON file + +1. Open the main menu in the footer +2. Select the `Export Local File` option +3. Choose a file name and location to save the JSON file +4. The JSON file will contain all your current configurations + +You can then import this JSON file at any time to restore your configurations. + +Example Json configuration File: + +```json +[ + { + "service": "argocd-server", + "namespace": "argocd", + "local_port": 8888, + "remote_port": 8080, + "context": "test-cluster", + "workload_type": "service", + "protocol": "tcp", + "remote_address": "", + "local_address": "127.0.0.1", + "alias": "argocd", + "domain_enabled": true + } +] +``` + +## Sharing the configurations through Git + +now, with the local json saved, you can share your configurations with your team members by committing the JSON file to a GitHub repository. This allows for easy collaboration and synchronization of KFtray configurations across your team. + +To import and sync your GitHub configs in kftray: + + +1. Open the application's main menu +2. Select the button with GitHub icon in the footer menu +4. Enter the URL of your Git repository and path containing the JSON file +5. If your GitHub repository is private, you will need to enter the private token. Credentials are securely saved in the SO keyring (Keychain on macOS). Kftray does not store or save credentials in any local file; they are only stored in the local keyring. +6. Select the polling time for when Kftray will synchronize configurations and retrieve them from GitHub. + + +6. KFtray will now sync with the Git repository to automatically import any new configurations or changes committed to the JSON file. + +This allows you to quickly deploy any port forward changes to all team members. And if someone on your team adds a new configuration, it will be automatically synced to everyone else's KFtray. diff --git a/examples/configs.json b/examples/configs.json index afeb050a..aee78f34 100644 --- a/examples/configs.json +++ b/examples/configs.json @@ -1,28 +1 @@ -[ - { - "service": "argocd-server", - "namespace": "argocd", - "local_port": 8888, - "remote_port": 8080, - "context": "test-cluster", - "workload_type": "service", - "protocol": "tcp", - "remote_address": "", - "local_address": "127.0.0.1", - "alias": "argocd", - "domain_enabled": true - }, - { - "service": "backstage-ui", - "namespace": "backstage", - "local_port": 8085, - "remote_port": 8080, - "context": "test-cluster", - "workload_type": "service", - "protocol": "tcp", - "remote_address": "", - "local_address": "127.0.0.1", - "alias": "argocd", - "domain_enabled": true - } -] +[{"service":"argocd-server","namespace":"argocd","local_port":8888,"remote_port":8080,"context":"test-cluster","workload_type":"service","protocol":"tcp","alias":"argocd","domain_enabled":true},{"service":"httpbin","namespace":"default","local_port":8081,"remote_port":8080,"context":"kind1","workload_type":"service","protocol":"tcp","alias":"httpbin"},{"target":"app.kubernetes.io/component=server","namespace":"default","local_port":8181,"remote_port":8080,"context":"kind1","workload_type":"pod","protocol":"tcp","alias":"argocd"}] \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json index 7ba7e57c..69ff42f9 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "@kftray/ui", - "version": "0.12.2", + "version": "0.13.0", "private": true, "type": "module", "scripts": { diff --git a/frontend/src/components/Footer/BulkDeleteButton/index.tsx b/frontend/src/components/Footer/BulkDeleteButton/index.tsx index 95c82c92..b01ed57e 100644 --- a/frontend/src/components/Footer/BulkDeleteButton/index.tsx +++ b/frontend/src/components/Footer/BulkDeleteButton/index.tsx @@ -15,14 +15,12 @@ import { } from '@chakra-ui/react' import { invoke } from '@tauri-apps/api/tauri' -import { BulkDeleteButtonProps, Status } from '../../../types' +import { BulkDeleteButtonProps } from '../../../types' import useCustomToast from '../../CustomToast' const BulkDeleteButton: React.FC = ({ selectedConfigs, setSelectedConfigs, - configs, - setConfigs, }) => { const cancelRef = React.useRef(null) const [isBulkAlertOpen, setIsBulkAlertOpen] = useState(false) @@ -46,19 +44,8 @@ const BulkDeleteButton: React.FC = ({ } try { - await invoke('delete_configs', { ids: configsToDelete }) + await invoke('delete_configs_cmd', { ids: configsToDelete }) - const configsAfterDeletion = await invoke('get_configs') - const runningStateMap = new Map( - configs.map(conf => [conf.id, conf.isRunning]), - ) - - const updatedConfigs = configsAfterDeletion.map(conf => ({ - ...conf, - isRunning: runningStateMap.get(conf.id) ?? false, - })) - - setConfigs(updatedConfigs) setSelectedConfigs([]) toast({ diff --git a/frontend/src/components/Footer/SyncConfigsButton/index.tsx b/frontend/src/components/Footer/SyncConfigsButton/index.tsx index a00545e2..e846c272 100644 --- a/frontend/src/components/Footer/SyncConfigsButton/index.tsx +++ b/frontend/src/components/Footer/SyncConfigsButton/index.tsx @@ -10,7 +10,6 @@ import useCustomToast from '../../CustomToast' const SyncConfigsButton: React.FC = ({ serviceName, accountName, - onConfigsSynced, onSyncFailure, credentialsSaved, setCredentialsSaved, @@ -134,7 +133,6 @@ const SyncConfigsButton: React.FC = ({ setLastSync(lastSyncDate) setNextSync(nextSyncDate) - onConfigsSynced?.() toast({ title: 'Configs synced successfully', status: 'success', diff --git a/frontend/src/components/Footer/index.tsx b/frontend/src/components/Footer/index.tsx index 1212054e..66004888 100644 --- a/frontend/src/components/Footer/index.tsx +++ b/frontend/src/components/Footer/index.tsx @@ -28,7 +28,6 @@ const Footer: React.FC = ({ openGitSyncModal, handleExportConfigs, handleImportConfigs, - onConfigsSynced, credentialsSaved, setCredentialsSaved, isGitSyncModalOpen, @@ -37,7 +36,6 @@ const Footer: React.FC = ({ selectedConfigs, setSelectedConfigs, configs, - setConfigs, }) => { const borderColor = useColorModeValue('gray.500', 'gray.700') const [logSize, setLogSize] = useState(0) @@ -132,7 +130,6 @@ const Footer: React.FC = ({ setSelectedConfigs={setSelectedConfigs} selectedConfigs={selectedConfigs} configs={configs} - setConfigs={setConfigs} /> @@ -161,7 +158,6 @@ const Footer: React.FC = ({ console.error('Sync failed:', error)} credentialsSaved={credentialsSaved} setCredentialsSaved={setCredentialsSaved} diff --git a/frontend/src/components/GitSyncModal/index.tsx b/frontend/src/components/GitSyncModal/index.tsx index 58383eb6..83298bb5 100644 --- a/frontend/src/components/GitSyncModal/index.tsx +++ b/frontend/src/components/GitSyncModal/index.tsx @@ -38,7 +38,6 @@ import useCustomToast from '../CustomToast' const GitSyncModal: React.FC = ({ isGitSyncModalOpen, closeGitSyncModal, - onSettingsSaved, credentialsSaved, setCredentialsSaved, setPollingInterval, @@ -120,7 +119,6 @@ const GitSyncModal: React.FC = ({ setPollingInterval(0) setGitToken('') - onSettingsSaved?.() setCredentialsSaved(true) closeGitSyncModal() toast({ @@ -186,7 +184,6 @@ const GitSyncModal: React.FC = ({ password: credentials, }) - onSettingsSaved?.() setCredentialsSaved(true) toast({ title: 'Settings saved successfully', diff --git a/frontend/src/components/Main/index.tsx b/frontend/src/components/Main/index.tsx index b02d1f9c..9d16f926 100644 --- a/frontend/src/components/Main/index.tsx +++ b/frontend/src/components/Main/index.tsx @@ -1,7 +1,8 @@ -import React, { useEffect, useState } from 'react' +import React, { useCallback, useEffect, useState } from 'react' import { Box, useColorModeValue, VStack } from '@chakra-ui/react' import { open, save } from '@tauri-apps/api/dialog' +import { listen } from '@tauri-apps/api/event' import { readTextFile, writeTextFile } from '@tauri-apps/api/fs' import { invoke } from '@tauri-apps/api/tauri' @@ -12,10 +13,11 @@ import Footer from '../Footer' import GitSyncModal from '../GitSyncModal' import PortForwardTable from '../PortForwardTable' -const initalRemotePort = 0 +const initialRemotePort = 0 const initialLocalPort = 0 const initialId = 0 const initialStatus = 0 + const KFTray = () => { const toast = useCustomToast() const [pollingInterval, setPollingInterval] = useState(0) @@ -24,9 +26,7 @@ const KFTray = () => { const [isGitSyncModalOpen, setIsGitSyncModalOpen] = useState(false) const [selectedConfigs, setSelectedConfigs] = useState([]) const [credentialsSaved, setCredentialsSaved] = useState(false) - const [isEdit, setIsEdit] = useState(false) - const [newConfig, setNewConfig] = useState({ id: 0, service: '', @@ -43,33 +43,67 @@ const KFTray = () => { alias: '', kubeconfig: 'default', }) + const cancelRef = React.useRef(null) + const [isInitiating, setIsInitiating] = useState(false) + const [isStopping, setIsStopping] = useState(false) + const [isAlertOpen, setIsAlertOpen] = useState(false) + const [configToDelete, setConfigToDelete] = useState() - const updateConfigRunningState = (id: number, isRunning: boolean) => { - setConfigs(prevConfigs => - prevConfigs.map(config => - config.id === id ? { ...config, isRunning } : config, - ), - ) + const fetchConfigsWithState = useCallback(async () => { + try { + const configsResponse = await invoke('get_configs_cmd') + const configStates = await invoke('get_config_states') - if (isRunning) { - setSelectedConfigs(prevSelectedConfigs => - prevSelectedConfigs.filter(config => config.id !== id), - ) + return configsResponse.map(config => ({ + ...config, + isRunning: + configStates.find(state => state.config_id === config.id) + ?.is_running || false, + })) + } catch (error) { + console.error('Failed to fetch configs:', error) + throw error } - } - const syncConfigsAndUpdateState = async () => { - try { - const updatedConfigs = await invoke('get_configs') + }, []) - if (!updatedConfigs) { - return - } + const updateConfigsWithState = useCallback(async () => { + try { + const updatedConfigs = await fetchConfigsWithState() setConfigs(updatedConfigs) } catch (error) { - console.error('Error syncing configs:', error) + console.error('Error updating configs:', error) } - } + }, [fetchConfigsWithState]) + + useEffect(() => { + let isMounted = true + + const fetchConfigs = async () => { + try { + const configsWithState = await fetchConfigsWithState() + + if (isMounted) { + setConfigs(configsWithState) + console.log('configsWithState:', configsWithState) + } + } catch (error) { + console.error('Failed to fetch configs:', error) + } + } + + fetchConfigs() + + const unlisten = listen('config_state_changed', async () => { + await updateConfigsWithState() + console.log('config_state_changed') + }) + + return () => { + isMounted = false + unlisten.then(unsub => unsub()) + } + }, [fetchConfigsWithState, updateConfigsWithState]) const openModal = () => { setNewConfig({ @@ -79,7 +113,7 @@ const KFTray = () => { local_port: initialLocalPort, local_address: '127.0.0.1', domain_enabled: false, - remote_port: initalRemotePort, + remote_port: initialRemotePort, namespace: '', workload_type: '', target: '', @@ -91,6 +125,7 @@ const KFTray = () => { setIsEdit(false) setIsModalOpen(true) } + const closeModal = () => { setIsModalOpen(false) setIsEdit(false) @@ -106,59 +141,20 @@ const KFTray = () => { const handleInputChange = (e: React.ChangeEvent) => { const { name, value } = e.target - - let updatedValue: string | number - - if (name === 'local_port' || name === 'remote_port') { - updatedValue = value === '' ? '' : Number(value) - } else { - updatedValue = value - } - - setNewConfig(prev => ({ - ...prev, - [name]: updatedValue, - })) + const updatedValue = + name === 'local_port' || name === 'remote_port' + ? value === '' + ? '' + : Number(value) + : value + + setNewConfig(prev => ({ ...prev, [name]: updatedValue })) } - const cancelRef = React.useRef(null) - const [isInitiating, setIsInitiating] = useState(false) - const [isStopping, setIsStopping] = useState(false) - const [isPortForwarding, setIsPortForwarding] = useState(false) - const [isAlertOpen, setIsAlertOpen] = useState(false) - const [configToDelete, setConfigToDelete] = useState() - - useEffect(() => { - let isMounted = true - - const fetchConfigs = async () => { - try { - const configsResponse = await invoke('get_configs') - - if (isMounted) { - setConfigs( - configsResponse.map(config => ({ - ...config, - isRunning: false, - })), - ) - } - } catch (error) { - console.error('Failed to fetch configs:', error) - } - } - - fetchConfigs() - - return () => { - isMounted = false - } - }, []) const handleExportConfigs = async () => { try { await invoke('open_save_dialog') - - const json = await invoke('export_configs') + const json = await invoke('export_configs_cmd') if (typeof json !== 'string') { throw new Error('The exported config is not a string') @@ -191,28 +187,21 @@ const KFTray = () => { }) } } + const handleImportConfigs = async () => { try { await invoke('open_save_dialog') - const selected = await open({ - filters: [ - { - name: 'JSON', - extensions: ['json'], - }, - ], + filters: [{ name: 'JSON', extensions: ['json'] }], multiple: false, }) await invoke('close_save_dialog') + if (typeof selected === 'string') { const jsonContent = await readTextFile(selected) - await invoke('import_configs', { json: jsonContent }) - const updatedConfigs = await invoke('get_configs') - - setConfigs(updatedConfigs) + await invoke('import_configs_cmd', { json: jsonContent }) toast({ title: 'Success', description: 'Configuration imported successfully.', @@ -234,26 +223,12 @@ const KFTray = () => { }) } } + const handleEditConfig = async (id: number) => { try { - const configToEdit = await invoke('get_config', { id }) - - setNewConfig({ - id: configToEdit.id, - service: configToEdit.service, - namespace: configToEdit.namespace, - local_port: configToEdit.local_port, - local_address: configToEdit.local_address, - domain_enabled: configToEdit.domain_enabled, - remote_port: configToEdit.remote_port, - context: configToEdit.context, - workload_type: configToEdit.workload_type, - target: configToEdit.target, - protocol: configToEdit.protocol, - remote_address: configToEdit.remote_address, - alias: configToEdit.alias, - kubeconfig: configToEdit.kubeconfig, - }) + const configToEdit = await invoke('get_config_cmd', { id }) + + setNewConfig(configToEdit) setIsEdit(true) setIsModalOpen(true) } catch (error) { @@ -263,165 +238,52 @@ const KFTray = () => { ) } } + const handleEditSubmit = async (e: React.FormEvent) => { e.preventDefault() - try { - const editedConfig = { - id: newConfig.id, - service: newConfig.service, - context: newConfig.context, - local_port: newConfig.local_port, - local_address: newConfig.local_address, - domain_enabled: newConfig.domain_enabled, - remote_port: newConfig.remote_port, - namespace: newConfig.namespace, - workload_type: newConfig.workload_type, - target: newConfig.target, - protocol: newConfig.protocol, - remote_address: newConfig.remote_address, - alias: newConfig.alias, - kubeconfig: newConfig.kubeconfig, - } - - await invoke('update_config', { config: editedConfig }) - - const updatedConfigs = await invoke('get_configs') - - setConfigs(updatedConfigs) + await invoke('update_config_cmd', { config: newConfig }) toast({ title: 'Success', description: 'Configuration updated successfully.', status: 'success', }) - closeModal() } catch (error) { toast({ title: 'Error', - description: 'Failed to update configuration.', + description: `Failed to update configuration. ${error.message}`, status: 'error', }) } } - const fetchAndUpdateConfigs = async () => { - try { - const updatedConfigs = await invoke('get_configs') - - setConfigs(updatedConfigs) - } catch (error) { - console.error('Failed to fetch updated configs:', error) - } - } - // eslint-disable-next-line complexity const handleSaveConfig = async (_configToSave: Config) => { - const updatedConfigToSave: Config = { - id: isEdit ? newConfig.id : 0, - service: newConfig.service, - context: newConfig.context, - local_port: newConfig.local_port, - local_address: newConfig.local_address, - domain_enabled: newConfig.domain_enabled, - remote_port: newConfig.remote_port, - namespace: newConfig.namespace, - workload_type: newConfig.workload_type, - target: newConfig.target, - protocol: newConfig.protocol, - remote_address: newConfig.remote_address, - alias: newConfig.alias, - kubeconfig: newConfig.kubeconfig, - } - - console.log('Sending config to save:', updatedConfigToSave) try { + const updatedConfigToSave: Config = { + ...newConfig, + id: isEdit ? newConfig.id : 0, + } let wasRunning = false - const originalConfigsRunningState = new Map() - - configs.forEach(conf => - originalConfigsRunningState.set(conf.id, conf.isRunning), + const originalConfigsRunningState = new Map( + configs.map(conf => [conf.id, conf.isRunning]), ) if (isEdit && originalConfigsRunningState.get(newConfig.id)) { wasRunning = true - - if ( - (newConfig.workload_type === 'service' || - newConfig.workload_type === 'pod') && - newConfig.protocol === 'tcp' - ) { - await invoke('stop_port_forward', { - serviceName: newConfig.service, - configId: newConfig.id.toString(), - }) - } else if ( - newConfig.workload_type.startsWith('proxy') || - ((newConfig.workload_type === 'service' || - newConfig.workload_type === 'pod') && - newConfig.protocol === 'udp') - ) { - await invoke('stop_proxy_forward', { - configId: newConfig.id.toString(), - namespace: newConfig.namespace, - serviceName: newConfig.service, - localPort: newConfig.local_port, - remoteAddress: newConfig.remote_address, - protocol: 'tcp', - }) - } else { - throw new Error( - `Unsupported workload type: ${newConfig.workload_type}`, - ) - } + await stopPortForwardingForConfig(newConfig) } if (isEdit) { - await invoke('update_config', { config: updatedConfigToSave }) + await invoke('update_config_cmd', { config: updatedConfigToSave }) } else { - await invoke('insert_config', { config: updatedConfigToSave }) + await invoke('insert_config_cmd', { config: updatedConfigToSave }) } - let updatedConfigs = await invoke('get_configs') - - updatedConfigs = updatedConfigs.map(conf => ({ - ...conf, - isRunning: - conf.id === newConfig.id - ? wasRunning - : originalConfigsRunningState.get(conf.id) || false, - })) - if (wasRunning) { - const updatedConfig = updatedConfigs.find( - conf => conf.id === newConfig.id, - ) - - if (updatedConfig) { - if ( - (updatedConfig.workload_type === 'service' || - updatedConfig.workload_type === 'pod') && - updatedConfig.protocol === 'tcp' - ) { - await invoke('start_port_forward_tcp', { configs: [updatedConfig] }) - } else if ( - updatedConfig.workload_type.startsWith('proxy') || - ((newConfig.workload_type === 'service' || - newConfig.workload_type === 'pod') && - updatedConfig.protocol === 'udp') - ) { - await invoke('deploy_and_forward_pod', { configs: [updatedConfig] }) - } else { - throw new Error( - `Unsupported workload type: ${updatedConfig.workload_type}`, - ) - } - updatedConfigs = updatedConfigs.map(conf => - conf.id === updatedConfig.id ? { ...conf, isRunning: true } : conf, - ) - } + await startPortForwardingForConfig(newConfig) } - setConfigs(updatedConfigs) toast({ title: 'Success', description: `Configuration ${ @@ -429,7 +291,6 @@ const KFTray = () => { } successfully.`, status: 'success', }) - closeModal() } catch (error) { console.error(`Failed to ${isEdit ? 'update' : 'add'} config:`, error) @@ -441,6 +302,50 @@ const KFTray = () => { } } + const stopPortForwardingForConfig = async (config: Config) => { + if ( + (config.workload_type === 'service' || config.workload_type === 'pod') && + config.protocol === 'tcp' + ) { + await invoke('stop_port_forward_cmd', { + serviceName: config.service, + configId: config.id.toString(), + }) + } else if ( + config.workload_type.startsWith('proxy') || + ((config.workload_type === 'service' || config.workload_type === 'pod') && + config.protocol === 'udp') + ) { + await invoke('stop_proxy_forward_cmd', { + configId: config.id.toString(), + namespace: config.namespace, + serviceName: config.service, + localPort: config.local_port, + remoteAddress: config.remote_address, + protocol: 'tcp', + }) + } else { + throw new Error(`Unsupported workload type: ${config.workload_type}`) + } + } + + const startPortForwardingForConfig = async (config: Config) => { + if ( + (config.workload_type === 'service' || config.workload_type === 'pod') && + config.protocol === 'tcp' + ) { + await invoke('start_port_forward_tcp_cmd', { configs: [config] }) + } else if ( + config.workload_type.startsWith('proxy') || + ((config.workload_type === 'service' || config.workload_type === 'pod') && + config.protocol === 'udp') + ) { + await invoke('deploy_and_forward_pod_cmd', { configs: [config] }) + } else { + throw new Error(`Unsupported workload type: ${config.workload_type}`) + } + } + const initiatePortForwarding = async (configsToStart: Status[]) => { setIsInitiating(true) const errors = [] @@ -448,10 +353,8 @@ const KFTray = () => { for (const config of configsToStart) { try { await handlePortForwarding(config) - updateConfigRunningState(config.id, true) } catch (error) { errors.push({ id: config.id, error }) - updateConfigRunningState(config.id, false) } } @@ -470,29 +373,33 @@ const KFTray = () => { setIsInitiating(false) } - async function handlePortForwarding(config: Status) { + const handlePortForwarding = async (config: Status) => { switch (config.workload_type) { case 'service': case 'pod': if (config.protocol === 'tcp') { - await invoke('start_port_forward_tcp', { + await invoke('start_port_forward_tcp_cmd', { configs: [config], }) } else if (config.protocol === 'udp') { - await invoke('deploy_and_forward_pod', { + await invoke('deploy_and_forward_pod_cmd', { configs: [config], }) } break case 'proxy': - await invoke('deploy_and_forward_pod', { configs: [config] }) + await invoke('deploy_and_forward_pod_cmd', { + configs: [config], + }) break default: throw new Error(`Unsupported workload type: ${config.workload_type}`) } } - const handleDeleteConfig = (id: number) => { + + const handleDeleteConfig = async (id: number) => { setConfigToDelete(id) + setIsAlertOpen(true) } @@ -508,19 +415,7 @@ const KFTray = () => { } try { - await invoke('delete_config', { id: configToDelete }) - - const configsAfterDeletion = await invoke('get_configs') - const runningStateMap = new Map( - configs.map(conf => [conf.id, conf.isRunning]), - ) - - const updatedConfigs = configsAfterDeletion.map(conf => ({ - ...conf, - isRunning: runningStateMap.get(conf.id) ?? false, - })) - - setConfigs(updatedConfigs) + await invoke('delete_config_cmd', { id: configToDelete }) toast({ title: 'Success', @@ -535,25 +430,16 @@ const KFTray = () => { status: 'error', }) } - setIsAlertOpen(false) } - const stopPortForwarding = async () => { + const stopAllPortForwarding = async () => { setIsStopping(true) try { - const responses = await invoke('stop_all_port_forward') - + const responses = await invoke('stop_all_port_forward_cmd') const allStopped = responses.every(res => res.status === initialStatus) if (allStopped) { - const updatedConfigs = configs.map(config => ({ - ...config, - isRunning: false, - })) - - setConfigs(updatedConfigs) - setIsPortForwarding(false) toast({ title: 'Success', description: @@ -632,26 +518,22 @@ const KFTray = () => { setIsInitiating={setIsInitiating} isStopping={isStopping} handleEditConfig={handleEditConfig} - stopPortForwarding={stopPortForwarding} + stopAllPortForwarding={stopAllPortForwarding} handleDeleteConfig={handleDeleteConfig} confirmDeleteConfig={confirmDeleteConfig} isAlertOpen={isAlertOpen} setIsAlertOpen={setIsAlertOpen} - updateConfigRunningState={updateConfigRunningState} - isPortForwarding={isPortForwarding} selectedConfigs={selectedConfigs} setSelectedConfigs={setSelectedConfigs} /> - { openGitSyncModal={openGitSyncModal} handleExportConfigs={handleExportConfigs} handleImportConfigs={handleImportConfigs} - onConfigsSynced={syncConfigsAndUpdateState} setCredentialsSaved={setCredentialsSaved} credentialsSaved={credentialsSaved} isGitSyncModalOpen={isGitSyncModalOpen} @@ -677,7 +558,6 @@ const KFTray = () => { pollingInterval={pollingInterval} setSelectedConfigs={setSelectedConfigs} configs={configs} - setConfigs={setConfigs} /> diff --git a/frontend/src/components/PortForwardTable/ContextsAccordion/PortForwardRow/index.tsx b/frontend/src/components/PortForwardTable/ContextsAccordion/PortForwardRow/index.tsx index 380226eb..cc026e18 100644 --- a/frontend/src/components/PortForwardTable/ContextsAccordion/PortForwardRow/index.tsx +++ b/frontend/src/components/PortForwardTable/ContextsAccordion/PortForwardRow/index.tsx @@ -46,11 +46,9 @@ const PortForwardRow: React.FC = ({ handleEditConfig, setIsAlertOpen, isAlertOpen, - updateConfigRunningState, showContext = false, selected, onSelectionChange, - updateSelectionState, isInitiating, setIsInitiating, }) => { @@ -70,7 +68,9 @@ const PortForwardRow: React.FC = ({ const fetchHttpLogState = async () => { try { - const enabled = await invoke('get_http_logs', { configId: config.id }) + const enabled = await invoke('get_http_logs_cmd', { + configId: config.id, + }) setHttpLogsEnabled(prevState => ({ ...prevState, @@ -107,19 +107,17 @@ const PortForwardRow: React.FC = ({ config.workload_type === 'pod') && config.protocol === 'tcp' ) { - await invoke('start_port_forward_tcp', { configs: [config] }) + await invoke('start_port_forward_tcp_cmd', { configs: [config] }) } else if ( config.workload_type.startsWith('proxy') || ((config.workload_type === 'service' || config.workload_type === 'pod') && config.protocol === 'udp') ) { - await invoke('deploy_and_forward_pod', { configs: [config] }) + await invoke('deploy_and_forward_pod_cmd', { configs: [config] }) } else { throw new Error(`Unsupported workload type: ${config.workload_type}`) } - updateConfigRunningState(config.id, true) - updateSelectionState(config.id, true) } catch (error) { console.error('An error occurred during port forwarding start:', error) const errorMessage = @@ -130,8 +128,6 @@ const PortForwardRow: React.FC = ({ description: errorMessage, status: 'error', }) - - updateConfigRunningState(config.id, false) } } @@ -142,7 +138,7 @@ const PortForwardRow: React.FC = ({ config.workload_type === 'pod') && config.protocol === 'tcp' ) { - await invoke('stop_port_forward', { + await invoke('stop_port_forward_cmd', { serviceName: config.service, configId: config.id.toString(), }) @@ -152,7 +148,7 @@ const PortForwardRow: React.FC = ({ config.workload_type === 'pod') && config.protocol === 'udp') ) { - await invoke('stop_proxy_forward', { + await invoke('stop_proxy_forward_cmd', { configId: config.id.toString(), namespace: config.namespace, serviceName: config.service, @@ -163,7 +159,6 @@ const PortForwardRow: React.FC = ({ } else { throw new Error(`Unsupported workload type: ${config.workload_type}`) } - updateConfigRunningState(config.id, false) } catch (error) { console.error('An error occurred during port forwarding stop:', error) const errorMessage = @@ -223,7 +218,10 @@ const PortForwardRow: React.FC = ({ try { const newState = !httpLogsEnabled[config.id] - await invoke('set_http_logs', { configId: config.id, enable: newState }) + await invoke('set_http_logs_cmd', { + configId: config.id, + enable: newState, + }) setHttpLogsEnabled(prevState => ({ ...prevState, [config.id]: newState })) } catch (error) { console.error('Error toggling HTTP logs:', error) diff --git a/frontend/src/components/PortForwardTable/ContextsAccordion/index.tsx b/frontend/src/components/PortForwardTable/ContextsAccordion/index.tsx index be0a079a..fad425d9 100644 --- a/frontend/src/components/PortForwardTable/ContextsAccordion/index.tsx +++ b/frontend/src/components/PortForwardTable/ContextsAccordion/index.tsx @@ -35,9 +35,7 @@ const ContextsAccordion: React.FC = ({ handleEditConfig, isAlertOpen, setIsAlertOpen, - updateConfigRunningState, handleSelectionChange, - updateSelectionState, selectedConfigsByContext, handleCheckboxChange, isInitiating, @@ -181,9 +179,7 @@ const ContextsAccordion: React.FC = ({ onSelectionChange={isSelected => handleSelectionChange(config, isSelected) } - updateSelectionState={updateSelectionState} setIsAlertOpen={setIsAlertOpen} - updateConfigRunningState={updateConfigRunningState} isInitiating={isInitiating} setIsInitiating={setIsInitiating} isStopping={isStopping} diff --git a/frontend/src/components/PortForwardTable/index.tsx b/frontend/src/components/PortForwardTable/index.tsx index 03d7fa58..d44de777 100644 --- a/frontend/src/components/PortForwardTable/index.tsx +++ b/frontend/src/components/PortForwardTable/index.tsx @@ -15,13 +15,12 @@ const PortForwardTable: React.FC = ({ setIsInitiating, isStopping, initiatePortForwarding, - stopPortForwarding, + stopAllPortForwarding, handleEditConfig, handleDeleteConfig, confirmDeleteConfig, isAlertOpen, setIsAlertOpen, - updateConfigRunningState, selectedConfigs, setSelectedConfigs, }) => { @@ -35,12 +34,6 @@ const PortForwardTable: React.FC = ({ >({}) const [isCheckboxAction, setIsCheckboxAction] = useState(false) - const updateSelectionState = (id: number, isRunning: boolean) => { - if (isRunning) { - setSelectedConfigs(prev => prev.filter(config => config.id !== id)) - } - } - useEffect(() => { const isConfigRunning = (selectedConfig: Status) => configs.some( @@ -91,12 +84,6 @@ const PortForwardTable: React.FC = ({ } } - const stopAllPortForwarding = () => { - const runningConfigs = configs.filter(config => config.isRunning) - - stopPortForwarding(runningConfigs) - } - const configsByContext = useConfigsByContext(filteredConfigs) const borderColor = useColorModeValue('gray.200', 'gray.700') const boxShadow = useColorModeValue('base', 'lg') @@ -271,9 +258,7 @@ const PortForwardTable: React.FC = ({ handleEditConfig={handleEditConfig} isAlertOpen={isAlertOpen} setIsAlertOpen={setIsAlertOpen} - updateConfigRunningState={updateConfigRunningState} handleSelectionChange={handleSelectionChange} - updateSelectionState={updateSelectionState} selectedConfigsByContext={selectedConfigsByContext} handleCheckboxChange={handleCheckboxChange} isInitiating={isInitiating} diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts index ca9e5242..984ad544 100644 --- a/frontend/src/types/index.ts +++ b/frontend/src/types/index.ts @@ -2,21 +2,7 @@ import { RefObject } from 'react' export interface Status { id: number - service: string - context: string - local_port: number isRunning: boolean - namespace: string - remote_port: number - local_address: string - domain_enabled: boolean - workload_type: string - target: string - protocol: string - alias: string - remote_address: string - cancelRef?: RefObject - kubeconfig?: string } export interface Config { @@ -76,7 +62,6 @@ export interface ConfigProps { export interface GitSyncModalProps { isGitSyncModalOpen: boolean closeGitSyncModal: () => void - onSettingsSaved: () => void credentialsSaved: boolean setCredentialsSaved: React.Dispatch> setPollingInterval: React.Dispatch> @@ -87,15 +72,13 @@ export interface TableProps { configs: Status[] isInitiating: boolean isStopping: boolean - isPortForwarding: boolean initiatePortForwarding: (configs: Status[]) => Promise - stopPortForwarding: (configs: Status[]) => Promise + stopAllPortForwarding: (configs: Status[]) => Promise confirmDeleteConfig: () => void handleDeleteConfig: (id: number) => void handleEditConfig: (id: number) => void isAlertOpen: boolean setIsAlertOpen: (isOpen: boolean) => void - updateConfigRunningState: (id: number, isRunning: boolean) => void selectedConfigs: Status[] setSelectedConfigs: React.Dispatch> setIsInitiating: React.Dispatch> @@ -108,10 +91,8 @@ export interface PortForwardRowProps { handleEditConfig: (id: number) => void isAlertOpen: boolean setIsAlertOpen: (isOpen: boolean) => void - updateConfigRunningState: (id: number, isRunning: boolean) => void showContext?: boolean onSelectionChange: (isSelected: boolean) => void - updateSelectionState: (id: number, isRunning: boolean) => void selected: boolean isInitiating: boolean setIsInitiating: React.Dispatch> @@ -125,20 +106,17 @@ export interface FooterProps { handleImportConfigs: () => void credentialsSaved: boolean setCredentialsSaved: React.Dispatch> - onConfigsSynced: () => void isGitSyncModalOpen: boolean selectedConfigs: Status[] setPollingInterval: React.Dispatch> pollingInterval: number setSelectedConfigs: React.Dispatch> configs: Status[] - setConfigs: React.Dispatch> } export interface SyncConfigsButtonProps { serviceName: string accountName: string - onConfigsSynced?: () => void onSyncFailure?: (error: Error) => void credentialsSaved: boolean setCredentialsSaved: React.Dispatch> @@ -238,7 +216,6 @@ export interface BulkDeleteButtonProps { selectedConfigs: Status[] setSelectedConfigs: React.Dispatch> configs: Status[] - setConfigs: React.Dispatch> } export interface ContextsAccordionProps { @@ -250,9 +227,7 @@ export interface ContextsAccordionProps { handleEditConfig: (id: number) => void isAlertOpen: boolean setIsAlertOpen: (isOpen: boolean) => void - updateConfigRunningState: (id: number, isRunning: boolean) => void handleSelectionChange: (config: Status, isSelected: boolean) => void - updateSelectionState: (id: number, isRunning: boolean) => void selectedConfigsByContext: Record handleCheckboxChange: (context: string, isChecked: boolean) => void isInitiating: boolean diff --git a/hacks/kftray-utils/Cargo.lock b/hacks/kftray-utils/Cargo.lock new file mode 100644 index 00000000..283400a9 --- /dev/null +++ b/hacks/kftray-utils/Cargo.lock @@ -0,0 +1,1292 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "aligned-vec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" + +[[package]] +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "anstyle-parse" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" + +[[package]] +name = "arg_enum_proc_macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "av1-grain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6678909d8c5d46a42abcf571271e15fdbc0a225e3646cf23762cd415046c78bf" +dependencies = [ + "anyhow", + "arrayvec", + "log", + "nom", + "num-rational", + "v_frame", +] + +[[package]] +name = "avif-serialize" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876c75a42f6364451a033496a14c44bffe41f5f4a8236f697391f11024e596d2" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitstream-io" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcde5f311c85b8ca30c2e4198d4326bc342c76541590106f5fa4a50946ea499" + +[[package]] +name = "built" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "236e6289eda5a812bc6b53c3b024039382a2895fbbeef2d748b2931546d392c4" + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "bytemuck" +version = "1.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "102087e286b4677862ea56cf8fc58bb2cdfa8725c40ffb80fe3a008eb7f2fc83" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + +[[package]] +name = "cc" +version = "1.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68064e60dbf1f17005c2fde4d07c16d8baa506fd7ffed8ccab702d93617975c7" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "colorchoice" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "env_filter" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "exr" +version = "1.72.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4" +dependencies = [ + "bit_field", + "flume", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + +[[package]] +name = "fdeflate" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "flate2" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f211bbe8e69bbd0cfdea405084f128ae8b4aaa6b0b522fc8f2b009084797920" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "spin", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gif" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "ico" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3804960be0bb5e4edb1e1ad67afd321a9ecfd875c3e65c099468fd2717d7cae" +dependencies = [ + "byteorder", + "png", +] + +[[package]] +name = "image" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99314c8a2152b8ddb211f924cdae532d8c5e4c8bb54728e12fff1b0cd5963a10" +dependencies = [ + "bytemuck", + "byteorder-lite", + "color_quant", + "exr", + "gif", + "image-webp", + "num-traits", + "png", + "qoi", + "ravif", + "rayon", + "rgb", + "tiff", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "image-webp" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f79afb8cbee2ef20f59ccd477a218c12a93943d075b492015ecb1bb81f8ee904" +dependencies = [ + "byteorder-lite", + "quick-error", +] + +[[package]] +name = "imgref" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44feda355f4159a7c757171a77de25daf6411e217b4cabd03bd6650690468126" + +[[package]] +name = "indexmap" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "interpolate_name" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + +[[package]] +name = "jpeg-decoder" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" + +[[package]] +name = "kftray-utils" +version = "0.0.1" +dependencies = [ + "env_logger", + "ico", + "image", + "log", + "regex", + "serde_json", + "thiserror", +] + +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + +[[package]] +name = "libc" +version = "0.2.156" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5f43f184355eefb8d17fc948dbecf6c13be3c141f20d834ae842193a448c72a" + +[[package]] +name = "libfuzzer-sys" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" +dependencies = [ + "arbitrary", + "cc", + "once_cell", +] + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] + +[[package]] +name = "maybe-rayon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", + "simd-adler32", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "noop_proc_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "png" +version = "0.17.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" +dependencies = [ + "bitflags", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "profiling" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58" +dependencies = [ + "profiling-procmacros", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rav1e" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" +dependencies = [ + "arbitrary", + "arg_enum_proc_macro", + "arrayvec", + "av1-grain", + "bitstream-io", + "built", + "cfg-if", + "interpolate_name", + "itertools", + "libc", + "libfuzzer-sys", + "log", + "maybe-rayon", + "new_debug_unreachable", + "noop_proc_macro", + "num-derive", + "num-traits", + "once_cell", + "paste", + "profiling", + "rand", + "rand_chacha", + "simd_helpers", + "system-deps", + "thiserror", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.11.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f0bfd976333248de2078d350bfdf182ff96e168a24d23d2436cef320dd4bdd" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error", + "rav1e", + "rgb", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "regex" +version = "1.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "rgb" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f86ae463694029097b846d8f99fd5536740602ae00022c0c50c5600720b2f71" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.208" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.208" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +dependencies = [ + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "simd_helpers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +dependencies = [ + "quote", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "syn" +version = "2.0.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "thiserror" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tiff" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "v_frame" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b" +dependencies = [ + "aligned-vec", + "num-traits", + "wasm-bindgen", +] + +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" + +[[package]] +name = "weezl" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +dependencies = [ + "memchr", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "zune-jpeg" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16099418600b4d8f028622f73ff6e3deaabdff330fb9a2a131dea781ee8b0768" +dependencies = [ + "zune-core", +] diff --git a/crates/kftray-utils/Cargo.toml b/hacks/kftray-utils/Cargo.toml similarity index 89% rename from crates/kftray-utils/Cargo.toml rename to hacks/kftray-utils/Cargo.toml index 1b1dc86f..ed74bdf2 100644 --- a/crates/kftray-utils/Cargo.toml +++ b/hacks/kftray-utils/Cargo.toml @@ -10,6 +10,7 @@ serde_json = "1.0" image = "0.25.2" ico = "0.3.0" thiserror = "1" +env_logger = "0.11.5" [[bin]] name = "bump_version" @@ -18,3 +19,5 @@ path = "src/bump_version.rs" [[bin]] name = "generate_icons" path = "src/generate_icons.rs" + +[workspace] diff --git a/crates/kftray-utils/src/bump_version.rs b/hacks/kftray-utils/src/bump_version.rs similarity index 83% rename from crates/kftray-utils/src/bump_version.rs rename to hacks/kftray-utils/src/bump_version.rs index 75292cab..056c1c9d 100644 --- a/crates/kftray-utils/src/bump_version.rs +++ b/hacks/kftray-utils/src/bump_version.rs @@ -2,6 +2,7 @@ use std::{ env, fs, io, + path::Path, process::{ Command, ExitCode, @@ -11,11 +12,14 @@ use std::{ use log::{ error, info, + debug, }; use regex::Regex; use serde_json::Value; fn main() -> ExitCode { + env_logger::init(); + let args: Vec = env::args().collect(); const USAGE: &str = "Usage: bump_version "; @@ -59,16 +63,21 @@ fn main() -> ExitCode { fn bump_version(bump_type: &str) -> io::Result<()> { log::info!("Bumping version to {}", bump_type); - let dir = "frontend"; - log::info!("Bumping version to {} in directory {}", bump_type, dir); + let dir = "../../frontend"; + let current_dir = env::current_dir()?; + let absolute_dir = current_dir.join(dir); + + log::info!("Current directory: {:?}", current_dir); + log::info!("Bumping version to {} in directory {:?}", bump_type, absolute_dir); let npm_output = Command::new("npm") .args(["version", bump_type, "--no-git-tag-version"]) - .current_dir(dir) + .current_dir(&absolute_dir) .output()?; if !npm_output.status.success() { let error_output = String::from_utf8_lossy(&npm_output.stderr).to_string(); + error!("NPM command failed: {}", error_output); return Err(io::Error::new(io::ErrorKind::Other, error_output)); } @@ -92,7 +101,7 @@ fn bump_version(bump_type: &str) -> io::Result<()> { info!("Updating version in Cargo.toml, README.md and tauri.conf.json"); update_file_content( - "crates/kftray-tauri/Cargo.toml", + "../../crates/kftray-tauri/Cargo.toml", new_version, update_cargo_toml_version, )?; @@ -100,28 +109,33 @@ fn bump_version(bump_type: &str) -> io::Result<()> { log::info!("kftray-tauri Cargo.toml updated"); update_file_content( - "crates/kftray-server/Cargo.toml", + "../../crates/kftray-server/Cargo.toml", + new_version, + update_cargo_toml_version, + )?; + + log::info!("kftray-server Cargo.toml updated"); + + update_file_content( + "../../crates/kftui/Cargo.toml", new_version, update_cargo_toml_version, )?; - info!("kftray-server Cargo.toml updated"); + log::info!("kftui Cargo.toml updated"); - update_file_content("README.md", new_version, update_markdown_version)?; + update_file_content("../../docs/kftray/INSTALL.md", new_version, update_markdown_version)?; log::info!("README.md updated"); update_file_content( - "crates/kftray-tauri/tauri.conf.json", + "../../crates/kftray-tauri/tauri.conf.json", new_version, update_json_version, )?; log::info!("tauri.conf.json updated"); - git_tag(new_version)?; - - log::info!("Git tag and push completed"); log::info!("All versions updated to: {}", new_version); @@ -132,6 +146,7 @@ fn update_file_content(file_path: &str, new_version: &str, update_fn: F) -> i where F: Fn(&str, &str) -> io::Result, { + debug!("Reading file: {}", file_path); let content = fs::read_to_string(file_path).map_err(|e| { io::Error::new( io::ErrorKind::Other, @@ -139,6 +154,7 @@ where ) })?; + debug!("Updating content for file: {}", file_path); let updated_content = update_fn(&content, new_version).map_err(|e| { io::Error::new( io::ErrorKind::Other, @@ -146,6 +162,7 @@ where ) })?; + debug!("Writing updated content to file: {}", file_path); fs::write(file_path, updated_content).map_err(|e| { io::Error::new( io::ErrorKind::Other, @@ -211,20 +228,3 @@ fn update_json_version(content: &str, new_version: &str) -> io::Result { serde_json::to_string_pretty(&json_content) .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) } - -fn git_tag(new_version: &str) -> io::Result<()> { - let tag_name = format!("v{}", new_version); - - let git_tag_output = Command::new("git") - .args(["tag", "-f", &tag_name]) - .output()?; - - if !git_tag_output.status.success() { - return Err(io::Error::new( - io::ErrorKind::Other, - String::from_utf8_lossy(&git_tag_output.stderr).into_owned(), - )); - } - - Ok(()) -} diff --git a/crates/kftray-utils/src/generate_icons.rs b/hacks/kftray-utils/src/generate_icons.rs similarity index 100% rename from crates/kftray-utils/src/generate_icons.rs rename to hacks/kftray-utils/src/generate_icons.rs diff --git a/hacks/kftui_installer.ps1 b/hacks/kftui_installer.ps1 new file mode 100644 index 00000000..2678c51b --- /dev/null +++ b/hacks/kftui_installer.ps1 @@ -0,0 +1,66 @@ +# PowerShell script to install kftui on Windows + +$INSTALL_DIR = "$HOME\.local\bin" +$PROFILE_FILES = @("$HOME\.profile", "$HOME\.bashrc", "$HOME\.zshrc", "$HOME\.config\fish\config.fish") + +function Print-Message { + param ( + [string]$Color, + [string]$Message + ) + $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + switch ($Color) { + "red" { Write-Host "[$timestamp] $Message" -ForegroundColor Red } + "green" { Write-Host "[$timestamp] $Message" -ForegroundColor Green } + "yellow" { Write-Host "[$timestamp] $Message" -ForegroundColor Yellow } + "blue" { Write-Host "[$timestamp] $Message" -ForegroundColor Blue } + default { Write-Host "[$timestamp] $Message" } + } +} + +function Download-File { + param ( + [string]$Url, + [string]$Output + ) + Print-Message -Color "blue" -Message "Downloading kftui from $Url" + Invoke-WebRequest -Uri $Url -OutFile $Output +} + +function Install-Kftui { + param ( + [string]$Url, + [string]$Filename + ) + Download-File -Url $Url -Output $Filename + New-Item -ItemType Directory -Force -Path $INSTALL_DIR + Print-Message -Color "blue" -Message "Installing kftui to $INSTALL_DIR" + Move-Item -Force -Path $Filename -Destination $INSTALL_DIR + Print-Message -Color "green" -Message "kftui installed successfully" + + # Add $INSTALL_DIR to PATH if it's not already there + if (-not ($env:PATH -contains $INSTALL_DIR)) { + foreach ($profile in $PROFILE_FILES) { + if (Test-Path $profile) { + Add-Content -Path $profile -Value "export PATH=$INSTALL_DIR:`$PATH" + } + } + $env:PATH = "$INSTALL_DIR;$env:PATH" + Print-Message -Color "yellow" -Message "Added $INSTALL_DIR to PATH. Please restart your terminal or run 'source ~/.profile' to update your PATH." + } + + & "$INSTALL_DIR\kftui.exe" --version +} + +# Get the latest release tag from GitHub +$LATEST_RELEASE = (Invoke-RestMethod -Uri https://api.github.com/repos/hcavarsan/kftray/releases/latest).tag_name +$BASE_URL = "https://github.com/hcavarsan/kftray/releases/download/$LATEST_RELEASE" + +# Determine architecture +$ARCH = if ([System.Environment]::Is64BitOperatingSystem) { "x86_64" } else { "x86" } + +# Set the download URL based on architecture +$URL = "$BASE_URL/kftui_$ARCH.exe" + +# Install kftui +Install-Kftui -Url $URL -Filename "kftui.exe" diff --git a/hacks/kftui_installer.sh b/hacks/kftui_installer.sh new file mode 100755 index 00000000..ba1d8edd --- /dev/null +++ b/hacks/kftui_installer.sh @@ -0,0 +1,142 @@ +#!/bin/bash + +set -e + +INSTALL_DIR="$HOME/.local/bin" +PROFILE_FILES=("$HOME/.profile" "$HOME/.bashrc" "$HOME/.zshrc" "$HOME/.config/fish/config.fish") + +# Function to print messages in color with timestamp +print_msg() { + local color="$1" + local msg="$2" + local timestamp=$(date +"%Y-%m-%d %H:%M:%S") + case "$color" in + red) echo -e "\033[31m[$timestamp] $msg\033[0m" ;; + green) echo -e "\033[32m[$timestamp] $msg\033[0m" ;; + yellow) echo -e "\033[33m[$timestamp] $msg\033[0m" ;; + blue) echo -e "\033[34m[$timestamp] $msg\033[0m" ;; + *) echo "[$timestamp] $msg" ;; + esac +} + +# Function to determine the OS +detect_os() { + case "$(uname -s)" in + Darwin) echo "macos" ;; + Linux) + if grep -q Microsoft /proc/version; then + echo "wsl" + else + echo "linux" + fi + ;; + *) print_msg red "Unsupported OS"; exit 1 ;; + esac +} + +# Function to determine the architecture +detect_arch() { + case "$(uname -m)" in + x86_64) echo "amd64" ;; + arm64|aarch64) echo "arm64" ;; + i386|i686) echo "x86" ;; + *) print_msg red "Unsupported architecture"; exit 1 ;; + esac +} + +# Function to check if a command exists +command_exists() { + command -v "$1" >/dev/null 2>&1 +} + +# Ensure required tools are available +if ! command_exists curl && ! command_exists wget; then + print_msg red "Error: Neither curl nor wget is installed. Please install one of them and try again." + exit 1 +fi + +# Get the latest release tag from GitHub +LATEST_RELEASE=$(curl -s https://api.github.com/repos/hcavarsan/kftray/releases/latest | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') +if [ -z "$LATEST_RELEASE" ]; then + print_msg red "Error: Unable to fetch the latest release tag from GitHub." + exit 1 +fi + +BASE_URL="https://github.com/hcavarsan/kftray/releases/download/${LATEST_RELEASE}" + +# Determine OS and architecture +OS=$(detect_os) +ARCH=$(detect_arch) + +# Set the download URL based on OS and architecture +case "$OS" in + macos) URL="${BASE_URL}/kftui_macos_universal" ;; + linux|wsl) + if [ "$ARCH" = "amd64" ]; then + URL="${BASE_URL}/kftui_amd64" + elif [ "$ARCH" = "arm64" ]; then + URL="${BASE_URL}/kftui_arm64" + else + print_msg red "Error: Unsupported architecture for Linux/WSL." + exit 1 + fi + ;; + *) + print_msg red "Error: Unsupported OS." + exit 1 + ;; +esac + +# Function to download a file using curl or wget +download_file() { + local url=$1 + local output=$2 + + if command_exists curl; then + print_msg blue "Downloading kftui using curl from $url" + curl -L "$url" -o "$output" || { print_msg red "Error: Failed to download file using curl."; exit 1; } + elif command_exists wget; then + print_msg blue "Downloading kftui using wget from $url" + wget "$url" -O "$output" || { print_msg red "Error: Failed to download file using wget."; exit 1; } + else + print_msg red "Error: Neither curl nor wget is available for downloading." + exit 1 + fi +} + +# Function to download and install kftui +install_kftui() { + local url=$1 + local filename=$2 + + print_msg blue "Starting download and installation process for kftui" + download_file "$url" "$filename" + chmod +x "$filename" + mkdir -p "$INSTALL_DIR" + print_msg blue "Installing kftui to $INSTALL_DIR" + mv "$filename" "$INSTALL_DIR/" || { print_msg red "Error: Failed to move kftui to $INSTALL_DIR."; exit 1; } + print_msg green "kftui installed successfully" + + # Add $INSTALL_DIR to PATH if it's not already there + if ! echo "$PATH" | grep -q "$INSTALL_DIR"; then + for profile in "${PROFILE_FILES[@]}"; do + if [ -f "$profile" ]; then + case "$profile" in + *config.fish) echo "set -U fish_user_paths $INSTALL_DIR \$fish_user_paths" >> "$profile" ;; + *) echo "export PATH=$INSTALL_DIR:\$PATH" >> "$profile" ;; + esac + fi + done + export PATH="$INSTALL_DIR:$PATH" + print_msg yellow "Added $INSTALL_DIR to PATH. Please restart your terminal or run 'source ~/.profile' to update your PATH." + fi + + if ! kftui --version; then + print_msg red "Error: kftui installation verification failed." + exit 1 + fi + print_msg green "kftui installation completed successfully" +} + +# Install kftui +install_kftui "$URL" "kftui" diff --git a/icon.png b/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..133082daca2a3eaa9b2880b5766618bc6d83041a GIT binary patch literal 122541 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

do5J1V8(4}p+Lz~xR=7=|WeN0>~qvVz@aw*m7 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%0DB%a1h|83H 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�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 NsiB3zD?P_M{{q#spiKY( literal 0 HcmV?d00001 diff --git a/img/sstui.png b/img/sstui.png new file mode 100644 index 0000000000000000000000000000000000000000..585e3d8393f54b61ed0a8ee4da211196da9035e3 GIT binary patch literal 96853 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%0DiS&4!xb_CF~FH!XC?%O}~s0k#unx}id9)TfR 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 QjvN+aeTZI#j&s~U0iU_UnE(I) literal 0 HcmV?d00001 diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d1dc3800..f7245a70 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,7 +19,7 @@ importers: version: 2.1.1(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(react@18.3.1) '@chakra-ui/react': specifier: ^2.8.2 - version: 2.8.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(framer-motion@11.3.24(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 2.8.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(framer-motion@11.3.12(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@emotion/react': specifier: ^11.13.0 version: 11.13.0(@types/react@18.3.3)(react@18.3.1) @@ -37,10 +37,10 @@ importers: version: 0.2.2(@fortawesome/fontawesome-svg-core@6.6.0)(react@18.3.1) '@vitejs/plugin-react-swc': specifier: ^3.7.0 - version: 3.7.0(vite@5.4.0(@types/node@20.14.15)(terser@5.31.5)) + version: 3.7.0(vite@5.3.4(@types/node@20.14.12)(terser@5.31.3)) framer-motion: specifier: ^11.3.12 - version: 11.3.24(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 11.3.12(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: specifier: ^18.3.1 version: 18.3.1 @@ -56,7 +56,7 @@ importers: devDependencies: '@rollup/plugin-terser': specifier: ^0.4.4 - version: 0.4.4(rollup@4.20.0) + version: 0.4.4(rollup@4.19.0) '@tauri-apps/api': specifier: ^1.6.0 version: 1.6.0 @@ -65,7 +65,7 @@ importers: version: 1.6.0 '@types/node': specifier: ^20.14.12 - version: 20.14.15 + version: 20.14.12 '@types/react': specifier: ^18.3.3 version: 18.3.3 @@ -74,13 +74,13 @@ importers: version: 18.3.0 '@typescript-eslint/eslint-plugin': specifier: ^8.0.0 - version: 8.0.1(@typescript-eslint/parser@8.0.1(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0)(typescript@5.5.4) + version: 8.0.0(@typescript-eslint/parser@8.0.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0)(typescript@5.5.4) '@typescript-eslint/parser': specifier: ^8.0.0 - version: 8.0.1(eslint@8.57.0)(typescript@5.5.4) + version: 8.0.0(eslint@8.57.0)(typescript@5.5.4) core-js: specifier: ^3.37.1 - version: 3.38.0 + version: 3.37.1 eslint: specifier: ^8.57.0 version: 8.57.0 @@ -89,7 +89,7 @@ importers: version: 9.1.0(eslint@8.57.0) eslint-plugin-import: specifier: ^2.29.1 - version: 2.29.1(@typescript-eslint/parser@8.0.1(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0) + version: 2.29.1(@typescript-eslint/parser@8.0.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0) eslint-plugin-react: specifier: ^7.35.0 version: 7.35.0(eslint@8.57.0) @@ -116,7 +116,7 @@ importers: version: 5.5.4 vite: specifier: ^5.3.4 - version: 5.4.0(@types/node@20.14.15)(terser@5.31.5) + version: 5.3.4(@types/node@20.14.12)(terser@5.31.3) packages: @@ -124,14 +124,30 @@ packages: resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} engines: {node: '>=6.9.0'} - '@babel/generator@7.25.0': - resolution: {integrity: sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==} + '@babel/generator@7.24.10': + resolution: {integrity: sha512-o9HBZL1G2129luEUlG1hB4N/nlYNWHnpwlND9eOMclRqqu1YDy2sSYVCFUZwl8I1Gxh+QSRrP2vD7EpUmFVXxg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-environment-visitor@7.24.7': + resolution: {integrity: sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-function-name@7.24.7': + resolution: {integrity: sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-hoist-variables@7.24.7': + resolution: {integrity: sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==} engines: {node: '>=6.9.0'} '@babel/helper-module-imports@7.24.7': resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==} engines: {node: '>=6.9.0'} + '@babel/helper-split-export-declaration@7.24.7': + resolution: {integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==} + engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.24.8': resolution: {integrity: sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==} engines: {node: '>=6.9.0'} @@ -144,25 +160,25 @@ packages: resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} engines: {node: '>=6.9.0'} - '@babel/parser@7.25.3': - resolution: {integrity: sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw==} + '@babel/parser@7.24.8': + resolution: {integrity: sha512-WzfbgXOkGzZiXXCqk43kKwZjzwx4oulxZi3nq2TYL9mOjQv6kYwul9mz6ID36njuL7Xkp6nJEfok848Zj10j/w==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/runtime@7.25.0': - resolution: {integrity: sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==} + '@babel/runtime@7.24.8': + resolution: {integrity: sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==} engines: {node: '>=6.9.0'} - '@babel/template@7.25.0': - resolution: {integrity: sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==} + '@babel/template@7.24.7': + resolution: {integrity: sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.25.3': - resolution: {integrity: sha512-HefgyP1x754oGCsKmV5reSmtV7IXj/kpaE1XYY+D9G5PvKKoFfSbiS4M77MdjuwlZKDIKFCffq9rPU+H/s3ZdQ==} + '@babel/traverse@7.24.8': + resolution: {integrity: sha512-t0P1xxAPzEDcEPmjprAQq19NWum4K0EQPjMwZQZbHt+GiZqvjCHjj755Weq1YRPVzBI+3zSfvScfpnuIecVFJQ==} engines: {node: '>=6.9.0'} - '@babel/types@7.25.2': - resolution: {integrity: sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==} + '@babel/types@7.24.9': + resolution: {integrity: sha512-xm8XrMKz0IlUdocVbYJe0Z9xEgidU7msskG8BbhnTPK/HZ2z/7FP7ykqPgrUH+C+r414mNfNWam1f2vqOjqjYQ==} engines: {node: '>=6.9.0'} '@chakra-ui/accordion@2.3.1': @@ -647,8 +663,8 @@ packages: '@emotion/babel-plugin@11.12.0': resolution: {integrity: sha512-y2WQb+oP8Jqvvclh8Q55gLUyb7UFvgv7eJfsj7td5TToBrIUtPay2kMrZi4xjq9qw2vD0ZR5fSho0yqoFgX7Rw==} - '@emotion/cache@11.13.1': - resolution: {integrity: sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw==} + '@emotion/cache@11.13.0': + resolution: {integrity: sha512-hPV345J/tH0Cwk2wnU/3PBzORQ9HeX+kQSbwI+jslzpRCHE6fSGTohswksA/Ensr8znPzwfzKZCmAM9Lmlhp7g==} '@emotion/hash@0.9.2': resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} @@ -854,14 +870,14 @@ packages: resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - '@floating-ui/core@1.6.7': - resolution: {integrity: sha512-yDzVT/Lm101nQ5TCVeK65LtdN7Tj4Qpr9RTXJ2vPFLqtLxwOrpoxAHAJI8J3yYWUc40J0BDBheaitK5SJmno2g==} + '@floating-ui/core@1.6.5': + resolution: {integrity: sha512-8GrTWmoFhm5BsMZOTHeGD2/0FLKLQQHvO/ZmQga4tKempYRLz8aqJGqXVuQgisnMObq2YZ2SgkwctN1LOOxcqA==} - '@floating-ui/dom@1.6.10': - resolution: {integrity: sha512-fskgCFv8J8OamCmyun8MfjB1Olfn+uZKjOKZ0vhYF3gRmEUXcGOjxWL8bBr7i4kIuPZ2KD2S3EUIOxnjC8kl2A==} + '@floating-ui/dom@1.6.8': + resolution: {integrity: sha512-kx62rP19VZ767Q653wsP1XZCGIirkE09E0QUGNYTM/ttbbQHqcGPdSfWFxUyyNLc/W6aoJRBajOSXhP6GXjC0Q==} - '@floating-ui/utils@0.2.7': - resolution: {integrity: sha512-X8R8Oj771YRl/w+c1HqAC1szL8zWQRwFvgDwT129k9ACdBoud/+/rX9V0qiMl6LWUdP9voC2nDVZYPMQQsb6eA==} + '@floating-ui/utils@0.2.5': + resolution: {integrity: sha512-sTcG+QZ6fdEUObICavU+aB3Mp8HY4n14wYHdxK4fXjPmv3PXZZeY5RaguJmGyeH/CJQhX3fqKUtS4qc1LoHwhQ==} '@fortawesome/fontawesome-common-types@6.6.0': resolution: {integrity: sha512-xyX0X9mc0kyz9plIyryrRbl7ngsA9jz77mCZJsUkLl+ZKs0KWObgaEBoSgQiYWAsSmjz/yjl0F++Got0Mdp4Rw==} @@ -939,148 +955,148 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.20.0': - resolution: {integrity: sha512-TSpWzflCc4VGAUJZlPpgAJE1+V60MePDQnBd7PPkpuEmOy8i87aL6tinFGKBFKuEDikYpig72QzdT3QPYIi+oA==} + '@rollup/rollup-android-arm-eabi@4.19.0': + resolution: {integrity: sha512-JlPfZ/C7yn5S5p0yKk7uhHTTnFlvTgLetl2VxqE518QgyM7C9bSfFTYvB/Q/ftkq0RIPY4ySxTz+/wKJ/dXC0w==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.20.0': - resolution: {integrity: sha512-u00Ro/nok7oGzVuh/FMYfNoGqxU5CPWz1mxV85S2w9LxHR8OoMQBuSk+3BKVIDYgkpeOET5yXkx90OYFc+ytpQ==} + '@rollup/rollup-android-arm64@4.19.0': + resolution: {integrity: sha512-RDxUSY8D1tWYfn00DDi5myxKgOk6RvWPxhmWexcICt/MEC6yEMr4HNCu1sXXYLw8iAsg0D44NuU+qNq7zVWCrw==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.20.0': - resolution: {integrity: sha512-uFVfvzvsdGtlSLuL0ZlvPJvl6ZmrH4CBwLGEFPe7hUmf7htGAN+aXo43R/V6LATyxlKVC/m6UsLb7jbG+LG39Q==} + '@rollup/rollup-darwin-arm64@4.19.0': + resolution: {integrity: sha512-emvKHL4B15x6nlNTBMtIaC9tLPRpeA5jMvRLXVbl/W9Ie7HhkrE7KQjvgS9uxgatL1HmHWDXk5TTS4IaNJxbAA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.20.0': - resolution: {integrity: sha512-xbrMDdlev53vNXexEa6l0LffojxhqDTBeL+VUxuuIXys4x6xyvbKq5XqTXBCEUA8ty8iEJblHvFaWRJTk/icAQ==} + '@rollup/rollup-darwin-x64@4.19.0': + resolution: {integrity: sha512-fO28cWA1dC57qCd+D0rfLC4VPbh6EOJXrreBmFLWPGI9dpMlER2YwSPZzSGfq11XgcEpPukPTfEVFtw2q2nYJg==} cpu: [x64] os: [darwin] - '@rollup/rollup-linux-arm-gnueabihf@4.20.0': - resolution: {integrity: sha512-jMYvxZwGmoHFBTbr12Xc6wOdc2xA5tF5F2q6t7Rcfab68TT0n+r7dgawD4qhPEvasDsVpQi+MgDzj2faOLsZjA==} + '@rollup/rollup-linux-arm-gnueabihf@4.19.0': + resolution: {integrity: sha512-2Rn36Ubxdv32NUcfm0wB1tgKqkQuft00PtM23VqLuCUR4N5jcNWDoV5iBC9jeGdgS38WK66ElncprqgMUOyomw==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.20.0': - resolution: {integrity: sha512-1asSTl4HKuIHIB1GcdFHNNZhxAYEdqML/MW4QmPS4G0ivbEcBr1JKlFLKsIRqjSwOBkdItn3/ZDlyvZ/N6KPlw==} + '@rollup/rollup-linux-arm-musleabihf@4.19.0': + resolution: {integrity: sha512-gJuzIVdq/X1ZA2bHeCGCISe0VWqCoNT8BvkQ+BfsixXwTOndhtLUpOg0A1Fcx/+eA6ei6rMBzlOz4JzmiDw7JQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.20.0': - resolution: {integrity: sha512-COBb8Bkx56KldOYJfMf6wKeYJrtJ9vEgBRAOkfw6Ens0tnmzPqvlpjZiLgkhg6cA3DGzCmLmmd319pmHvKWWlQ==} + '@rollup/rollup-linux-arm64-gnu@4.19.0': + resolution: {integrity: sha512-0EkX2HYPkSADo9cfeGFoQ7R0/wTKb7q6DdwI4Yn/ULFE1wuRRCHybxpl2goQrx4c/yzK3I8OlgtBu4xvted0ug==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.20.0': - resolution: {integrity: sha512-+it+mBSyMslVQa8wSPvBx53fYuZK/oLTu5RJoXogjk6x7Q7sz1GNRsXWjn6SwyJm8E/oMjNVwPhmNdIjwP135Q==} + '@rollup/rollup-linux-arm64-musl@4.19.0': + resolution: {integrity: sha512-GlIQRj9px52ISomIOEUq/IojLZqzkvRpdP3cLgIE1wUWaiU5Takwlzpz002q0Nxxr1y2ZgxC2obWxjr13lvxNQ==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.20.0': - resolution: {integrity: sha512-yAMvqhPfGKsAxHN8I4+jE0CpLWD8cv4z7CK7BMmhjDuz606Q2tFKkWRY8bHR9JQXYcoLfopo5TTqzxgPUjUMfw==} + '@rollup/rollup-linux-powerpc64le-gnu@4.19.0': + resolution: {integrity: sha512-N6cFJzssruDLUOKfEKeovCKiHcdwVYOT1Hs6dovDQ61+Y9n3Ek4zXvtghPPelt6U0AH4aDGnDLb83uiJMkWYzQ==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.20.0': - resolution: {integrity: sha512-qmuxFpfmi/2SUkAw95TtNq/w/I7Gpjurx609OOOV7U4vhvUhBcftcmXwl3rqAek+ADBwSjIC4IVNLiszoj3dPA==} + '@rollup/rollup-linux-riscv64-gnu@4.19.0': + resolution: {integrity: sha512-2DnD3mkS2uuam/alF+I7M84koGwvn3ZVD7uG+LEWpyzo/bq8+kKnus2EVCkcvh6PlNB8QPNFOz6fWd5N8o1CYg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.20.0': - resolution: {integrity: sha512-I0BtGXddHSHjV1mqTNkgUZLnS3WtsqebAXv11D5BZE/gfw5KoyXSAXVqyJximQXNvNzUo4GKlCK/dIwXlz+jlg==} + '@rollup/rollup-linux-s390x-gnu@4.19.0': + resolution: {integrity: sha512-D6pkaF7OpE7lzlTOFCB2m3Ngzu2ykw40Nka9WmKGUOTS3xcIieHe82slQlNq69sVB04ch73thKYIWz/Ian8DUA==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.20.0': - resolution: {integrity: sha512-y+eoL2I3iphUg9tN9GB6ku1FA8kOfmF4oUEWhztDJ4KXJy1agk/9+pejOuZkNFhRwHAOxMsBPLbXPd6mJiCwew==} + '@rollup/rollup-linux-x64-gnu@4.19.0': + resolution: {integrity: sha512-HBndjQLP8OsdJNSxpNIN0einbDmRFg9+UQeZV1eiYupIRuZsDEoeGU43NQsS34Pp166DtwQOnpcbV/zQxM+rWA==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.20.0': - resolution: {integrity: sha512-hM3nhW40kBNYUkZb/r9k2FKK+/MnKglX7UYd4ZUy5DJs8/sMsIbqWK2piZtVGE3kcXVNj3B2IrUYROJMMCikNg==} + '@rollup/rollup-linux-x64-musl@4.19.0': + resolution: {integrity: sha512-HxfbvfCKJe/RMYJJn0a12eiOI9OOtAUF4G6ozrFUK95BNyoJaSiBjIOHjZskTUffUrB84IPKkFG9H9nEvJGW6A==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.20.0': - resolution: {integrity: sha512-psegMvP+Ik/Bg7QRJbv8w8PAytPA7Uo8fpFjXyCRHWm6Nt42L+JtoqH8eDQ5hRP7/XW2UiIriy1Z46jf0Oa1kA==} + '@rollup/rollup-win32-arm64-msvc@4.19.0': + resolution: {integrity: sha512-HxDMKIhmcguGTiP5TsLNolwBUK3nGGUEoV/BO9ldUBoMLBssvh4J0X8pf11i1fTV7WShWItB1bKAKjX4RQeYmg==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.20.0': - resolution: {integrity: sha512-GabekH3w4lgAJpVxkk7hUzUf2hICSQO0a/BLFA11/RMxQT92MabKAqyubzDZmMOC/hcJNlc+rrypzNzYl4Dx7A==} + '@rollup/rollup-win32-ia32-msvc@4.19.0': + resolution: {integrity: sha512-xItlIAZZaiG/u0wooGzRsx11rokP4qyc/79LkAOdznGRAbOFc+SfEdfUOszG1odsHNgwippUJavag/+W/Etc6Q==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.20.0': - resolution: {integrity: sha512-aJ1EJSuTdGnM6qbVC4B5DSmozPTqIag9fSzXRNNo+humQLG89XpPgdt16Ia56ORD7s+H8Pmyx44uczDQ0yDzpg==} + '@rollup/rollup-win32-x64-msvc@4.19.0': + resolution: {integrity: sha512-xNo5fV5ycvCCKqiZcpB65VMR11NJB+StnxHz20jdqRAktfdfzhgjTiJ2doTDQE/7dqGaV5I7ZGqKpgph6lCIag==} cpu: [x64] os: [win32] - '@swc/core-darwin-arm64@1.7.10': - resolution: {integrity: sha512-TYp4x/9w/C/yMU1olK5hTKq/Hi7BjG71UJ4V1U1WxI1JA3uokjQ/GoktDfmH5V5pX4dgGSOJwUe2RjoN8Z/XnA==} + '@swc/core-darwin-arm64@1.7.0': + resolution: {integrity: sha512-2ylhM7f0HwUwLrFYZAe/dse8PCbPsYcJS3Dt7Q8NT3PUn7vy6QOMxNcOPPuDrnmaXqQQO3oxdmRapguTxaat9g==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] - '@swc/core-darwin-x64@1.7.10': - resolution: {integrity: sha512-P3LJjAWh5yLc6p5IUwV5LgRfA3R1oDCZDMabYyb2BVQuJTD4MfegW9DhBcUUF5dhBLwq3191KpLVzE+dLTbiXw==} + '@swc/core-darwin-x64@1.7.0': + resolution: {integrity: sha512-SgVnN4gT1Rb9YfTkp4FCUITqSs7Yj0uB2SUciu5CV3HuGvS5YXCUzh+KrwpLFtx8NIgivISKcNnb41mJi98X8Q==} engines: {node: '>=10'} cpu: [x64] os: [darwin] - '@swc/core-linux-arm-gnueabihf@1.7.10': - resolution: {integrity: sha512-yGOFjE7w/akRTmqGY3FvWYrqbxO7OB2N2FHj2LO5HtzXflfoABb5RyRvdEquX+17J6mEpu4EwjYNraTD/WHIEQ==} + '@swc/core-linux-arm-gnueabihf@1.7.0': + resolution: {integrity: sha512-+Z9Dayart1iKJQEJJ9N/KS4z5EdXJE3WPFikY0jonKTo4Dd8RuyVz5yLvqcIMeVdz/SwximATaL6iJXw7hZS9A==} engines: {node: '>=10'} cpu: [arm] os: [linux] - '@swc/core-linux-arm64-gnu@1.7.10': - resolution: {integrity: sha512-SPWsgWHfdWKKjLrYlvhxcdBJ7Ruy6crJbPoE9NfD95eJEjMnS2yZTqj2ChFsY737WeyhWYlHzgYhYOVCp83YwQ==} + '@swc/core-linux-arm64-gnu@1.7.0': + resolution: {integrity: sha512-UnLrCiZ1EI4shznJn0xP6DLgsXUSwtfsdgHhGYCrvbgVBBve3S9iFgVFEB3SPl7Q/TdowNbrN4zHU0oChfiNfw==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-arm64-musl@1.7.10': - resolution: {integrity: sha512-PUi50bkNqnBL3Z/Zq6jSfwgN9A/taA6u2Zou0tjDJi7oVdpjdr7SxNgCGzMJ/nNg5D/IQn1opM1jktMvpsPAuQ==} + '@swc/core-linux-arm64-musl@1.7.0': + resolution: {integrity: sha512-H724UANA+ptsfwKRr9mnaDa9cb5fw0oFysiGKTgb3DMYcgk3Od0jMTnXVPFSVpo7FlmyxeC9K8ueUPBOoOK6XA==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-x64-gnu@1.7.10': - resolution: {integrity: sha512-Sc+pY55gknCAmBQBR6DhlA7jZSxHaLSDb5Sevzi6DOFMXR79NpA6zWTNKwp1GK2AnRIkbAfvYLgOxS5uWTFVpg==} + '@swc/core-linux-x64-gnu@1.7.0': + resolution: {integrity: sha512-SY3HA0K0Dpqt1HIfMLGpwL4hd4UaL2xHP5oZXPlRQPhUDZrbb4PbI3ZJnh66c63eL4ZR8EJ+HRFI0Alx5p69Zw==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-linux-x64-musl@1.7.10': - resolution: {integrity: sha512-g5NKx2LXaGd0K26hmEts1Cvb7ptIvq3MHSgr6/D1tRPcDZw1Sp0dYsmyOv0ho4F5GOJyiCooG3oE9FXdb7jIpQ==} + '@swc/core-linux-x64-musl@1.7.0': + resolution: {integrity: sha512-cEJ2ebtV1v/5Ilb55E05J6F5SrHKQWzUttIhR5Mkayyo+yvPslcpByuFC3D+J7X1ebziTOBpWuMpUdjLfh3SMQ==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-win32-arm64-msvc@1.7.10': - resolution: {integrity: sha512-plRIsOcfy9t9Q/ivm5DA7I0HaIvfAWPbI+bvVRrr3C/1K2CSqnqZJjEWOAmx2LiyipijNnEaFYuLBp0IkGuJpg==} + '@swc/core-win32-arm64-msvc@1.7.0': + resolution: {integrity: sha512-ecQOOmzEssz+m0pR4xDYCGuvn3E/l0nQ3tk5jp1NA1lsAy4bMV0YbYCHjptYvWL/UjhIerIp3IlCJ8x5DodSog==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - '@swc/core-win32-ia32-msvc@1.7.10': - resolution: {integrity: sha512-GntrVNT23viHtbfzmlK8lfBiKeajH24GzbDT7qXhnoO20suUPcyYZxyvCb4gWM2zu8ZBTPHNlqfrNsriQCZ+lQ==} + '@swc/core-win32-ia32-msvc@1.7.0': + resolution: {integrity: sha512-gz81seZkRn3zMnVOc7L5k6F4vQC82gIxmHiL+GedK+A37XI/X26AASU3zxvORnqQbwQYXQ+AEVckxBmFlz3v2g==} engines: {node: '>=10'} cpu: [ia32] os: [win32] - '@swc/core-win32-x64-msvc@1.7.10': - resolution: {integrity: sha512-uXIF8GuSappe1imm6Lf7pHGepfCBjDQlS+qTqvEGE0wZAsL1IVATK9P/cH/OCLfJXeQDTLeSYmrpwjtXNt46tQ==} + '@swc/core-win32-x64-msvc@1.7.0': + resolution: {integrity: sha512-b5Fd1xEOw9uqBpj2lqsaR4Iq9UhiL84hNDcEsi6DQA7Y1l85waQAslTbS0E4/pJ1PISAs0jW0zIGLco1eaWBOg==} engines: {node: '>=10'} cpu: [x64] os: [win32] - '@swc/core@1.7.10': - resolution: {integrity: sha512-l0xrFwBQ9atizhmV94yC2nwcecTk/oftofwMNPiFMGe56dqdmi2ArHaTV3PCtMlgaUH6rGCehoRMt5OrCI1ktg==} + '@swc/core@1.7.0': + resolution: {integrity: sha512-d4vMzH6ICllDwlPuhset2h8gu/USHdbyfJim+2hQEdxC0UONtfpmu38XBgNqRjStrji1Q5M10jfeUZL3cu1i8g==} engines: {node: '>=10'} peerDependencies: '@swc/helpers': '*' @@ -1175,8 +1191,8 @@ packages: '@types/lodash@4.17.7': resolution: {integrity: sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA==} - '@types/node@20.14.15': - resolution: {integrity: sha512-Fz1xDMCF/B00/tYSVMlmK7hVeLh7jE5f3B7X1/hmV0MJBwE27KlS7EvD/Yp+z1lm8mVhwV5w+n8jOZG8AfTlKw==} + '@types/node@20.14.12': + resolution: {integrity: sha512-r7wNXakLeSsGT0H1AU863vS2wa5wBOK4bWMjZz2wj+8nBx+m5PeIn0k8AloSLpRuiwdRQZwarZqHE4FNArPuJQ==} '@types/parse-json@4.0.2': resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} @@ -1187,14 +1203,14 @@ packages: '@types/react-dom@18.3.0': resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==} - '@types/react-transition-group@4.4.11': - resolution: {integrity: sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA==} + '@types/react-transition-group@4.4.10': + resolution: {integrity: sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==} '@types/react@18.3.3': resolution: {integrity: sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==} - '@typescript-eslint/eslint-plugin@8.0.1': - resolution: {integrity: sha512-5g3Y7GDFsJAnY4Yhvk8sZtFfV6YNF2caLzjrRPUBzewjPCaj0yokePB4LJSobyCzGMzjZZYFbwuzbfDHlimXbQ==} + '@typescript-eslint/eslint-plugin@8.0.0': + resolution: {integrity: sha512-STIZdwEQRXAHvNUS6ILDf5z3u95Gc8jzywunxSNqX00OooIemaaNIA0vEgynJlycL5AjabYLLrIyHd4iazyvtg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 @@ -1204,8 +1220,8 @@ packages: typescript: optional: true - '@typescript-eslint/parser@8.0.1': - resolution: {integrity: sha512-5IgYJ9EO/12pOUwiBKFkpU7rS3IU21mtXzB81TNwq2xEybcmAZrE9qwDtsb5uQd9aVO9o0fdabFyAmKveXyujg==} + '@typescript-eslint/parser@8.0.0': + resolution: {integrity: sha512-pS1hdZ+vnrpDIxuFXYQpLTILglTjSYJ9MbetZctrUawogUsPdz31DIIRZ9+rab0LhYNTsk88w4fIzVheiTbWOQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -1214,12 +1230,12 @@ packages: typescript: optional: true - '@typescript-eslint/scope-manager@8.0.1': - resolution: {integrity: sha512-NpixInP5dm7uukMiRyiHjRKkom5RIFA4dfiHvalanD2cF0CLUuQqxfg8PtEUo9yqJI2bBhF+pcSafqnG3UBnRQ==} + '@typescript-eslint/scope-manager@8.0.0': + resolution: {integrity: sha512-V0aa9Csx/ZWWv2IPgTfY7T4agYwJyILESu/PVqFtTFz9RIS823mAze+NbnBI8xiwdX3iqeQbcTYlvB04G9wyQw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/type-utils@8.0.1': - resolution: {integrity: sha512-+/UT25MWvXeDX9YaHv1IS6KI1fiuTto43WprE7pgSMswHbn1Jm9GEM4Txp+X74ifOWV8emu2AWcbLhpJAvD5Ng==} + '@typescript-eslint/type-utils@8.0.0': + resolution: {integrity: sha512-mJAFP2mZLTBwAn5WI4PMakpywfWFH5nQZezUQdSKV23Pqo6o9iShQg1hP2+0hJJXP2LnZkWPphdIq4juYYwCeg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '*' @@ -1227,12 +1243,12 @@ packages: typescript: optional: true - '@typescript-eslint/types@8.0.1': - resolution: {integrity: sha512-PpqTVT3yCA/bIgJ12czBuE3iBlM3g4inRSC5J0QOdQFAn07TYrYEQBBKgXH1lQpglup+Zy6c1fxuwTk4MTNKIw==} + '@typescript-eslint/types@8.0.0': + resolution: {integrity: sha512-wgdSGs9BTMWQ7ooeHtu5quddKKs5Z5dS+fHLbrQI+ID0XWJLODGMHRfhwImiHoeO2S5Wir2yXuadJN6/l4JRxw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.0.1': - resolution: {integrity: sha512-8V9hriRvZQXPWU3bbiUV4Epo7EvgM6RTs+sUmxp5G//dBGy402S7Fx0W0QkB2fb4obCF8SInoUzvTYtc3bkb5w==} + '@typescript-eslint/typescript-estree@8.0.0': + resolution: {integrity: sha512-5b97WpKMX+Y43YKi4zVcCVLtK5F98dFls3Oxui8LbnmRsseKenbbDinmvxrWegKDMmlkIq/XHuyy0UGLtpCDKg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '*' @@ -1240,14 +1256,14 @@ packages: typescript: optional: true - '@typescript-eslint/utils@8.0.1': - resolution: {integrity: sha512-CBFR0G0sCt0+fzfnKaciu9IBsKvEKYwN9UZ+eeogK1fYHg4Qxk1yf/wLQkLXlq8wbU2dFlgAesxt8Gi76E8RTA==} + '@typescript-eslint/utils@8.0.0': + resolution: {integrity: sha512-k/oS/A/3QeGLRvOWCg6/9rATJL5rec7/5s1YmdS0ZU6LHveJyGFwBvLhSRBv6i9xaj7etmosp+l+ViN1I9Aj/Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - '@typescript-eslint/visitor-keys@8.0.1': - resolution: {integrity: sha512-W5E+o0UfUcK5EgchLZsyVWqARmsM7v54/qEq6PY3YI5arkgmCzHiuk0zKSJJbm71V0xdRna4BGomkCTXz2/LkQ==} + '@typescript-eslint/visitor-keys@8.0.0': + resolution: {integrity: sha512-oN0K4nkHuOyF3PVMyETbpP5zp6wfyOvm7tWhTMfoqxSSsPmJIh6JNASuZDlODE8eE+0EB9uar+6+vxr9DBTYOA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@ungap/structured-clone@1.2.0': @@ -1424,8 +1440,8 @@ packages: copy-to-clipboard@3.3.3: resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==} - core-js@3.38.0: - resolution: {integrity: sha512-XPpwqEodRljce9KswjZShh95qJ1URisBeKCjUdq27YdenkslVe7OO0ZJhlYXAChW7OhXaRLl8AAba7IBfoIHug==} + core-js@3.37.1: + resolution: {integrity: sha512-Xn6qmxrQZyB0FFY8E3bgRXei3lWDJHhvI+u0q9TKIYM49G8pAr0FgnnrFRAmsbptZL1yxRADVXn+x5AGsbBfyw==} cosmiconfig@7.1.0: resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} @@ -1469,8 +1485,8 @@ packages: supports-color: optional: true - debug@4.3.6: - resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==} + debug@4.3.5: + resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -1707,8 +1723,8 @@ packages: resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} engines: {node: '>= 6'} - framer-motion@11.3.24: - resolution: {integrity: sha512-kl0YI7HwAtyV0VOAWuU/rXoOS8+z5qSkMN6rZS+a9oe6fIha6SC3vjJN6u/hBpvjrg5MQNdSnqnjYxm0WYTX9g==} + framer-motion@11.3.12: + resolution: {integrity: sha512-ulc8EHFZpKIj+NAyJv+alLUEUIXZKOQnE+JHkGjfoIcxbZwV+CSvfOoACaOpAW4nVznFMF2y3r+ViUtPtP4qiw==} peerDependencies: '@emotion/is-prop-valid': '*' react: ^18.0.0 @@ -2193,8 +2209,8 @@ packages: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} engines: {node: '>= 0.4'} - postcss@8.4.41: - resolution: {integrity: sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==} + postcss@8.4.39: + resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} engines: {node: ^10 || ^12 || >=14} prelude-ls@1.2.1: @@ -2355,8 +2371,8 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true - rollup@4.20.0: - resolution: {integrity: sha512-6rbWBChcnSGzIlXeIdNIZTopKYad8ZG8ajhl78lGRLsI2rX8IkaotQhVas2Ma+GPxJav19wrSzvRvuiv0YKzWw==} + rollup@4.19.0: + resolution: {integrity: sha512-5r7EYSQIowHsK4eTZ0Y81qpZuJz+MUuYeqmmYmRMl1nwhdmbiYqt5jwzf6u7wyOzJgYqtCRMtVRKOtHANBz7rA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -2492,8 +2508,8 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - terser@5.31.5: - resolution: {integrity: sha512-YPmas0L0rE1UyLL/llTWA0SiDOqIcAQYLeUj7cJYzXHlRTAnMSg9pPe4VJ5PlKvTrPQsdVFuiRiwyeNlYgwh2Q==} + terser@5.31.3: + resolution: {integrity: sha512-pAfYn3NIZLyZpa83ZKigvj6Rn9c/vd5KfYGX7cN1mnzqgDcxWvrU5ZtAfIKhEXz9nRecw4z3LXkjaq96/qZqAA==} engines: {node: '>=10'} hasBin: true @@ -2614,8 +2630,8 @@ packages: '@types/react': optional: true - vite@5.4.0: - resolution: {integrity: sha512-5xokfMX0PIiwCMCMb9ZJcMyh5wbBun0zUzKib+L65vAZ8GY9ePZMXxFrHbr/Kyll2+LSCY7xtERPpxkBDKngwg==} + vite@5.3.4: + resolution: {integrity: sha512-Cw+7zL3ZG9/NZBB8C+8QbQZmR54GwqIz+WMI4b3JgdYJvX+ny9AjJXqkGQlDXSXRP9rP0B4tbciRMOVEKulVOA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -2623,7 +2639,6 @@ packages: less: '*' lightningcss: ^1.21.0 sass: '*' - sass-embedded: '*' stylus: '*' sugarss: '*' terser: ^5.4.0 @@ -2636,8 +2651,6 @@ packages: optional: true sass: optional: true - sass-embedded: - optional: true stylus: optional: true sugarss: @@ -2668,8 +2681,8 @@ packages: which-boxed-primitive@1.0.2: resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} - which-builtin-type@1.1.4: - resolution: {integrity: sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w==} + which-builtin-type@1.1.3: + resolution: {integrity: sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==} engines: {node: '>= 0.4'} which-collection@1.0.2: @@ -2726,20 +2739,37 @@ snapshots: '@babel/highlight': 7.24.7 picocolors: 1.0.1 - '@babel/generator@7.25.0': + '@babel/generator@7.24.10': dependencies: - '@babel/types': 7.25.2 + '@babel/types': 7.24.9 '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 jsesc: 2.5.2 + '@babel/helper-environment-visitor@7.24.7': + dependencies: + '@babel/types': 7.24.9 + + '@babel/helper-function-name@7.24.7': + dependencies: + '@babel/template': 7.24.7 + '@babel/types': 7.24.9 + + '@babel/helper-hoist-variables@7.24.7': + dependencies: + '@babel/types': 7.24.9 + '@babel/helper-module-imports@7.24.7': dependencies: - '@babel/traverse': 7.25.3 - '@babel/types': 7.25.2 + '@babel/traverse': 7.24.8 + '@babel/types': 7.24.9 transitivePeerDependencies: - supports-color + '@babel/helper-split-export-declaration@7.24.7': + dependencies: + '@babel/types': 7.24.9 + '@babel/helper-string-parser@7.24.8': {} '@babel/helper-validator-identifier@7.24.7': {} @@ -2751,39 +2781,42 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.0.1 - '@babel/parser@7.25.3': + '@babel/parser@7.24.8': dependencies: - '@babel/types': 7.25.2 + '@babel/types': 7.24.9 - '@babel/runtime@7.25.0': + '@babel/runtime@7.24.8': dependencies: regenerator-runtime: 0.14.1 - '@babel/template@7.25.0': + '@babel/template@7.24.7': dependencies: '@babel/code-frame': 7.24.7 - '@babel/parser': 7.25.3 - '@babel/types': 7.25.2 + '@babel/parser': 7.24.8 + '@babel/types': 7.24.9 - '@babel/traverse@7.25.3': + '@babel/traverse@7.24.8': dependencies: '@babel/code-frame': 7.24.7 - '@babel/generator': 7.25.0 - '@babel/parser': 7.25.3 - '@babel/template': 7.25.0 - '@babel/types': 7.25.2 - debug: 4.3.6 + '@babel/generator': 7.24.10 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-function-name': 7.24.7 + '@babel/helper-hoist-variables': 7.24.7 + '@babel/helper-split-export-declaration': 7.24.7 + '@babel/parser': 7.24.8 + '@babel/types': 7.24.9 + debug: 4.3.5 globals: 11.12.0 transitivePeerDependencies: - supports-color - '@babel/types@7.25.2': + '@babel/types@7.24.9': dependencies: '@babel/helper-string-parser': 7.24.8 '@babel/helper-validator-identifier': 7.24.7 to-fast-properties: 2.0.0 - '@chakra-ui/accordion@2.3.1(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(framer-motion@11.3.24(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': + '@chakra-ui/accordion@2.3.1(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(framer-motion@11.3.12(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': dependencies: '@chakra-ui/descendant': 3.1.0(react@18.3.1) '@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(react@18.3.1) @@ -2792,8 +2825,8 @@ snapshots: '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 '@chakra-ui/system': 2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1) - '@chakra-ui/transition': 2.1.0(framer-motion@11.3.24(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) - framer-motion: 11.3.24(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@chakra-ui/transition': 2.1.0(framer-motion@11.3.12(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + framer-motion: 11.3.12(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 '@chakra-ui/alert@2.2.2(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(react@18.3.1)': @@ -2997,7 +3030,7 @@ snapshots: '@chakra-ui/system': 2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1) react: 18.3.1 - '@chakra-ui/menu@2.2.1(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(framer-motion@11.3.24(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': + '@chakra-ui/menu@2.2.1(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(framer-motion@11.3.12(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': dependencies: '@chakra-ui/clickable': 2.1.0(react@18.3.1) '@chakra-ui/descendant': 3.1.0(react@18.3.1) @@ -3014,11 +3047,11 @@ snapshots: '@chakra-ui/react-use-update-effect': 2.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 '@chakra-ui/system': 2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1) - '@chakra-ui/transition': 2.1.0(framer-motion@11.3.24(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) - framer-motion: 11.3.24(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@chakra-ui/transition': 2.1.0(framer-motion@11.3.12(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + framer-motion: 11.3.12(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 - '@chakra-ui/modal@2.3.1(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(@types/react@18.3.3)(framer-motion@11.3.24(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@chakra-ui/modal@2.3.1(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(@types/react@18.3.3)(framer-motion@11.3.12(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@chakra-ui/close-button': 2.1.1(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(react@18.3.1) '@chakra-ui/focus-lock': 2.1.0(@types/react@18.3.3)(react@18.3.1) @@ -3028,9 +3061,9 @@ snapshots: '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 '@chakra-ui/system': 2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1) - '@chakra-ui/transition': 2.1.0(framer-motion@11.3.24(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + '@chakra-ui/transition': 2.1.0(framer-motion@11.3.12(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) aria-hidden: 1.2.4 - framer-motion: 11.3.24(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + framer-motion: 11.3.12(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) react-remove-scroll: 2.5.10(@types/react@18.3.3)(react@18.3.1) @@ -3069,7 +3102,7 @@ snapshots: '@chakra-ui/system': 2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1) react: 18.3.1 - '@chakra-ui/popover@2.2.1(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(framer-motion@11.3.24(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': + '@chakra-ui/popover@2.2.1(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(framer-motion@11.3.12(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': dependencies: '@chakra-ui/close-button': 2.1.1(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(react@18.3.1) '@chakra-ui/lazy-utils': 2.0.5 @@ -3083,7 +3116,7 @@ snapshots: '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 '@chakra-ui/system': 2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1) - framer-motion: 11.3.24(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + framer-motion: 11.3.12(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 '@chakra-ui/popper@3.1.0(react@18.3.1)': @@ -3236,9 +3269,9 @@ snapshots: '@chakra-ui/utils': 2.0.15 react: 18.3.1 - '@chakra-ui/react@2.8.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(framer-motion@11.3.24(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@chakra-ui/react@2.8.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(framer-motion@11.3.12(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@chakra-ui/accordion': 2.3.1(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(framer-motion@11.3.24(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + '@chakra-ui/accordion': 2.3.1(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(framer-motion@11.3.12(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) '@chakra-ui/alert': 2.2.2(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(react@18.3.1) '@chakra-ui/avatar': 2.3.0(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(react@18.3.1) '@chakra-ui/breadcrumb': 2.2.0(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(react@18.3.1) @@ -3259,11 +3292,11 @@ snapshots: '@chakra-ui/layout': 2.3.1(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(react@18.3.1) '@chakra-ui/live-region': 2.1.0(react@18.3.1) '@chakra-ui/media-query': 3.3.0(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(react@18.3.1) - '@chakra-ui/menu': 2.2.1(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(framer-motion@11.3.24(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) - '@chakra-ui/modal': 2.3.1(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(@types/react@18.3.3)(framer-motion@11.3.24(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@chakra-ui/menu': 2.2.1(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(framer-motion@11.3.12(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + '@chakra-ui/modal': 2.3.1(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(@types/react@18.3.3)(framer-motion@11.3.12(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@chakra-ui/number-input': 2.1.2(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(react@18.3.1) '@chakra-ui/pin-input': 2.1.0(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(react@18.3.1) - '@chakra-ui/popover': 2.2.1(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(framer-motion@11.3.24(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + '@chakra-ui/popover': 2.2.1(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(framer-motion@11.3.12(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) '@chakra-ui/popper': 3.1.0(react@18.3.1) '@chakra-ui/portal': 2.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@chakra-ui/progress': 2.2.0(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(react@18.3.1) @@ -3278,7 +3311,7 @@ snapshots: '@chakra-ui/stat': 2.1.1(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(react@18.3.1) '@chakra-ui/stepper': 2.3.1(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(react@18.3.1) '@chakra-ui/styled-system': 2.9.2 - '@chakra-ui/switch': 2.1.2(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(framer-motion@11.3.24(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + '@chakra-ui/switch': 2.1.2(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(framer-motion@11.3.12(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) '@chakra-ui/system': 2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1) '@chakra-ui/table': 2.1.0(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(react@18.3.1) '@chakra-ui/tabs': 3.0.0(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(react@18.3.1) @@ -3286,14 +3319,14 @@ snapshots: '@chakra-ui/textarea': 2.1.2(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(react@18.3.1) '@chakra-ui/theme': 3.3.1(@chakra-ui/styled-system@2.9.2) '@chakra-ui/theme-utils': 2.0.21 - '@chakra-ui/toast': 7.0.2(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(framer-motion@11.3.24(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@chakra-ui/tooltip': 2.3.1(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(framer-motion@11.3.24(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@chakra-ui/transition': 2.1.0(framer-motion@11.3.24(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + '@chakra-ui/toast': 7.0.2(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(framer-motion@11.3.12(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@chakra-ui/tooltip': 2.3.1(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(framer-motion@11.3.12(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@chakra-ui/transition': 2.1.0(framer-motion@11.3.12(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) '@chakra-ui/utils': 2.0.15 '@chakra-ui/visually-hidden': 2.2.0(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(react@18.3.1) '@emotion/react': 11.13.0(@types/react@18.3.3)(react@18.3.1) '@emotion/styled': 11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1) - framer-motion: 11.3.24(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + framer-motion: 11.3.12(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) transitivePeerDependencies: @@ -3364,12 +3397,12 @@ snapshots: csstype: 3.1.3 lodash.mergewith: 4.6.2 - '@chakra-ui/switch@2.1.2(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(framer-motion@11.3.24(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': + '@chakra-ui/switch@2.1.2(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(framer-motion@11.3.12(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': dependencies: '@chakra-ui/checkbox': 2.3.2(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 '@chakra-ui/system': 2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1) - framer-motion: 11.3.24(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + framer-motion: 11.3.12(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 '@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1)': @@ -3441,7 +3474,7 @@ snapshots: '@chakra-ui/styled-system': 2.9.2 '@chakra-ui/theme-tools': 2.1.2(@chakra-ui/styled-system@2.9.2) - '@chakra-ui/toast@7.0.2(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(framer-motion@11.3.24(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@chakra-ui/toast@7.0.2(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(framer-motion@11.3.12(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@chakra-ui/alert': 2.2.2(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(react@18.3.1) '@chakra-ui/close-button': 2.1.1(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(react@18.3.1) @@ -3453,11 +3486,11 @@ snapshots: '@chakra-ui/styled-system': 2.9.2 '@chakra-ui/system': 2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1) '@chakra-ui/theme': 3.3.1(@chakra-ui/styled-system@2.9.2) - framer-motion: 11.3.24(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + framer-motion: 11.3.12(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@chakra-ui/tooltip@2.3.1(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(framer-motion@11.3.24(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@chakra-ui/tooltip@2.3.1(@chakra-ui/system@2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1))(framer-motion@11.3.12(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@chakra-ui/dom-utils': 2.1.0 '@chakra-ui/popper': 3.1.0(react@18.3.1) @@ -3468,14 +3501,14 @@ snapshots: '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 '@chakra-ui/system': 2.6.2(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1) - framer-motion: 11.3.24(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + framer-motion: 11.3.12(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@chakra-ui/transition@2.1.0(framer-motion@11.3.24(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': + '@chakra-ui/transition@2.1.0(framer-motion@11.3.12(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': dependencies: '@chakra-ui/shared-utils': 2.0.5 - framer-motion: 11.3.24(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + framer-motion: 11.3.12(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 '@chakra-ui/utils@2.0.15': @@ -3493,7 +3526,7 @@ snapshots: '@emotion/babel-plugin@11.12.0': dependencies: '@babel/helper-module-imports': 7.24.7 - '@babel/runtime': 7.25.0 + '@babel/runtime': 7.24.8 '@emotion/hash': 0.9.2 '@emotion/memoize': 0.9.0 '@emotion/serialize': 1.3.0 @@ -3506,7 +3539,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@emotion/cache@11.13.1': + '@emotion/cache@11.13.0': dependencies: '@emotion/memoize': 0.9.0 '@emotion/sheet': 1.4.0 @@ -3524,9 +3557,9 @@ snapshots: '@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1)': dependencies: - '@babel/runtime': 7.25.0 + '@babel/runtime': 7.24.8 '@emotion/babel-plugin': 11.12.0 - '@emotion/cache': 11.13.1 + '@emotion/cache': 11.13.0 '@emotion/serialize': 1.3.0 '@emotion/use-insertion-effect-with-fallbacks': 1.1.0(react@18.3.1) '@emotion/utils': 1.4.0 @@ -3550,7 +3583,7 @@ snapshots: '@emotion/styled@11.13.0(@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1)': dependencies: - '@babel/runtime': 7.25.0 + '@babel/runtime': 7.24.8 '@emotion/babel-plugin': 11.12.0 '@emotion/is-prop-valid': 1.3.0 '@emotion/react': 11.13.0(@types/react@18.3.3)(react@18.3.1) @@ -3652,7 +3685,7 @@ snapshots: '@eslint/eslintrc@2.1.4': dependencies: ajv: 6.12.6 - debug: 4.3.6 + debug: 4.3.5 espree: 9.6.1 globals: 13.24.0 ignore: 5.3.1 @@ -3665,16 +3698,16 @@ snapshots: '@eslint/js@8.57.0': {} - '@floating-ui/core@1.6.7': + '@floating-ui/core@1.6.5': dependencies: - '@floating-ui/utils': 0.2.7 + '@floating-ui/utils': 0.2.5 - '@floating-ui/dom@1.6.10': + '@floating-ui/dom@1.6.8': dependencies: - '@floating-ui/core': 1.6.7 - '@floating-ui/utils': 0.2.7 + '@floating-ui/core': 1.6.5 + '@floating-ui/utils': 0.2.5 - '@floating-ui/utils@0.2.7': {} + '@floating-ui/utils@0.2.5': {} '@fortawesome/fontawesome-common-types@6.6.0': {} @@ -3695,7 +3728,7 @@ snapshots: '@humanwhocodes/config-array@0.11.14': dependencies: '@humanwhocodes/object-schema': 2.0.3 - debug: 4.3.6 + debug: 4.3.5 minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -3740,107 +3773,107 @@ snapshots: '@popperjs/core@2.11.8': {} - '@rollup/plugin-terser@0.4.4(rollup@4.20.0)': + '@rollup/plugin-terser@0.4.4(rollup@4.19.0)': dependencies: serialize-javascript: 6.0.2 smob: 1.5.0 - terser: 5.31.5 + terser: 5.31.3 optionalDependencies: - rollup: 4.20.0 + rollup: 4.19.0 - '@rollup/rollup-android-arm-eabi@4.20.0': + '@rollup/rollup-android-arm-eabi@4.19.0': optional: true - '@rollup/rollup-android-arm64@4.20.0': + '@rollup/rollup-android-arm64@4.19.0': optional: true - '@rollup/rollup-darwin-arm64@4.20.0': + '@rollup/rollup-darwin-arm64@4.19.0': optional: true - '@rollup/rollup-darwin-x64@4.20.0': + '@rollup/rollup-darwin-x64@4.19.0': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.20.0': + '@rollup/rollup-linux-arm-gnueabihf@4.19.0': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.20.0': + '@rollup/rollup-linux-arm-musleabihf@4.19.0': optional: true - '@rollup/rollup-linux-arm64-gnu@4.20.0': + '@rollup/rollup-linux-arm64-gnu@4.19.0': optional: true - '@rollup/rollup-linux-arm64-musl@4.20.0': + '@rollup/rollup-linux-arm64-musl@4.19.0': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.20.0': + '@rollup/rollup-linux-powerpc64le-gnu@4.19.0': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.20.0': + '@rollup/rollup-linux-riscv64-gnu@4.19.0': optional: true - '@rollup/rollup-linux-s390x-gnu@4.20.0': + '@rollup/rollup-linux-s390x-gnu@4.19.0': optional: true - '@rollup/rollup-linux-x64-gnu@4.20.0': + '@rollup/rollup-linux-x64-gnu@4.19.0': optional: true - '@rollup/rollup-linux-x64-musl@4.20.0': + '@rollup/rollup-linux-x64-musl@4.19.0': optional: true - '@rollup/rollup-win32-arm64-msvc@4.20.0': + '@rollup/rollup-win32-arm64-msvc@4.19.0': optional: true - '@rollup/rollup-win32-ia32-msvc@4.20.0': + '@rollup/rollup-win32-ia32-msvc@4.19.0': optional: true - '@rollup/rollup-win32-x64-msvc@4.20.0': + '@rollup/rollup-win32-x64-msvc@4.19.0': optional: true - '@swc/core-darwin-arm64@1.7.10': + '@swc/core-darwin-arm64@1.7.0': optional: true - '@swc/core-darwin-x64@1.7.10': + '@swc/core-darwin-x64@1.7.0': optional: true - '@swc/core-linux-arm-gnueabihf@1.7.10': + '@swc/core-linux-arm-gnueabihf@1.7.0': optional: true - '@swc/core-linux-arm64-gnu@1.7.10': + '@swc/core-linux-arm64-gnu@1.7.0': optional: true - '@swc/core-linux-arm64-musl@1.7.10': + '@swc/core-linux-arm64-musl@1.7.0': optional: true - '@swc/core-linux-x64-gnu@1.7.10': + '@swc/core-linux-x64-gnu@1.7.0': optional: true - '@swc/core-linux-x64-musl@1.7.10': + '@swc/core-linux-x64-musl@1.7.0': optional: true - '@swc/core-win32-arm64-msvc@1.7.10': + '@swc/core-win32-arm64-msvc@1.7.0': optional: true - '@swc/core-win32-ia32-msvc@1.7.10': + '@swc/core-win32-ia32-msvc@1.7.0': optional: true - '@swc/core-win32-x64-msvc@1.7.10': + '@swc/core-win32-x64-msvc@1.7.0': optional: true - '@swc/core@1.7.10': + '@swc/core@1.7.0': dependencies: '@swc/counter': 0.1.3 '@swc/types': 0.1.12 optionalDependencies: - '@swc/core-darwin-arm64': 1.7.10 - '@swc/core-darwin-x64': 1.7.10 - '@swc/core-linux-arm-gnueabihf': 1.7.10 - '@swc/core-linux-arm64-gnu': 1.7.10 - '@swc/core-linux-arm64-musl': 1.7.10 - '@swc/core-linux-x64-gnu': 1.7.10 - '@swc/core-linux-x64-musl': 1.7.10 - '@swc/core-win32-arm64-msvc': 1.7.10 - '@swc/core-win32-ia32-msvc': 1.7.10 - '@swc/core-win32-x64-msvc': 1.7.10 + '@swc/core-darwin-arm64': 1.7.0 + '@swc/core-darwin-x64': 1.7.0 + '@swc/core-linux-arm-gnueabihf': 1.7.0 + '@swc/core-linux-arm64-gnu': 1.7.0 + '@swc/core-linux-arm64-musl': 1.7.0 + '@swc/core-linux-x64-gnu': 1.7.0 + '@swc/core-linux-x64-musl': 1.7.0 + '@swc/core-win32-arm64-msvc': 1.7.0 + '@swc/core-win32-ia32-msvc': 1.7.0 + '@swc/core-win32-x64-msvc': 1.7.0 '@swc/counter@0.1.3': {} @@ -3903,7 +3936,7 @@ snapshots: '@types/lodash@4.17.7': {} - '@types/node@20.14.15': + '@types/node@20.14.12': dependencies: undici-types: 5.26.5 @@ -3915,7 +3948,7 @@ snapshots: dependencies: '@types/react': 18.3.3 - '@types/react-transition-group@4.4.11': + '@types/react-transition-group@4.4.10': dependencies: '@types/react': 18.3.3 @@ -3924,14 +3957,14 @@ snapshots: '@types/prop-types': 15.7.12 csstype: 3.1.3 - '@typescript-eslint/eslint-plugin@8.0.1(@typescript-eslint/parser@8.0.1(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0)(typescript@5.5.4)': + '@typescript-eslint/eslint-plugin@8.0.0(@typescript-eslint/parser@8.0.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0)(typescript@5.5.4)': dependencies: '@eslint-community/regexpp': 4.11.0 - '@typescript-eslint/parser': 8.0.1(eslint@8.57.0)(typescript@5.5.4) - '@typescript-eslint/scope-manager': 8.0.1 - '@typescript-eslint/type-utils': 8.0.1(eslint@8.57.0)(typescript@5.5.4) - '@typescript-eslint/utils': 8.0.1(eslint@8.57.0)(typescript@5.5.4) - '@typescript-eslint/visitor-keys': 8.0.1 + '@typescript-eslint/parser': 8.0.0(eslint@8.57.0)(typescript@5.5.4) + '@typescript-eslint/scope-manager': 8.0.0 + '@typescript-eslint/type-utils': 8.0.0(eslint@8.57.0)(typescript@5.5.4) + '@typescript-eslint/utils': 8.0.0(eslint@8.57.0)(typescript@5.5.4) + '@typescript-eslint/visitor-keys': 8.0.0 eslint: 8.57.0 graphemer: 1.4.0 ignore: 5.3.1 @@ -3942,29 +3975,29 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.0.1(eslint@8.57.0)(typescript@5.5.4)': + '@typescript-eslint/parser@8.0.0(eslint@8.57.0)(typescript@5.5.4)': dependencies: - '@typescript-eslint/scope-manager': 8.0.1 - '@typescript-eslint/types': 8.0.1 - '@typescript-eslint/typescript-estree': 8.0.1(typescript@5.5.4) - '@typescript-eslint/visitor-keys': 8.0.1 - debug: 4.3.6 + '@typescript-eslint/scope-manager': 8.0.0 + '@typescript-eslint/types': 8.0.0 + '@typescript-eslint/typescript-estree': 8.0.0(typescript@5.5.4) + '@typescript-eslint/visitor-keys': 8.0.0 + debug: 4.3.5 eslint: 8.57.0 optionalDependencies: typescript: 5.5.4 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.0.1': + '@typescript-eslint/scope-manager@8.0.0': dependencies: - '@typescript-eslint/types': 8.0.1 - '@typescript-eslint/visitor-keys': 8.0.1 + '@typescript-eslint/types': 8.0.0 + '@typescript-eslint/visitor-keys': 8.0.0 - '@typescript-eslint/type-utils@8.0.1(eslint@8.57.0)(typescript@5.5.4)': + '@typescript-eslint/type-utils@8.0.0(eslint@8.57.0)(typescript@5.5.4)': dependencies: - '@typescript-eslint/typescript-estree': 8.0.1(typescript@5.5.4) - '@typescript-eslint/utils': 8.0.1(eslint@8.57.0)(typescript@5.5.4) - debug: 4.3.6 + '@typescript-eslint/typescript-estree': 8.0.0(typescript@5.5.4) + '@typescript-eslint/utils': 8.0.0(eslint@8.57.0)(typescript@5.5.4) + debug: 4.3.5 ts-api-utils: 1.3.0(typescript@5.5.4) optionalDependencies: typescript: 5.5.4 @@ -3972,13 +4005,13 @@ snapshots: - eslint - supports-color - '@typescript-eslint/types@8.0.1': {} + '@typescript-eslint/types@8.0.0': {} - '@typescript-eslint/typescript-estree@8.0.1(typescript@5.5.4)': + '@typescript-eslint/typescript-estree@8.0.0(typescript@5.5.4)': dependencies: - '@typescript-eslint/types': 8.0.1 - '@typescript-eslint/visitor-keys': 8.0.1 - debug: 4.3.6 + '@typescript-eslint/types': 8.0.0 + '@typescript-eslint/visitor-keys': 8.0.0 + debug: 4.3.5 globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.5 @@ -3989,28 +4022,28 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.0.1(eslint@8.57.0)(typescript@5.5.4)': + '@typescript-eslint/utils@8.0.0(eslint@8.57.0)(typescript@5.5.4)': dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@typescript-eslint/scope-manager': 8.0.1 - '@typescript-eslint/types': 8.0.1 - '@typescript-eslint/typescript-estree': 8.0.1(typescript@5.5.4) + '@typescript-eslint/scope-manager': 8.0.0 + '@typescript-eslint/types': 8.0.0 + '@typescript-eslint/typescript-estree': 8.0.0(typescript@5.5.4) eslint: 8.57.0 transitivePeerDependencies: - supports-color - typescript - '@typescript-eslint/visitor-keys@8.0.1': + '@typescript-eslint/visitor-keys@8.0.0': dependencies: - '@typescript-eslint/types': 8.0.1 + '@typescript-eslint/types': 8.0.0 eslint-visitor-keys: 3.4.3 '@ungap/structured-clone@1.2.0': {} - '@vitejs/plugin-react-swc@3.7.0(vite@5.4.0(@types/node@20.14.15)(terser@5.31.5))': + '@vitejs/plugin-react-swc@3.7.0(vite@5.3.4(@types/node@20.14.12)(terser@5.31.3))': dependencies: - '@swc/core': 1.7.10 - vite: 5.4.0(@types/node@20.14.15)(terser@5.31.5) + '@swc/core': 1.7.0 + vite: 5.3.4(@types/node@20.14.12)(terser@5.31.3) transitivePeerDependencies: - '@swc/helpers' @@ -4030,7 +4063,7 @@ snapshots: agent-base@7.1.1: dependencies: - debug: 4.3.6 + debug: 4.3.5 transitivePeerDependencies: - supports-color @@ -4132,7 +4165,7 @@ snapshots: babel-plugin-macros@3.1.0: dependencies: - '@babel/runtime': 7.25.0 + '@babel/runtime': 7.24.8 cosmiconfig: 7.1.0 resolve: 1.22.8 @@ -4155,7 +4188,7 @@ snapshots: broadcast-channel@3.7.0: dependencies: - '@babel/runtime': 7.25.0 + '@babel/runtime': 7.24.8 detect-node: 2.1.0 js-sha3: 0.8.0 microseconds: 0.2.0 @@ -4217,7 +4250,7 @@ snapshots: dependencies: toggle-selection: 1.0.6 - core-js@3.38.0: {} + core-js@3.37.1: {} cosmiconfig@7.1.0: dependencies: @@ -4270,7 +4303,7 @@ snapshots: dependencies: ms: 2.1.3 - debug@4.3.6: + debug@4.3.5: dependencies: ms: 2.1.2 @@ -4310,7 +4343,7 @@ snapshots: dom-helpers@5.2.1: dependencies: - '@babel/runtime': 7.25.0 + '@babel/runtime': 7.24.8 csstype: 3.1.3 entities@4.5.0: {} @@ -4453,17 +4486,17 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@8.0.1(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0): + eslint-module-utils@2.8.1(@typescript-eslint/parser@8.0.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.0.1(eslint@8.57.0)(typescript@5.5.4) + '@typescript-eslint/parser': 8.0.0(eslint@8.57.0)(typescript@5.5.4) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color - eslint-plugin-import@2.29.1(@typescript-eslint/parser@8.0.1(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0): + eslint-plugin-import@2.29.1(@typescript-eslint/parser@8.0.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0): dependencies: array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 @@ -4473,7 +4506,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@8.0.1(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@8.0.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0) hasown: 2.0.2 is-core-module: 2.15.0 is-glob: 4.0.3 @@ -4484,7 +4517,7 @@ snapshots: semver: 6.3.1 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.0.1(eslint@8.57.0)(typescript@5.5.4) + '@typescript-eslint/parser': 8.0.0(eslint@8.57.0)(typescript@5.5.4) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -4540,7 +4573,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.6 + debug: 4.3.5 doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.2.2 @@ -4643,7 +4676,7 @@ snapshots: combined-stream: 1.0.8 mime-types: 2.1.35 - framer-motion@11.3.24(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + framer-motion@11.3.12(@emotion/is-prop-valid@1.3.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: tslib: 2.6.3 optionalDependencies: @@ -4763,14 +4796,14 @@ snapshots: http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.1 - debug: 4.3.6 + debug: 4.3.5 transitivePeerDependencies: - supports-color https-proxy-agent@7.0.5: dependencies: agent-base: 7.1.1 - debug: 4.3.6 + debug: 4.3.5 transitivePeerDependencies: - supports-color @@ -4994,7 +5027,7 @@ snapshots: match-sorter@6.3.4: dependencies: - '@babel/runtime': 7.25.0 + '@babel/runtime': 7.24.8 remove-accents: 0.5.0 memoize-one@6.0.0: {} @@ -5130,7 +5163,7 @@ snapshots: possible-typed-array-names@1.0.0: {} - postcss@8.4.41: + postcss@8.4.39: dependencies: nanoid: 3.3.7 picocolors: 1.0.1 @@ -5160,7 +5193,7 @@ snapshots: react-clientside-effect@1.2.6(react@18.3.1): dependencies: - '@babel/runtime': 7.25.0 + '@babel/runtime': 7.24.8 react: 18.3.1 react-dom@18.3.1(react@18.3.1): @@ -5173,7 +5206,7 @@ snapshots: react-focus-lock@2.12.1(@types/react@18.3.3)(react@18.3.1): dependencies: - '@babel/runtime': 7.25.0 + '@babel/runtime': 7.24.8 focus-lock: 1.3.5 prop-types: 15.8.1 react: 18.3.1 @@ -5191,7 +5224,7 @@ snapshots: react-query@3.39.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@babel/runtime': 7.25.0 + '@babel/runtime': 7.24.8 broadcast-channel: 3.7.0 match-sorter: 6.3.4 react: 18.3.1 @@ -5221,11 +5254,11 @@ snapshots: react-select@5.8.0(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@babel/runtime': 7.25.0 - '@emotion/cache': 11.13.1 + '@babel/runtime': 7.24.8 + '@emotion/cache': 11.13.0 '@emotion/react': 11.13.0(@types/react@18.3.3)(react@18.3.1) - '@floating-ui/dom': 1.6.10 - '@types/react-transition-group': 4.4.11 + '@floating-ui/dom': 1.6.8 + '@types/react-transition-group': 4.4.10 memoize-one: 6.0.0 prop-types: 15.8.1 react: 18.3.1 @@ -5247,7 +5280,7 @@ snapshots: react-transition-group@4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@babel/runtime': 7.25.0 + '@babel/runtime': 7.24.8 dom-helpers: 5.2.1 loose-envify: 1.4.0 prop-types: 15.8.1 @@ -5266,7 +5299,7 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.2.4 globalthis: 1.0.4 - which-builtin-type: 1.1.4 + which-builtin-type: 1.1.3 regenerator-runtime@0.14.1: {} @@ -5301,26 +5334,26 @@ snapshots: dependencies: glob: 7.2.3 - rollup@4.20.0: + rollup@4.19.0: dependencies: '@types/estree': 1.0.5 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.20.0 - '@rollup/rollup-android-arm64': 4.20.0 - '@rollup/rollup-darwin-arm64': 4.20.0 - '@rollup/rollup-darwin-x64': 4.20.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.20.0 - '@rollup/rollup-linux-arm-musleabihf': 4.20.0 - '@rollup/rollup-linux-arm64-gnu': 4.20.0 - '@rollup/rollup-linux-arm64-musl': 4.20.0 - '@rollup/rollup-linux-powerpc64le-gnu': 4.20.0 - '@rollup/rollup-linux-riscv64-gnu': 4.20.0 - '@rollup/rollup-linux-s390x-gnu': 4.20.0 - '@rollup/rollup-linux-x64-gnu': 4.20.0 - '@rollup/rollup-linux-x64-musl': 4.20.0 - '@rollup/rollup-win32-arm64-msvc': 4.20.0 - '@rollup/rollup-win32-ia32-msvc': 4.20.0 - '@rollup/rollup-win32-x64-msvc': 4.20.0 + '@rollup/rollup-android-arm-eabi': 4.19.0 + '@rollup/rollup-android-arm64': 4.19.0 + '@rollup/rollup-darwin-arm64': 4.19.0 + '@rollup/rollup-darwin-x64': 4.19.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.19.0 + '@rollup/rollup-linux-arm-musleabihf': 4.19.0 + '@rollup/rollup-linux-arm64-gnu': 4.19.0 + '@rollup/rollup-linux-arm64-musl': 4.19.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.19.0 + '@rollup/rollup-linux-riscv64-gnu': 4.19.0 + '@rollup/rollup-linux-s390x-gnu': 4.19.0 + '@rollup/rollup-linux-x64-gnu': 4.19.0 + '@rollup/rollup-linux-x64-musl': 4.19.0 + '@rollup/rollup-win32-arm64-msvc': 4.19.0 + '@rollup/rollup-win32-ia32-msvc': 4.19.0 + '@rollup/rollup-win32-x64-msvc': 4.19.0 fsevents: 2.3.3 rrweb-cssom@0.6.0: {} @@ -5469,7 +5502,7 @@ snapshots: symbol-tree@3.2.4: {} - terser@5.31.5: + terser@5.31.3: dependencies: '@jridgewell/source-map': 0.3.6 acorn: 8.12.1 @@ -5567,7 +5600,7 @@ snapshots: unload@2.2.0: dependencies: - '@babel/runtime': 7.25.0 + '@babel/runtime': 7.24.8 detect-node: 2.1.0 uri-js@4.4.1: @@ -5600,15 +5633,15 @@ snapshots: optionalDependencies: '@types/react': 18.3.3 - vite@5.4.0(@types/node@20.14.15)(terser@5.31.5): + vite@5.3.4(@types/node@20.14.12)(terser@5.31.3): dependencies: esbuild: 0.21.5 - postcss: 8.4.41 - rollup: 4.20.0 + postcss: 8.4.39 + rollup: 4.19.0 optionalDependencies: - '@types/node': 20.14.15 + '@types/node': 20.14.12 fsevents: 2.3.3 - terser: 5.31.5 + terser: 5.31.3 w3c-xmlserializer@5.0.0: dependencies: @@ -5635,7 +5668,7 @@ snapshots: is-string: 1.0.7 is-symbol: 1.0.4 - which-builtin-type@1.1.4: + which-builtin-type@1.1.3: dependencies: function.prototype.name: 1.1.6 has-tostringtag: 1.0.2

%`W7SA>lE(|0C%UpItn9DPsF@ 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 literal 0 HcmV?d00001 diff --git a/img/ss.png b/img/ss.png new file mode 100644 index 0000000000000000000000000000000000000000..db95f42ba4391e7a465b2d50e4ad2a68186ddcb0 GIT binary patch literal 329947 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