├── .eslintignore ├── .eslintrc.json ├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ ├── build.yml │ ├── draft_stable.yml │ ├── draft_stable_arm.yml │ └── eslint_clippy.yml ├── .gitignore ├── .vscode └── settings.json ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── app ├── globals.css ├── layout.tsx └── page.tsx ├── components.json ├── components ├── app-loader.tsx ├── app-splash-screen.tsx ├── profile-dialog-delete.tsx ├── profile-dialog-form.tsx ├── profile-form.tsx ├── profile-table.tsx ├── theme-provider.tsx └── ui │ ├── alert-confirm.tsx │ ├── alert-dialog.tsx │ ├── button.tsx │ ├── data-table.tsx │ ├── dialog.tsx │ ├── dropdown-menu.tsx │ ├── form.tsx │ ├── icons.tsx │ ├── input.tsx │ ├── label.tsx │ ├── table.tsx │ ├── textarea.tsx │ └── theme-toggle.tsx ├── flake.lock ├── flake.nix ├── lib ├── effects.ts └── utils.ts ├── next-env.d.ts ├── next.config.mjs ├── nix └── wireguard-gui.nix ├── package-lock.json ├── package.json ├── postcss.config.js ├── prettier.config.js ├── public └── img │ ├── app.png │ ├── wireguard.png │ └── wireguard_connected.png ├── rustfmt.toml ├── snap └── snapcraft.yaml ├── src-tauri ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── build.rs ├── capabilities │ └── migrated.json ├── gen │ └── schemas │ │ ├── acl-manifests.json │ │ ├── capabilities.json │ │ ├── desktop-schema.json │ │ └── linux-schema.json ├── icons │ ├── 128x128.png │ ├── 128x128@2x.png │ ├── 32x32.png │ ├── icon.icns │ ├── icon.ico │ ├── icon.png │ ├── tray.png │ └── tray_connected.png ├── scripts │ ├── wg.sh │ └── zenity.sh ├── src │ └── main.rs └── tauri.conf.json ├── tailwind.config.ts ├── tsconfig.json └── types ├── app.ts └── profile.ts /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/* 2 | .cache 3 | public 4 | node_modules 5 | *.esm.js 6 | *.config.js 7 | src-tauri/* 8 | out/* 9 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/eslintrc", 3 | "root": true, 4 | "plugins": [ 5 | "import" 6 | // "tailwindcss", 7 | ], 8 | "extends": [ 9 | "next/core-web-vitals", 10 | "prettier" 11 | // "airbnb-typescript", 12 | // "airbnb/hooks", 13 | // "plugin:tailwindcss/recommended" 14 | ], 15 | "parserOptions": { 16 | "project": "./tsconfig.json", 17 | "tsConfigRootDir": "./" 18 | }, 19 | "rules": { 20 | "import/no-unresolved": "error", 21 | "@next/next/no-html-link-for-pages": "off", 22 | "react/jsx-key": "off", 23 | "tailwindcss/no-custom-classname": "off", 24 | "comma-dangle": "off", 25 | "@typescript-eslint/comma-dangle": "off", 26 | "indent": "off", 27 | "@typescript-eslint/indent": "off" 28 | }, 29 | "settings": { 30 | "import/parsers": { 31 | "@typescript-eslint/parser": [".ts", ".tsx"] 32 | }, 33 | "import/resolver": { 34 | "typescript": { 35 | "alwaysTryTypes": true 36 | } 37 | }, 38 | "tailwindcss": { 39 | "callees": ["cn"], 40 | "config": "./tailwind.config.js" 41 | }, 42 | "next": { 43 | "rootDir": ["./"] 44 | } 45 | }, 46 | "overrides": [ 47 | { 48 | "files": ["*.ts", "*.tsx"], 49 | "parser": "@typescript-eslint/parser" 50 | } 51 | ] 52 | } 53 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [leon3s] 4 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "npm" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | - package-ecosystem: "cargo" # See documentation for possible values 13 | directory: "/src-tauri" # Location of package manifests 14 | schedule: 15 | interval: "weekly" 16 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # Build the application 2 | 3 | name: Build 4 | 5 | on: [push, pull_request] 6 | 7 | jobs: 8 | build: 9 | strategy: 10 | fail-fast: true 11 | matrix: 12 | os: [ubuntu-latest] 13 | # Todo make it work on other OS ? 14 | # , macos-latest, windows-latest] 15 | 16 | runs-on: ${{ matrix.os }} 17 | 18 | steps: 19 | - name: Checkout repository code 20 | uses: actions/checkout@v3 21 | 22 | # Cache Rust 23 | - uses: actions/cache@v3 24 | with: 25 | path: ./src-tauri/target 26 | key: ${{matrix.os}}-${{ hashFiles('./src-tauri/Cargo.lock') }} 27 | 28 | # Cache Rust 29 | - uses: actions/cache@v3 30 | with: 31 | path: ~/.cargo 32 | key: ${{matrix.os}}-${{ hashFiles('./src-tauri/Cargo.lock') }} 33 | 34 | # Cache Node 35 | - uses: actions/cache@v3 36 | with: 37 | path: ./node_modules 38 | key: ${{matrix.os}}-${{ hashFiles('./package-lock.json') }} 39 | 40 | # Cache Next.js 41 | - uses: actions/cache@v3 42 | with: 43 | path: ./src-next/.next 44 | key: ${{matrix.os}} 45 | 46 | - name: Install Node.js v20 LTS 47 | uses: actions/setup-node@v3 48 | with: 49 | node-version: 20 50 | 51 | - name: Install Rust with Clippy 52 | uses: dtolnay/rust-toolchain@stable 53 | with: 54 | components: rustfmt, clippy 55 | 56 | - name: Install Tauri dependencies 57 | if: matrix.os == 'ubuntu-latest' 58 | run: | 59 | sudo apt-get update 60 | sudo apt install --reinstall ca-certificates 61 | sudo update-ca-certificates -f 62 | sudo apt-get install javascriptcoregtk-4.1 libsoup-3.0 webkit2gtk-4.1 libayatana-appindicator3-dev librsvg2-dev -y 63 | 64 | - name: Install node dependencies 65 | run: npm install 66 | 67 | - name: Build application 68 | run: npm run tauri build -- --verbose 69 | -------------------------------------------------------------------------------- /.github/workflows/draft_stable.yml: -------------------------------------------------------------------------------- 1 | name: Draft stable 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'release/stable/**' 7 | pull_request: 8 | branches: 9 | - 'release/stable/**' 10 | types: [opened, synchronize] 11 | 12 | env: 13 | CARGO_TERM_COLOR: always 14 | 15 | jobs: 16 | draft_stable: 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - uses: actions/checkout@v3 21 | # Extract branch info 22 | - name: Set info 23 | run: | 24 | echo "CHANNEL=$(echo ${GITHUB_REF} | awk -F/ '{print $4}')" >> $GITHUB_ENV 25 | echo "VERSION=$(echo ${GITHUB_REF} | awk -F/ '{print $5}')" >> $GITHUB_ENV 26 | # Print info for debug 27 | - name: Print Info 28 | run: | 29 | echo $GITHUB_REF 30 | echo $CHANNEL 31 | echo $VERSION 32 | 33 | # Cache Rust 34 | - uses: actions/cache@v3 35 | with: 36 | path: ./src-tauri/target 37 | key: ${{ hashFiles('./src-tauri/Cargo.lock') }} 38 | 39 | # Cache Rust 40 | - uses: actions/cache@v3 41 | with: 42 | path: ~/.cargo 43 | key: ${{ hashFiles('./src-tauri/Cargo.lock') }} 44 | 45 | # Cache Node 46 | - uses: actions/cache@v3 47 | with: 48 | path: ./node_modules 49 | key: ${{ hashFiles('./package-lock.json') }} 50 | 51 | # Cache Next.js 52 | - uses: actions/cache@v3 53 | with: 54 | path: ./src-next/.next 55 | key: ${{ hashFiles('./src-next/package-lock.json') }} 56 | 57 | - name: Install Tauri dependencies 58 | run: | 59 | sudo apt-get update 60 | sudo apt-get install javascriptcoregtk-4.1 libsoup-3.0 webkit2gtk-4.1 libayatana-appindicator3-dev librsvg2-dev -y 61 | 62 | - name: Install node dependencies 63 | run: npm install 64 | 65 | - name: Build application 66 | run: npm run tauri build 67 | 68 | - name: Test if release already exists 69 | id: release-exists 70 | continue-on-error: true 71 | run: gh release view $VERSION-$CHANNEL 72 | env: 73 | GITHUB_TOKEN: ${{ secrets.CI_TOKEN }} 74 | - name: Create draft release 75 | if: steps.release-exists.outcome == 'failure' && steps.release-exists.conclusion == 'success' 76 | run: | 77 | gh release create -d \ 78 | $VERSION-$CHANNEL \ 79 | -t $VERSION-$CHANNEL \ 80 | -F ./CHANGELOG.md \ 81 | ./src-tauri/target/release/bundle/deb/wireguard-gui_${VERSION}_amd64.deb#wireguard-gui_amd64.deb \ 82 | ./src-tauri/target/release/bundle/appimage/wireguard-gui_${VERSION}_amd64.AppImage#wireguard-gui_amd64.AppImage 83 | env: 84 | GITHUB_TOKEN: ${{ secrets.CI_TOKEN }} 85 | - name: Update draft release 86 | if: steps.release-exists.outcome == 'success' && steps.release-exists.conclusion == 'success' 87 | run: | 88 | gh release delete-asset -y \ 89 | $VERSION-$CHANNEL \ 90 | wireguard-gui_${VERSION}_amd64.deb || true 91 | gh release delete-asset -y \ 92 | $VERSION-$CHANNEL \ 93 | wireguard-gui_${VERSION}_amd64.AppImage|| true 94 | gh release upload \ 95 | $VERSION-$CHANNEL \ 96 | ./src-tauri/target/release/bundle/deb/wireguard-gui_${VERSION}_amd64.deb#wireguard-gui_amd64.deb 97 | gh release upload \ 98 | $VERSION-$CHANNEL \ 99 | ./src-tauri/target/release/bundle/appimage/wireguard-gui_${VERSION}_amd64.AppImage#wireguard-gui_amd64.AppImage 100 | env: 101 | GITHUB_TOKEN: ${{ secrets.CI_TOKEN }} 102 | -------------------------------------------------------------------------------- /.github/workflows/draft_stable_arm.yml: -------------------------------------------------------------------------------- 1 | name: Draft stable arm 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'release/stable/**' 7 | pull_request: 8 | branches: 9 | - 'release/stable/**' 10 | types: [opened, synchronize] 11 | 12 | env: 13 | CARGO_TERM_COLOR: always 14 | 15 | jobs: 16 | draft_stable: 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - uses: actions/checkout@v3 21 | # Extract branch info 22 | - name: Set info 23 | run: | 24 | echo "CHANNEL=$(echo ${GITHUB_REF} | awk -F/ '{print $4}')" >> $GITHUB_ENV 25 | echo "VERSION=$(echo ${GITHUB_REF} | awk -F/ '{print $5}')" >> $GITHUB_ENV 26 | # Print info for debug 27 | - name: Print Info 28 | run: | 29 | echo $GITHUB_REF 30 | echo $CHANNEL 31 | echo $VERSION 32 | sudo apt update && sudo apt upgrade -y 33 | sudo apt install qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils virt-manager qemu-system-arm -y 34 | sudo apt install -y qemu-user-static binfmt-support 35 | sudo apt install make -y 36 | sudo apt install ninja-build -y 37 | sudo apt install libpixman-1-dev -y 38 | wget https://download.qemu.org/qemu-7.0.0.tar.xz 39 | tar xvJf qemu-7.0.0.tar.xz 40 | cd qemu-7.0.0 41 | ./configure 42 | make 43 | docker run --privileged --rm tonistiigi/binfmt --install all 44 | docker run --rm --privileged multiarch/qemu-user-static --reset -p yes -c yes 45 | 46 | # Cache Rust 47 | - uses: actions/cache@v3 48 | with: 49 | path: ./src-tauri/target 50 | key: ${{ hashFiles('./src-tauri/Cargo.lock') }} 51 | 52 | # Cache Rust 53 | - uses: actions/cache@v3 54 | with: 55 | path: ~/.cargo 56 | key: ${{ hashFiles('./src-tauri/Cargo.lock') }} 57 | 58 | # Cache Node 59 | - uses: actions/cache@v3 60 | with: 61 | path: ./node_modules 62 | key: ${{ hashFiles('./package-lock.json') }} 63 | 64 | # Cache Next.js 65 | - uses: actions/cache@v3 66 | with: 67 | path: ./src-next/.next 68 | key: ${{ hashFiles('./src-next/package-lock.json') }} 69 | 70 | - uses: pguyot/arm-runner-action@v2.6.5 71 | with: 72 | base_image: https://dietpi.com/downloads/images/DietPi_RPi-ARMv8-Bullseye.img.xz 73 | cpu: cortex-a53 74 | bind_mount_repository: true 75 | image_additional_mb: 10240 76 | optimize_image: no 77 | commands: | 78 | # Rust complains (rightly) that $HOME doesn't match eid home 79 | export HOME=/root 80 | # Workaround to CI worker being stuck on Updating crates.io index 81 | export CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse 82 | # Install setup prerequisites 83 | apt-get update -y --allow-releaseinfo-change 84 | apt-get upgrade -y 85 | apt-get autoremove -y 86 | apt-get install curl 87 | curl https://sh.rustup.rs -sSf | sh -s -- -y 88 | . "$HOME/.cargo/env" 89 | curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash 90 | # Install framework specific packages 91 | apt-get install -y nodejs 92 | # npm install next@latest react@latest react-dom@latest eslint-config-next@latest 93 | # Install build tools and tauri-cli requirements 94 | apt-get install -yf file libdbus-1-dev libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev 95 | apt-get install javascriptcoregtk-4.1 libsoup-3.0 webkit2gtk-4.1 libayatana-appindicator3-dev librsvg2-dev -y 96 | # cargo install tauri-cli 97 | # Install frontend dependencies 98 | npm install 99 | # Build the application 100 | npm run tauri build -- --verbose 101 | 102 | - name: Test if release already exists 103 | id: release-exists 104 | continue-on-error: true 105 | run: gh release view $VERSION-$CHANNEL 106 | env: 107 | GITHUB_TOKEN: ${{ secrets.CI_TOKEN }} 108 | - name: Create draft release 109 | if: steps.release-exists.outcome == 'failure' && steps.release-exists.conclusion == 'success' 110 | run: | 111 | gh release create -d \ 112 | $VERSION-$CHANNEL \ 113 | -t $VERSION-$CHANNEL \ 114 | -F ./CHANGELOG.md \ 115 | ./src-tauri/target/release/bundle/deb/wireguard-gui_${VERSION}_arm64.deb#wireguard-gui_arm64.deb \ 116 | ./src-tauri/target/release/bundle/appimage/wireguard-gui_${VERSION}_aarch64.AppImage#wireguard-gui_aarch64.AppImage 117 | env: 118 | GITHUB_TOKEN: ${{ secrets.CI_TOKEN }} 119 | 120 | - name: Update draft release 121 | if: steps.release-exists.outcome == 'success' && steps.release-exists.conclusion == 'success' 122 | run: | 123 | gh release delete-asset -y \ 124 | $VERSION-$CHANNEL \ 125 | wireguard-gui_${VERSION}_arm64.deb || true 126 | gh release delete-asset -y \ 127 | $VERSION-$CHANNEL \ 128 | wireguard-gui_${VERSION}_arm64.AppImage|| true 129 | gh release upload \ 130 | $VERSION-$CHANNEL \ 131 | ./src-tauri/target/release/bundle/deb/wireguard-gui_${VERSION}_arm64.deb#wireguard-gui_arm64.deb 132 | gh release upload \ 133 | $VERSION-$CHANNEL \ 134 | ./src-tauri/target/release/bundle/appimage/wireguard-gui_${VERSION}_arm64.AppImage#wireguard-gui_arm64.AppImage 135 | env: 136 | GITHUB_TOKEN: ${{ secrets.CI_TOKEN }} 137 | -------------------------------------------------------------------------------- /.github/workflows/eslint_clippy.yml: -------------------------------------------------------------------------------- 1 | # Run Eslint and Clippy 2 | 3 | name: Eslint & Clippy 4 | 5 | on: [push, pull_request] 6 | 7 | jobs: 8 | eslint_and_clippy: 9 | strategy: 10 | fail-fast: true 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout repository code 15 | uses: actions/checkout@v3 16 | 17 | # Cache Rust 18 | - uses: actions/cache@v3 19 | with: 20 | path: ./src-tauri/target 21 | key: ${{ hashFiles('./src-tauri/Cargo.lock') }} 22 | 23 | # Cache Rust 24 | - uses: actions/cache@v3 25 | with: 26 | path: ~/.cargo 27 | key: ${{ hashFiles('./src-tauri/Cargo.lock') }} 28 | 29 | # Cache Node 30 | - uses: actions/cache@v3 31 | with: 32 | path: ./node_modules 33 | key: ${{ hashFiles('./package-lock.json') }} 34 | 35 | - name: Install Node.js v20 LTS 36 | uses: actions/setup-node@v3 37 | with: 38 | node-version: 20 39 | 40 | - name: Install Rust with Clippy 41 | uses: dtolnay/rust-toolchain@stable 42 | with: 43 | components: rustfmt, clippy 44 | 45 | - name: Install Tauri dependencies 46 | run: | 47 | sudo apt-get update 48 | sudo apt-get install javascriptcoregtk-4.1 libsoup-3.0 webkit2gtk-4.1 libayatana-appindicator3-dev -y 49 | 50 | - name: Create empty 'out' directory for Tauri generate context 51 | run: mkdir out 52 | 53 | - name: Install node dependencies 54 | run: npm install 55 | 56 | - name: Run Eslint 57 | run: npm run lint 58 | 59 | - name: Run Clippy 60 | run: npm run clippy 61 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .next 4 | target 5 | out 6 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.rulers": [80], 3 | "editor.tabSize": 2, 4 | "editor.detectIndentation": false, 5 | "editor.trimAutoWhitespace": true, 6 | "editor.formatOnSave": true, 7 | "files.insertFinalNewline": true, 8 | "files.trimTrailingWhitespace": true, 9 | "rust-analyzer.showUnlinkedFileNotification": false, 10 | "rust-analyzer.checkOnSave": true, 11 | "rust-analyzer.check.command": "clippy", 12 | "rust-analyzer.cargo.noDefaultFeatures": true, 13 | "rust-analyzer.cargo.features": ["dev"], 14 | // "typescript.enablePromptUseWorkspaceTsdk": true 15 | "editor.defaultFormatter": "esbenp.prettier-vscode", 16 | } 17 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [0.1.6] - 2024-10-14 9 | 10 | ### Added 11 | 12 | - Upgrade to tauri v2 13 | - Sort profiles by name case insensitive 14 | 15 | ## [0.1.5] - 2024-09-05 16 | 17 | ### Added 18 | 19 | - Sort profiles by name 20 | - Search bar to filter profiles by name 21 | 22 | ## [0.1.4] - 2024-09-02 23 | 24 | ### Changed 25 | 26 | - Switch from zenity to polkit for authentication by [@CD11b](https://github.com/CD11b) 27 | 28 | ## [0.1.3] - 2024-08-04 29 | 30 | ### Fixed 31 | 32 | - Close modal when creating a new profile 33 | 34 | ## Added 35 | 36 | - Show current version in the header 37 | 38 | ## [0.1.2] - 2024-07-07 39 | 40 | ### Added 41 | 42 | - Better error handling 43 | 44 | ## [0.1.1] - 2024-02-25 45 | 46 | ### Changed 47 | 48 | - Icon instead of dropdown menu on profile table to delete, edit and connect 49 | 50 | ## [0.1.0] - 2024-01-30 51 | 52 | ### Added 53 | 54 | - List profile 55 | - Create profile 56 | - Delete Profile 57 | - Connect profile 58 | - Disconnect 59 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | - Demonstrating empathy and kindness toward other people 21 | - Being respectful of differing opinions, viewpoints, and experiences 22 | - Giving and gracefully accepting constructive feedback 23 | - Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | - Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | - The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | - Trolling, insulting or derogatory comments, and personal or political attacks 33 | - Public or private harassment 34 | - Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | - Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | contact@next-hat.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2023 Wireguard GUI Contributors 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024-2025 Wireguard GUI Contributors 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

Wireguard GUI

3 |

❤️ Made with love with Nextauri ❤️

4 | 5 |

6 | 7 | [![Stars](https://img.shields.io/github/stars/leon3s/wireguard-gui?style=social)](https://github.com/leon3s/wireguard-gui) 8 | [![Rust](https://img.shields.io/badge/built_with-Rust-dca282.svg)](https://github.com/leon3s/wireguard-gui) 9 | [![Typescript](https://img.shields.io/badge/built_with-Typescript-3178C6.svg)](https://github.com/leon3s/wireguard-gui) 10 | [![Discord](https://img.shields.io/discord/1011267493114949693?label=chat&logo=discord)](https://discord.gg/WV4Aac8uZg) 11 | 12 |

13 | 14 |

15 | 16 | [![Eslint & Clippy](https://github.com/leon3s/wireguard-gui/actions/workflows/eslint_clippy.yml/badge.svg)](https://github.com/leon3s/wireguard-gui/actions/workflows/eslint_clippy.yml) 17 | [![Build](https://github.com/leon3s/wireguard-gui/actions/workflows/build.yml/badge.svg)](https://github.com/leon3s/wireguard-gui/actions/workflows/build.yml) 18 | 19 |

20 | 21 | 22 | 23 |

24 | 25 | ## IMPORTANT 26 | 27 | The snap version is currently not working, please use the .deb package instead. 28 | 29 | ## ❓ What is Wireguard GUI ? 30 | 31 | Wireguard GUI is a Linux application that allow you to manage your Wireguard VPN configuration. 32 | 33 | ## 💡 Features 34 | 35 | - [x] List all profile 36 | - [x] Add a new profile 37 | - [x] Edit a profile 38 | - [x] Remove a profile 39 | - [x] Start a profile 40 | - [x] Stop a profile 41 | - [ ] Import a profile 42 | - [ ] Export a profile 43 | 44 | ## 💪 Motivation 45 | 46 | I didn't found any GUI application that allow me to manage my Wireguard VPN configuration.
47 | I wanted to make an application with nextauri since a while, so i took this opportunity to make it. 48 | 49 | ## Pre-requisites 50 | 51 | In order to work properly, the application needs the following dependencies: 52 | 53 | ```sh 54 | sudo apt-get install javascriptcoregtk-4.1 libsoup-3.0 webkit2gtk-4.1 libayatana-appindicator3-dev librsvg2-dev wireguard resolvconf -y 55 | ``` 56 | 57 | ## 📦 Installation 58 | 59 | Release are currently available as .deb package for Debian/Ubuntu or Appimage for others distro. 60 | It can be found on the [release page](https://github.com/leon3s/wireguard-gui/releases/latest) 61 | 62 | ## 🚀 Demo 63 | 64 | A demo will be recorded soon. 65 | 66 | ## 🎨 Developing 67 | 68 | Be sure you have [NodeJS](https://nodejs.org/en/) and [Rust](https://www.rust-lang.org/) installed on your system 69 | 70 | 1. See Tauri [prerequisites](https://tauri.app/v1/guides/getting-started/prerequisites/) to prepare your system to build `Tauri` 71 | 72 | 2. Clone or fork this repository 73 | ```sh 74 | git clone https://github.com/leon3s/wireguard-gui 75 | cd wireguard-gui 76 | ``` 77 | 3. Install node dependencies 78 | ```sh 79 | npm install 80 | ``` 81 | 82 | To get started you only need one command 83 | 84 | ```sh 85 | npm run dev 86 | ``` 87 | 88 | ## ⚡Production 89 | 90 | To build in production you can do it in a single command. 91 | This will build and export Next.js and build Tauri for your current environnement. 92 | 93 | ```sh 94 | npm run tauri build 95 | ``` 96 | 97 | ## 📚 Documentation 98 | 99 | To learn more about Tauri and Next.js, take a look at the following resources: 100 | 101 | - [Tauri Guides](https://tauri.app/v1/guides/) - guide about Tauri. 102 | - [Tauri API](https://tauri.app/v1/api/js) - discover javascript Tauri api. 103 | - [Next.js Documentation](https://nextjs.org/docs) - learn more about Next.js. 104 | - [Next.js Tutorial](https://nextjs.org/learn) - interactive Next.js tutorial. 105 | -------------------------------------------------------------------------------- /app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | :root { 7 | --background: 0 0% 100%; 8 | --foreground: 240 10% 3.9%; 9 | 10 | --card: 0 0% 100%; 11 | --card-foreground: 240 10% 3.9%; 12 | 13 | --popover: 0 0% 100%; 14 | --popover-foreground: 240 10% 3.9%; 15 | 16 | --primary: 240 5.9% 10%; 17 | --primary-foreground: 0 0% 98%; 18 | 19 | --secondary: 240 4.8% 95.9%; 20 | --secondary-foreground: 240 5.9% 10%; 21 | 22 | --muted: 240 4.8% 95.9%; 23 | --muted-foreground: 240 3.8% 46.1%; 24 | 25 | --accent: 240 4.8% 95.9%; 26 | --accent-foreground: 240 5.9% 10%; 27 | 28 | --destructive: 0 84.2% 60.2%; 29 | --destructive-foreground: 0 0% 98%; 30 | 31 | --border: 240 5.9% 90%; 32 | --input: 240 5.9% 90%; 33 | --ring: 240 10% 3.9%; 34 | 35 | --radius: 0.5rem; 36 | 37 | --chart-1: 12 76% 61%; 38 | 39 | --chart-2: 173 58% 39%; 40 | 41 | --chart-3: 197 37% 24%; 42 | 43 | --chart-4: 43 74% 66%; 44 | 45 | --chart-5: 27 87% 67%; 46 | } 47 | 48 | .dark { 49 | --background: 240 10% 3.9%; 50 | --foreground: 0 0% 98%; 51 | 52 | --card: 240 10% 3.9%; 53 | --card-foreground: 0 0% 98%; 54 | 55 | --popover: 240 10% 3.9%; 56 | --popover-foreground: 0 0% 98%; 57 | 58 | --primary: 0 0% 98%; 59 | --primary-foreground: 240 5.9% 10%; 60 | 61 | --secondary: 240 3.7% 15.9%; 62 | --secondary-foreground: 0 0% 98%; 63 | 64 | --muted: 240 3.7% 15.9%; 65 | --muted-foreground: 240 5% 64.9%; 66 | 67 | --accent: 240 3.7% 15.9%; 68 | --accent-foreground: 0 0% 98%; 69 | 70 | --destructive: 0 62.8% 30.6%; 71 | --destructive-foreground: 0 0% 98%; 72 | 73 | --border: 240 3.7% 15.9%; 74 | --input: 240 3.7% 15.9%; 75 | --ring: 240 4.9% 83.9%; 76 | --chart-1: 220 70% 50%; 77 | --chart-2: 160 60% 45%; 78 | --chart-3: 30 80% 55%; 79 | --chart-4: 280 65% 60%; 80 | --chart-5: 340 75% 55%; 81 | } 82 | } 83 | 84 | /* @layer base { 85 | * { 86 | @apply border-border; 87 | } 88 | body { 89 | @apply bg-background text-foreground; 90 | } 91 | } */ 92 | 93 | html, 94 | body, 95 | #__next { 96 | height: 100%; 97 | width: 100%; 98 | } 99 | 100 | * { 101 | font-family: 102 | Consolas, 103 | -apple-system, 104 | BlinkMacSystemFont, 105 | Segoe UI, 106 | Roboto, 107 | Oxygen, 108 | Ubuntu, 109 | Cantarell, 110 | Fira Sans, 111 | Droid Sans, 112 | Helvetica Neue, 113 | sans-serif; 114 | } 115 | -------------------------------------------------------------------------------- /app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Viewport } from 'next'; 2 | 3 | import '@/app/globals.css'; 4 | 5 | import { ThemeProvider } from '@/components/theme-provider'; 6 | 7 | export const viewport: Viewport = { 8 | themeColor: [ 9 | { media: '(prefers-color-scheme: light)', color: 'white' }, 10 | { media: '(prefers-color-scheme: dark)', color: 'black' }, 11 | ], 12 | width: 'device-width', 13 | initialScale: 1, 14 | viewportFit: 'cover', 15 | }; 16 | 17 | export default function RootLayout({ 18 | children, 19 | }: { 20 | children: React.ReactNode; 21 | }) { 22 | return ( 23 | 24 | 25 | 26 | 31 | {children} 32 | 33 | 34 | 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /app/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { Suspense, useCallback, useEffect, useState } from 'react'; 4 | import Image from 'next/image'; 5 | import { getVersion } from '@tauri-apps/api/app'; 6 | import { Lock, PowerOff, Unlock } from 'lucide-react'; 7 | 8 | import { connect, disconnect, useAppLoader, useAppState } from '@/lib/effects'; 9 | import { Button } from '@/components/ui/button'; 10 | import { AppLoader } from '@/components/app-loader'; 11 | import { ProfileTable } from '@/components/profile-table'; 12 | 13 | export default function Index() { 14 | const [state, , , , fetchState] = useAppState(); 15 | const [appLoader, setAppLoader] = useAppLoader(); 16 | const [appVersion, setAppVersion] = useState(null); 17 | 18 | useEffect(() => { 19 | getVersion().then((v) => setAppVersion(v)); 20 | }, []); 21 | 22 | const onConnectionFinish = useCallback(() => { 23 | return () => { 24 | fetchState(); 25 | setAppLoader((l) => ({ ...l, isOpen: false })); 26 | }; 27 | }, [fetchState, setAppLoader]); 28 | 29 | const onError = useCallback((err: any) => { 30 | alert(err?.message || 'Unknow error'); 31 | }, []); 32 | 33 | const onConnect = useCallback( 34 | (profile: string) => { 35 | return () => { 36 | setAppLoader({ 37 | kind: 'Connecting', 38 | isOpen: true, 39 | message: `Connecting to ${profile}`, 40 | }); 41 | connect(profile, onConnectionFinish(), onError); 42 | }; 43 | }, 44 | [setAppLoader, onConnectionFinish, onError], 45 | ); 46 | 47 | const onDisconnect = useCallback(() => { 48 | setAppLoader({ 49 | kind: 'Disconnecting', 50 | isOpen: true, 51 | message: `Disconnecting from ${state.current}`, 52 | }); 53 | disconnect(onConnectionFinish(), onError); 54 | }, [state, setAppLoader, onConnectionFinish, onError]); 55 | 56 | return ( 57 |
58 | 59 |
60 |
61 | wireguard 68 | v{appVersion} 69 | 78 |
79 |
80 | {state.conn_st === 'Connected' ? ( 81 | 82 | ) : ( 83 | 84 | )} 85 |

{state.current || 'Not connected'}

86 |

{state?.pub_ip || 'Unknown'}

87 |
88 | 89 | 90 | 91 |
92 |
93 | ); 94 | } 95 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": false, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "app/globals.css", 9 | "baseColor": "zinc", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /components/app-loader.tsx: -------------------------------------------------------------------------------- 1 | import { Globe2Icon } from 'lucide-react'; 2 | 3 | export interface AppLoaderProps { 4 | isOpen?: boolean; 5 | kind: 'Connecting' | 'Disconnecting'; 6 | message?: string; 7 | } 8 | 9 | const hiddenStyle = { display: 'none', zIndex: -1, width: 0, height: 0 }; 10 | 11 | export function AppLoader({ isOpen, message, kind }: AppLoaderProps) { 12 | return ( 13 |
17 |
18 | {kind === 'Connecting' ? ( 19 | 20 | ) : ( 21 | 22 | )} 23 |

{message}

24 |
25 |
26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /components/app-splash-screen.tsx: -------------------------------------------------------------------------------- 1 | import Image from 'next/image'; 2 | 3 | export function AppSplashScreen() { 4 | return ( 5 |
6 | wireguard 13 |
14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /components/profile-dialog-delete.tsx: -------------------------------------------------------------------------------- 1 | import { useCallback, useEffect, useState } from 'react'; 2 | import { usePathname, useRouter, useSearchParams } from 'next/navigation'; 3 | 4 | import { deleteProfile } from '@/lib/effects'; 5 | 6 | import { AlertConfirm } from './ui/alert-confirm'; 7 | 8 | export function ProfileDialogDelete({ onDelete }: { onDelete: () => void }) { 9 | const router = useRouter(); 10 | const pathname = usePathname(); 11 | const qs = useSearchParams(); 12 | const [isOpen, setIsOpen] = useState(false); 13 | const deleteId = qs.get('d'); 14 | 15 | useEffect(() => { 16 | if (deleteId && !isOpen) return setIsOpen(true); 17 | if (!deleteId && isOpen) return setIsOpen(false); 18 | }, [isOpen, deleteId]); 19 | 20 | const setOpen = useCallback(() => { 21 | router.replace(pathname); 22 | }, [pathname, router]); 23 | 24 | const onConfirm = useCallback(() => { 25 | deleteProfile(deleteId as string, undefined, undefined, () => { 26 | onDelete(); 27 | setOpen(); 28 | }); 29 | }, [deleteId, setOpen, onDelete]); 30 | 31 | return ( 32 | 39 | ); 40 | } 41 | -------------------------------------------------------------------------------- /components/profile-dialog-form.tsx: -------------------------------------------------------------------------------- 1 | import { useCallback, useEffect, useState } from 'react'; 2 | import { Plus } from 'lucide-react'; 3 | 4 | import type { Profile, ProfilePartial } from '@/types/profile'; 5 | import { cn } from '@/lib/utils'; 6 | import { 7 | Dialog, 8 | DialogContent, 9 | DialogHeader, 10 | DialogTitle, 11 | } from '@/components/ui/dialog'; 12 | import ProfileForm from '@/components/profile-form'; 13 | 14 | export interface ProfileDialogFormProps { 15 | className?: string; 16 | data?: ProfilePartial | null; 17 | editId?: string | null; 18 | afterSubmit?: (data: Profile) => void; 19 | onOpenChange?: (o: boolean) => void; 20 | } 21 | 22 | export function ProfileDialogForm({ 23 | className, 24 | data, 25 | editId, 26 | afterSubmit, 27 | onOpenChange, 28 | }: ProfileDialogFormProps) { 29 | const [open, setOpen] = useState(false); 30 | 31 | const hookSetOpen = useCallback( 32 | (o: boolean) => { 33 | setOpen(o); 34 | onOpenChange?.(o); 35 | }, 36 | [onOpenChange], 37 | ); 38 | 39 | useEffect(() => { 40 | if (editId && data) { 41 | return setOpen(true); 42 | } else { 43 | return setOpen(false); 44 | } 45 | }, [editId, data]); 46 | 47 | const openModal = useCallback(() => { 48 | setOpen(true); 49 | }, []); 50 | 51 | const hookAfterSubmit = useCallback( 52 | (profile: Profile) => { 53 | afterSubmit?.(profile); 54 | if (!editId) { 55 | hookSetOpen(false); 56 | } 57 | }, 58 | [afterSubmit, hookSetOpen, editId], 59 | ); 60 | 61 | return ( 62 | 63 |
68 | 69 |
70 | 71 | 72 | {editId ? 'Edit' : 'Create'} profile 73 | 74 | 79 | 80 |
81 | ); 82 | } 83 | -------------------------------------------------------------------------------- /components/profile-form.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { useCallback, useMemo, useState } from 'react'; 4 | import { zodResolver } from '@hookform/resolvers/zod'; 5 | import { useForm } from 'react-hook-form'; 6 | import * as z from 'zod'; 7 | 8 | import { ProfilePartial } from '@/types/profile'; 9 | import { createProfile, updateProfile } from '@/lib/effects'; 10 | import { Button } from '@/components/ui/button'; 11 | import { 12 | Form, 13 | FormControl, 14 | FormField, 15 | FormItem, 16 | FormLabel, 17 | FormMessage, 18 | } from '@/components/ui/form'; 19 | import { Input } from '@/components/ui/input'; 20 | 21 | import { Textarea } from './ui/textarea'; 22 | 23 | const formSchema = z.object({ 24 | name: z.string().min(2, { 25 | message: 'Name must be at least 2 characters.', 26 | }), 27 | content: z.string().min(8, { 28 | message: 'Content must be at least 8 characters.', 29 | }), 30 | }); 31 | 32 | const contentPlaceholder = `[Interface] 33 | Address = 34 | PrivateKey = 35 | ListenPort = 51820 36 | DNS = 37 | 38 | [Peer] 39 | PublicKey = 40 | PresharedKey = 41 | Endpoint = 42 | AllowedIPs = 43 | `; 44 | 45 | interface ProfileFormProps { 46 | data?: ProfilePartial | null; 47 | editId?: string | null; 48 | afterSubmit?: (profile: ProfilePartial) => void; 49 | } 50 | 51 | export default function ProfileForm({ 52 | data, 53 | editId, 54 | afterSubmit, 55 | }: ProfileFormProps) { 56 | const [isLoading, setIsLoading] = useState(false); 57 | const [error, setError] = useState(''); 58 | const defaultValues = useMemo( 59 | () => 60 | data || { 61 | name: '', 62 | content: '', 63 | }, 64 | [data], 65 | ); 66 | 67 | console.log({ defaultValues }); 68 | 69 | const form = useForm>({ 70 | resolver: zodResolver(formSchema), 71 | defaultValues: defaultValues, 72 | }); 73 | 74 | const onSubmit = useCallback( 75 | (profile: z.infer) => { 76 | setIsLoading(true); 77 | if (editId) { 78 | updateProfile( 79 | editId, 80 | profile, 81 | () => afterSubmit?.(profile), 82 | (err) => setError(err.message ? err.message : err), 83 | () => setIsLoading(false), 84 | ); 85 | } else { 86 | createProfile( 87 | profile, 88 | () => afterSubmit?.(profile), 89 | (err) => setError(err.message ? err.message : err), 90 | () => setIsLoading(false), 91 | ); 92 | } 93 | }, 94 | [editId, afterSubmit], 95 | ); 96 | 97 | return ( 98 |
99 | 100 | ( 104 | 105 | Name 106 | 107 | 108 | 109 | 110 | 111 | )} 112 | /> 113 | ( 117 | 118 | Content 119 | 120 |