Compare commits
112 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 326717444b | |||
| 4e4bfc75e3 | |||
| 5c97a8ba73 | |||
| cba7a160ea | |||
| 4a75e1379d | |||
| 10a140d188 | |||
| edc3c5af57 | |||
| 26708305d6 | |||
| 6a282e6ab9 | |||
| a8ba7f7247 | |||
| 502fe718d5 | |||
| 5c7aac9fd2 | |||
| 2f96d9934b | |||
| daff15463d | |||
| 5f68d261c0 | |||
| 013ea6dba9 | |||
| ab9d75757f | |||
| 9be20109af | |||
| 9430b76258 | |||
| 99f1a004d5 | |||
| 2b42b639ea | |||
| 76d5944726 | |||
| 81bd19cfb6 | |||
| 0365ca7fb6 | |||
| 529d65b3fb | |||
| cbbf5d3d25 | |||
| 7b2d234ea5 | |||
| a282ca35f1 | |||
| 0792862c0d | |||
| 6c0d8e91fa | |||
| 8892c43a7d | |||
| 7cfe04286a | |||
| 000513f212 | |||
| 6ca3da1b80 | |||
| fe394ecbe4 | |||
| ec6f0423ca | |||
| e12208af75 | |||
| 2d9c75fd81 | |||
| 0c54b1c3da | |||
| be7943dc9b | |||
| 303c2f1e39 | |||
| 0b32ab899c | |||
| ef463d3d85 | |||
| c10daa2fd4 | |||
| cb8aa45cde | |||
| 54e0a1e886 | |||
| 71a452080e | |||
| 5be7fb3038 | |||
| 59f0c8b0d2 | |||
| e1ed93674d | |||
| 6b65a8099b | |||
| c1dd494d0f | |||
| bf9e8a6e21 | |||
| 371d7617ff | |||
| 0b435d9ed8 | |||
| 07f89d15a9 | |||
| b5318086d2 | |||
| b7f9b7c830 | |||
| 2e4f386f49 | |||
| 9ead985798 | |||
| 6326ee31c4 | |||
| 984dc95550 | |||
| cd0a9b8e33 | |||
| 90b9ba15ec | |||
| 0abc419db7 | |||
| b185d7a63d | |||
| 4bb880c4b7 | |||
| b0ba9e43f9 | |||
| ddb01fb968 | |||
| 22812e0632 | |||
| 4d84e1d9d3 | |||
| 9368b857e8 | |||
| 0f663df7f6 | |||
| 68fde7d165 | |||
| 501a784264 | |||
| 629c184195 | |||
| 76b8818a33 | |||
| 6c52301a64 | |||
| be26db63ca | |||
| 962bfa213f | |||
| ee00e5c914 | |||
| 6343213538 | |||
| 187bdc0836 | |||
| f2520f0481 | |||
| 5e9a73645b | |||
| 783fa3286c | |||
| 77b4f9db65 | |||
| 0f6f07161b | |||
| d75a836de9 | |||
| f79b0fd025 | |||
| d2ce82241b | |||
| 8c395ec275 | |||
| 10f42170f3 | |||
| a8aa85bdaa | |||
| 7e1aa5807d | |||
| df57921b8e | |||
| 4fbf059e61 | |||
| b150e3f3b4 | |||
| aed10ab0bd | |||
| 478e7207b2 | |||
| c8dc710435 | |||
| 19d9e3b7d7 | |||
| c4efb1d03a | |||
| 529b9eeb88 | |||
| 69ee741d7e | |||
| f2dc38e91b | |||
| 64e9484331 | |||
| c777fa30b3 | |||
| 734d91fd98 | |||
| 84ed7b299f | |||
| 1cfe6404b2 | |||
| 2a32c1b9c0 |
@@ -1,23 +0,0 @@
|
||||
# http://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
# The JSON files contain newlines inconsistently
|
||||
[*.json]
|
||||
insert_final_newline = ignore
|
||||
|
||||
# Minified JavaScript files shouldn't be changed
|
||||
[**.min.js]
|
||||
indent_style = ignore
|
||||
insert_final_newline = ignore
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
@@ -27,17 +27,17 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
uses: github/codeql-action/init@v4
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
uses: github/codeql-action/autobuild@v4
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
uses: github/codeql-action/analyze@v4
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
|
||||
@@ -4,21 +4,38 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
name: Build & Deploy Docker
|
||||
runs-on: ubuntu-latest
|
||||
docker-build:
|
||||
name: Build Docker
|
||||
runs-on: ${{ matrix.arch.os }}
|
||||
if: github.repository_owner == 'wg-easy'
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
arch:
|
||||
- platform: linux/amd64
|
||||
os: ubuntu-latest
|
||||
- platform: linux/arm64
|
||||
os: ubuntu-24.04-arm
|
||||
- platform: linux/arm/v7
|
||||
os: ubuntu-24.04-arm
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Prepare
|
||||
run: |
|
||||
platform=${{ matrix.arch.platform }}
|
||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
ghcr.io/wg-easy/wg-easy
|
||||
flavor: |
|
||||
latest=false
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
@@ -27,15 +44,91 @@ jobs:
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build & Publish Docker Image
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build and push by digest
|
||||
id: build
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
platforms: linux/amd64,linux/arm64
|
||||
tags: ghcr.io/wg-easy/wg-easy:development
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=min
|
||||
platforms: ${{ matrix.arch.platform }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
tags: ghcr.io/wg-easy/wg-easy
|
||||
outputs: type=image,push-by-digest=true,name-canonical=true,push=true
|
||||
cache-from: type=gha,scope=build-${{ env.PLATFORM_PAIR }}
|
||||
cache-to: type=gha,mode=min,scope=build-${{ env.PLATFORM_PAIR }}
|
||||
|
||||
- name: Export digest
|
||||
run: |
|
||||
mkdir -p ${{ runner.temp }}/digests
|
||||
digest="${{ steps.build.outputs.digest }}"
|
||||
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
||||
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: digests-${{ env.PLATFORM_PAIR }}
|
||||
path: ${{ runner.temp }}/digests/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
docker-merge:
|
||||
name: Merge & Deploy Docker
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository_owner == 'wg-easy'
|
||||
permissions:
|
||||
packages: write
|
||||
needs: docker-build
|
||||
steps:
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@v6
|
||||
with:
|
||||
path: ${{ runner.temp }}/digests
|
||||
pattern: digests-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Login to Codeberg
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: codeberg.org
|
||||
username: ${{ secrets.CODEBERG_USER }}
|
||||
password: ${{ secrets.CODEBERG_PASS }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
ghcr.io/wg-easy/wg-easy
|
||||
codeberg.org/wg-easy/wg-easy
|
||||
flavor: |
|
||||
latest=false
|
||||
tags: |
|
||||
type=raw,value=development
|
||||
|
||||
- name: Create manifest list and push
|
||||
working-directory: ${{ runner.temp }}/digests
|
||||
run: |
|
||||
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||
$(printf 'ghcr.io/wg-easy/wg-easy@sha256:%s ' *)
|
||||
|
||||
- name: Inspect image
|
||||
run: |
|
||||
docker buildx imagetools inspect ghcr.io/wg-easy/wg-easy:${{ steps.meta.outputs.version }}
|
||||
|
||||
docs:
|
||||
name: Build & Deploy Docs
|
||||
@@ -43,12 +136,12 @@ jobs:
|
||||
if: github.repository_owner == 'wg-easy'
|
||||
permissions:
|
||||
contents: write
|
||||
needs: docker
|
||||
needs: docker-merge
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: 3.11.9
|
||||
cache: "pip"
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
name: Edge
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
docker-build:
|
||||
name: Build Docker
|
||||
runs-on: ${{ matrix.arch.os }}
|
||||
if: github.repository_owner == 'wg-easy'
|
||||
permissions:
|
||||
packages: write
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
arch:
|
||||
- platform: linux/amd64
|
||||
os: ubuntu-latest
|
||||
- platform: linux/arm64
|
||||
os: ubuntu-24.04-arm
|
||||
- platform: linux/arm/v7
|
||||
os: ubuntu-24.04-arm
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
ref: master
|
||||
|
||||
- name: Prepare
|
||||
run: |
|
||||
platform=${{ matrix.arch.platform }}
|
||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
ghcr.io/wg-easy/wg-easy
|
||||
flavor: |
|
||||
latest=false
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build and push by digest
|
||||
id: build
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
platforms: ${{ matrix.arch.platform }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
tags: ghcr.io/wg-easy/wg-easy
|
||||
outputs: type=image,push-by-digest=true,name-canonical=true,push=true
|
||||
cache-from: type=gha,scope=build-${{ env.PLATFORM_PAIR }}
|
||||
cache-to: type=gha,mode=min,scope=build-${{ env.PLATFORM_PAIR }}
|
||||
|
||||
- name: Export digest
|
||||
run: |
|
||||
mkdir -p ${{ runner.temp }}/digests
|
||||
digest="${{ steps.build.outputs.digest }}"
|
||||
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
||||
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: digests-${{ env.PLATFORM_PAIR }}
|
||||
path: ${{ runner.temp }}/digests/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
docker-merge:
|
||||
name: Merge & Deploy Docker
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository_owner == 'wg-easy'
|
||||
permissions:
|
||||
packages: write
|
||||
needs: docker-build
|
||||
steps:
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@v6
|
||||
with:
|
||||
path: ${{ runner.temp }}/digests
|
||||
pattern: digests-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Login to Codeberg
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: codeberg.org
|
||||
username: ${{ secrets.CODEBERG_USER }}
|
||||
password: ${{ secrets.CODEBERG_PASS }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
ghcr.io/wg-easy/wg-easy
|
||||
codeberg.org/wg-easy/wg-easy
|
||||
flavor: |
|
||||
latest=false
|
||||
tags: |
|
||||
type=raw,value=edge
|
||||
|
||||
- name: Create manifest list and push
|
||||
working-directory: ${{ runner.temp }}/digests
|
||||
run: |
|
||||
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||
$(printf 'ghcr.io/wg-easy/wg-easy@sha256:%s ' *)
|
||||
|
||||
- name: Inspect image
|
||||
run: |
|
||||
docker buildx imagetools inspect ghcr.io/wg-easy/wg-easy:${{ steps.meta.outputs.version }}
|
||||
|
||||
docs:
|
||||
name: Build & Deploy Docs
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository_owner == 'wg-easy'
|
||||
permissions:
|
||||
contents: write
|
||||
needs: docker-merge
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
ref: master
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: 3.11.9
|
||||
cache: "pip"
|
||||
cache-dependency-path: docs/requirements.txt
|
||||
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
pip install -r docs/requirements.txt
|
||||
|
||||
- name: Setup Git User
|
||||
run: |
|
||||
git config --global user.name 'github-actions[bot]'
|
||||
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
|
||||
|
||||
- name: Build Docs Website
|
||||
run: |
|
||||
cd docs
|
||||
git fetch origin gh-pages --depth=1 || true
|
||||
|
||||
mike deploy --push --update-aliases edge
|
||||
@@ -1,77 +0,0 @@
|
||||
name: Nightly
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
name: Build & Deploy Docker
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository_owner == 'wg-easy'
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: master
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build & Publish Docker Image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
platforms: linux/amd64,linux/arm64
|
||||
tags: ghcr.io/wg-easy/wg-easy:nightly
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=min
|
||||
|
||||
docs:
|
||||
name: Build & Deploy Docs
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository_owner == 'wg-easy'
|
||||
permissions:
|
||||
contents: write
|
||||
needs: docker
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: master
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.11.9
|
||||
cache: "pip"
|
||||
cache-dependency-path: docs/requirements.txt
|
||||
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
pip install -r docs/requirements.txt
|
||||
|
||||
- name: Setup Git User
|
||||
run: |
|
||||
git config --global user.name 'github-actions[bot]'
|
||||
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
|
||||
|
||||
- name: Build Docs Website
|
||||
run: |
|
||||
cd docs
|
||||
git fetch origin gh-pages --depth=1 || true
|
||||
|
||||
mike deploy --push --update-aliases nightly
|
||||
@@ -11,13 +11,25 @@ concurrency:
|
||||
jobs:
|
||||
docker:
|
||||
name: Build Docker
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ${{ matrix.arch.os }}
|
||||
if: github.repository_owner == 'wg-easy'
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
arch:
|
||||
- platform: linux/amd64
|
||||
os: ubuntu-latest
|
||||
- platform: linux/arm64
|
||||
os: ubuntu-24.04-arm
|
||||
- platform: linux/arm/v7
|
||||
os: ubuntu-24.04-arm
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Prepare
|
||||
run: |
|
||||
platform=${{ matrix.arch.platform }}
|
||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
@@ -37,7 +49,7 @@ jobs:
|
||||
with:
|
||||
context: .
|
||||
push: false
|
||||
platforms: linux/amd64,linux/arm64
|
||||
platforms: ${{ matrix.arch.platform }}
|
||||
tags: ghcr.io/wg-easy/wg-easy:pr
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=min
|
||||
cache-to: type=gha,mode=min,scope=build-${{ env.PLATFORM_PAIR }}
|
||||
|
||||
+108
-25
@@ -10,20 +10,110 @@ on:
|
||||
# as this will break the latest and major tags
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
name: Build & Deploy Docker
|
||||
docker-build:
|
||||
name: Build Docker
|
||||
runs-on: ${{ matrix.arch.os }}
|
||||
if: |
|
||||
github.repository_owner == 'wg-easy' &&
|
||||
startsWith(github.ref, 'refs/tags/v')
|
||||
permissions:
|
||||
packages: write
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
arch:
|
||||
- platform: linux/amd64
|
||||
os: ubuntu-latest
|
||||
- platform: linux/arm64
|
||||
os: ubuntu-24.04-arm
|
||||
- platform: linux/arm/v7
|
||||
os: ubuntu-24.04-arm
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Prepare
|
||||
run: |
|
||||
platform=${{ matrix.arch.platform }}
|
||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
ghcr.io/wg-easy/wg-easy
|
||||
flavor: |
|
||||
latest=false
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build and push by digest
|
||||
id: build
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
platforms: ${{ matrix.arch.platform }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
tags: ghcr.io/wg-easy/wg-easy
|
||||
outputs: type=image,push-by-digest=true,name-canonical=true,push=true
|
||||
cache-from: type=gha,scope=build-${{ env.PLATFORM_PAIR }}
|
||||
cache-to: type=gha,mode=min,scope=build-${{ env.PLATFORM_PAIR }}
|
||||
|
||||
- name: Export digest
|
||||
run: |
|
||||
mkdir -p ${{ runner.temp }}/digests
|
||||
digest="${{ steps.build.outputs.digest }}"
|
||||
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
||||
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: digests-${{ env.PLATFORM_PAIR }}
|
||||
path: ${{ runner.temp }}/digests/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
docker-merge:
|
||||
name: Merge & Deploy Docker
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.repository_owner == 'wg-easy' &&
|
||||
startsWith(github.ref, 'refs/tags/v')
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
needs: docker-build
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@v6
|
||||
with:
|
||||
path: ${{ runner.temp }}/digests
|
||||
pattern: digests-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Login to Codeberg
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: codeberg.org
|
||||
username: ${{ secrets.CODEBERG_USER }}
|
||||
password: ${{ secrets.CODEBERG_PASS }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
@@ -34,6 +124,7 @@ jobs:
|
||||
with:
|
||||
images: |
|
||||
ghcr.io/wg-easy/wg-easy
|
||||
codeberg.org/wg-easy/wg-easy
|
||||
flavor: |
|
||||
latest=false
|
||||
tags: |
|
||||
@@ -41,23 +132,15 @@ jobs:
|
||||
type=semver,pattern={{major}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Create manifest list and push
|
||||
working-directory: ${{ runner.temp }}/digests
|
||||
run: |
|
||||
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||
$(printf 'ghcr.io/wg-easy/wg-easy@sha256:%s ' *)
|
||||
|
||||
- name: Build & Publish Docker Image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
platforms: linux/amd64,linux/arm64
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=min
|
||||
- name: Inspect image
|
||||
run: |
|
||||
docker buildx imagetools inspect ghcr.io/wg-easy/wg-easy:${{ steps.meta.outputs.version }}
|
||||
|
||||
docs:
|
||||
name: Build & Deploy Docs
|
||||
@@ -67,12 +150,12 @@ jobs:
|
||||
startsWith(github.ref, 'refs/tags/v')
|
||||
permissions:
|
||||
contents: write
|
||||
needs: docker
|
||||
needs: docker-merge
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: 3.11.9
|
||||
cache: "pip"
|
||||
|
||||
@@ -7,9 +7,36 @@ on:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
docs:
|
||||
name: Check Docs
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository_owner == 'wg-easy'
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
run_install: false
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: "lts/jod"
|
||||
check-latest: true
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Check docs formatting
|
||||
run: |
|
||||
pnpm install
|
||||
pnpm format:check:docs
|
||||
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
needs: docs
|
||||
if: github.repository_owner == 'wg-easy'
|
||||
|
||||
strategy:
|
||||
@@ -20,7 +47,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
@@ -28,9 +55,9 @@ jobs:
|
||||
run_install: false
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: "lts/*"
|
||||
node-version: "lts/jod"
|
||||
check-latest: true
|
||||
cache: "pnpm"
|
||||
|
||||
|
||||
@@ -15,13 +15,16 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository_owner == 'wg-easy'
|
||||
permissions:
|
||||
actions: write
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
- uses: actions/stale@v10
|
||||
with:
|
||||
# Stale after 30 days of inactivity
|
||||
days-before-issue-stale: 30
|
||||
# Close after 14 days of being stale
|
||||
days-before-issue-close: 14
|
||||
stale-issue-label: "stale"
|
||||
stale-issue-message: "This issue is stale because it has been open for 30 days with no activity."
|
||||
@@ -32,3 +35,9 @@ jobs:
|
||||
close-pr-message: "This PR was closed because it has been inactive for 14 days since being marked as stale."
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
operations-per-run: 100
|
||||
# Ignore Feature requests (https://github.com/actions/stale/issues/1293)
|
||||
only-issue-types: "Bug"
|
||||
# Ignore confirmed bugs
|
||||
exempt-issue-labels: "status: confirmed"
|
||||
# Ignore PRs with milestones
|
||||
exempt-all-pr-milestones: true
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
.DS_Store
|
||||
*.swp
|
||||
node_modules
|
||||
Vendored
+2
-3
@@ -3,12 +3,11 @@
|
||||
"aaron-bond.better-comments",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"antfu.goto-alias",
|
||||
"visualstudioexptteam.vscodeintellicode",
|
||||
"Nuxtr.nuxtr-vscode",
|
||||
"esbenp.prettier-vscode",
|
||||
"yoavbls.pretty-ts-errors",
|
||||
"bradlc.vscode-tailwindcss",
|
||||
"vue.volar",
|
||||
"lokalise.i18n-ally"
|
||||
"lokalise.i18n-ally",
|
||||
"DavidAnson.vscode-markdownlint"
|
||||
]
|
||||
}
|
||||
|
||||
Vendored
+5
-3
@@ -3,9 +3,6 @@
|
||||
"editor.useTabStops": false,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
"nuxtr.vueFiles.style.addStyleTag": false,
|
||||
"nuxtr.piniaFiles.defaultTemplate": "setup",
|
||||
"nuxtr.monorepoMode.DirectoryName": "src",
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "always"
|
||||
},
|
||||
@@ -18,6 +15,11 @@
|
||||
"[json]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[markdown]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.tabSize": 4,
|
||||
"editor.useTabStops": false
|
||||
},
|
||||
"typescript.tsdk": "./src/node_modules/typescript/lib",
|
||||
"i18n-ally.enabledFrameworks": ["vue"],
|
||||
"i18n-ally.localesPaths": ["src/i18n/locales"],
|
||||
|
||||
+57
-3
@@ -7,14 +7,66 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## Added
|
||||
|
||||
- AmneziaWG integration (https://github.com/wg-easy/wg-easy/pull/2102, https://github.com/wg-easy/wg-easy/pull/2226)
|
||||
- Search / filter box (https://github.com/wg-easy/wg-easy/pull/2170)
|
||||
- `INIT_ALLOWED_IPS` env var (https://github.com/wg-easy/wg-easy/pull/2164)
|
||||
- Show client endpoint (https://github.com/wg-easy/wg-easy/pull/2058)
|
||||
- Add option to view and copy config (https://github.com/wg-easy/wg-easy/pull/2289)
|
||||
|
||||
## Fixed
|
||||
|
||||
- Fix download as conf.txt (https://github.com/wg-easy/wg-easy/pull/2269)
|
||||
- Clean filename for OTL download (https://github.com/wg-easy/wg-easy/pull/2253)
|
||||
|
||||
## Changed
|
||||
|
||||
- Allow lower MTU (https://github.com/wg-easy/wg-easy/pull/2228)
|
||||
- Use /32 and /128 for client Cidr (https://github.com/wg-easy/wg-easy/pull/2217)
|
||||
- Return client id on create (https://github.com/wg-easy/wg-easy/pull/2190)
|
||||
- Publish on Codeberg (https://github.com/wg-easy/wg-easy/pull/2160)
|
||||
- Allow empty DNS (https://github.com/wg-easy/wg-easy/pull/2052, https://github.com/wg-easy/wg-easy/pull/2057)
|
||||
- Don't include keys in API responses (https://github.com/wg-easy/wg-easy/pull/2015)
|
||||
- Try all QR ecc levels (https://github.com/wg-easy/wg-easy/pull/2288)
|
||||
|
||||
## Docs
|
||||
|
||||
- Add AdGuard Home (https://github.com/wg-easy/wg-easy/pull/2175)
|
||||
- Add Routed (No NAT) docs (https://github.com/wg-easy/wg-easy/pull/2181)
|
||||
|
||||
## [15.1.0] - 2025-07-01
|
||||
|
||||
### Added
|
||||
|
||||
- Added Ukrainian language (#1906)
|
||||
- Add French language (#1924)
|
||||
- docs for caddy example (#1939)
|
||||
- add docs on how to add/update translation (be26db6)
|
||||
- Add german translations (#1889)
|
||||
- feat: Add Traditional Chinese (zh-HK) i18n Support (#1988)
|
||||
- Add Chinese Simplified (#1990)
|
||||
- Add option to disable ipv6 (#1951)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Updated container launch commands (#1989)
|
||||
- update screenshot (962bfa2)
|
||||
|
||||
### Changed
|
||||
|
||||
- Updated dependencies
|
||||
|
||||
## [15.0.0] - 2025-05-28
|
||||
|
||||
We're super excited to announce v15!
|
||||
This update is an entire rewrite to make it even easier to set up your own VPN.
|
||||
|
||||
## Breaking Changes
|
||||
### Breaking Changes
|
||||
|
||||
As the whole setup has changed, we recommend to start from scratch. And import your existing configs.
|
||||
|
||||
## Major Changes
|
||||
### Major Changes
|
||||
|
||||
- Almost all Environment variables removed
|
||||
- New and Improved UI
|
||||
@@ -27,11 +79,13 @@ As the whole setup has changed, we recommend to start from scratch. And import y
|
||||
- SQLite Database
|
||||
- Deprecated Dockerless Installations
|
||||
- Added Docker Volume Mount (`/lib/modules`)
|
||||
- Removed ARMv6 and ARMv7 support
|
||||
- Removed ARMv6 support
|
||||
- Connections over HTTP require setting the `INSECURE` env var
|
||||
- Changed license from CC BY-NC-SA 4.0 to AGPL-3.0-only
|
||||
- Added 2FA using TOTP
|
||||
- Improved mobile support
|
||||
- CLI
|
||||
- Replaced `nightly` with `edge`
|
||||
|
||||
## [14.0.0] - 2024-09-04
|
||||
|
||||
|
||||
+23
-4
@@ -1,4 +1,4 @@
|
||||
FROM docker.io/library/node:lts-alpine AS build
|
||||
FROM docker.io/library/node:jod-alpine AS build
|
||||
WORKDIR /app
|
||||
|
||||
# update corepack
|
||||
@@ -14,9 +14,15 @@ RUN pnpm install
|
||||
COPY src ./
|
||||
RUN pnpm build
|
||||
|
||||
# Build amneziawg-tools
|
||||
RUN apk add linux-headers build-base git && \
|
||||
git clone https://github.com/amnezia-vpn/amneziawg-tools.git && \
|
||||
cd amneziawg-tools/src && \
|
||||
make
|
||||
|
||||
# Copy build result to a new image.
|
||||
# This saves a lot of disk space.
|
||||
FROM docker.io/library/node:lts-alpine
|
||||
FROM docker.io/library/node:jod-alpine
|
||||
WORKDIR /app
|
||||
|
||||
HEALTHCHECK --interval=1m --timeout=5s --retries=3 CMD /usr/bin/timeout 5s /bin/sh -c "/usr/bin/wg show | /bin/grep -q interface || exit 1"
|
||||
@@ -25,8 +31,17 @@ HEALTHCHECK --interval=1m --timeout=5s --retries=3 CMD /usr/bin/timeout 5s /bin/
|
||||
COPY --from=build /app/.output /app
|
||||
# Copy migrations
|
||||
COPY --from=build /app/server/database/migrations /app/server/database/migrations
|
||||
# libsql
|
||||
RUN cd /app/server && npm install --no-save libsql
|
||||
# libsql (https://github.com/nitrojs/nitro/issues/3328)
|
||||
RUN cd /app/server && \
|
||||
npm install --no-save libsql && \
|
||||
npm cache clean --force
|
||||
# cli
|
||||
COPY --from=build /app/cli/cli.sh /usr/local/bin/cli
|
||||
RUN chmod +x /usr/local/bin/cli
|
||||
# Copy amneziawg-tools
|
||||
COPY --from=build /app/amneziawg-tools/src/wg /usr/bin/awg
|
||||
COPY --from=build /app/amneziawg-tools/src/wg-quick/linux.bash /usr/bin/awg-quick
|
||||
RUN chmod +x /usr/bin/awg /usr/bin/awg-quick
|
||||
|
||||
# Install Linux packages
|
||||
RUN apk add --no-cache \
|
||||
@@ -39,6 +54,9 @@ RUN apk add --no-cache \
|
||||
iptables-legacy \
|
||||
wireguard-tools
|
||||
|
||||
RUN mkdir -p /etc/amnezia
|
||||
RUN ln -s /etc/wireguard /etc/amnezia/amneziawg
|
||||
|
||||
# Use iptables-legacy
|
||||
RUN update-alternatives --install /usr/sbin/iptables iptables /usr/sbin/iptables-legacy 10 --slave /usr/sbin/iptables-restore iptables-restore /usr/sbin/iptables-legacy-restore --slave /usr/sbin/iptables-save iptables-save /usr/sbin/iptables-legacy-save
|
||||
RUN update-alternatives --install /usr/sbin/ip6tables ip6tables /usr/sbin/ip6tables-legacy 10 --slave /usr/sbin/ip6tables-restore ip6tables-restore /usr/sbin/ip6tables-legacy-restore --slave /usr/sbin/ip6tables-save ip6tables-save /usr/sbin/ip6tables-legacy-save
|
||||
@@ -49,6 +67,7 @@ ENV PORT=51821
|
||||
ENV HOST=0.0.0.0
|
||||
ENV INSECURE=false
|
||||
ENV INIT_ENABLED=false
|
||||
ENV DISABLE_IPV6=false
|
||||
|
||||
LABEL org.opencontainers.image.source=https://github.com/wg-easy/wg-easy
|
||||
|
||||
|
||||
+3
-1
@@ -1,4 +1,4 @@
|
||||
FROM docker.io/library/node:lts-alpine
|
||||
FROM docker.io/library/node:jod-alpine
|
||||
WORKDIR /app
|
||||
|
||||
# update corepack
|
||||
@@ -28,6 +28,7 @@ ENV PORT=51821
|
||||
ENV HOST=0.0.0.0
|
||||
ENV INSECURE=true
|
||||
ENV INIT_ENABLED=false
|
||||
ENV DISABLE_IPV6=false
|
||||
|
||||
# Install Dependencies
|
||||
COPY src/package.json src/pnpm-lock.yaml ./
|
||||
@@ -36,3 +37,4 @@ RUN pnpm install
|
||||
# Copy Project
|
||||
COPY src ./
|
||||
|
||||
ENTRYPOINT [ "pnpm", "run" ]
|
||||
|
||||
@@ -7,18 +7,12 @@
|
||||
[](https://github.com/wg-easy/wg-easy/releases/latest)
|
||||
[](https://github.com/wg-easy/wg-easy/pkgs/container/wg-easy)
|
||||
|
||||
<!-- TODO: remove after release -->
|
||||
|
||||
> [!WARNING]
|
||||
> You are viewing the README of the pre-release of v15.
|
||||
> If you want to setup wg-easy right now. Read the README in the production branch here: [README](https://github.com/wg-easy/wg-easy/tree/production) or here for the last nightly: [README](https://github.com/wg-easy/wg-easy/tree/c6dce0f6fb2e28e7e40ddac1498bd67e9bb17cba)
|
||||
|
||||
You have found the easiest way to install & manage WireGuard on any Linux host!
|
||||
|
||||
<!-- TOOD: update screenshot -->
|
||||
|
||||
<p align="center">
|
||||
<img src="./assets/screenshot.png" width="802" />
|
||||
<img src="./assets/screenshot.png" width="802" alt="wg-easy Screenshot" />
|
||||
</p>
|
||||
|
||||
## Features
|
||||
@@ -43,11 +37,6 @@ You have found the easiest way to install & manage WireGuard on any Linux host!
|
||||
> [!NOTE]
|
||||
> To better manage documentation for this project, it has its own site here: [https://wg-easy.github.io/wg-easy/latest](https://wg-easy.github.io/wg-easy/latest)
|
||||
|
||||
<!-- TODO: remove after release -->
|
||||
|
||||
> [!WARNING]
|
||||
> As the Docs are still in Pre-release, you can access them here [https://wg-easy.github.io/wg-easy/Pre-release](https://wg-easy.github.io/wg-easy/Pre-release)
|
||||
|
||||
- [Getting Started](https://wg-easy.github.io/wg-easy/latest/getting-started/)
|
||||
- [Basic Installation](https://wg-easy.github.io/wg-easy/latest/examples/tutorials/basic-installation/)
|
||||
- [Caddy](https://wg-easy.github.io/wg-easy/latest/examples/tutorials/caddy/)
|
||||
@@ -79,11 +68,11 @@ And log in again.
|
||||
|
||||
The easiest way to run WireGuard Easy is with Docker Compose.
|
||||
|
||||
Just download [`docker-compose.yml`](docker-compose.yml) and execute `sudo docker compose up -d`.
|
||||
Just follow [these steps](https://wg-easy.github.io/wg-easy/latest/examples/tutorials/basic-installation/) in the detailed documentation.
|
||||
|
||||
Now setup a reverse proxy to be able to access the Web UI from the internet.
|
||||
You can also install WireGuard Easy with the [docker run command](https://wg-easy.github.io/wg-easy/latest/examples/tutorials/docker-run/) or via [podman](https://wg-easy.github.io/wg-easy/latest/examples/tutorials/podman-nft/).
|
||||
|
||||
If you want to access the Web UI over HTTP, change the env var `INSECURE` to `true`. This is not recommended. Only use this for testing
|
||||
Now [setup a reverse proxy](https://wg-easy.github.io/wg-easy/latest/examples/tutorials/basic-installation/#setup-reverse-proxy) to be able to access the Web UI securely from the internet. This step is optional, just make sure to follow the guide [here](https://wg-easy.github.io/wg-easy/latest/examples/tutorials/reverse-proxyless/) if you decide not to do it.
|
||||
|
||||
## Donate
|
||||
|
||||
@@ -116,6 +105,15 @@ If you add something that should be auto-importable and VSCode complains, run:
|
||||
```shell
|
||||
cd src
|
||||
pnpm install
|
||||
cd ..
|
||||
```
|
||||
|
||||
### Test Cli
|
||||
|
||||
This starts the cli with docker
|
||||
|
||||
```shell
|
||||
pnpm cli:dev
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 185 KiB |
@@ -2,7 +2,7 @@ services:
|
||||
wg-easy:
|
||||
build:
|
||||
dockerfile: ./Dockerfile.dev
|
||||
command: pnpm run dev
|
||||
command: dev
|
||||
volumes:
|
||||
- ./src/:/app/
|
||||
- temp:/app/.nuxt/
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ services:
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
- SYS_MODULE
|
||||
# - NET_RAW # ⚠️ Uncomment if using Podman Compose
|
||||
# - NET_RAW # ⚠️ Uncomment if using Podman
|
||||
sysctls:
|
||||
- net.ipv4.ip_forward=1
|
||||
- net.ipv4.conf.all.src_valid_mark=1
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"tabWidth": 4,
|
||||
"semi": true,
|
||||
"singleQuote": true
|
||||
}
|
||||
@@ -2,6 +2,11 @@
|
||||
title: API
|
||||
---
|
||||
|
||||
/// warning | Breaking Changes
|
||||
|
||||
This API is not yet stable and may change in the future. The API is currently in development and is subject to change without notice. The API is not yet documented, but we will add documentation as the API stabilizes.
|
||||
///
|
||||
|
||||
You can use the API to interact with the application programmatically. The API is available at `/api` and supports both GET and POST requests. The API is designed to be simple and easy to use, with a focus on providing a consistent interface for all endpoints.
|
||||
|
||||
There is no documentation for the API yet, but this will be added as the underlying library supports it.
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
title: AmneziaWG
|
||||
---
|
||||
|
||||
Experimental support for AmneziaWG can be enabled by setting the `EXPERIMENTAL_AWG` environment variable to `true`. This feature is still under development and may change in future releases.
|
||||
|
||||
AmneziaWG adds multi-level transport-layer obfuscation by:
|
||||
|
||||
- Modifying packet headers
|
||||
- Randomizing handshake message sizes
|
||||
- Disguising traffic to resemble popular UDP protocols
|
||||
|
||||
These measures make it harder for third parties to analyze or identify your traffic, enhancing both privacy and security.
|
||||
|
||||
When enabled, wg-easy will automatically detect whether the AmneziaWG kernel module is available. If it is not, the system will fall back to the standard WireGuard module.
|
||||
|
||||
To override this automatic detection, set the `OVERRIDE_AUTO_AWG` environment variable. By default, this variable is unset.
|
||||
|
||||
Possible values:
|
||||
|
||||
- `awg` — Force use of AmneziaWG
|
||||
- `wg` — Force use of standard WireGuard
|
||||
|
||||
To be able to connect to wg-easy if AmneziaWG is enabled, you must have a AmneziaWG-compatible client.
|
||||
|
||||
Android:
|
||||
|
||||
- [AmneziaWG](https://play.google.com/store/apps/details?id=org.amnezia.awg) - Official Client
|
||||
- [WG Tunnel](https://play.google.com/store/apps/details?id=com.zaneschepke.wireguardautotunnel) - Third Party Client
|
||||
|
||||
iOS and macOS:
|
||||
|
||||
- [AmneziaWG](https://apps.apple.com/us/app/amneziawg/id6478942365) - Official Client
|
||||
|
||||
Windows:
|
||||
|
||||
- [AmneziaWG](https://github.com/amnezia-vpn/amneziawg-windows-client/releases) - Official Client
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
title: Experimental Configuration
|
||||
---
|
||||
|
||||
There are several experimental features that can be enabled by setting the appropriate environment variables. These features are not guaranteed to be stable and may change in future releases.
|
||||
|
||||
| Env | Default | Example | Description | Notes | More Info |
|
||||
| ---------------- | ------- | ------- | -------------------------------------- | --------------------------------------- | ------------------------ |
|
||||
| EXPERIMENTAL_AWG | false | true | Enables experimental AmneziaWG support | Planned to be enabled by default in v16 | [See here](./amnezia.md) |
|
||||
@@ -5,7 +5,18 @@ title: Optional Configuration
|
||||
You can set these environment variables to configure the container. They are not required, but can be useful in some cases.
|
||||
|
||||
| Env | Default | Example | Description |
|
||||
| ---------- | --------- | ----------- | ------------------------------ |
|
||||
| -------------- | --------- | ----------- | ---------------------------------- |
|
||||
| `PORT` | `51821` | `6789` | TCP port for Web UI. |
|
||||
| `HOST` | `0.0.0.0` | `localhost` | IP address web UI binds to. |
|
||||
| `INSECURE` | `false` | `true` | If access over http is allowed |
|
||||
| `DISABLE_IPV6` | `false` | `true` | If IPv6 support should be disabled |
|
||||
|
||||
/// note | IPv6 Caveats
|
||||
|
||||
Disabling IPv6 will disable the creation of the default IPv6 firewall rules and won't add a IPv6 address to the interface and clients.
|
||||
|
||||
You will however still see a IPv6 address in the Web UI, but it won't be used.
|
||||
|
||||
This option can be removed in the future, as more devices support IPv6.
|
||||
|
||||
///
|
||||
|
||||
@@ -7,7 +7,7 @@ If you want to run the setup without any user interaction, e.g. with a tool like
|
||||
These will only be used during the first start of the container. After that, the setup will be disabled.
|
||||
|
||||
| Env | Example | Description | Group |
|
||||
| ---------------- | ----------------- | --------------------------------------------------------- | ----- |
|
||||
| ------------------ | ---------------------------- | --------------------------------------------------------- | ----- |
|
||||
| `INIT_ENABLED` | `true` | Enables the below env vars | 0 |
|
||||
| `INIT_USERNAME` | `admin` | Sets admin username | 1 |
|
||||
| `INIT_PASSWORD` | `Se!ureP%ssw` | Sets admin password | 1 |
|
||||
@@ -16,8 +16,9 @@ These will only be used during the first start of the container. After that, the
|
||||
| `INIT_DNS` | `1.1.1.1,8.8.8.8` | Sets global dns setting | 2 |
|
||||
| `INIT_IPV4_CIDR` | `10.8.0.0/24` | Sets IPv4 cidr | 3 |
|
||||
| `INIT_IPV6_CIDR` | `2001:0DB8::/32` | Sets IPv6 cidr | 3 |
|
||||
| `INIT_ALLOWED_IPS` | `10.8.0.0/24,2001:0DB8::/32` | Sets global Allowed IPs | 4 |
|
||||
|
||||
/// warning | Variables have to be used together
|
||||
/// warning | Variables have to be used together
|
||||
|
||||
If variables are in the same group, you have to set all of them. For example, if you set `INIT_IPV4_CIDR`, you also have to set `INIT_IPV6_CIDR`.
|
||||
|
||||
|
||||
@@ -16,15 +16,15 @@ You need to add a scrape config to your Prometheus configuration file. Here is a
|
||||
|
||||
```yaml
|
||||
scrape_configs:
|
||||
- job_name: "wg-easy"
|
||||
- job_name: 'wg-easy'
|
||||
scrape_interval: 30s
|
||||
metrics_path: /metrics/prometheus
|
||||
static_configs:
|
||||
- targets:
|
||||
- "localhost:51821"
|
||||
- 'localhost:51821'
|
||||
authorization:
|
||||
type: Bearer
|
||||
credentials: "SuperSecurePassword"
|
||||
credentials: 'SuperSecurePassword'
|
||||
```
|
||||
|
||||
## Grafana Dashboard
|
||||
|
||||
@@ -6,9 +6,9 @@ This guide will help you migrate from `v14` to version `v15` of `wg-easy`.
|
||||
|
||||
## Changes
|
||||
|
||||
- This is a complete rewrite of the `wg-easy` project. Therefore the configuration files and the way you interact with the project have changed.
|
||||
- If you use armv6 or armv7, you can't migrate to `v15` yet. We are working on it.
|
||||
- If you are connecting to the web ui via HTTP, you need to set the `INSECURE` environment variable to `true` in the new container.
|
||||
- This is a complete rewrite of the `wg-easy` project, therefore the configuration files and the way you interact with the project have changed.
|
||||
- If you use armv6, you unfortunately won't be able to migrate to `v15`.
|
||||
- If you are connecting to the Web UI via HTTP, you need to set the `INSECURE` environment variable to `true` in the new container.
|
||||
|
||||
## Migration
|
||||
|
||||
@@ -16,12 +16,14 @@ This guide will help you migrate from `v14` to version `v15` of `wg-easy`.
|
||||
|
||||
Before you start the migration, make sure to back up your existing configuration files.
|
||||
|
||||
Go into the Web Ui and click the Backup button, this should download a `wg0.json` file.
|
||||
Go into the Web UI and click the Backup button, this should download a `wg0.json` file.
|
||||
|
||||
Or download the `wg0.json` file from your container volume to your pc.
|
||||
|
||||
You will need this file for the migration
|
||||
|
||||
You will also need to back up the old environment variables you set for the container, as they will not be automatically migrated.
|
||||
|
||||
### Remove old container
|
||||
|
||||
1. Stop the running container
|
||||
@@ -32,21 +34,25 @@ If you are using `docker run`
|
||||
docker stop wg-easy
|
||||
```
|
||||
|
||||
If you are using `docker-compose`
|
||||
If you are using `docker compose`
|
||||
|
||||
```shell
|
||||
docker-compose down
|
||||
docker compose down
|
||||
```
|
||||
|
||||
### Start new container
|
||||
|
||||
Follow the instructions in the [Getting Started][docs-getting-started] or [Basic Installation][docs-examples] guide to start the new container.
|
||||
|
||||
In the setup wizard, select that you already already have a configuration file and upload the `wg0.json` file you downloaded in the backup step.
|
||||
In the setup wizard, select that you already have a configuration file and upload the `wg0.json` file you downloaded in the backup step.
|
||||
|
||||
[docs-getting-started]: ../../getting-started.md
|
||||
[docs-examples]: ../../examples/tutorials/basic-installation.md
|
||||
|
||||
### Environment Variables
|
||||
|
||||
v15 does not use the same environment variables as v14, most of them have been moved to the Admin Panel in the Web UI.
|
||||
|
||||
### Done
|
||||
|
||||
You have now successfully migrated to `v15` of `wg-easy`.
|
||||
|
||||
@@ -12,7 +12,7 @@ When refactoring, writing or altering files, adhere to these rules:
|
||||
|
||||
## Documentation
|
||||
|
||||
Make sure to select `nightly` in the dropdown menu at the top. Navigate to the page you would like to edit and click the edit button in the top right. This allows you to make changes and create a pull-request.
|
||||
Make sure to select `edge` in the dropdown menu at the top. Navigate to the page you would like to edit and click the edit button in the top right. This allows you to make changes and create a pull-request.
|
||||
|
||||
Alternatively you can make the changes locally. For that you'll need to have Docker installed. Run
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ The development workflow is the following:
|
||||
3. Document your improvements if necessary
|
||||
4. [Commit][commit] (and [sign your commit][gpg]), push and create a pull-request to merge into `master`. Please **use the pull-request template** to provide a minimum of contextual information and make sure to meet the requirements of the checklist.
|
||||
|
||||
Pull requests are automatically tested against the CI and will be reviewed when tests pass. When your changes are validated, your branch is merged. CI builds the new `:nightly` image every night and your changes will be includes in the next version release.
|
||||
Pull requests are automatically tested against the CI and will be reviewed when tests pass. When your changes are validated, your branch is merged. CI builds the new `:edge` image on every push to the `master` branch and your changes will be included in the next version release.
|
||||
|
||||
[docs-latest]: https://wg-easy.github.io/wg-easy/latest
|
||||
[github-file-readme]: https://github.com/wg-easy/wg-easy/blob/master/README.md
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
---
|
||||
title: Translation
|
||||
---
|
||||
|
||||
This project supports multiple languages. If you would like to contribute a translation, please follow these steps:
|
||||
|
||||
## Add new Translation
|
||||
|
||||
Create a new file in `src/i18n/locales`. Name it `<locale_code>.json` (e.g. `fr.json` for French).
|
||||
|
||||
Import and add the newly created file in `src/i18n/i18n.config.ts`.
|
||||
|
||||
Add your language in the `src/nuxt.config.ts` file. You have to specify code, language and name.
|
||||
|
||||
`code` is the name of the translation file without the extension (e.g. `fr` for `fr.json`).
|
||||
|
||||
`language` is the BCP 47 language tag with region (e.g. `fr-FR` for French). See [www.lingoes.net](http://www.lingoes.net/en/translator/langcode.htm) for a list of language codes.
|
||||
|
||||
`name` is the display name of the language (e.g. `Français` for French).
|
||||
|
||||
## Update existing Translation
|
||||
|
||||
If you need to update an existing translation, simply edit the corresponding `<locale_code>.json` file in `src/i18n/locales`.
|
||||
|
||||
## Contribute changes
|
||||
|
||||
See [Pull Requests](./issues-and-pull-requests.md#pull-requests) on how to contribute your translation.
|
||||
@@ -2,8 +2,176 @@
|
||||
title: AdGuard Home
|
||||
---
|
||||
|
||||
It seems like the Docs on how to setup AdGuard Home are not available yet.
|
||||
This tutorial is a follow-up to the official [Traefik tutorial](./traefik.md). It will guide you through integrating AdGuard Home with your existing `wg-easy` and Traefik setup to provide network-wide DNS ad-blocking.
|
||||
|
||||
Feel free to create a PR and add them here.
|
||||
## Prerequisites
|
||||
|
||||
<!-- TODO -->
|
||||
- A working [wg-easy](./basic-installation.md) and [Traefik](./traefik.md) setup from the previous guides.
|
||||
|
||||
/// warning | Important: Following this guide will reset your WireGuard configuration.
|
||||
The process involves re-creating the `wg-easy` container and its data, which means **all existing WireGuard clients and settings will be deleted.**
|
||||
|
||||
You will need to create your clients again after completing this guide.
|
||||
///
|
||||
|
||||
## Add `adguard` configuration
|
||||
|
||||
1. Create a directory for the configuration files:
|
||||
|
||||
```shell
|
||||
sudo mkdir -p /etc/docker/containers/adguard
|
||||
```
|
||||
|
||||
2. Create volumes for persistent data:
|
||||
|
||||
```shell
|
||||
sudo mkdir -p /etc/docker/volumes/adguard/adguard_work
|
||||
sudo mkdir -p /etc/docker/volumes/adguard/adguard_conf
|
||||
sudo chmod -R 700 /etc/docker/volumes/adguard
|
||||
```
|
||||
|
||||
3. Create the `docker-compose.yml` file.
|
||||
|
||||
File: `/etc/docker/containers/adguard/docker-compose.yml`
|
||||
|
||||
```yaml
|
||||
services:
|
||||
adguard:
|
||||
image: adguard/adguardhome:v0.107.64
|
||||
container_name: adguard
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- /etc/docker/volumes/adguard/adguard_work:/opt/adguardhome/work
|
||||
- /etc/docker/volumes/adguard/adguard_conf:/opt/adguardhome/conf
|
||||
networks:
|
||||
wg:
|
||||
interface_name: eth0
|
||||
ipv4_address: 10.42.42.43
|
||||
ipv6_address: fdcc:ad94:bacf:61a3::2b
|
||||
traefik:
|
||||
interface_name: eth1
|
||||
labels:
|
||||
- 'traefik.enable=true'
|
||||
- 'traefik.http.routers.adguard.rule=Host(`adguard.$example.com$`)'
|
||||
- 'traefik.http.routers.adguard.entrypoints=websecure'
|
||||
- 'traefik.http.routers.adguard.service=adguard'
|
||||
- 'traefik.http.services.adguard.loadbalancer.server.port=3000'
|
||||
- 'traefik.docker.network=traefik'
|
||||
|
||||
networks:
|
||||
wg:
|
||||
external: true
|
||||
traefik:
|
||||
external: true
|
||||
```
|
||||
|
||||
## Update `wg-easy` configuration
|
||||
|
||||
Modify the corresponding sections of your existing `wg-easy` compose file to match the updated version below.
|
||||
|
||||
File: `/etc/docker/containers/wg-easy/docker-compose.yml`
|
||||
|
||||
```yaml
|
||||
services:
|
||||
wg-easy:
|
||||
ports:
|
||||
- "51820:51820/udp"
|
||||
...
|
||||
networks:
|
||||
wg:
|
||||
interface_name: eth0
|
||||
...
|
||||
traefik:
|
||||
interface_name: eth1
|
||||
...
|
||||
...
|
||||
environment:
|
||||
# Unattended Setup
|
||||
- INIT_ENABLED=true
|
||||
# Replace $username$ with your username
|
||||
- INIT_USERNAME=$username$
|
||||
# Replace $password$ with your unhashed password
|
||||
- INIT_PASSWORD=$password$
|
||||
# Replace $example.com$ with your domain
|
||||
- INIT_HOST=wg-easy.$example.com$
|
||||
- INIT_PORT=51820
|
||||
- INIT_DNS=10.42.42.43,fdcc:ad94:bacf:61a3::2b
|
||||
- INIT_IPV4_CIDR=10.8.0.0/24
|
||||
- INIT_IPV6_CIDR=fd42:42:42::/64
|
||||
...
|
||||
|
||||
networks:
|
||||
wg:
|
||||
# Prevents Docker Compose from prefixing the network name.
|
||||
name: wg
|
||||
...
|
||||
...
|
||||
```
|
||||
|
||||
## Setup Wireguard
|
||||
|
||||
1. Restart `wg-easy`:
|
||||
|
||||
```shell
|
||||
cd /etc/docker/containers/wg-easy
|
||||
sudo docker compose down -v
|
||||
sudo docker compose up -d
|
||||
```
|
||||
|
||||
2. Edit Wireguard's Hooks.
|
||||
|
||||
In the Admin Panel of your WireGuard server, go to the Hooks tab and replace it with:
|
||||
|
||||
**_PostUp_**
|
||||
|
||||
```shell
|
||||
iptables -A INPUT -p udp -m udp --dport {{port}} -j ACCEPT; ip6tables -A INPUT -p udp -m udp --dport {{port}} -j ACCEPT; iptables -t nat -A PREROUTING -i wg0 -p udp --dport 53 -j DNAT --to-destination 10.42.42.43; iptables -t nat -A PREROUTING -i wg0 -p tcp --dport 53 -j DNAT --to-destination 10.42.42.43; ip6tables -t nat -A PREROUTING -i wg0 -p udp --dport 53 -j DNAT --to-destination fdcc:ad94:bacf:61a3::2b; ip6tables -t nat -A PREROUTING -i wg0 -p tcp --dport 53 -j DNAT --to-destination fdcc:ad94:bacf:61a3::2b; iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; ip6tables -A FORWARD -i wg0 -j ACCEPT; ip6tables -A FORWARD -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -s {{ipv4Cidr}} -o {{device}} -j MASQUERADE; ip6tables -t nat -A POSTROUTING -s {{ipv6Cidr}} -o {{device}} -j MASQUERADE;
|
||||
```
|
||||
|
||||
**_PostDown_**
|
||||
|
||||
```shell
|
||||
iptables -D INPUT -p udp -m udp --dport {{port}} -j ACCEPT || true; ip6tables -D INPUT -p udp -m udp --dport {{port}} -j ACCEPT || true; iptables -t nat -D PREROUTING -i wg0 -p udp --dport 53 -j DNAT --to-destination 10.42.42.43 || true; iptables -t nat -D PREROUTING -i wg0 -p tcp --dport 53 -j DNAT --to-destination 10.42.42.43 || true; ip6tables -t nat -D PREROUTING -i wg0 -p udp --dport 53 -j DNAT --to-destination fdcc:ad94:bacf:61a3::2b || true; ip6tables -t nat -D PREROUTING -i wg0 -p tcp --dport 53 -j DNAT --to-destination fdcc:ad94:bacf:61a3::2b || true; iptables -D FORWARD -i wg0 -j ACCEPT || true; iptables -D FORWARD -o wg0 -j ACCEPT || true; ip6tables -D FORWARD -i wg0 -j ACCEPT || true; ip6tables -D FORWARD -o wg0 -j ACCEPT || true; iptables -t nat -D POSTROUTING -s {{ipv4Cidr}} -o {{device}} -j MASQUERADE || true; ip6tables -t nat -D POSTROUTING -s {{ipv6Cidr}} -o {{device}} -j MASQUERADE || true;
|
||||
```
|
||||
|
||||
3. Restart `wg-easy` to apply changes:
|
||||
|
||||
```shell
|
||||
sudo docker restart wg-easy
|
||||
```
|
||||
|
||||
## Setup Adguard Home
|
||||
|
||||
1. Start `adguard` service:
|
||||
|
||||
```shell
|
||||
cd /etc/docker/containers/adguard
|
||||
sudo docker compose up -d
|
||||
```
|
||||
|
||||
2. Navigate to `https://adguard.$example.com$` to begin the AdGuard Home setup.
|
||||
|
||||
/// warning | Important: Configure AdGuard Home Admin Web Interface Port
|
||||
During the initial AdGuard Home setup on the `Step 2/5` page, you **must** set the **Admin Web Interface Port** to **3000**. Do not use the default port 80, as it will not work with the Traefik configuration.
|
||||
|
||||
After completing the setup, the AdGuard UI might appear unresponsive. This is expected. **Simply reload the page**, and the panel will display correctly.
|
||||
///
|
||||
|
||||
> If you accidentally left it default (80), you will need to manually edit the `docker-compose.yml` file for AdGuard Home (`/etc/docker/containers/adguard/docker-compose.yml`) and change the line `traefik.http.services.adguard.loadbalancer.server.port=3000` to `traefik.http.services.adguard.loadbalancer.server.port=80`. After making this change, restart AdGuard Home by navigating to `/etc/docker/containers/adguard` and running `sudo docker compose up -d`.
|
||||
|
||||
## Final System Checks
|
||||
|
||||
### Firewall
|
||||
|
||||
Ensure the ports `80/tcp`, `443/tcp`, `443/udp`, and `51820/udp` are open.
|
||||
|
||||
### Optional: Optimizing UDP Buffer Sizes
|
||||
|
||||
AdGuard Home, as a DNS server, handles a large volume of UDP packets. To ensure optimal performance, it is recommended to increase the system's UDP buffer sizes. You can apply these settings using your system's `sysctl` configuration (e.g., by creating a file in `/etc/sysctl.d/`).
|
||||
|
||||
```shell
|
||||
net.core.rmem_max = 7500000
|
||||
net.core.wmem_max = 7500000
|
||||
```
|
||||
|
||||
After adding these settings, remember to apply them (e.g., by running `sudo sysctl --system` or rebooting)
|
||||
|
||||
@@ -8,7 +8,7 @@ title: Basic Installation
|
||||
|
||||
1. You need to have a host that you can manage
|
||||
2. You need to have a domain name or a public IP address
|
||||
3. You need a supported architecture (x86_64, arm64)
|
||||
3. You need a supported architecture (x86_64, arm64, armv7)
|
||||
4. You need curl installed on your host
|
||||
|
||||
## Install Docker
|
||||
@@ -33,7 +33,7 @@ Follow the Docs here: <https://docs.docker.com/engine/install/> and install Dock
|
||||
|
||||
```shell
|
||||
cd /etc/docker/containers/wg-easy
|
||||
sudo docker-compose up -d
|
||||
sudo docker compose up -d
|
||||
```
|
||||
|
||||
## Setup Firewall
|
||||
@@ -48,6 +48,7 @@ These ports can be changed, so if you change them you have to update your firewa
|
||||
|
||||
- To setup traefik follow the instructions here: [Traefik](./traefik.md)
|
||||
- To setup caddy follow the instructions here: [Caddy](./caddy.md)
|
||||
- If you do not want to use a reverse proxy follow the instructions here: [No Reverse Proxy](./reverse-proxyless.md)
|
||||
|
||||
## Update `wg-easy`
|
||||
|
||||
@@ -55,8 +56,8 @@ To update `wg-easy` to the latest version, run:
|
||||
|
||||
```shell
|
||||
cd /etc/docker/containers/wg-easy
|
||||
sudo docker-compose pull
|
||||
sudo docker-compose up -d
|
||||
sudo docker compose pull
|
||||
sudo docker compose up -d
|
||||
```
|
||||
|
||||
## Auto Update
|
||||
|
||||
@@ -2,8 +2,101 @@
|
||||
title: Caddy
|
||||
---
|
||||
|
||||
It seems like the Docs on how to setup Caddy are not available yet.
|
||||
/// note | Opinionated
|
||||
|
||||
Feel free to create a PR and add them here.
|
||||
This guide is opinionated. If you use other conventions or folder layouts, feel free to change the commands and paths.
|
||||
///
|
||||
|
||||
<!-- TODO -->
|
||||
We're using [Caddy](https://caddyserver.com/) here as reverse proxy to serve `wg-easy` on [https://wg-easy.example.com](https://wg-easy.example.com) via TLS.
|
||||
|
||||
## Create a docker composition for `caddy`
|
||||
|
||||
```txt
|
||||
.
|
||||
├── compose.yml
|
||||
└── Caddyfile
|
||||
|
||||
1 directory, 2 files
|
||||
```
|
||||
|
||||
```yaml
|
||||
# compose.yml
|
||||
|
||||
services:
|
||||
caddy:
|
||||
container_name: caddy
|
||||
image: caddy:2.10.0-alpine
|
||||
# publish everything you deem necessary
|
||||
ports:
|
||||
- '80:80/tcp'
|
||||
- '443:443/tcp'
|
||||
- '443:443/udp'
|
||||
networks:
|
||||
- caddy
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- './Caddyfile:/etc/caddy/Caddyfile:ro'
|
||||
- config:/config
|
||||
- data:/data
|
||||
|
||||
networks:
|
||||
caddy:
|
||||
name: caddy
|
||||
|
||||
volumes:
|
||||
config:
|
||||
data:
|
||||
```
|
||||
|
||||
```txt
|
||||
# Caddyfile
|
||||
|
||||
{
|
||||
# setup your email address
|
||||
email mail@example.com
|
||||
}
|
||||
|
||||
wg-easy.example.com {
|
||||
# since the container will share the network with wg-easy
|
||||
# we can use the proper container name
|
||||
reverse_proxy wg-easy:80
|
||||
tls internal
|
||||
}
|
||||
```
|
||||
|
||||
...and start it with:
|
||||
|
||||
```shell
|
||||
sudo docker compose up -d
|
||||
```
|
||||
|
||||
## Adapt the docker composition of `wg-easy`
|
||||
|
||||
```yaml
|
||||
services:
|
||||
wg-easy:
|
||||
# sync container name and port according to Caddyfile
|
||||
container_name: wg-easy
|
||||
environment:
|
||||
- PORT=80
|
||||
# no need to publish the HTTP server anymore
|
||||
ports:
|
||||
- "51820:51820/udp"
|
||||
# add to caddy network
|
||||
networks:
|
||||
caddy:
|
||||
...
|
||||
|
||||
networks:
|
||||
caddy:
|
||||
external: true
|
||||
...
|
||||
```
|
||||
|
||||
...and restart it with:
|
||||
|
||||
```shell
|
||||
sudo docker compose up -d
|
||||
```
|
||||
|
||||
You can now access `wg-easy` at [https://wg-easy.example.com](https://wg-easy.example.com) and start the setup.
|
||||
|
||||
@@ -7,9 +7,9 @@ To setup the IPv6 Network, simply run once:
|
||||
```shell
|
||||
docker network create \
|
||||
-d bridge --ipv6 \
|
||||
-d default \
|
||||
--subnet 10.42.42.0/24 \
|
||||
--subnet fdcc:ad94:bacf:61a3::/64 wg \
|
||||
--subnet fdcc:ad94:bacf:61a3::/64 \
|
||||
wg
|
||||
```
|
||||
|
||||
<!-- ref: major version -->
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
title: No Reverse Proxy
|
||||
---
|
||||
|
||||
/// warning | Insecure
|
||||
|
||||
This is insecure. You should use a reverse proxy to secure the connection.
|
||||
|
||||
Only use this method if you know what you are doing.
|
||||
///
|
||||
|
||||
If you only allow access to the web UI from your local network, you can skip the reverse proxy setup. This is not recommended, but it is possible.
|
||||
|
||||
## Setup
|
||||
|
||||
- Edit the `docker-compose.yml` file and uncomment `environment` and `INSECURE`
|
||||
|
||||
- Set `INSECURE` to `true` to allow access to the web UI over a non-secure connection.
|
||||
|
||||
- The `docker-compose.yml` file should look something like this:
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
- INSECURE=true
|
||||
```
|
||||
|
||||
- Save the file and restart `wg-easy`.
|
||||
|
||||
- Make sure that the Web UI is not accessible from outside your local network.
|
||||
@@ -0,0 +1,95 @@
|
||||
---
|
||||
title: Routed setup (No NAT)
|
||||
---
|
||||
|
||||
This guide shows how to run **wg-easy** with a routed setup, so packets are forwarded instead of NATed.
|
||||
|
||||
In a routed design, each WireGuard client keeps its own IPv4/IPv6 address. That means you can identify clients by their real addresses instead of seeing everything as the WireGuard server’s IP.
|
||||
|
||||
## Requirements
|
||||
|
||||
1. You know how to add static routes on your router to the WireGuard server.
|
||||
|
||||
## Docker setup
|
||||
|
||||
To make use of our own IPv4/IPv6 addresses, run the container with the `network_mode: host` option.
|
||||
|
||||
```yaml
|
||||
services:
|
||||
wg-easy:
|
||||
image: ghcr.io/wg-easy/wg-easy:15
|
||||
container_name: wg-easy
|
||||
network_mode: 'host'
|
||||
volumes:
|
||||
- ./config:/etc/wireguard
|
||||
- /lib/modules:/lib/modules:ro
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
- SYS_MODULE
|
||||
devices:
|
||||
- /dev/net/tun:/dev/net/tun
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
Because we’re on the host network, remove any `ports:` and container `sysctls:` you might have had before.
|
||||
|
||||
## Kernel parameters (on the host)
|
||||
|
||||
With host networking, system sysctls must be set on the **host**. On your host, create `/etc/sysctl.d/90-wireguard.conf`:
|
||||
|
||||
```txt
|
||||
net.ipv4.ip_forward=1
|
||||
net.ipv4.conf.all.src_valid_mark=1
|
||||
net.ipv6.conf.all.disable_ipv6=0
|
||||
net.ipv6.conf.all.forwarding=1
|
||||
net.ipv6.conf.default.forwarding=1
|
||||
```
|
||||
|
||||
Apply and verify:
|
||||
|
||||
```shell
|
||||
sysctl -p /etc/sysctl.d/90-wireguard.conf
|
||||
sysctl -n net.ipv4.ip_forward # should print 1
|
||||
```
|
||||
|
||||
## Add static routes on your router
|
||||
|
||||
Pick an IPv4 and IPv6 subnet for your clients and add static routes on your router, pointing to the WireGuard server's LAN addresses.
|
||||
|
||||
### Example
|
||||
|
||||
/// note | 2001:db8::/32
|
||||
|
||||
The _documentation prefix_ `2001:db8::/32` (RFC 3849) used in this example is not meant for production use, replace it with your own ISP-assigned IPv6 prefix (GUA) or local prefix (ULA)
|
||||
///
|
||||
|
||||
I want my WireGuard clients in `192.168.0.0/24` and `2001:db8:abc:0::/64`.
|
||||
|
||||
- Routed IPv4 subnet: `192.168.0.0/24`
|
||||
- Routed IPv6 prefix: `2001:db8:abc:0::/64`
|
||||
- WireGuard server IPs: `192.168.10.118` and `2001:db8:abc:10:216:3eff:fedb:949e`
|
||||
|
||||
On your router:
|
||||
|
||||
- Route `192.168.0.0/24` → next hop `192.168.10.118`
|
||||
- Route `2001:db8:abc:0::/64` → next hop `2001:db8:abc:10:216:3eff:fedb:949e`
|
||||
|
||||
Don't forget to create the necessary firewall rules to allow these subnets to travel across your LAN. Some routers or servers may require specific Outbound NAT rules for the chosen IPv4 and IPv6 subnets to allow traffic to traverse your LAN.
|
||||
|
||||
## `wg-easy` configuration
|
||||
|
||||
In the Web UI → Admin → Interface, click Change CIDR and set the IPv4/IPv6 routed subnets you chose above. Save.
|
||||
|
||||
Then go to Admin → Hooks and add:
|
||||
|
||||
PostUp
|
||||
|
||||
```shell
|
||||
iptables -A INPUT -p udp -m udp --dport {{port}} -j ACCEPT; iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; ip6tables -A INPUT -p udp -m udp --dport {{port}} -j ACCEPT; ip6tables -A FORWARD -i wg0 -j ACCEPT; ip6tables -A FORWARD -o wg0 -j ACCEPT
|
||||
```
|
||||
|
||||
PostDown
|
||||
|
||||
```shell
|
||||
iptables -D INPUT -p udp -m udp --dport {{port}} -j ACCEPT; iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; ip6tables -D INPUT -p udp -m udp --dport {{port}} -j ACCEPT; ip6tables -D FORWARD -i wg0 -j ACCEPT; ip6tables -D FORWARD -o wg0 -j ACCEPT
|
||||
```
|
||||
@@ -25,9 +25,9 @@ services:
|
||||
container_name: traefik
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443/tcp"
|
||||
- "443:443/udp"
|
||||
- '80:80'
|
||||
- '443:443/tcp'
|
||||
- '443:443/udp'
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- /etc/docker/volumes/traefik/traefik.yml:/traefik.yml:ro
|
||||
@@ -51,14 +51,14 @@ log:
|
||||
|
||||
entryPoints:
|
||||
web:
|
||||
address: ":80/tcp"
|
||||
address: ':80/tcp'
|
||||
http:
|
||||
redirections:
|
||||
entryPoint:
|
||||
to: websecure
|
||||
scheme: https
|
||||
websecure:
|
||||
address: ":443/tcp"
|
||||
address: ':443/tcp'
|
||||
http:
|
||||
middlewares:
|
||||
- compress@file
|
||||
@@ -100,7 +100,7 @@ http:
|
||||
services:
|
||||
basicAuth:
|
||||
users:
|
||||
- "$username$:$password$"
|
||||
- '$username$:$password$'
|
||||
compress:
|
||||
compress: {}
|
||||
hsts:
|
||||
@@ -141,10 +141,10 @@ sudo docker network create traefik
|
||||
## Start traefik
|
||||
|
||||
```shell
|
||||
sudo docker-compose up -d
|
||||
sudo docker compose up -d
|
||||
```
|
||||
|
||||
You can no access the Traefik dashboard at `https://traefik.$example.com$` with the credentials you set in `traefik_dynamic.yml`.
|
||||
You can now access the Traefik dashboard at `https://traefik.$example.com$` with the credentials you set in `traefik_dynamic.yml`.
|
||||
|
||||
## Add Labels to `wg-easy`
|
||||
|
||||
@@ -166,6 +166,7 @@ services:
|
||||
- "traefik.http.routers.wg-easy.entrypoints=websecure"
|
||||
- "traefik.http.routers.wg-easy.service=wg-easy"
|
||||
- "traefik.http.services.wg-easy.loadbalancer.server.port=51821"
|
||||
- "traefik.docker.network=traefik"
|
||||
...
|
||||
|
||||
networks:
|
||||
@@ -178,7 +179,7 @@ networks:
|
||||
|
||||
```shell
|
||||
cd /etc/docker/containers/wg-easy
|
||||
sudo docker-compose up -d
|
||||
sudo docker compose up -d
|
||||
```
|
||||
|
||||
You can now access `wg-easy` at `https://wg-easy.$example.com$` and start the setup.
|
||||
|
||||
+10
-10
@@ -14,13 +14,13 @@ To resolve this issue, you can try the following steps:
|
||||
|
||||
1. **Load the WireGuard kernel module**: If the WireGuard kernel module is not loaded, you can load it manually by running:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
sudo modprobe wireguard
|
||||
```
|
||||
|
||||
2. **Load the WireGuard kernel module on boot**: If you want to ensure that the WireGuard kernel module is loaded automatically on boot, you can add it to the `/etc/modules` file:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
echo "wireguard" | sudo tee -a /etc/modules
|
||||
```
|
||||
|
||||
@@ -32,13 +32,13 @@ To resolve this issue, you can try the following steps:
|
||||
|
||||
1. **Load the `nat` kernel module**: If the `nat` kernel module is not loaded, you can load it manually by running:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
sudo modprobe iptable_nat
|
||||
```
|
||||
|
||||
2. **Load the `nat` kernel module on boot**: If you want to ensure that the `nat` kernel module is loaded automatically on boot, you can add it to the `/etc/modules` file:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
echo "iptable_nat" | sudo tee -a /etc/modules
|
||||
```
|
||||
|
||||
@@ -50,13 +50,13 @@ To resolve this issue, you can try the following steps:
|
||||
|
||||
1. **Load the `nat` kernel module**: If the `nat` kernel module is not loaded, you can load it manually by running:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
sudo modprobe ip6table_nat
|
||||
```
|
||||
|
||||
2. **Load the `nat` kernel module on boot**: If you want to ensure that the `nat` kernel module is loaded automatically on boot, you can add it to the `/etc/modules` file:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
echo "ip6table_nat" | sudo tee -a /etc/modules
|
||||
```
|
||||
|
||||
@@ -68,13 +68,13 @@ To resolve this issue, you can try the following steps:
|
||||
|
||||
1. **Load the `filter` kernel module**: If the `filter` kernel module is not loaded, you can load it manually by running:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
sudo modprobe iptable_filter
|
||||
```
|
||||
|
||||
2. **Load the `filter` kernel module on boot**: If you want to ensure that the `filter` kernel module is loaded automatically on boot, you can add it to the `/etc/modules` file:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
echo "iptable_filter" | sudo tee -a /etc/modules
|
||||
```
|
||||
|
||||
@@ -86,12 +86,12 @@ To resolve this issue, you can try the following steps:
|
||||
|
||||
1. **Load the `filter` kernel module**: If the `filter` kernel module is not loaded, you can load it manually by running:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
sudo modprobe ip6table_filter
|
||||
```
|
||||
|
||||
2. **Load the `filter` kernel module on boot**: If you want to ensure that the `filter` kernel module is loaded automatically on boot, you can add it to the `/etc/modules` file:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
echo "ip6table_filter" | sudo tee -a /etc/modules
|
||||
```
|
||||
|
||||
@@ -12,7 +12,7 @@ Before you can get started with deploying your own VPN, there are some requireme
|
||||
|
||||
1. You need to have a host that you can manage
|
||||
2. You need to have a domain name or a public IP address
|
||||
3. You need a supported architecture (x86_64, arm64)
|
||||
3. You need a supported architecture (x86_64, arm64, armv7)
|
||||
|
||||
### Host Setup
|
||||
|
||||
@@ -38,59 +38,37 @@ If you're using podman, make sure to read the related [documentation][docs-podma
|
||||
To understand which tags you should use, read this section carefully. [Our CI][github-ci] will automatically build, test and push new images to the following container registry:
|
||||
|
||||
1. GitHub Container Registry ([`ghcr.io/wg-easy/wg-easy`][ghcr-image])
|
||||
2. Codeberg Container Registry ([`codeberg.org/wg-easy/wg-easy`][codeberg-image]) (IPv6 support)
|
||||
|
||||
All workflows are using the tagging convention listed below. It is subsequently applied to all images.
|
||||
|
||||
| tag | Type | Example | Description |
|
||||
| ------------- | ---------------------------------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `15` | latest minor for that major tag | `ghcr.io/wg-easy/wg-easy:15` | latest features for specific major versions, no breaking changes |
|
||||
| `latest` | latest tag | `ghcr.io/wg-easy/wg-easy:latest` or `ghcr.io/wg-easy/wg-easy` | stable as possible get bug fixes quickly when needed, see Releases for more information. |
|
||||
| ------------- | ------------------------------- | ------------------------------------------------------------- | ----------------------------------------------------------------------------- |
|
||||
| `15` | latest minor for that major tag | `ghcr.io/wg-easy/wg-easy:15` | latest features for specific major versions, no breaking changes, recommended |
|
||||
| `latest` | latest tag | `ghcr.io/wg-easy/wg-easy:latest` or `ghcr.io/wg-easy/wg-easy` | points to latest release, can include breaking changes |
|
||||
| `15.0` | latest patch for that minor tag | `ghcr.io/wg-easy/wg-easy:15.0` | latest patches for specific minor version |
|
||||
| `15.0.0` | specific tag | `ghcr.io/wg-easy/wg-easy:15.0.0` | specific release, don't use this as this will not get updated |
|
||||
| `nightly` | [`master`](https://github.com/wg-easy/wg-easy/tree/master) | `ghcr.io/wg-easy/wg-easy:nightly` | mostly unstable gets frequent package and code updates, deployed against [`master`](https://github.com/wg-easy/wg-easy/tree/master). |
|
||||
| `development` | pull requests | `ghcr.io/wg-easy/wg-easy:development` | used for development, testing code from PRs before landing into [`master`](https://github.com/wg-easy/wg-easy/tree/master). |
|
||||
| `15.0.0` | specific tag | `ghcr.io/wg-easy/wg-easy:15.0.0` | specific release, no updates |
|
||||
| `edge` | push to `master` | `ghcr.io/wg-easy/wg-easy:edge` | mostly unstable, gets frequent package and code updates |
|
||||
| `development` | pull requests | `ghcr.io/wg-easy/wg-easy:development` | used for development, testing code from PRs |
|
||||
|
||||
<!-- ref: major version (check links too) -->
|
||||
|
||||
When publishing a tag we follow the [Semantic Versioning][semver] specification. The `latest` tag is always pointing to the latest stable release. If you want to avoid breaking changes, use the major version tag (e.g. `15`).
|
||||
|
||||
[github-ci]: https://github.com/wg-easy/wg-easy/actions
|
||||
[ghcr-image]: https://github.com/wg-easy/wg-easy/pkgs/container/wg-easy
|
||||
[codeberg-image]: https://codeberg.org/wg-easy/-/packages/container/wg-easy/15
|
||||
[semver]: https://semver.org/
|
||||
|
||||
### Get All Files
|
||||
### Follow tutorials
|
||||
|
||||
Issue the following command to acquire the necessary file:
|
||||
- [Basic Installation with Docker Compose (Recommended)](./examples/tutorials/basic-installation.md)
|
||||
- [Simple Installation with Docker Run](./examples/tutorials/docker-run.md)
|
||||
- [Advanced Installation with Podman](./examples/tutorials/podman-nft.md)
|
||||
|
||||
```shell
|
||||
wget "https://raw.githubusercontent.com/wg-easy/wg-easy/master/docker-compose.yml"
|
||||
```
|
||||
|
||||
### Start the Container
|
||||
|
||||
To start the container, issue the following command:
|
||||
|
||||
```shell
|
||||
sudo docker compose up -d
|
||||
```
|
||||
|
||||
### Configuration Steps
|
||||
|
||||
Now follow the setup process in your web browser
|
||||
|
||||
### Stopping the Container
|
||||
|
||||
To stop the container, issue the following command:
|
||||
|
||||
```shell
|
||||
sudo docker compose down
|
||||
```
|
||||
|
||||
/// danger | Using the Correct Commands For Stopping and Starting `wg-easy`
|
||||
/// danger | Use the Correct Commands For Stopping and Starting `wg-easy`
|
||||
|
||||
**Use `sudo docker compose up / down`, not `sudo docker compose start / stop`**. Otherwise, the container is not properly destroyed and you may experience problems during startup because of inconsistent state.
|
||||
///
|
||||
|
||||
**That's it! It really is that easy**.
|
||||
|
||||
If you need more help you can read the [Basic Installation Tutorial][basic-installation].
|
||||
|
||||
[basic-installation]: ./examples/tutorials/basic-installation.md
|
||||
|
||||
@@ -2,4 +2,25 @@
|
||||
title: 2FA
|
||||
---
|
||||
|
||||
TODO
|
||||
The user can enable 2FA from the Account page. The Account page is accessible from the dropdown menu in the top right corner of the application.
|
||||
|
||||
## Enable TOTP
|
||||
|
||||
- **Enable Two Factor Authentication**: Enable TOTP for the user.
|
||||
|
||||
## Configure TOTP
|
||||
|
||||
A QR code will be displayed. Scan the QR code with your TOTP application (e.g., Google Authenticator, Authy, etc.) to add the account.
|
||||
|
||||
To verify that the TOTP key is working, the user must enter the TOTP code generated by the TOTP application.
|
||||
|
||||
- **TOTP Key**: The TOTP key for the user. This key is used to generate the TOTP code.
|
||||
- **TOTP Code**: The current TOTP code for the user. This code is used to verify the TOTP key.
|
||||
- **Enable Two Factor Authentication**: Enable TOTP for the user.
|
||||
|
||||
## Disable TOTP
|
||||
|
||||
To disable TOTP, the user must enter the current password.
|
||||
|
||||
- **Current Password**: The current password of the user.
|
||||
- **Disable Two Factor Authentication**: Disable TOTP for the user.
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
title: Edit Account
|
||||
---
|
||||
|
||||
TODO
|
||||
@@ -0,0 +1,43 @@
|
||||
---
|
||||
title: CLI
|
||||
---
|
||||
|
||||
If you want to use the CLI, you can run it with
|
||||
|
||||
### Docker Compose
|
||||
|
||||
```shell
|
||||
cd /etc/docker/containers/wg-easy
|
||||
docker compose exec -it wg-easy cli
|
||||
```
|
||||
|
||||
### Docker Run
|
||||
|
||||
```shell
|
||||
docker run --rm -it \
|
||||
-v ~/.wg-easy:/etc/wireguard \
|
||||
ghcr.io/wg-easy/wg-easy:15 \
|
||||
cli
|
||||
```
|
||||
|
||||
### Reset Password
|
||||
|
||||
If you want to reset the password for the admin user, you can run the following command:
|
||||
|
||||
#### By Prompt
|
||||
|
||||
```shell
|
||||
cd /etc/docker/containers/wg-easy
|
||||
docker compose exec -it wg-easy cli db:admin:reset
|
||||
```
|
||||
|
||||
You are asked to provide the new password
|
||||
|
||||
#### By Argument
|
||||
|
||||
```shell
|
||||
cd /etc/docker/containers/wg-easy
|
||||
docker compose exec -it wg-easy cli db:admin:reset --password <new_password>
|
||||
```
|
||||
|
||||
This will reset the password for the admin user to the new password you provided. If you include special characters in the password, make sure to escape them properly.
|
||||
@@ -2,4 +2,49 @@
|
||||
title: Edit Client
|
||||
---
|
||||
|
||||
TODO
|
||||
## General
|
||||
|
||||
- **Name**: The name of the client.
|
||||
- **Enabled**: Whether the client can connect to the VPN.
|
||||
- **Expire Date**: The date the client will be disabled.
|
||||
|
||||
## Address
|
||||
|
||||
- **IPv4**: The IPv4 address of the client.
|
||||
- **IPv6**: The IPv6 address of the client.
|
||||
|
||||
## Allowed IPs
|
||||
|
||||
Which IPs will be routed through the VPN.
|
||||
|
||||
This will not prevent the user from modifying it locally and accessing IP ranges that they should not be able to access.
|
||||
|
||||
Use firewall rules to prevent access to IP ranges that the user should not be able to access.
|
||||
|
||||
## Server Allowed IPs
|
||||
|
||||
Which IPs will be routed to the client.
|
||||
|
||||
## DNS
|
||||
|
||||
The DNS server that the client will use.
|
||||
|
||||
## Advanced
|
||||
|
||||
- **MTU**: The maximum transmission unit for the client.
|
||||
- **Persistent Keepalive**: The interval for sending keepalive packets to the server.
|
||||
|
||||
## Hooks
|
||||
|
||||
This can only be used for clients that use `wg-quick`. Setting this will throw a error when importing the config on other clients.
|
||||
|
||||
- **PreUp**: Commands to run before the interface is brought up.
|
||||
- **PostUp**: Commands to run after the interface is brought up.
|
||||
- **PreDown**: Commands to run before the interface is brought down.
|
||||
- **PostDown**: Commands to run after the interface is brought down.
|
||||
|
||||
## Actions
|
||||
|
||||
- **Save**: Save the changes made in the form.
|
||||
- **Revert**: Revert the changes made in the form.
|
||||
- **Delete**: Delete the client.
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
title: Login
|
||||
---
|
||||
|
||||
TODO
|
||||
@@ -2,4 +2,23 @@
|
||||
title: Setup
|
||||
---
|
||||
|
||||
TODO
|
||||
## User Setup
|
||||
|
||||
- **Username**: The username of the user.
|
||||
- **Password**: The password of the user.
|
||||
- **Confirm Password**: The password of the user.
|
||||
|
||||
## Existing Setup
|
||||
|
||||
If you have the config from the previous version, you can import it by clicking "Yes". This currently expects a config from v14.
|
||||
|
||||
If this is the first time you are using this, you can click "No" to create a new config.
|
||||
|
||||
### No - Host Setup
|
||||
|
||||
- **Host**: The host of the server. The clients will connect to this address. This can be a domain name or an IP address. Make sure to wrap it in brackets if it is an IPv6 address. For example: `[::1]` or `[2001:db8::1]`.
|
||||
- **Port**: The port of the server. The clients will connect to this port. The server will listen on this port.
|
||||
|
||||
### Yes - Migration
|
||||
|
||||
Select the `wg0.json` file from the previous version. Read [Migrate from v14 to v15](../advanced/migrate/from-14-to-15.md) for more information.
|
||||
|
||||
+7
-7
@@ -1,6 +1,6 @@
|
||||
site_name: "wg-easy"
|
||||
site_description: "The easiest way to run WireGuard VPN + Web-based Admin UI."
|
||||
site_author: "WireGuard Easy"
|
||||
site_name: 'wg-easy'
|
||||
site_description: 'The easiest way to run WireGuard VPN + Web-based Admin UI.'
|
||||
site_author: 'WireGuard Easy'
|
||||
copyright: >
|
||||
<p>
|
||||
© <a href="https://github.com/wg-easy"><em>Wireguard Easy</em></a><br/>
|
||||
@@ -12,9 +12,9 @@ copyright: >
|
||||
repo_url: https://github.com/wg-easy/wg-easy
|
||||
repo_name: wg-easy
|
||||
|
||||
edit_uri: "edit/master/docs/content"
|
||||
edit_uri: 'edit/master/docs/content'
|
||||
|
||||
docs_dir: "content/"
|
||||
docs_dir: 'content/'
|
||||
|
||||
site_url: https://wg-easy.github.io/wg-easy
|
||||
|
||||
@@ -34,7 +34,7 @@ theme:
|
||||
- content.code.annotate
|
||||
palette:
|
||||
# Light mode
|
||||
- media: "(prefers-color-scheme: light)"
|
||||
- media: '(prefers-color-scheme: light)'
|
||||
scheme: default
|
||||
primary: grey
|
||||
accent: red
|
||||
@@ -42,7 +42,7 @@ theme:
|
||||
icon: material/weather-night
|
||||
name: Switch to dark mode
|
||||
# Dark mode
|
||||
- media: "(prefers-color-scheme: dark)"
|
||||
- media: '(prefers-color-scheme: dark)'
|
||||
scheme: slate
|
||||
primary: grey
|
||||
accent: red
|
||||
|
||||
+8
-3
@@ -2,10 +2,15 @@
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "docker compose -f docker-compose.dev.yml up --build",
|
||||
"dev": "docker compose -f docker-compose.dev.yml up wg-easy --build",
|
||||
"cli:dev": "docker compose -f docker-compose.dev.yml run --build --rm -it wg-easy cli:dev",
|
||||
"build": "docker build -t wg-easy .",
|
||||
"docs:preview": "docker run --rm -it -p 8080:8080 -v ./docs:/docs squidfunk/mkdocs-material serve -a 0.0.0.0:8080",
|
||||
"scripts:version": "bash scripts/version.sh"
|
||||
"scripts:version": "bash scripts/version.sh",
|
||||
"format:check:docs": "prettier --check docs"
|
||||
},
|
||||
"packageManager": "pnpm@10.8.0"
|
||||
"devDependencies": {
|
||||
"prettier": "^3.6.2"
|
||||
},
|
||||
"packageManager": "pnpm@10.21.0"
|
||||
}
|
||||
|
||||
Generated
+16
-1
@@ -6,4 +6,19 @@ settings:
|
||||
|
||||
importers:
|
||||
|
||||
.: {}
|
||||
.:
|
||||
devDependencies:
|
||||
prettier:
|
||||
specifier: ^3.6.2
|
||||
version: 3.6.2
|
||||
|
||||
packages:
|
||||
|
||||
prettier@3.6.2:
|
||||
resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==}
|
||||
engines: {node: '>=14'}
|
||||
hasBin: true
|
||||
|
||||
snapshots:
|
||||
|
||||
prettier@3.6.2: {}
|
||||
|
||||
Executable
+19
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
|
||||
folder="src/i18n/locales"
|
||||
base_file="$folder/en.json"
|
||||
|
||||
# Get all leaf keys from the English base file
|
||||
base_keys=$(jq -r 'paths(scalars) | map(tostring) | join(".")' "$base_file")
|
||||
total=$(echo "$base_keys" | wc -l)
|
||||
|
||||
# Loop through all JSON files in the folder
|
||||
for file in "$folder"/*.json; do
|
||||
name=$(basename "$file" .json)
|
||||
translated_keys=$(jq -r 'paths(scalars) | map(tostring) | join(".")' "$file")
|
||||
done=$(comm -12 <(echo "$base_keys" | sort) <(echo "$translated_keys" | sort) | wc -l)
|
||||
percent=$((100 * done / total))
|
||||
check="[ ]"
|
||||
[ "$percent" -eq 100 ] && check="[x]"
|
||||
printf "%s %s (%d%%)\n" "- $check" "$name" "$percent"
|
||||
done
|
||||
@@ -1 +0,0 @@
|
||||
public-hoist-pattern[]=@libsql/linux*
|
||||
@@ -10,12 +10,12 @@
|
||||
</template>
|
||||
<template #actions>
|
||||
<DialogClose as-child>
|
||||
<BaseButton>{{ $t('dialog.cancel') }}</BaseButton>
|
||||
<BaseSecondaryButton>{{ $t('dialog.cancel') }}</BaseSecondaryButton>
|
||||
</DialogClose>
|
||||
<DialogClose as-child>
|
||||
<BaseButton @click="$emit('change', ipv4Cidr, ipv6Cidr)">
|
||||
<BasePrimaryButton @click="$emit('change', ipv4Cidr, ipv6Cidr)">
|
||||
{{ $t('dialog.change') }}
|
||||
</BaseButton>
|
||||
</BasePrimaryButton>
|
||||
</DialogClose>
|
||||
</template>
|
||||
</BaseDialog>
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
</template>
|
||||
<template #actions>
|
||||
<DialogClose as-child>
|
||||
<BaseButton>{{ $t('dialog.cancel') }}</BaseButton>
|
||||
<BaseSecondaryButton>{{ $t('dialog.cancel') }}</BaseSecondaryButton>
|
||||
</DialogClose>
|
||||
<DialogClose as-child>
|
||||
<BaseButton @click="$emit('restart')">
|
||||
<BasePrimaryButton @click="$emit('restart')">
|
||||
{{ $t('admin.interface.restart') }}
|
||||
</BaseButton>
|
||||
</BasePrimaryButton>
|
||||
</DialogClose>
|
||||
</template>
|
||||
</BaseDialog>
|
||||
|
||||
@@ -13,12 +13,12 @@
|
||||
</template>
|
||||
<template #actions>
|
||||
<DialogClose as-child>
|
||||
<BaseButton>{{ $t('dialog.cancel') }}</BaseButton>
|
||||
<BaseSecondaryButton>{{ $t('dialog.cancel') }}</BaseSecondaryButton>
|
||||
</DialogClose>
|
||||
<DialogClose as-child>
|
||||
<BaseButton @click="$emit('change', selected)">
|
||||
<BasePrimaryButton @click="$emit('change', selected)">
|
||||
{{ $t('dialog.change') }}
|
||||
</BaseButton>
|
||||
</BasePrimaryButton>
|
||||
</DialogClose>
|
||||
</template>
|
||||
</BaseDialog>
|
||||
|
||||
@@ -11,10 +11,10 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { VueApexChartsComponent } from 'vue3-apexcharts';
|
||||
import type { VueApexChartsComponentProps } from 'vue3-apexcharts';
|
||||
|
||||
defineProps<{
|
||||
options: VueApexChartsComponent['options'];
|
||||
series: VueApexChartsComponent['series'];
|
||||
options: VueApexChartsComponentProps['options'];
|
||||
series: VueApexChartsComponentProps['series'];
|
||||
}>();
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<div class="overflow-x-auto rounded border-2 border-red-800 py-2">
|
||||
<pre
|
||||
class="mx-2 inline-block"
|
||||
@click="selectCode"
|
||||
><code ref="codeBlock">{{ code }}</code></pre>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
code: string;
|
||||
}>();
|
||||
|
||||
const codeBlock = useTemplateRef('codeBlock');
|
||||
|
||||
function selectCode() {
|
||||
// TODO: keyboard support?
|
||||
if (codeBlock.value) {
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(codeBlock.value);
|
||||
const sel = window.getSelection();
|
||||
if (sel) {
|
||||
sel.removeAllRanges();
|
||||
sel.addRange(range);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<component
|
||||
:is="elementType"
|
||||
role="button"
|
||||
class="inline-flex items-center rounded border-2 border-red-800 bg-red-800 px-4 py-2 text-white transition hover:border-red-600 hover:bg-red-600"
|
||||
v-bind="attrs"
|
||||
>
|
||||
<slot />
|
||||
</component>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const props = defineProps({
|
||||
as: {
|
||||
type: String,
|
||||
default: 'button',
|
||||
},
|
||||
});
|
||||
|
||||
const elementType = computed(() => props.as);
|
||||
|
||||
const attrs = computed(() => {
|
||||
const { as, ...attrs } = props;
|
||||
return attrs;
|
||||
});
|
||||
</script>
|
||||
@@ -20,7 +20,7 @@ const props = defineProps({
|
||||
const elementType = computed(() => props.as);
|
||||
|
||||
const attrs = computed(() => {
|
||||
const { as, ...attrs } = props;
|
||||
return attrs;
|
||||
const { as, ...rest } = props;
|
||||
return rest;
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<BaseDialog :trigger-class="triggerClass">
|
||||
<template #trigger>
|
||||
<slot />
|
||||
</template>
|
||||
<template #title>
|
||||
{{ $t('client.config') }}
|
||||
</template>
|
||||
<template #description>
|
||||
<div v-if="status === 'success'">
|
||||
<BaseCodeBlock :code="config ?? ''" />
|
||||
</div>
|
||||
<div v-else>
|
||||
<span>{{ $t('general.loading') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #actions>
|
||||
<DialogClose as-child>
|
||||
<BaseSecondaryButton>{{ $t('dialog.cancel') }}</BaseSecondaryButton>
|
||||
</DialogClose>
|
||||
<DialogClose as-child>
|
||||
<BasePrimaryButton @click="copyCode">
|
||||
{{ $t('copy.copy') }}
|
||||
</BasePrimaryButton>
|
||||
</DialogClose>
|
||||
</template>
|
||||
</BaseDialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const props = defineProps<{ triggerClass?: string; clientId: number }>();
|
||||
|
||||
const toast = useToast();
|
||||
const { copied, copy, isSupported } = useClipboard({
|
||||
// fallback does not work
|
||||
legacy: false,
|
||||
});
|
||||
|
||||
const { data: config, status } = useFetch(
|
||||
`/api/client/${props.clientId}/configuration`,
|
||||
{
|
||||
responseType: 'text',
|
||||
server: false,
|
||||
}
|
||||
);
|
||||
|
||||
async function copyCode() {
|
||||
if (status.value !== 'success') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isSupported.value) {
|
||||
toast.showToast({
|
||||
type: 'error',
|
||||
message: $t('copy.notSupported'),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await copy(config.value ?? '');
|
||||
|
||||
if (copied.value) {
|
||||
toast.showToast({
|
||||
type: 'success',
|
||||
message: $t('copy.copied'),
|
||||
});
|
||||
} else {
|
||||
toast.showToast({
|
||||
type: 'error',
|
||||
message: $t('copy.failed'),
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -18,10 +18,12 @@
|
||||
</template>
|
||||
<template #actions>
|
||||
<DialogClose as-child>
|
||||
<BaseButton>{{ $t('dialog.cancel') }}</BaseButton>
|
||||
<BaseSecondaryButton>{{ $t('dialog.cancel') }}</BaseSecondaryButton>
|
||||
</DialogClose>
|
||||
<DialogClose as-child>
|
||||
<BaseButton @click="createClient">{{ $t('client.create') }}</BaseButton>
|
||||
<BasePrimaryButton @click="createClient">
|
||||
{{ $t('client.create') }}
|
||||
</BasePrimaryButton>
|
||||
</DialogClose>
|
||||
</template>
|
||||
</BaseDialog>
|
||||
|
||||
@@ -9,12 +9,12 @@
|
||||
</template>
|
||||
<template #actions>
|
||||
<DialogClose as-child>
|
||||
<BaseButton>{{ $t('dialog.cancel') }}</BaseButton>
|
||||
<BasePrimaryButton>{{ $t('dialog.cancel') }}</BasePrimaryButton>
|
||||
</DialogClose>
|
||||
<DialogClose as-child>
|
||||
<BaseButton @click="$emit('delete')">{{
|
||||
$t('client.deleteClient')
|
||||
}}</BaseButton>
|
||||
<BaseSecondaryButton @click="$emit('delete')">
|
||||
{{ $t('client.deleteClient') }}
|
||||
</BaseSecondaryButton>
|
||||
</DialogClose>
|
||||
</template>
|
||||
</BaseDialog>
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
<p class="m-10 text-center text-sm text-gray-400 dark:text-neutral-400">
|
||||
{{ $t('client.empty') }}<br /><br />
|
||||
<ClientsCreateDialog>
|
||||
<BaseButton as="span">
|
||||
<BaseSecondaryButton as="span">
|
||||
<IconsPlus class="w-4 md:mr-2" />
|
||||
<span class="text-sm">{{ $t('client.new') }}</span>
|
||||
</BaseButton>
|
||||
</BaseSecondaryButton>
|
||||
</ClientsCreateDialog>
|
||||
</p>
|
||||
</template>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<ClientsCreateDialog>
|
||||
<BaseButton as="span">
|
||||
<BaseSecondaryButton as="span">
|
||||
<IconsPlus class="w-4 md:mr-2" />
|
||||
<span class="text-sm max-md:hidden">{{ $t('client.newShort') }}</span>
|
||||
</BaseButton>
|
||||
</BaseSecondaryButton>
|
||||
</ClientsCreateDialog>
|
||||
</template>
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
</div>
|
||||
</template>
|
||||
<template #actions>
|
||||
<DialogClose>
|
||||
<BaseButton>{{ $t('dialog.cancel') }}</BaseButton>
|
||||
<DialogClose as-child>
|
||||
<BaseSecondaryButton>{{ $t('dialog.cancel') }}</BaseSecondaryButton>
|
||||
</DialogClose>
|
||||
</template>
|
||||
</BaseDialog>
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<div class="relative w-60 md:mr-2">
|
||||
<div class="relative flex h-full items-center">
|
||||
<MagnifyingGlassIcon
|
||||
class="absolute left-2.5 h-4 w-4 text-gray-400 dark:text-neutral-500"
|
||||
/>
|
||||
<input
|
||||
v-model="searchQuery"
|
||||
type="text"
|
||||
:placeholder="$t('client.search')"
|
||||
class="w-full rounded bg-white py-2 pr-8 text-sm text-gray-900 shadow-sm ring-1 ring-gray-300 transition-all placeholder:text-gray-400 focus:border-transparent focus:outline-none focus:ring-2 focus:ring-red-600 dark:bg-neutral-800 dark:text-white dark:ring-neutral-700 dark:placeholder:text-neutral-500 dark:focus:ring-red-700"
|
||||
@input="updateSearch"
|
||||
/>
|
||||
<button
|
||||
v-if="searchQuery"
|
||||
class="absolute right-2 flex h-5 w-5 items-center justify-center rounded-full bg-gray-200 text-gray-600 hover:bg-gray-300 hover:text-gray-800 dark:bg-neutral-700 dark:text-neutral-300 dark:hover:bg-neutral-600 dark:hover:text-neutral-100"
|
||||
aria-label="Clear search"
|
||||
@click="clearSearch"
|
||||
>
|
||||
<IconsClose class="h-3 w-3" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const clientsStore = useClientsStore();
|
||||
const searchQuery = ref('');
|
||||
|
||||
const updateSearch = useDebounceFn(() => {
|
||||
clientsStore.setSearchQuery(searchQuery.value);
|
||||
}, 300);
|
||||
|
||||
function clearSearch() {
|
||||
searchQuery.value = '';
|
||||
clientsStore.setSearchQuery('');
|
||||
}
|
||||
</script>
|
||||
@@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<BaseButton @click="toggleSort">
|
||||
<BasePrimaryButton @click="toggleSort">
|
||||
<IconsArrowDown
|
||||
v-if="globalStore.sortClient === true"
|
||||
class="w-4 md:mr-2"
|
||||
/>
|
||||
<IconsArrowUp v-else class="w-4 md:mr-2" />
|
||||
<span class="text-sm max-md:hidden"> {{ $t('client.sort') }}</span>
|
||||
</BaseButton>
|
||||
</BasePrimaryButton>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
class="rounded-lg border-2 border-gray-100 text-gray-500 focus:border-red-800 focus:outline-0 focus:ring-0 dark:border-neutral-800 dark:bg-neutral-700 dark:text-neutral-200 dark:placeholder:text-neutral-400"
|
||||
@input="update($event, i)"
|
||||
/>
|
||||
<BaseButton
|
||||
<BaseSecondaryButton
|
||||
as="input"
|
||||
type="button"
|
||||
class="rounded-lg"
|
||||
@@ -22,7 +22,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<BaseButton
|
||||
<BasePrimaryButton
|
||||
as="input"
|
||||
type="button"
|
||||
class="rounded-lg"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<IconsInfo class="size-4" />
|
||||
</BaseTooltip>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<div class="flex gap-1">
|
||||
<BaseInput
|
||||
:id="id"
|
||||
v-model.trim="data"
|
||||
@@ -18,12 +18,14 @@
|
||||
/>
|
||||
<ClientOnly>
|
||||
<AdminSuggestDialog :url="url" @change="data = $event">
|
||||
<BaseButton as="span">
|
||||
<BasePrimaryButton as="span">
|
||||
<div class="flex items-center gap-3">
|
||||
<IconsSparkles class="w-4" />
|
||||
<span>{{ $t('admin.config.suggest') }}</span>
|
||||
<span class="whitespace-nowrap">
|
||||
{{ $t('admin.config.suggest') }}
|
||||
</span>
|
||||
</div>
|
||||
</BaseButton>
|
||||
</BasePrimaryButton>
|
||||
</AdminSuggestDialog>
|
||||
</ClientOnly>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
<template>
|
||||
<div class="flex items-center">
|
||||
<FormLabel :for="id">
|
||||
{{ label }}
|
||||
</FormLabel>
|
||||
<BaseTooltip v-if="description" :text="description">
|
||||
<IconsInfo class="size-4" />
|
||||
</BaseTooltip>
|
||||
</div>
|
||||
<span :id="id" class="flex flex-col justify-center">{{ data }}</span>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
defineProps<{
|
||||
id: string;
|
||||
label: string;
|
||||
description?: string;
|
||||
data?: string;
|
||||
}>();
|
||||
</script>
|
||||
@@ -12,7 +12,7 @@
|
||||
class="rounded-lg border-2 border-gray-100 text-gray-500 focus:border-red-800 focus:outline-0 focus:ring-0 dark:border-neutral-800 dark:bg-neutral-700 dark:text-neutral-200 dark:placeholder:text-neutral-400"
|
||||
@input="update($event, i)"
|
||||
/>
|
||||
<BaseButton
|
||||
<BaseSecondaryButton
|
||||
as="input"
|
||||
type="button"
|
||||
class="rounded-lg"
|
||||
@@ -22,7 +22,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<BaseButton
|
||||
<BasePrimaryButton
|
||||
as="input"
|
||||
type="button"
|
||||
class="rounded-lg"
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<div class="flex items-center">
|
||||
<FormLabel :for="id">
|
||||
{{ label }}
|
||||
</FormLabel>
|
||||
<BaseTooltip v-if="description" :text="description">
|
||||
<IconsInfo class="size-4" />
|
||||
</BaseTooltip>
|
||||
</div>
|
||||
<BaseInput :id="id" v-model.number="data" :name="id" type="number" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
defineProps<{ id: string; label: string; description?: string }>();
|
||||
|
||||
const data = defineModel<number | null>({
|
||||
set(value) {
|
||||
const temp = value ?? null;
|
||||
if (temp === 0) {
|
||||
return null;
|
||||
}
|
||||
if ((temp as string | null) === '') {
|
||||
return null;
|
||||
}
|
||||
return temp;
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<input
|
||||
:value="label"
|
||||
:type="type ?? 'button'"
|
||||
class="col-span-2 rounded-lg border-2 border-red-800 bg-red-800 py-2 text-white hover:border-red-600 hover:bg-red-600 focus:border-red-800 focus:outline-0 focus:ring-0"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { InputTypeHTMLAttribute } from 'vue';
|
||||
|
||||
defineProps<{
|
||||
label: string;
|
||||
type?: InputTypeHTMLAttribute;
|
||||
}>();
|
||||
</script>
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="flex flex-shrink-0 space-x-1 md:block">
|
||||
<div class="flex flex-shrink-0 items-center space-x-2">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
:to="`/admin/${item.id}`"
|
||||
active-class="bg-red-800 rounded"
|
||||
>
|
||||
<BaseButton
|
||||
<BaseSecondaryButton
|
||||
as="span"
|
||||
class="w-full cursor-pointer rounded p-2 font-medium transition-colors duration-200 hover:bg-red-800 dark:text-neutral-200"
|
||||
>
|
||||
{{ item.name }}
|
||||
</BaseButton>
|
||||
</BaseSecondaryButton>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
@@ -44,18 +44,19 @@ const { t } = useI18n();
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const menuItems = [
|
||||
const menuItems = computed(() => [
|
||||
{ id: 'general', name: t('pages.admin.general') },
|
||||
{ id: 'config', name: t('pages.admin.config') },
|
||||
{ id: 'interface', name: t('pages.admin.interface') },
|
||||
{ id: 'hooks', name: t('pages.admin.hooks') },
|
||||
];
|
||||
]);
|
||||
|
||||
const defaultItem = { id: '', name: t('pages.admin.panel') };
|
||||
|
||||
const activeMenuItem = computed(() => {
|
||||
return (
|
||||
menuItems.find((item) => route.path === `/admin/${item.id}`) ?? defaultItem
|
||||
menuItems.value.find((item) => route.path === `/admin/${item.id}`) ??
|
||||
defaultItem
|
||||
);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -47,16 +47,73 @@
|
||||
:description="$t('admin.config.persistentKeepaliveDesc')"
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup v-if="globalStore.information?.isAwg">
|
||||
<FormHeading>{{ $t('awg.obfuscationParameters') }}</FormHeading>
|
||||
|
||||
<FormNullNumberField
|
||||
id="jC"
|
||||
v-model="data.defaultJC"
|
||||
:label="$t('awg.jCLabel')"
|
||||
:description="$t('awg.jCDescription')"
|
||||
/>
|
||||
<FormNullNumberField
|
||||
id="jMin"
|
||||
v-model="data.defaultJMin"
|
||||
:label="$t('awg.jMinLabel')"
|
||||
:description="$t('awg.jMinDescription')"
|
||||
/>
|
||||
<FormNullNumberField
|
||||
id="jMax"
|
||||
v-model="data.defaultJMax"
|
||||
:label="$t('awg.jMaxLabel')"
|
||||
:description="$t('awg.jMaxDescription')"
|
||||
/>
|
||||
|
||||
<div class="col-span-full text-sm">* {{ $t('awg.mtuNote') }}</div>
|
||||
|
||||
<FormNullTextField
|
||||
id="i1"
|
||||
v-model="data.defaultI1"
|
||||
:label="$t('awg.i1Label')"
|
||||
:description="$t('awg.i1Description')"
|
||||
/>
|
||||
<FormNullTextField
|
||||
id="i2"
|
||||
v-model="data.defaultI2"
|
||||
:label="$t('awg.i2Label')"
|
||||
:description="$t('awg.i2Description')"
|
||||
/>
|
||||
<FormNullTextField
|
||||
id="i3"
|
||||
v-model="data.defaultI3"
|
||||
:label="$t('awg.i3Label')"
|
||||
:description="$t('awg.i3Description')"
|
||||
/>
|
||||
<FormNullTextField
|
||||
id="i4"
|
||||
v-model="data.defaultI4"
|
||||
:label="$t('awg.i4Label')"
|
||||
:description="$t('awg.i4Description')"
|
||||
/>
|
||||
<FormNullTextField
|
||||
id="i5"
|
||||
v-model="data.defaultI5"
|
||||
:label="$t('awg.i5Label')"
|
||||
:description="$t('awg.i5Description')"
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<FormHeading>{{ $t('form.actions') }}</FormHeading>
|
||||
<FormActionField type="submit" :label="$t('form.save')" />
|
||||
<FormActionField :label="$t('form.revert')" @click="revert" />
|
||||
<FormPrimaryActionField type="submit" :label="$t('form.save')" />
|
||||
<FormSecondaryActionField :label="$t('form.revert')" @click="revert" />
|
||||
</FormGroup>
|
||||
</FormElement>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const globalStore = useGlobalStore();
|
||||
|
||||
const { data: _data, refresh } = await useFetch(`/api/admin/userconfig`, {
|
||||
method: 'get',
|
||||
});
|
||||
|
||||
@@ -32,8 +32,8 @@
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<FormHeading>{{ $t('form.actions') }}</FormHeading>
|
||||
<FormActionField type="submit" :label="$t('form.save')" />
|
||||
<FormActionField :label="$t('form.revert')" @click="revert" />
|
||||
<FormPrimaryActionField type="submit" :label="$t('form.save')" />
|
||||
<FormSecondaryActionField :label="$t('form.revert')" @click="revert" />
|
||||
</FormGroup>
|
||||
</FormElement>
|
||||
</main>
|
||||
|
||||
@@ -25,8 +25,8 @@
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<FormHeading>{{ $t('form.actions') }}</FormHeading>
|
||||
<FormActionField type="submit" :label="$t('form.save')" />
|
||||
<FormActionField :label="$t('form.revert')" @click="revert" />
|
||||
<FormPrimaryActionField type="submit" :label="$t('form.save')" />
|
||||
<FormSecondaryActionField :label="$t('form.revert')" @click="revert" />
|
||||
</FormGroup>
|
||||
</FormElement>
|
||||
</main>
|
||||
|
||||
@@ -21,17 +21,120 @@
|
||||
:description="$t('admin.interface.deviceDesc')"
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup v-if="globalStore.information?.isAwg">
|
||||
<FormHeading>{{ $t('awg.obfuscationParameters') }}</FormHeading>
|
||||
|
||||
<FormNullNumberField
|
||||
id="jC"
|
||||
v-model="data.jC"
|
||||
:label="$t('awg.jCLabel')"
|
||||
:description="$t('awg.jCDescription')"
|
||||
/>
|
||||
<FormNullNumberField
|
||||
id="jMin"
|
||||
v-model="data.jMin"
|
||||
:label="$t('awg.jMinLabel')"
|
||||
:description="$t('awg.jMinDescription')"
|
||||
/>
|
||||
<FormNullNumberField
|
||||
id="jMax"
|
||||
v-model="data.jMax"
|
||||
:label="$t('awg.jMaxLabel')"
|
||||
:description="$t('awg.jMaxDescription')"
|
||||
/>
|
||||
<FormNullNumberField
|
||||
id="s1"
|
||||
v-model="data.s1"
|
||||
:label="$t('awg.s1Label')"
|
||||
:description="$t('awg.s1Description')"
|
||||
/>
|
||||
<FormNullNumberField
|
||||
id="s2"
|
||||
v-model="data.s2"
|
||||
:label="$t('awg.s2Label')"
|
||||
:description="$t('awg.s2Description')"
|
||||
/>
|
||||
|
||||
<div class="col-span-full text-sm">* {{ $t('awg.mtuNote') }}</div>
|
||||
|
||||
<FormNullNumberField
|
||||
id="s3"
|
||||
v-model="data.s3"
|
||||
:label="$t('awg.s3Label')"
|
||||
:description="$t('awg.s3Description')"
|
||||
/>
|
||||
<FormNullNumberField
|
||||
id="s4"
|
||||
v-model="data.s4"
|
||||
:label="$t('awg.s4Label')"
|
||||
:description="$t('awg.s4Description')"
|
||||
/>
|
||||
<FormNullTextField
|
||||
id="i1"
|
||||
v-model="data.i1"
|
||||
:label="$t('awg.i1Label')"
|
||||
:description="$t('awg.i1Description')"
|
||||
/>
|
||||
<FormNullTextField
|
||||
id="i2"
|
||||
v-model="data.i2"
|
||||
:label="$t('awg.i2Label')"
|
||||
:description="$t('awg.i2Description')"
|
||||
/>
|
||||
<FormNullTextField
|
||||
id="i3"
|
||||
v-model="data.i3"
|
||||
:label="$t('awg.i3Label')"
|
||||
:description="$t('awg.i3Description')"
|
||||
/>
|
||||
<FormNullTextField
|
||||
id="i4"
|
||||
v-model="data.i4"
|
||||
:label="$t('awg.i4Label')"
|
||||
:description="$t('awg.i4Description')"
|
||||
/>
|
||||
<FormNullTextField
|
||||
id="i5"
|
||||
v-model="data.i5"
|
||||
:label="$t('awg.i5Label')"
|
||||
:description="$t('awg.i5Description')"
|
||||
/>
|
||||
<FormNullNumberField
|
||||
id="h1"
|
||||
v-model="data.h1"
|
||||
:label="$t('awg.h1Label')"
|
||||
:description="$t('awg.h1Description')"
|
||||
/>
|
||||
<FormNullNumberField
|
||||
id="h2"
|
||||
v-model="data.h2"
|
||||
:label="$t('awg.h2Label')"
|
||||
:description="$t('awg.h2Description')"
|
||||
/>
|
||||
<FormNullNumberField
|
||||
id="h3"
|
||||
v-model="data.h3"
|
||||
:label="$t('awg.h3Label')"
|
||||
:description="$t('awg.h3Description')"
|
||||
/>
|
||||
<FormNullNumberField
|
||||
id="h4"
|
||||
v-model="data.h4"
|
||||
:label="$t('awg.h4Label')"
|
||||
:description="$t('awg.h4Description')"
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<FormHeading>{{ $t('form.actions') }}</FormHeading>
|
||||
<FormActionField type="submit" :label="$t('form.save')" />
|
||||
<FormActionField :label="$t('form.revert')" @click="revert" />
|
||||
<FormPrimaryActionField type="submit" :label="$t('form.save')" />
|
||||
<FormSecondaryActionField :label="$t('form.revert')" @click="revert" />
|
||||
<AdminCidrDialog
|
||||
trigger-class="col-span-2"
|
||||
:ipv4-cidr="data.ipv4Cidr"
|
||||
:ipv6-cidr="data.ipv6Cidr"
|
||||
@change="changeCidr"
|
||||
>
|
||||
<FormActionField
|
||||
<FormSecondaryActionField
|
||||
:label="$t('admin.interface.changeCidr')"
|
||||
class="w-full"
|
||||
tabindex="-1"
|
||||
@@ -41,7 +144,7 @@
|
||||
trigger-class="col-span-2"
|
||||
@restart="restartInterface"
|
||||
>
|
||||
<FormActionField
|
||||
<FormSecondaryActionField
|
||||
:label="$t('admin.interface.restart')"
|
||||
class="w-full"
|
||||
tabindex="-1"
|
||||
@@ -53,6 +156,8 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const globalStore = useGlobalStore();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const { data: _data, refresh } = await useFetch(`/api/admin/interface`, {
|
||||
|
||||
@@ -39,6 +39,12 @@
|
||||
v-model="data.ipv6Address"
|
||||
label="IPv6"
|
||||
/>
|
||||
<FormInfoField
|
||||
id="endpoint"
|
||||
:data="data.endpoint ?? $t('client.notConnected')"
|
||||
:label="$t('client.endpoint')"
|
||||
:description="$t('client.endpointDesc')"
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<FormHeading :description="$t('client.allowedIpsDesc')">
|
||||
@@ -76,6 +82,61 @@
|
||||
:label="$t('general.persistentKeepalive')"
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup v-if="globalStore.information?.isAwg">
|
||||
<FormHeading>{{ $t('awg.obfuscationParameters') }}</FormHeading>
|
||||
|
||||
<FormNullNumberField
|
||||
id="jC"
|
||||
v-model="data.jC"
|
||||
:label="$t('awg.jCLabel')"
|
||||
:description="$t('awg.jCDescription')"
|
||||
/>
|
||||
<FormNullNumberField
|
||||
id="Jmin"
|
||||
v-model="data.jMin"
|
||||
:label="$t('awg.jMinLabel')"
|
||||
:description="$t('awg.jMinDescription')"
|
||||
/>
|
||||
<FormNullNumberField
|
||||
id="Jmax"
|
||||
v-model="data.jMax"
|
||||
:label="$t('awg.jMaxLabel')"
|
||||
:description="$t('awg.jMaxDescription')"
|
||||
/>
|
||||
|
||||
<div class="col-span-full text-sm">* {{ $t('awg.mtuNote') }}</div>
|
||||
|
||||
<FormNullTextField
|
||||
id="i1"
|
||||
v-model="data.i1"
|
||||
:label="$t('awg.i1Label')"
|
||||
:description="$t('awg.i1Description')"
|
||||
/>
|
||||
<FormNullTextField
|
||||
id="i2"
|
||||
v-model="data.i2"
|
||||
:label="$t('awg.i2Label')"
|
||||
:description="$t('awg.i2Description')"
|
||||
/>
|
||||
<FormNullTextField
|
||||
id="i3"
|
||||
v-model="data.i3"
|
||||
:label="$t('awg.i3Label')"
|
||||
:description="$t('awg.i3Description')"
|
||||
/>
|
||||
<FormNullTextField
|
||||
id="i4"
|
||||
v-model="data.i4"
|
||||
:label="$t('awg.i4Label')"
|
||||
:description="$t('awg.i4Description')"
|
||||
/>
|
||||
<FormNullTextField
|
||||
id="i5"
|
||||
v-model="data.i5"
|
||||
:label="$t('awg.i5Label')"
|
||||
:description="$t('awg.i5Description')"
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<FormHeading :description="$t('client.hooksDescription')">
|
||||
{{ $t('client.hooks') }}
|
||||
@@ -107,14 +168,17 @@
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<FormHeading>{{ $t('form.actions') }}</FormHeading>
|
||||
<FormActionField type="submit" :label="$t('form.save')" />
|
||||
<FormActionField :label="$t('form.revert')" @click="revert" />
|
||||
<FormPrimaryActionField type="submit" :label="$t('form.save')" />
|
||||
<FormSecondaryActionField
|
||||
:label="$t('form.revert')"
|
||||
@click="revert"
|
||||
/>
|
||||
<ClientsDeleteDialog
|
||||
trigger-class="col-span-2"
|
||||
:client-name="data.name"
|
||||
@delete="deleteClient"
|
||||
>
|
||||
<FormActionField
|
||||
<FormSecondaryActionField
|
||||
label="Delete"
|
||||
class="w-full"
|
||||
type="button"
|
||||
@@ -122,6 +186,18 @@
|
||||
as="span"
|
||||
/>
|
||||
</ClientsDeleteDialog>
|
||||
<ClientsConfigDialog
|
||||
trigger-class="col-span-2"
|
||||
:client-id="data.id"
|
||||
>
|
||||
<FormSecondaryActionField
|
||||
:label="$t('client.viewConfig')"
|
||||
class="w-full"
|
||||
type="button"
|
||||
tabindex="-1"
|
||||
as="span"
|
||||
/>
|
||||
</ClientsConfigDialog>
|
||||
</FormGroup>
|
||||
</FormElement>
|
||||
</PanelBody>
|
||||
@@ -131,6 +207,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
const authStore = useAuthStore();
|
||||
const globalStore = useGlobalStore();
|
||||
authStore.update();
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<PanelHead>
|
||||
<PanelHeadTitle :text="$t('pages.clients')" />
|
||||
<PanelHeadBoat>
|
||||
<ClientsSearch />
|
||||
<ClientsSort />
|
||||
<ClientsNew />
|
||||
</PanelHeadBoat>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
v-model="email"
|
||||
:label="$t('user.email')"
|
||||
/>
|
||||
<FormActionField type="submit" :label="$t('form.save')" />
|
||||
<FormSecondaryActionField type="submit" :label="$t('form.save')" />
|
||||
</FormGroup>
|
||||
</FormElement>
|
||||
<FormElement @submit.prevent="updatePassword">
|
||||
@@ -42,7 +42,7 @@
|
||||
autocomplete="new-password"
|
||||
:label="$t('general.confirmPassword')"
|
||||
/>
|
||||
<FormActionField
|
||||
<FormSecondaryActionField
|
||||
type="submit"
|
||||
:label="$t('general.updatePassword')"
|
||||
/>
|
||||
@@ -55,7 +55,10 @@
|
||||
v-if="!authStore.userData?.totpVerified && !twofa"
|
||||
class="col-span-2 flex flex-col"
|
||||
>
|
||||
<FormActionField :label="$t('me.enable2fa')" @click="setup2fa" />
|
||||
<FormSecondaryActionField
|
||||
:label="$t('me.enable2fa')"
|
||||
@click="setup2fa"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-if="!authStore.userData?.totpVerified && twofa"
|
||||
@@ -81,7 +84,7 @@
|
||||
v-model="code"
|
||||
:label="$t('general.2faCode')"
|
||||
/>
|
||||
<FormActionField
|
||||
<FormSecondaryActionField
|
||||
:label="$t('me.enable2fa')"
|
||||
@click="enable2fa"
|
||||
/>
|
||||
@@ -101,7 +104,7 @@
|
||||
type="password"
|
||||
autocomplete="current-password"
|
||||
/>
|
||||
<FormActionField
|
||||
<FormSecondaryActionField
|
||||
:label="$t('me.disable2fa')"
|
||||
@click="disable2fa"
|
||||
/>
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
{{ $t('setup.welcomeDesc') }}
|
||||
</p>
|
||||
<NuxtLink to="/setup/2" class="mt-8">
|
||||
<BaseButton as="span">{{ $t('general.continue') }}</BaseButton>
|
||||
<BasePrimaryButton as="span">
|
||||
{{ $t('general.continue') }}
|
||||
</BasePrimaryButton>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -29,7 +29,9 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-4 flex justify-center">
|
||||
<BaseButton @click="submit">{{ $t('setup.createAccount') }}</BaseButton>
|
||||
<BasePrimaryButton @click="submit">
|
||||
{{ $t('setup.createAccount') }}
|
||||
</BasePrimaryButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
</p>
|
||||
<div class="mt-4 flex justify-center gap-3">
|
||||
<NuxtLink to="/setup/4" class="w-20">
|
||||
<BaseButton as="span" class="w-full justify-center">
|
||||
<BasePrimaryButton as="span" class="w-full justify-center">
|
||||
{{ $t('general.no') }}
|
||||
</BaseButton>
|
||||
</BasePrimaryButton>
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/setup/migrate" class="w-20">
|
||||
<BaseButton as="span" class="w-full justify-center">
|
||||
<BaseSecondaryButton as="span" class="w-full justify-center">
|
||||
{{ $t('general.yes') }}
|
||||
</BaseButton>
|
||||
</BaseSecondaryButton>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -23,7 +23,9 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-4 flex justify-center">
|
||||
<BaseButton @click="submit">{{ $t('general.continue') }}</BaseButton>
|
||||
<BasePrimaryButton @click="submit">
|
||||
{{ $t('general.continue') }}
|
||||
</BasePrimaryButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
<input id="migration" type="file" @change="onChangeFile" />
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<BaseButton @click="submit">{{ $t('setup.upload') }}</BaseButton>
|
||||
<BasePrimaryButton @click="submit">
|
||||
{{ $t('setup.upload') }}
|
||||
</BasePrimaryButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="flex flex-col items-center">
|
||||
<p>{{ $t('setup.successful') }}</p>
|
||||
<NuxtLink to="/login" class="mt-4">
|
||||
<BaseButton as="span">{{ $t('login.signIn') }}</BaseButton>
|
||||
<BasePrimaryButton as="span">{{ $t('login.signIn') }}</BasePrimaryButton>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import VueApexCharts from 'vue3-apexcharts';
|
||||
|
||||
export default defineNuxtPlugin((nuxtApp) => {
|
||||
nuxtApp.vueApp.use(VueApexCharts);
|
||||
// https://github.com/apexcharts/vue3-apexcharts/issues/141
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
nuxtApp.vueApp.use(VueApexCharts as any);
|
||||
});
|
||||
|
||||
@@ -31,8 +31,13 @@ export const useClientsStore = defineStore('Clients', () => {
|
||||
const clients = ref<null | LocalClient[]>(null);
|
||||
const clientsPersist = ref<Record<string, ClientPersist>>({});
|
||||
|
||||
const searchParams = ref({
|
||||
filter: undefined as string | undefined,
|
||||
});
|
||||
|
||||
const { data: _clients, refresh: _refresh } = useFetch('/api/client', {
|
||||
method: 'get',
|
||||
params: searchParams,
|
||||
});
|
||||
|
||||
// TODO: rewrite
|
||||
@@ -120,6 +125,7 @@ export const useClientsStore = defineStore('Clients', () => {
|
||||
};
|
||||
});
|
||||
|
||||
// TODO: move sort to backend
|
||||
if (transformedClients !== undefined) {
|
||||
transformedClients = sortByProperty(
|
||||
transformedClients,
|
||||
@@ -130,5 +136,11 @@ export const useClientsStore = defineStore('Clients', () => {
|
||||
|
||||
clients.value = transformedClients ?? null;
|
||||
}
|
||||
return { clients, clientsPersist, refresh, _clients };
|
||||
|
||||
function setSearchQuery(filter: string) {
|
||||
clients.value = null;
|
||||
searchParams.value.filter = filter || undefined;
|
||||
}
|
||||
|
||||
return { clients, clientsPersist, refresh, _clients, setSearchQuery };
|
||||
});
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
// @ts-check
|
||||
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import esbuild from 'esbuild';
|
||||
|
||||
esbuild.build({
|
||||
entryPoints: [fileURLToPath(new URL('./index.ts', import.meta.url))],
|
||||
bundle: true,
|
||||
outfile: fileURLToPath(new URL('../.output/server/cli.mjs', import.meta.url)),
|
||||
platform: 'node',
|
||||
format: 'esm',
|
||||
plugins: [
|
||||
{
|
||||
name: 'make-all-packages-external',
|
||||
setup(build) {
|
||||
let filter = /^[^./]|^\.[^./]|^\.\.[^/]/; // Must not start with "/" or "./" or "../"
|
||||
build.onResolve({ filter }, (args) => ({
|
||||
path: args.path,
|
||||
external: true,
|
||||
}));
|
||||
},
|
||||
},
|
||||
],
|
||||
logLevel: 'info',
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
node /app/server/cli.mjs "$@"
|
||||
@@ -0,0 +1,92 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
// ! Auto Imports are not supported in this file
|
||||
|
||||
import { drizzle } from 'drizzle-orm/libsql';
|
||||
import { createClient } from '@libsql/client';
|
||||
import { defineCommand, runMain } from 'citty';
|
||||
import { consola } from 'consola';
|
||||
import { eq } from 'drizzle-orm';
|
||||
|
||||
import packageJson from '../package.json';
|
||||
import * as schema from '../server/database/schema';
|
||||
import { hashPassword } from '../server/utils/password';
|
||||
|
||||
const client = createClient({ url: 'file:/etc/wireguard/wg-easy.db' });
|
||||
const db = drizzle({ client, schema });
|
||||
|
||||
const dbAdminReset = defineCommand({
|
||||
meta: {
|
||||
name: 'db:admin:reset',
|
||||
description: 'Reset the admin user',
|
||||
},
|
||||
args: {
|
||||
password: {
|
||||
type: 'string',
|
||||
description: 'New password for the admin user',
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
async run(ctx) {
|
||||
let password = ctx.args.password || undefined;
|
||||
if (!password) {
|
||||
password = await consola.prompt('Please enter a new password:', {
|
||||
type: 'text',
|
||||
});
|
||||
}
|
||||
if (!password) {
|
||||
consola.error('Password is required');
|
||||
return;
|
||||
}
|
||||
if (password.length < 12) {
|
||||
consola.error('Password must be at least 12 characters long');
|
||||
return;
|
||||
}
|
||||
console.info('Setting new password for admin user...');
|
||||
const hash = await hashPassword(password);
|
||||
|
||||
const user = await db.transaction(async (tx) => {
|
||||
const user = await tx
|
||||
.select()
|
||||
.from(schema.user)
|
||||
.where(eq(schema.user.id, 1))
|
||||
.get();
|
||||
|
||||
if (!user) {
|
||||
consola.error('Admin user not found');
|
||||
return;
|
||||
}
|
||||
|
||||
await tx
|
||||
.update(schema.user)
|
||||
.set({
|
||||
password: hash,
|
||||
})
|
||||
.where(eq(schema.user.id, 1));
|
||||
|
||||
return user;
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
consola.error('Failed to update admin user');
|
||||
return;
|
||||
}
|
||||
|
||||
consola.success(
|
||||
`Successfully updated admin user ${user.id} (${user.username})`
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
const main = defineCommand({
|
||||
meta: {
|
||||
name: 'wg-easy',
|
||||
version: packageJson.version,
|
||||
description: 'Command Line Interface',
|
||||
},
|
||||
subCommands: {
|
||||
'db:admin:reset': dbAdminReset,
|
||||
},
|
||||
});
|
||||
|
||||
runMain(main);
|
||||
@@ -1,9 +1,39 @@
|
||||
import en from './locales/en.json';
|
||||
import pl from './locales/pl.json';
|
||||
import uk from './locales/uk.json';
|
||||
import fr from './locales/fr.json';
|
||||
import de from './locales/de.json';
|
||||
import it from './locales/it.json';
|
||||
import ru from './locales/ru.json';
|
||||
import zhhk from './locales/zh-HK.json';
|
||||
import zhcn from './locales/zh-CN.json';
|
||||
import zhtw from './locales/zh-TW.json';
|
||||
import ko from './locales/ko.json';
|
||||
import es from './locales/es.json';
|
||||
import ptbr from './locales/pt-BR.json';
|
||||
import tr from './locales/tr.json';
|
||||
import bn from './locales/bn.json';
|
||||
import id from './locales/id.json';
|
||||
|
||||
export default defineI18nConfig(() => ({
|
||||
legacy: false,
|
||||
fallbackLocale: 'en',
|
||||
messages: {
|
||||
en,
|
||||
pl,
|
||||
uk,
|
||||
fr,
|
||||
de,
|
||||
it,
|
||||
ru,
|
||||
'zh-HK': zhhk,
|
||||
'zh-CN': zhcn,
|
||||
'zh-TW': zhtw,
|
||||
ko,
|
||||
es,
|
||||
'pt-BR': ptbr,
|
||||
tr,
|
||||
bn,
|
||||
id,
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
{
|
||||
"pages": {
|
||||
"me": "অ্যাকাউন্ট",
|
||||
"clients": "ক্লায়েন্টস",
|
||||
"admin": {
|
||||
"panel": "অ্যাডমিন প্যানেল",
|
||||
"general": "সাধারণ",
|
||||
"config": "কনফিগ",
|
||||
"interface": "ইন্টারফেস",
|
||||
"hooks": "হুকস"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"email": "ই-মেইল"
|
||||
},
|
||||
"me": {
|
||||
"currentPassword": "বর্তমান পাসওয়ার্ড",
|
||||
"enable2fa": "দুই স্তরের যাচাইকরণ চালু করুন",
|
||||
"enable2faDesc": "আপনার অথেন্টিকেটর অ্যাপ দিয়ে QR কোড স্ক্যান করুন অথবা কীটি ম্যানুয়ালি প্রবেশ করুন।",
|
||||
"2faKey": "TOTP কী",
|
||||
"2faCodeDesc": "আপনার অথেন্টিকেটর অ্যাপ থেকে কোডটি প্রবেশ করুন।",
|
||||
"disable2fa": "দুই স্তরের যাচাইকরণ বন্ধ করুন",
|
||||
"disable2faDesc": "দুই স্তরের যাচাইকরণ বন্ধ করতে আপনার পাসওয়ার্ড দিন।"
|
||||
},
|
||||
"general": {
|
||||
"name": "নাম",
|
||||
"username": "ইউজারনেম",
|
||||
"password": "পাসওয়ার্ড",
|
||||
"newPassword": "নতুন পাসওয়ার্ড",
|
||||
"updatePassword": "পাসওয়ার্ড আপডেট করুন",
|
||||
"mtu": "MTU",
|
||||
"allowedIps": "অনুমোদিত IPs",
|
||||
"dns": "DNS",
|
||||
"persistentKeepalive": "স্থায়ী কিপ-এলাইভ",
|
||||
"logout": "লগআউট",
|
||||
"continue": "চালিয়ে যান",
|
||||
"host": "হোস্ট",
|
||||
"port": "পোর্ট",
|
||||
"yes": "হ্যাঁ",
|
||||
"no": "না",
|
||||
"confirmPassword": "পাসওয়ার্ড নিশ্চিত করুন",
|
||||
"loading": "লোড হচ্ছে...",
|
||||
"2fa": "দুই স্তরের যাচাইকরণ",
|
||||
"2faCode": "TOTP কোড"
|
||||
},
|
||||
"setup": {
|
||||
"welcome": "wg-easy এর প্রথম সেটআপে স্বাগতম",
|
||||
"welcomeDesc": "আপনি WireGuard ইনস্টল ও পরিচালনার সবচেয়ে সহজ উপায় পেয়েছেন",
|
||||
"existingSetup": "আপনার কি পূর্ববর্তী সেটআপ আছে?",
|
||||
"createAdminDesc": "প্রথমে একটি অ্যাডমিন ইউজারনেম এবং শক্তিশালী পাসওয়ার্ড দিন। এই তথ্য অ্যাডমিন প্যানেলে লগইন করতে ব্যবহৃত হবে।",
|
||||
"setupConfigDesc": "হোস্ট এবং পোর্ট তথ্য দিন। এটি ক্লায়েন্ট কনফিগারেশনের জন্য ব্যবহৃত হবে যখন WireGuard তাদের ডিভাইসে সেটআপ করা হবে।",
|
||||
"setupMigrationDesc": "আপনার পূর্ববর্তী wg-easy থেকে নতুন সেটআপে ডেটা স্থানান্তর করতে চাইলে ব্যাকআপ ফাইল দিন।",
|
||||
"upload": "আপলোড",
|
||||
"migration": "ব্যাকআপ পুনরুদ্ধার করুন:",
|
||||
"createAccount": "অ্যাকাউন্ট তৈরি করুন",
|
||||
"successful": "সেটআপ সফল হয়েছে",
|
||||
"hostDesc": "পাবলিক হোস্টনেম ক্লায়েন্টরা সংযোগ করবে",
|
||||
"portDesc": "পাবলিক UDP পোর্ট ক্লায়েন্টরা সংযোগ করবে এবং WireGuard শুনবে"
|
||||
},
|
||||
"update": {
|
||||
"updateAvailable": "একটি আপডেট উপলব্ধ!",
|
||||
"update": "আপডেট"
|
||||
},
|
||||
"theme": {
|
||||
"dark": "ডার্ক থিম",
|
||||
"light": "লাইট থিম",
|
||||
"system": "সিস্টেম থিম"
|
||||
},
|
||||
"layout": {
|
||||
"toggleCharts": "চার্ট দেখান/লুকান",
|
||||
"donate": "ডোনেট"
|
||||
},
|
||||
"login": {
|
||||
"signIn": "সাইন ইন",
|
||||
"rememberMe": "মনে রাখুন",
|
||||
"rememberMeDesc": "ব্রাউজার বন্ধ করার পরেও লগইন থাকুন",
|
||||
"insecure": "অনিরাপদ সংযোগে লগইন করা যাবে না। HTTPS ব্যবহার করুন।",
|
||||
"2faRequired": "দুই স্তরের যাচাইকরণ প্রয়োজন",
|
||||
"2faWrong": "দুই স্তরের যাচাইকরণ ভুল"
|
||||
},
|
||||
"client": {
|
||||
"empty": "এখনো কোনো ক্লায়েন্ট নেই।",
|
||||
"newShort": "নতুন",
|
||||
"sort": "সাজান",
|
||||
"create": "ক্লায়েন্ট তৈরি করুন",
|
||||
"created": "ক্লায়েন্ট তৈরি হয়েছে",
|
||||
"new": "নতুন ক্লায়েন্ট",
|
||||
"name": "নাম",
|
||||
"expireDate": "মেয়াদ শেষের তারিখ",
|
||||
"expireDateDesc": "যেদিন ক্লায়েন্ট নিষ্ক্রিয় হবে। স্থায়ী করতে ফাঁকা রাখুন",
|
||||
"deleteClient": "ক্লায়েন্ট মুছে ফেলুন",
|
||||
"deleteDialog1": "আপনি কি নিশ্চিতভাবে মুছে ফেলতে চান",
|
||||
"deleteDialog2": "এই কাজটি পূর্বাবস্থায় ফেরানো যাবে না।",
|
||||
"enabled": "সক্রিয়",
|
||||
"address": "ঠিকানা",
|
||||
"serverAllowedIps": "সার্ভার অনুমোদিত IPs",
|
||||
"otlDesc": "সংক্ষিপ্ত একবারের লিঙ্ক তৈরি করুন",
|
||||
"permanent": "স্থায়ী",
|
||||
"createdOn": "তৈরি হয়েছে",
|
||||
"lastSeen": "শেষ দেখা হয়েছে",
|
||||
"totalDownload": "মোট ডাউনলোড: ",
|
||||
"totalUpload": "মোট আপলোড: ",
|
||||
"newClient": "নতুন ক্লায়েন্ট",
|
||||
"disableClient": "ক্লায়েন্ট নিষ্ক্রিয় করুন",
|
||||
"enableClient": "ক্লায়েন্ট সক্রিয় করুন",
|
||||
"noPrivKey": "এই ক্লায়েন্টের কোনো ব্যক্তিগত কী নেই। কনফিগারেশন তৈরি করা যাবে না।",
|
||||
"showQR": "QR কোড দেখান",
|
||||
"downloadConfig": "কনফিগারেশন ডাউনলোড করুন",
|
||||
"allowedIpsDesc": "কোন IPs VPN এর মাধ্যমে রাউট হবে (গ্লোবাল কনফিগ ওভাররাইড করবে)",
|
||||
"serverAllowedIpsDesc": "কোন IPs সার্ভার ক্লায়েন্টে রাউট করবে",
|
||||
"mtuDesc": "VPN টানেলের জন্য সর্বাধিক প্যাকেট সাইজ নির্ধারণ করে",
|
||||
"persistentKeepaliveDesc": "কিপ-এলাইভ প্যাকেটের জন্য ইন্টারভাল (সেকেন্ডে)। 0 হলে বন্ধ",
|
||||
"hooks": "হুকস",
|
||||
"hooksDescription": "হুকস শুধুমাত্র wg-quick এর সাথে কাজ করে",
|
||||
"hooksLeaveEmpty": "শুধুমাত্র wg-quick এর জন্য। অন্যথায় ফাঁকা রাখুন",
|
||||
"dnsDesc": "DNS সার্ভার ক্লায়েন্টরা ব্যবহার করবে (গ্লোবাল কনফিগ ওভাররাইড করবে)",
|
||||
"notConnected": "ক্লায়েন্ট সংযুক্ত নয়",
|
||||
"endpoint": "এন্ডপয়েন্ট",
|
||||
"endpointDesc": "ক্লায়েন্টের IP যেখান থেকে WireGuard সংযোগ স্থাপন করা হয়েছে"
|
||||
},
|
||||
"dialog": {
|
||||
"change": "পরিবর্তন করুন",
|
||||
"cancel": "বাতিল করুন",
|
||||
"create": "তৈরি করুন"
|
||||
},
|
||||
"toast": {
|
||||
"success": "সফলতা",
|
||||
"saved": "সংরক্ষিত",
|
||||
"error": "ত্রুটি"
|
||||
},
|
||||
"form": {
|
||||
"actions": "কর্মসমূহ",
|
||||
"save": "সংরক্ষণ করুন",
|
||||
"revert": "পূর্বাবস্থায় ফেরান",
|
||||
"sectionGeneral": "সাধারণ",
|
||||
"sectionAdvanced": "উন্নত",
|
||||
"noItems": "কোনো আইটেম নেই",
|
||||
"nullNoItems": "কোনো আইটেম নেই। গ্লোবাল কনফিগ ব্যবহার হচ্ছে",
|
||||
"add": "যোগ করুন"
|
||||
},
|
||||
"admin": {
|
||||
"general": {
|
||||
"sessionTimeout": "সেশন টাইমআউট",
|
||||
"sessionTimeoutDesc": "মনে রাখুনের জন্য সেশন সময়কাল (সেকেন্ডে)",
|
||||
"metrics": "মেট্রিক্স",
|
||||
"metricsPassword": "পাসওয়ার্ড",
|
||||
"metricsPasswordDesc": "মেট্রিক্স এন্ডপয়েন্টের জন্য বেয়ারার পাসওয়ার্ড (পাসওয়ার্ড বা argon2 হ্যাশ)",
|
||||
"json": "JSON",
|
||||
"jsonDesc": "JSON ফরম্যাটে মেট্রিক্সের রুট",
|
||||
"prometheus": "Prometheus",
|
||||
"prometheusDesc": "Prometheus মেট্রিক্সের রুট"
|
||||
},
|
||||
"config": {
|
||||
"connection": "সংযোগ",
|
||||
"hostDesc": "পাবলিক হোস্টনেম ক্লায়েন্টরা সংযোগ করবে (কনফিগ বাতিল হবে)",
|
||||
"portDesc": "পাবলিক UDP পোর্ট ক্লায়েন্টরা সংযোগ করবে (কনফিগ বাতিল হবে, আপনি সম্ভবত ইন্টারফেস পোর্টও পরিবর্তন করতে চাইবেন)",
|
||||
"allowedIpsDesc": "অনুমোদিত IPs ক্লায়েন্টরা ব্যবহার করবে (গ্লোবাল কনফিগ)",
|
||||
"dnsDesc": "DNS সার্ভার ক্লায়েন্টরা ব্যবহার করবে (গ্লোবাল কনফিগ)",
|
||||
"mtuDesc": "MTU ক্লায়েন্টরা ব্যবহার করবে (শুধুমাত্র নতুন ক্লায়েন্টের জন্য)",
|
||||
"persistentKeepaliveDesc": "সার্ভারে কিপ-এলাইভ পাঠানোর ইন্টারভাল (সেকেন্ডে)। 0 = বন্ধ (শুধুমাত্র নতুন ক্লায়েন্টের জন্য)",
|
||||
"suggest": "পরামর্শ",
|
||||
"suggestDesc": "হোস্ট ফিল্ডের জন্য একটি IP-ঠিকানা বা হোস্টনেম নির্বাচন করুন"
|
||||
},
|
||||
"interface": {
|
||||
"cidrSuccess": "CIDR পরিবর্তন হয়েছে",
|
||||
"device": "ডিভাইস",
|
||||
"deviceDesc": "ইথারনেট ডিভাইস যার মাধ্যমে wireguard ট্রাফিক ফরওয়ার্ড হবে",
|
||||
"mtuDesc": "WireGuard ব্যবহার করবে এমন MTU",
|
||||
"portDesc": "UDP পোর্ট WireGuard শুনবে (আপনি সম্ভবত কনফিগ পোর্টও পরিবর্তন করতে চাইবেন)",
|
||||
"changeCidr": "CIDR পরিবর্তন করুন",
|
||||
"restart": "ইন্টারফেস রিস্টার্ট করুন",
|
||||
"restartDesc": "WireGuard ইন্টারফেস রিস্টার্ট করুন",
|
||||
"restartWarn": "আপনি কি নিশ্চিতভাবে ইন্টারফেস রিস্টার্ট করতে চান? এতে সব ক্লায়েন্ট সংযোগ বিচ্ছিন্ন হবে।",
|
||||
"restartSuccess": "ইন্টারফেস রিস্টার্ট হয়েছে"
|
||||
},
|
||||
"introText": "অ্যাডমিন প্যানেলে স্বাগতম।\n\nএখানে আপনি সাধারণ সেটিংস, কনফিগারেশন, ইন্টারফেস সেটিংস এবং হুকস পরিচালনা করতে পারবেন।\n\nসাইডবার থেকে একটি বিভাগ নির্বাচন করে শুরু করুন।"
|
||||
},
|
||||
"zod": {
|
||||
"generic": {
|
||||
"required": "{0} আবশ্যক",
|
||||
"validNumber": "{0} একটি বৈধ সংখ্যা হতে হবে",
|
||||
"validString": "{0} একটি বৈধ স্ট্রিং হতে হবে",
|
||||
"validBoolean": "{0} একটি বৈধ বুলিয়ান হতে হবে",
|
||||
"validArray": "{0} একটি বৈধ অ্যারে হতে হবে",
|
||||
"stringMin": "{0} কমপক্ষে {1} অক্ষর হতে হবে",
|
||||
"numberMin": "{0} কমপক্ষে {1} হতে হবে"
|
||||
},
|
||||
"client": {
|
||||
"id": "ক্লায়েন্ট আইডি",
|
||||
"name": "নাম",
|
||||
"expiresAt": "মেয়াদ শেষ",
|
||||
"address4": "IPv4 ঠিকানা",
|
||||
"address6": "IPv6 ঠিকানা",
|
||||
"serverAllowedIps": "সার্ভার অনুমোদিত IPs"
|
||||
},
|
||||
"user": {
|
||||
"username": "ইউজারনেম",
|
||||
"password": "পাসওয়ার্ড",
|
||||
"remember": "মনে রাখুন",
|
||||
"name": "নাম",
|
||||
"email": "ই-মেইল",
|
||||
"emailInvalid": "ই-মেইল একটি বৈধ ই-মেইল হতে হবে",
|
||||
"passwordMatch": "পাসওয়ার্ড মিলতে হবে",
|
||||
"totpEnable": "TOTP চালু",
|
||||
"totpEnableTrue": "TOTP চালু সত্য হতে হবে",
|
||||
"totpCode": "TOTP কোড"
|
||||
},
|
||||
"userConfig": {
|
||||
"host": "হোস্ট"
|
||||
},
|
||||
"general": {
|
||||
"sessionTimeout": "সেশন টাইমআউট",
|
||||
"metricsEnabled": "মেট্রিক্স",
|
||||
"metricsPassword": "মেট্রিক্স পাসওয়ার্ড"
|
||||
},
|
||||
"interface": {
|
||||
"cidr": "CIDR",
|
||||
"device": "ডিভাইস",
|
||||
"cidrValid": "CIDR বৈধ হতে হবে"
|
||||
},
|
||||
"otl": "একবারের লিঙ্ক",
|
||||
"stringMalformed": "স্ট্রিং বিকৃত",
|
||||
"body": "বডি একটি বৈধ অবজেক্ট হতে হবে",
|
||||
"hook": "হুক",
|
||||
"enabled": "সক্রিয়",
|
||||
"mtu": "MTU",
|
||||
"port": "পোর্ট",
|
||||
"persistentKeepalive": "স্থায়ী কিপ-এলাইভ",
|
||||
"address": "IP ঠিকানা",
|
||||
"dns": "DNS",
|
||||
"allowedIps": "অনুমোদিত IPs",
|
||||
"file": "ফাইল"
|
||||
},
|
||||
"hooks": {
|
||||
"preUp": "PreUp",
|
||||
"postUp": "PostUp",
|
||||
"preDown": "PreDown",
|
||||
"postDown": "PostDown"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
{
|
||||
"pages": {
|
||||
"me": "Konto",
|
||||
"clients": "Clients",
|
||||
"admin": {
|
||||
"panel": "Admin-Konsole",
|
||||
"general": "Allgemein",
|
||||
"config": "Konfiguration",
|
||||
"interface": "Oberfläche",
|
||||
"hooks": "Hooks"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"email": "Email"
|
||||
},
|
||||
"me": {
|
||||
"currentPassword": "Aktuelles Passwort",
|
||||
"enable2fa": "Zwei-Faktor-Authentifizierunng aktivieren",
|
||||
"enable2faDesc": "Scannen Sie den QR-Code mit ihrer Authentifizierungs-App oder geben Sie den Schlüssel manuell ein.",
|
||||
"2faKey": "TOTP-Schlüssel",
|
||||
"2faCodeDesc": "Geben Sie den Code aus Ihrer Authentifizierungs-App ein.",
|
||||
"disable2fa": "Zwei-Faktor-Authentifizierung deaktivieren",
|
||||
"disable2faDesc": "Geben Sie Ihr Passwort ein, um die Zwei-Faktor-Authentifizierung zu deaktivieren."
|
||||
},
|
||||
"general": {
|
||||
"name": "Name",
|
||||
"username": "Benutzername",
|
||||
"password": "Passwort",
|
||||
"newPassword": "Neues Passwort",
|
||||
"updatePassword": "Passwort aktualisieren",
|
||||
"mtu": "MTU",
|
||||
"allowedIps": "Erlaubte IP-Adressen",
|
||||
"dns": "DNS",
|
||||
"persistentKeepalive": "Dauerhaftes Keepalive",
|
||||
"logout": "Abmelden",
|
||||
"continue": "Weiter",
|
||||
"host": "Host",
|
||||
"port": "Port",
|
||||
"yes": "Ja",
|
||||
"no": "Nein",
|
||||
"confirmPassword": "Passwort bestätigen",
|
||||
"loading": "Laden...",
|
||||
"2fa": "Zwei-Faktor-Authentifizierung",
|
||||
"2faCode": "TOTP-Code"
|
||||
},
|
||||
"setup": {
|
||||
"welcome": "Willkommen zur Ersteinrichtung von wg-easy",
|
||||
"welcomeDesc": "Das ist der einfachste Weg, um Wireguard auf jedem Linux-Server zu installieren und zu betreiben.",
|
||||
"existingSetup": "Haben Sie eine bestehende Einrichtung?",
|
||||
"createAdminDesc": "Bitte geben Sie zuerst einen Admin-Benutzernamen sowie ein starkes, sicheres Passwort ein. Diese Anmeldedaten benötigen Sie, um sich im Admin-Panel anzumelden.",
|
||||
"setupConfigDesc": "Bitte geben Sie die Host- und Portinformationen ein. Diese werden für die Client-Konfiguration verwendet, wenn Sie WireGuard auf Ihren Geräten einrichten.",
|
||||
"setupMigrationDesc": "Bitte halten Sie die Sicherungsdatei bereit, wenn Sie Ihre Daten von Ihrer vorherigen wg-easy Version auf ihre neue Einrichtung migrieren möchten.",
|
||||
"upload": "Hochladen",
|
||||
"migration": "Sicherung wiederherstellen:",
|
||||
"createAccount": "Konto erstellen",
|
||||
"successful": "Einrichtung erfolgreich",
|
||||
"hostDesc": "Öffentlicher Hostname mit dem sich die Clients verbinden",
|
||||
"portDesc": "Öffentlicher UDP-Port an dem sich die Clients verbinden und auf dem Wireguard läuft"
|
||||
},
|
||||
"update": {
|
||||
"updateAvailable": "Es ist ein neue Aktualisierung verfügbar!",
|
||||
"update": "Aktualisieren"
|
||||
},
|
||||
"theme": {
|
||||
"dark": "Dunkles Thema",
|
||||
"light": "Helles Thema",
|
||||
"system": "System-Thema"
|
||||
},
|
||||
"layout": {
|
||||
"toggleCharts": "Statistiken ein-/ausblenden",
|
||||
"donate": "Spenden"
|
||||
},
|
||||
"login": {
|
||||
"signIn": "Anmelden",
|
||||
"rememberMe": "Angemeldet bleiben",
|
||||
"rememberMeDesc": "Bleiben Sie auch nach dem Schließen des Browsers angemeldet",
|
||||
"insecure": "Sie können sich nicht über eine unsichere Verbindung anmelden. Bitte benutzen Sie HTTPS.",
|
||||
"2faRequired": "Zwei-Faktor-Authentifizierung wird benötigt",
|
||||
"2faWrong": "Zwei-Faktor-Authentifizierung ist fehlgeschlagen"
|
||||
},
|
||||
"client": {
|
||||
"empty": "Es gibt noch keine Clients.",
|
||||
"newShort": "Neu",
|
||||
"sort": "Sortieren",
|
||||
"create": "Client erstellen",
|
||||
"created": "Client wurde erstellt",
|
||||
"new": "Neuer client",
|
||||
"name": "Name",
|
||||
"expireDate": "Ablaufdatum",
|
||||
"expireDateDesc": "Datum, an dem der Client deaktiviert wird. Leer lassen, damit dies nie passiert.",
|
||||
"deleteClient": "Client löschen",
|
||||
"deleteDialog1": "Sind Sie sicher, dass Sie diesen Client löschen wollen",
|
||||
"deleteDialog2": "Diese Aktion kann nicht rückgängig gemacht werden.",
|
||||
"enabled": "Aktiviert",
|
||||
"address": "Adresse",
|
||||
"serverAllowedIps": "serverseitig erlaubte IP-Adressen",
|
||||
"otlDesc": "Einen kurzen Einmal-Link erzeugen",
|
||||
"permanent": "Dauerhaft",
|
||||
"createdOn": "Angelegt am ",
|
||||
"lastSeen": "Zuletzt verbunden am ",
|
||||
"totalDownload": "Gesamt-Download: ",
|
||||
"totalUpload": "Gesamt-Upload: ",
|
||||
"newClient": "Neuer Client",
|
||||
"disableClient": "Client deaktivieren",
|
||||
"enableClient": "Client aktivieren",
|
||||
"noPrivKey": "Dieser Client hat keinen bekannten privaten Schlüssel, weshalb keine Konfiguration angelegt werden kann.",
|
||||
"showQR": "QR-Code anzeigen",
|
||||
"downloadConfig": "Konfiguration herunterladen",
|
||||
"allowedIpsDesc": "Welche IP-Adressen durch das VPN geleitet werden (überschreibt die globale Konfiguration)",
|
||||
"serverAllowedIpsDesc": "Welche IP-Adressen der Server zum Client leiten wird",
|
||||
"mtuDesc": "Setzt die maximale Übertragungsgröße (Paketgröße) für den VPN-Tunnel",
|
||||
"persistentKeepaliveDesc": "Legt das Intervall (in Sekunden) für Keepalive-Pakete fest. 0 deaktiviert es",
|
||||
"hooks": "Hooks",
|
||||
"hooksDescription": "Hooks funktionieren nur mit wg-quick",
|
||||
"hooksLeaveEmpty": "Nur für wg-quick. Sonst leer lassen",
|
||||
"dnsDesc": "DNS-Server, den die Clients benutzen (überschreibt die globale Konfiguration)"
|
||||
},
|
||||
"dialog": {
|
||||
"change": "Ändern",
|
||||
"cancel": "Abbrechen",
|
||||
"create": "Erstellen"
|
||||
},
|
||||
"toast": {
|
||||
"success": "Erfolg",
|
||||
"saved": "Gespeichert",
|
||||
"error": "Fehler"
|
||||
},
|
||||
"form": {
|
||||
"actions": "Aktionen",
|
||||
"save": "Speichern",
|
||||
"revert": "Rückgängig machen",
|
||||
"sectionGeneral": "Allgemein",
|
||||
"sectionAdvanced": "Erweitert",
|
||||
"noItems": "Keine Einträge",
|
||||
"nullNoItems": "Keine Einträge. Die globale Konfiguration wird benutzt",
|
||||
"add": "Hinzufügen"
|
||||
},
|
||||
"admin": {
|
||||
"general": {
|
||||
"sessionTimeout": "Sitzungszeitüberschreitung",
|
||||
"sessionTimeoutDesc": "Sitzungsdauer für \"Angemeldet bleiben\" (Sekunden)",
|
||||
"metrics": "Statistiken",
|
||||
"metricsPassword": "Passwort",
|
||||
"metricsPasswordDesc": "Bearer-Passwort für den Statistik-Endpunkt (Passwort oder Argon2-Hash)",
|
||||
"json": "JSON",
|
||||
"jsonDesc": "Pfad zu den Statistiken als JSON",
|
||||
"prometheus": "Prometheus",
|
||||
"prometheusDesc": "Pfad zu den Prometheus-Statistiken"
|
||||
},
|
||||
"config": {
|
||||
"connection": "Verbindung",
|
||||
"hostDesc": "Öffentlicher Hostname mit dem sich die Clients verbinden (überschreibt die Konfiguration)",
|
||||
"portDesc": "Öffentlicher UDP-Port an dem sich die Clients verbinden (überschreibt die Konfiguration, vermutlich wollen Sie ebenfalls den Port der Weboberfläche ändern)",
|
||||
"allowedIpsDesc": "Erlaubte IP-Adressen, die die Clients nutzen werden (Globale Konfiguration)",
|
||||
"dnsDesc": "DNS-Server, den die Clients nutzen werden (Globale Konfiguration)",
|
||||
"mtuDesc": "MTU, den die Clients benutzen werden (nur für neue Clients)",
|
||||
"persistentKeepaliveDesc": "Intervall in Sekunden, in dem Keepalive-Packete an den Server gesendet werden. 0 = deaktiviert (nur für neue Clients)",
|
||||
"suggest": "Vorschlagen",
|
||||
"suggestDesc": "Wählen Sie eine IP-Adresse oder einen Hostnamen für das Host-Feld aus"
|
||||
},
|
||||
"interface": {
|
||||
"cidrSuccess": "CIDR wurde geändert",
|
||||
"device": "Gerät",
|
||||
"deviceDesc": "Ethernet-Gerät, durch das der Wireguard-Datenverkehr geleitet werden soll",
|
||||
"mtuDesc": "MTU, den WireGuard benutzen wird",
|
||||
"portDesc": "UDP Port, auf dem WireGuard lauschen wird (Sie wollen wahrscheinlich auch den Config-Port ändern)",
|
||||
"changeCidr": "CIDR ändern",
|
||||
"restart": "Interface neu starten",
|
||||
"restartDesc": "Das WireGuard-Interface neu starten",
|
||||
"restartWarn": "Sind Sie sicher, dass Sie das Interface neu starten wollen? Dies wird die Verbindungen aller Clients trennen.",
|
||||
"restartSuccess": "Interface neu gestartet"
|
||||
},
|
||||
"introText": "Willkommen in der Admin-Konsole.\n\nHier können Sie die allgemeinen Einstellungen, die Konfiguration, die Schnittstelleneinstellungen und die Hooks verwalten.\n\nBeginnen Sie, indem Sie einen der Bereiche in der Seitenleiste auswählen."
|
||||
},
|
||||
"zod": {
|
||||
"generic": {
|
||||
"required": "{0} ist erforderlich",
|
||||
"validNumber": "{0} muss eine Zahl sein",
|
||||
"validString": "{0} muss eine Zeichenkette sein",
|
||||
"validBoolean": "{0} muss ein Wahrheitswert sein",
|
||||
"validArray": "{0} muss eine Liste sein",
|
||||
"stringMin": "{0} muss mindestens {1} Zeichen lang sein",
|
||||
"numberMin": "{0} muss mindestens {1} sein"
|
||||
},
|
||||
"client": {
|
||||
"id": "Client-ID",
|
||||
"name": "Name",
|
||||
"expiresAt": "Läuft ab am",
|
||||
"address4": "IPv4-Adresse",
|
||||
"address6": "IPv6-Adresse",
|
||||
"serverAllowedIps": "serverseitig erlaubte IP-Adressen"
|
||||
},
|
||||
"user": {
|
||||
"username": "Benutzername",
|
||||
"password": "Passwort",
|
||||
"remember": "Merken",
|
||||
"name": "Name",
|
||||
"email": "Email",
|
||||
"emailInvalid": "Die Email-Adresse muss valide sein",
|
||||
"passwordMatch": "Die Passwörter müssen übereinstimmen",
|
||||
"totpEnable": "TOTP aktivieren",
|
||||
"totpEnableTrue": "\"TOTP aktivieren\" muss ausgewählt sein",
|
||||
"totpCode": "TOTP-Code"
|
||||
},
|
||||
"userConfig": {
|
||||
"host": "Host"
|
||||
},
|
||||
"general": {
|
||||
"sessionTimeout": "Sitzungszeitüberschreitung",
|
||||
"metricsEnabled": "Statistiken",
|
||||
"metricsPassword": "Passwort für Statistiken"
|
||||
},
|
||||
"interface": {
|
||||
"cidr": "CIDR",
|
||||
"device": "Geräte",
|
||||
"cidrValid": "CIDR muss valide sein"
|
||||
},
|
||||
"otl": "Einmal-Link",
|
||||
"stringMalformed": "Zeichenkette ist fehlerhaft",
|
||||
"body": "Body muss ein gültiges Objekt sein",
|
||||
"hook": "Hook",
|
||||
"enabled": "Aktiviert",
|
||||
"mtu": "MTU",
|
||||
"port": "Port",
|
||||
"persistentKeepalive": "Dauerhaftes Keepalive",
|
||||
"address": "IP-Adresse",
|
||||
"dns": "DNS",
|
||||
"allowedIps": "Erlaubte IP-Adressen",
|
||||
"file": "Datei"
|
||||
},
|
||||
"hooks": {
|
||||
"preUp": "PreUp",
|
||||
"postUp": "PostUp",
|
||||
"preDown": "PreDown",
|
||||
"postDown": "PostDown"
|
||||
}
|
||||
}
|
||||
@@ -113,7 +113,13 @@
|
||||
"hooks": "Hooks",
|
||||
"hooksDescription": "Hooks only work with wg-quick",
|
||||
"hooksLeaveEmpty": "Only for wg-quick. Otherwise, leave it empty",
|
||||
"dnsDesc": "DNS server clients will use (overrides global config)"
|
||||
"dnsDesc": "DNS server clients will use (overrides global config)",
|
||||
"notConnected": "Client not connected",
|
||||
"endpoint": "Endpoint",
|
||||
"endpointDesc": "IP of the client from which the WireGuard connection is established",
|
||||
"search": "Search clients...",
|
||||
"config": "Configuration",
|
||||
"viewConfig": "View Configuration"
|
||||
},
|
||||
"dialog": {
|
||||
"change": "Change",
|
||||
@@ -233,5 +239,47 @@
|
||||
"postUp": "PostUp",
|
||||
"preDown": "PreDown",
|
||||
"postDown": "PostDown"
|
||||
},
|
||||
"copy": {
|
||||
"notSupported": "Copy is not supported",
|
||||
"copied": "Copied!",
|
||||
"failed": "Copy failed",
|
||||
"copy": "Copy"
|
||||
},
|
||||
"awg": {
|
||||
"jCLabel": "Junk packet count (Jc)",
|
||||
"jCDescription": "Number of junk packets to send (1-128, recommended: 4-12)",
|
||||
"jMinLabel": "Junk packet min size (Jmin)",
|
||||
"jMinDescription": "Minimum size of junk packets (0-1279*, recommended: 8, must be < Jmax)",
|
||||
"jMaxLabel": "Junk packet max size (Jmax)",
|
||||
"jMaxDescription": "Maximum size of junk packets (1-1280*, recommended: 80, must be > Jmin)",
|
||||
"s1Label": "Init packet junk size (S1)",
|
||||
"s1Description": "Init packet junk size (0-1132[1280* - 148 = 1132], recommended: 15-150, S1+56 ≠ S2)",
|
||||
"s2Label": "Response packet junk size (S2)",
|
||||
"s2Description": "Response packet junk size (0-1188[1280* - 92 = 1188], recommended: 15-150)",
|
||||
"s3Label": "Cookie reply packet junk size (S3)",
|
||||
"s3Description": "Cookie reply packet junk size",
|
||||
"s4Label": "Transport packet junk size (S4)",
|
||||
"s4Description": "Transport packet junk size",
|
||||
"i1Label": "Special junk packet 1 (I1)",
|
||||
"i1Description": "Protocol mimic packet in hex format: <b 0x...>",
|
||||
"i2Label": "Special junk packet 2 (I2)",
|
||||
"i2Description": "Protocol mimic packet in hex format: <b 0x...>",
|
||||
"i3Label": "Special junk packet 3 (I3)",
|
||||
"i3Description": "Protocol mimic packet in hex format: <b 0x...>",
|
||||
"i4Label": "Special junk packet 4 (I4)",
|
||||
"i4Description": "Protocol mimic packet in hex format: <b 0x...>",
|
||||
"i5Label": "Special junk packet 5 (I5)",
|
||||
"i5Description": "Protocol mimic packet in hex format: <b 0x...>",
|
||||
"h1Label": "Init magic header (H1)",
|
||||
"h1Description": "Init packet header value (5-2147483647, must be unique from H2-H4)",
|
||||
"h2Label": "Response magic header (H2)",
|
||||
"h2Description": "Response packet header value (5-2147483647, must be unique from H1, H3, H4)",
|
||||
"h3Label": "Cookie reply magic header (H3)",
|
||||
"h3Description": "Cookie reply packet header value (5-2147483647, must be unique from H1, H2, H4)",
|
||||
"h4Label": "Transport magic header (H4)",
|
||||
"h4Description": "Transport packet header value (5-2147483647, must be unique from H1-H3)",
|
||||
"mtuNote": "Values depend on the MTU",
|
||||
"obfuscationParameters": "AmneziaWG Obfuscation Parameters"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
{
|
||||
"pages": {
|
||||
"me": "Cuenta",
|
||||
"clients": "Clientes",
|
||||
"admin": {
|
||||
"panel": "Panel de Admin.",
|
||||
"general": "General",
|
||||
"config": "Configuración",
|
||||
"interface": "Interfaz",
|
||||
"hooks": "Hooks"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"email": "Correo electrónico"
|
||||
},
|
||||
"me": {
|
||||
"currentPassword": "Contraseña actual",
|
||||
"enable2fa": "Habilitar autenticación en dos pasos",
|
||||
"enable2faDesc": "Escanea el código QR con tu aplicación de autenticación o introduce la clave manualmente.",
|
||||
"2faKey": "Clave TOTP",
|
||||
"2faCodeDesc": "Introduce el código de tu aplicación de autenticación.",
|
||||
"disable2fa": "Desactivar autenticación en dos pasos",
|
||||
"disable2faDesc": "Introduce tu contraseña para desactivar la autenticación en dos pasos."
|
||||
},
|
||||
"general": {
|
||||
"name": "Nombre",
|
||||
"username": "Usuario",
|
||||
"password": "Contraseña",
|
||||
"newPassword": "Nueva contraseña",
|
||||
"updatePassword": "Actualizar contraseña",
|
||||
"mtu": "MTU",
|
||||
"allowedIps": "IPs permitidas",
|
||||
"dns": "DNS",
|
||||
"persistentKeepalive": "Keepalive persistente",
|
||||
"logout": "Cerrar sesión",
|
||||
"continue": "Continuar",
|
||||
"host": "Host",
|
||||
"port": "Puerto",
|
||||
"yes": "Sí",
|
||||
"no": "No",
|
||||
"confirmPassword": "Confirmar contraseña",
|
||||
"loading": "Cargando...",
|
||||
"2fa": "Autenticación en dos pasos",
|
||||
"2faCode": "Código TOTP"
|
||||
},
|
||||
"setup": {
|
||||
"welcome": "Bienvenido a tu primera configuración de wg-easy",
|
||||
"welcomeDesc": "Has encontrado la forma más fácil de instalar y gestionar WireGuard en cualquier host Linux",
|
||||
"existingSetup": "¿Tienes una configuración existente?",
|
||||
"createAdminDesc": "Por favor, introduce un nombre de usuario y una contraseña segura. Esta información se usará para iniciar sesión en el panel de administración.",
|
||||
"setupConfigDesc": "Introduce la información del host y puerto. Se utilizará para la configuración del cliente al instalar WireGuard en sus dispositivos.",
|
||||
"setupMigrationDesc": "Proporciona el archivo de copia de seguridad si deseas migrar tus datos desde una versión anterior de wg-easy a esta nueva configuración.",
|
||||
"upload": "Subir",
|
||||
"migration": "Restaurar copia de seguridad:",
|
||||
"createAccount": "Crear cuenta",
|
||||
"successful": "Configuración completada",
|
||||
"hostDesc": "Nombre de host público al que se conectarán los clientes",
|
||||
"portDesc": "Puerto UDP público al que se conectarán los clientes y que escuchará WireGuard"
|
||||
},
|
||||
"update": {
|
||||
"updateAvailable": "¡Hay una actualización disponible!",
|
||||
"update": "Actualizar"
|
||||
},
|
||||
"theme": {
|
||||
"dark": "Tema oscuro",
|
||||
"light": "Tema claro",
|
||||
"system": "Tema del sistema"
|
||||
},
|
||||
"layout": {
|
||||
"toggleCharts": "Mostrar/ocultar gráficos",
|
||||
"donate": "Donar"
|
||||
},
|
||||
"login": {
|
||||
"signIn": "Iniciar sesión",
|
||||
"rememberMe": "Recordarme",
|
||||
"rememberMeDesc": "Mantener sesión iniciada tras cerrar el navegador",
|
||||
"insecure": "No puedes iniciar sesión con una conexión no segura. Usa HTTPS.",
|
||||
"2faRequired": "Se requiere autenticación en dos pasos",
|
||||
"2faWrong": "El código de autenticación en dos pasos es incorrecto"
|
||||
},
|
||||
"client": {
|
||||
"empty": "No hay clientes todavía.",
|
||||
"newShort": "Nuevo",
|
||||
"sort": "Ordenar",
|
||||
"create": "Crear cliente",
|
||||
"created": "Cliente creado",
|
||||
"new": "Nuevo cliente",
|
||||
"name": "Nombre",
|
||||
"expireDate": "Fecha de expiración",
|
||||
"expireDateDesc": "Fecha en que el cliente será desactivado. En blanco = permanente",
|
||||
"deleteClient": "Eliminar cliente",
|
||||
"deleteDialog1": "¿Estás seguro de que deseas eliminar a",
|
||||
"deleteDialog2": "Esta acción no se puede deshacer.",
|
||||
"enabled": "Habilitado",
|
||||
"address": "Dirección",
|
||||
"serverAllowedIps": "IPs permitidas del servidor",
|
||||
"otlDesc": "Generar enlace único de un solo uso",
|
||||
"permanent": "Permanente",
|
||||
"createdOn": "Creado el ",
|
||||
"lastSeen": "Última conexión el ",
|
||||
"totalDownload": "Descarga total: ",
|
||||
"totalUpload": "Subida total: ",
|
||||
"newClient": "Nuevo cliente",
|
||||
"disableClient": "Desactivar cliente",
|
||||
"enableClient": "Activar cliente",
|
||||
"noPrivKey": "Este cliente no tiene clave privada conocida. No se puede crear la configuración.",
|
||||
"showQR": "Mostrar código QR",
|
||||
"downloadConfig": "Descargar configuración",
|
||||
"allowedIpsDesc": "Qué IPs se enrutarán por la VPN (anula la configuración global)",
|
||||
"serverAllowedIpsDesc": "Qué IPs el servidor enviará al cliente",
|
||||
"mtuDesc": "Tamaño máximo de paquete (MTU) para el túnel VPN",
|
||||
"persistentKeepaliveDesc": "Intervalo (en segundos) para los paquetes keep-alive. 0 lo desactiva",
|
||||
"hooks": "Hooks",
|
||||
"hooksDescription": "Los hooks solo funcionan con wg-quick",
|
||||
"hooksLeaveEmpty": "Solo para wg-quick. En caso contrario, dejar vacío",
|
||||
"dnsDesc": "Servidor DNS que usarán los clientes (anula la configuración global)",
|
||||
"notConnected": "Cliente no conectado",
|
||||
"endpoint": "Punto de conexión",
|
||||
"endpointDesc": "IP del cliente desde donde se establece la conexión WireGuard"
|
||||
},
|
||||
"dialog": {
|
||||
"change": "Cambiar",
|
||||
"cancel": "Cancelar",
|
||||
"create": "Crear"
|
||||
},
|
||||
"toast": {
|
||||
"success": "Éxito",
|
||||
"saved": "Guardado",
|
||||
"error": "Error"
|
||||
},
|
||||
"form": {
|
||||
"actions": "Acciones",
|
||||
"save": "Guardar",
|
||||
"revert": "Revertir",
|
||||
"sectionGeneral": "General",
|
||||
"sectionAdvanced": "Avanzado",
|
||||
"noItems": "Sin elementos",
|
||||
"nullNoItems": "Sin elementos. Usando configuración global",
|
||||
"add": "Añadir"
|
||||
},
|
||||
"admin": {
|
||||
"general": {
|
||||
"sessionTimeout": "Tiempo de sesión",
|
||||
"sessionTimeoutDesc": "Duración de sesión con 'Recordarme' (en segundos)",
|
||||
"metrics": "Métricas",
|
||||
"metricsPassword": "Contraseña",
|
||||
"metricsPasswordDesc": "Contraseña Bearer para el endpoint de métricas (contraseña o hash argon2)",
|
||||
"json": "JSON",
|
||||
"jsonDesc": "Ruta para métricas en formato JSON",
|
||||
"prometheus": "Prometheus",
|
||||
"prometheusDesc": "Ruta para métricas de Prometheus"
|
||||
},
|
||||
"config": {
|
||||
"connection": "Conexión",
|
||||
"hostDesc": "Nombre de host público al que se conectarán los clientes (invalida config)",
|
||||
"portDesc": "Puerto UDP público al que se conectarán los clientes (invalida config, también debes cambiar el puerto de interfaz)",
|
||||
"allowedIpsDesc": "IPs permitidas que usarán los clientes (configuración global)",
|
||||
"dnsDesc": "Servidor DNS que usarán los clientes (configuración global)",
|
||||
"mtuDesc": "MTU que usarán los clientes (solo para nuevos clientes)",
|
||||
"persistentKeepaliveDesc": "Intervalo en segundos para enviar keepalives al servidor. 0 = desactivado (solo para nuevos clientes)",
|
||||
"suggest": "Sugerir",
|
||||
"suggestDesc": "Elegir dirección IP o nombre de host para el campo Host"
|
||||
},
|
||||
"interface": {
|
||||
"cidrSuccess": "CIDR cambiado",
|
||||
"device": "Dispositivo",
|
||||
"deviceDesc": "Dispositivo Ethernet por donde se reenviará el tráfico de WireGuard",
|
||||
"mtuDesc": "MTU que usará WireGuard",
|
||||
"portDesc": "Puerto UDP en el que escuchará WireGuard (debes cambiar también el puerto de config)",
|
||||
"changeCidr": "Cambiar CIDR",
|
||||
"restart": "Reiniciar interfaz",
|
||||
"restartDesc": "Reiniciar la interfaz de WireGuard",
|
||||
"restartWarn": "¿Estás seguro de reiniciar la interfaz? Esto desconectará a todos los clientes.",
|
||||
"restartSuccess": "Interfaz reiniciada"
|
||||
},
|
||||
"introText": "Bienvenido al panel de administración.\n\nAquí puedes gestionar los ajustes generales, la configuración, la interfaz y los hooks.\n\nEmpieza eligiendo una de las secciones en la barra lateral."
|
||||
},
|
||||
"zod": {
|
||||
"generic": {
|
||||
"required": "{0} es obligatorio",
|
||||
"validNumber": "{0} debe ser un número válido",
|
||||
"validString": "{0} debe ser una cadena válida",
|
||||
"validBoolean": "{0} debe ser un booleano válido",
|
||||
"validArray": "{0} debe ser una lista válida",
|
||||
"stringMin": "{0} debe tener al menos {1} caracteres",
|
||||
"numberMin": "{0} debe ser al menos {1}"
|
||||
},
|
||||
"client": {
|
||||
"id": "ID del cliente",
|
||||
"name": "Nombre",
|
||||
"expiresAt": "Expira el",
|
||||
"address4": "Dirección IPv4",
|
||||
"address6": "Dirección IPv6",
|
||||
"serverAllowedIps": "IPs permitidas del servidor"
|
||||
},
|
||||
"user": {
|
||||
"username": "Usuario",
|
||||
"password": "Contraseña",
|
||||
"remember": "Recordar",
|
||||
"name": "Nombre",
|
||||
"email": "Correo electrónico",
|
||||
"emailInvalid": "El correo electrónico debe ser válido",
|
||||
"passwordMatch": "Las contraseñas deben coincidir",
|
||||
"totpEnable": "Activar TOTP",
|
||||
"totpEnableTrue": "Debe estar activado",
|
||||
"totpCode": "Código TOTP"
|
||||
},
|
||||
"userConfig": {
|
||||
"host": "Host"
|
||||
},
|
||||
"general": {
|
||||
"sessionTimeout": "Tiempo de sesión",
|
||||
"metricsEnabled": "Métricas",
|
||||
"metricsPassword": "Contraseña de métricas"
|
||||
},
|
||||
"interface": {
|
||||
"cidr": "CIDR",
|
||||
"device": "Dispositivo",
|
||||
"cidrValid": "El CIDR debe ser válido"
|
||||
},
|
||||
"otl": "Enlace de un solo uso",
|
||||
"stringMalformed": "Cadena malformada",
|
||||
"body": "El cuerpo debe ser un objeto válido",
|
||||
"hook": "Hook",
|
||||
"enabled": "Habilitado",
|
||||
"mtu": "MTU",
|
||||
"port": "Puerto",
|
||||
"persistentKeepalive": "Keepalive persistente",
|
||||
"address": "Dirección IP",
|
||||
"dns": "DNS",
|
||||
"allowedIps": "IPs permitidas",
|
||||
"file": "Archivo"
|
||||
},
|
||||
"hooks": {
|
||||
"preUp": "PreUp",
|
||||
"postUp": "PostUp",
|
||||
"preDown": "PreDown",
|
||||
"postDown": "PostDown"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
{
|
||||
"pages": {
|
||||
"me": "Compte",
|
||||
"clients": "Clients",
|
||||
"admin": {
|
||||
"panel": "Panel Admin",
|
||||
"general": "Général",
|
||||
"config": "Config",
|
||||
"interface": "Interface",
|
||||
"hooks": "Hooks"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"email": "E-Mail"
|
||||
},
|
||||
"me": {
|
||||
"currentPassword": "Mot de passe actuel",
|
||||
"enable2fa": "Activer l'authentification à double facteur",
|
||||
"enable2faDesc": "Scannez le code QR avec votre application d'authentification ou saisissez la clé manuellement.",
|
||||
"2faKey": "Clé TOTP",
|
||||
"2faCodeDesc": "Saisissez le code de votre application d'authentification.",
|
||||
"disable2fa": "Désactiver l'authentification à double facteur",
|
||||
"disable2faDesc": "Saisissez votre mot de passe pour désactiver l'authentification à double facteur"
|
||||
},
|
||||
"general": {
|
||||
"name": "Nom",
|
||||
"username": "Nom d'utilisateur",
|
||||
"password": "Mot de passe",
|
||||
"newPassword": "Nouveau mot de passe",
|
||||
"updatePassword": "Mettre à jour le mot de passe",
|
||||
"mtu": "MTU",
|
||||
"allowedIps": "IPs autorisées",
|
||||
"dns": "DNS",
|
||||
"persistentKeepalive": "Keepalive persistant",
|
||||
"logout": "Déconnexion",
|
||||
"continue": "Continuer",
|
||||
"host": "Hôte",
|
||||
"port": "Port",
|
||||
"yes": "Oui",
|
||||
"no": "Non",
|
||||
"confirmPassword": "Confirmer le mot de passe",
|
||||
"loading": "Chargement...",
|
||||
"2fa": "Authentification à double facteur",
|
||||
"2faCode": "Code TOTP"
|
||||
},
|
||||
"setup": {
|
||||
"welcome": "Bienvenue dans votre première installation de wg-easy",
|
||||
"welcomeDesc": "Vous avez trouvé la façon la plus simple d'installer et de gérer WireGuard sur n'importe quel hôte Linux.",
|
||||
"existingSetup": "Avez-vous une installation existante ?",
|
||||
"createAdminDesc": "Veuillez d'abord saisir un nom d'utilisateur administrateur et un mot de passe sécurisé. Ces informations seront utilisées pour vous connecter à votre panel d'administration.",
|
||||
"setupConfigDesc": "Veuillez saisir les informations relatives à l'hôte et au port. Ceci sera utilisé pour la configuration du client lors de la mise en place de WireGuard sur les appareils.",
|
||||
"setupMigrationDesc": "Veuillez fournir le fichier de sauvegarde si vous souhaitez migrer vos données de la version précédente de wg-easy vers votre nouvelle installation.",
|
||||
"upload": "Téléverser",
|
||||
"migration": "Restaurer la sauvegarde:",
|
||||
"createAccount": "Créer un compte",
|
||||
"successful": "Installation réussie",
|
||||
"hostDesc": "Nom d'hôte public auquel les clients se connecteront",
|
||||
"portDesc": "Port UDP public auquel les clients se connecteront et sur lequel WireGuard écoutera"
|
||||
},
|
||||
"update": {
|
||||
"updateAvailable": "Une mise à jour est disponible!",
|
||||
"update": "Mise à jour"
|
||||
},
|
||||
"theme": {
|
||||
"dark": "Thème sombre",
|
||||
"light": "Thème clair",
|
||||
"system": "Thème système"
|
||||
},
|
||||
"layout": {
|
||||
"toggleCharts": "Afficher/masquer les graphiques",
|
||||
"donate": "Donation"
|
||||
},
|
||||
"login": {
|
||||
"signIn": "Se connecter",
|
||||
"rememberMe": "Se souvenir de moi",
|
||||
"rememberMeDesc": "Rester connecté après avoir fermé le navigateur",
|
||||
"insecure": "Vous ne pouvez pas vous connecter avec une connexion non sécurisée. Utilisez HTTPS.",
|
||||
"2faRequired": "Une authentification à double facteur est requise",
|
||||
"2faWrong": "L'authentification à double facteur est incorrecte"
|
||||
},
|
||||
"client": {
|
||||
"empty": "Il n'y a pas encore de clients.",
|
||||
"newShort": "Nouveau",
|
||||
"sort": "Trier",
|
||||
"create": "Créer un client",
|
||||
"created": "Client créé",
|
||||
"new": "Nouveau client",
|
||||
"name": "Nom",
|
||||
"expireDate": "Date d'expiration",
|
||||
"expireDateDesc": "Date à laquelle le client sera désactivé. Vide pour permanent",
|
||||
"deleteClient": "Supprimer le client",
|
||||
"deleteDialog1": "Êtes-vous sûr de vouloir supprimer",
|
||||
"deleteDialog2": "Cette action ne peut être annulée.",
|
||||
"enabled": "Activé",
|
||||
"address": "Adresse",
|
||||
"serverAllowedIps": "Serveur IPs autorisées",
|
||||
"otlDesc": "Générer un lien court et unique",
|
||||
"permanent": "Permanent",
|
||||
"createdOn": "Créé le ",
|
||||
"lastSeen": "Dernière visite le ",
|
||||
"totalDownload": "Téléchargement total: ",
|
||||
"totalUpload": "Téléversement total: ",
|
||||
"newClient": "Nouveau client",
|
||||
"disableClient": "Désactiver le client",
|
||||
"enableClient": "Activer le client",
|
||||
"noPrivKey": "Ce client n'a pas de clé privée connue. Impossible de créer une configuration.",
|
||||
"showQR": "Afficher le code QR",
|
||||
"downloadConfig": "Télécharger la configuration",
|
||||
"allowedIpsDesc": "Quelles IPs seront acheminées par le VPN (remplace la configuration globale)",
|
||||
"serverAllowedIpsDesc": "Les IPs que le serveur acheminera vers le client",
|
||||
"mtuDesc": "Définit le nombre maximum d'unités de transmission (taille des paquets) pour le tunnel VPN.",
|
||||
"persistentKeepaliveDesc": "Définit l'intervalle (en secondes) pour les paquets keep-alive. 0 le désactive",
|
||||
"hooks": "Hooks",
|
||||
"hooksDescription": "Les hooks ne fonctionnent qu'avec wg-quick",
|
||||
"hooksLeaveEmpty": "Uniquement pour wg-quick. Sinon, laissez-le vide",
|
||||
"dnsDesc": "Serveur DNS que les clients utiliseront (remplace la configuration globale)",
|
||||
"notConnected": "Client non connecté",
|
||||
"endpoint": "Endpoint",
|
||||
"endpointDesc": "Adresse IP du client à partir duquel la connexion WireGuard est établie"
|
||||
},
|
||||
"dialog": {
|
||||
"change": "Modifier",
|
||||
"cancel": "Annuler",
|
||||
"create": "Créer"
|
||||
},
|
||||
"toast": {
|
||||
"success": "Réussite",
|
||||
"saved": "Sauvegardé",
|
||||
"error": "Erreur"
|
||||
},
|
||||
"form": {
|
||||
"actions": "Actions",
|
||||
"save": "Sauvegarder",
|
||||
"revert": "Revenir en arrière",
|
||||
"sectionGeneral": "Général",
|
||||
"sectionAdvanced": "Avancé",
|
||||
"noItems": "Aucun élément",
|
||||
"nullNoItems": "Aucun élément. Utilisation de la configuration globale",
|
||||
"add": "Ajouter"
|
||||
},
|
||||
"admin": {
|
||||
"general": {
|
||||
"sessionTimeout": "Délai d'expiration de la session",
|
||||
"sessionTimeoutDesc": "Durée de la session pour Se souvenir de moi (secondes)",
|
||||
"metrics": "Métriques",
|
||||
"metricsPassword": "Mot de passe",
|
||||
"metricsPasswordDesc": "Mot de passe Bearer pour le endpoint des métriques (mot de passe ou argon2 hash)",
|
||||
"json": "JSON",
|
||||
"jsonDesc": "Acheminement pour les métriques au format JSON",
|
||||
"prometheus": "Prometheus",
|
||||
"prometheusDesc": "Acheminement pour les métriques de Prometheus"
|
||||
},
|
||||
"config": {
|
||||
"connection": "Connexion",
|
||||
"hostDesc": "Nom d'hôte public auquel les clients se connecteront (invalide la configuration)",
|
||||
"portDesc": "Port UDP public auquel les clients se connecteront (invalide la configuration, vous souhaiterez probablement modifier le port d'interface également)",
|
||||
"allowedIpsDesc": "IPs autorisées que les clients utiliseront (configuration globale)",
|
||||
"dnsDesc": "Serveur DNS que les clients utiliseront (configuration globale)",
|
||||
"mtuDesc": "MTU que les clients utiliseront (uniquement pour les nouveaux clients)",
|
||||
"persistentKeepaliveDesc": "Intervalle en secondes keepalives du serveur. 0 = désactivé (uniquement pour les nouveaux clients)",
|
||||
"suggest": "Suggérer",
|
||||
"suggestDesc": "Choisissez une adresse IP ou un nom d'hôte pour le champ Hôte."
|
||||
},
|
||||
"interface": {
|
||||
"cidrSuccess": "CIDR modifié",
|
||||
"device": "Périphérique",
|
||||
"deviceDesc": "Périphérique Ethernet dans lequel le trafic wireguard sera transmis.",
|
||||
"mtuDesc": "MTU que WireGuard utilisera",
|
||||
"portDesc": "Port UDP sur lequel WireGuard écoutera (vous souhaiterez probablement changer le port de configuration aussi)",
|
||||
"changeCidr": "Modifier CIDR",
|
||||
"restart": "Redémarrer l'interface",
|
||||
"restartDesc": "Redémarre l'interface WireGuard",
|
||||
"restartWarn": "Êtes-vous sûr de redémarrer l'interface ? Cela déconnectera tous les clients.",
|
||||
"restartSuccess": "Interface redémarrée"
|
||||
},
|
||||
"introText": "Bienvenue dans le panel d'administration.\n\nVous pouvez y gérer les paramètres généraux, la configuration, les paramètres de l'interface et les hooks.\n\nCommencez par choisir l'une des sections de la barre latérale."
|
||||
},
|
||||
"zod": {
|
||||
"generic": {
|
||||
"required": "{0} est requis",
|
||||
"validNumber": "{0} doit être un nombre valide",
|
||||
"validString": "{0} doit être une chaîne de caractères valide",
|
||||
"validBoolean": "{0} doit être une variable valide",
|
||||
"validArray": "{0} doit être un tableau valide",
|
||||
"stringMin": "{0} doit être d'au moins {1} Caractère",
|
||||
"numberMin": "{0} doit être d'au moins {1}"
|
||||
},
|
||||
"client": {
|
||||
"id": "Client ID",
|
||||
"name": "Nom",
|
||||
"expiresAt": "Expire le",
|
||||
"address4": "Adresse IPv4",
|
||||
"address6": "Adresse IPv6",
|
||||
"serverAllowedIps": "Serveur IPs autorisées"
|
||||
},
|
||||
"user": {
|
||||
"username": "Nom d'utilisateur",
|
||||
"password": "Mot de passe",
|
||||
"remember": "Se souvenir",
|
||||
"name": "Nom",
|
||||
"email": "Email",
|
||||
"emailInvalid": "L'email doit être valide",
|
||||
"passwordMatch": "Les mots de passe doivent correspondre",
|
||||
"totpEnable": "Activer TOTP",
|
||||
"totpEnableTrue": "Activer TOTP doit être activé",
|
||||
"totpCode": "Code TOTP"
|
||||
},
|
||||
"userConfig": {
|
||||
"host": "Hôte"
|
||||
},
|
||||
"general": {
|
||||
"sessionTimeout": "Délai d'expiration de la session",
|
||||
"metricsEnabled": "Métriques",
|
||||
"metricsPassword": "Mot de passe métriques"
|
||||
},
|
||||
"interface": {
|
||||
"cidr": "CIDR",
|
||||
"device": "Périphérique",
|
||||
"cidrValid": "CIDR doit être valide"
|
||||
},
|
||||
"otl": "Lien unique",
|
||||
"stringMalformed": "La chaîne est malformée",
|
||||
"body": "Le corps doit être un objet valide",
|
||||
"hook": "Hook",
|
||||
"enabled": "Activé",
|
||||
"mtu": "MTU",
|
||||
"port": "Port",
|
||||
"persistentKeepalive": "Keepalive persistant",
|
||||
"address": "Adresse IP",
|
||||
"dns": "DNS",
|
||||
"allowedIps": "IPs autorisées",
|
||||
"file": "Fichier"
|
||||
},
|
||||
"hooks": {
|
||||
"preUp": "PreUp",
|
||||
"postUp": "PostUp",
|
||||
"preDown": "PreDown",
|
||||
"postDown": "PostDown"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
{
|
||||
"pages": {
|
||||
"me": "Akun",
|
||||
"clients": "Klien",
|
||||
"admin": {
|
||||
"panel": "Panel Admin",
|
||||
"general": "General",
|
||||
"config": "Umum",
|
||||
"interface": "Antarmuka",
|
||||
"hooks": "Hooks"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"email": "E-Mail"
|
||||
},
|
||||
"me": {
|
||||
"currentPassword": "Kata Sandi Saat Ini",
|
||||
"enable2fa": "Aktifkan autentikasi dua faktor",
|
||||
"enable2faDesc": "Pindai kode QR dengan aplikasi autentikator anda atau masukkan kuncinya secara manual.",
|
||||
"2faKey": "Kunci TOTP",
|
||||
"2faCodeDesc": "Masukkan kode dari aplikasi autentikator Anda.",
|
||||
"disable2fa": "Nonaktifkan autentikasi dua faktor",
|
||||
"disable2faDesc": "Masukkan kata sandi anda untuk menonaktifkan autentikasi dua faktor."
|
||||
},
|
||||
"general": {
|
||||
"name": "Nama",
|
||||
"username": "Name Pengguna",
|
||||
"password": "Kata Sandi",
|
||||
"newPassword": "Kata Sandi Baru",
|
||||
"updatePassword": "Ubah Kata Sandi",
|
||||
"mtu": "MTU",
|
||||
"allowedIps": "IP yang diizinkan",
|
||||
"dns": "DNS",
|
||||
"persistentKeepalive": "Keepalive Persisten",
|
||||
"logout": "Keluar",
|
||||
"continue": "Lanjutkan",
|
||||
"host": "Host",
|
||||
"port": "Port",
|
||||
"yes": "Ya",
|
||||
"no": "Tidak",
|
||||
"confirmPassword": "Konfirmasi Kata Sandi",
|
||||
"loading": "Memuat...",
|
||||
"2fa": "Autentikasi Dua Faktor",
|
||||
"2faCode": "Kode TOTP"
|
||||
},
|
||||
"setup": {
|
||||
"welcome": "Selamat datang di pengaturan pertama wg-easy anda",
|
||||
"welcomeDesc": "Anda telah menemukan cara termudah untuk menginstal dan mengelola WireGuard di host Linux mana pun",
|
||||
"existingSetup": "Apakah anda sudah memiliki pengaturan sebelumnya?",
|
||||
"createAdminDesc": "Silakan masukkan dulu nama pengguna admin dan kata sandi yang kuat dan aman. Informasi ini akan digunakan untuk masuk ke panel administrasi Anda.",
|
||||
"setupConfigDesc": "Silakan masukkan informasi host dan port. Ini akan digunakan untuk konfigurasi klien saat mengatur WireGuard di perangkat mereka.",
|
||||
"setupMigrationDesc": "Silakan berikan file cadangan jika anda ingin memigrasikan data dari versi wg-easy sebelumnya ke pengaturan baru anda.",
|
||||
"upload": "Unggah",
|
||||
"migration": "Pulihkan cadangan:",
|
||||
"createAccount": "Buat Akun",
|
||||
"successful": "Pengaturan berhasil",
|
||||
"hostDesc": "Hostname publik yang akan digunakan klien untuk terhubung",
|
||||
"portDesc": "Port UDP publik yang akan digunakan klien untuk terhubung dan dibaca oleh WireGuard"
|
||||
},
|
||||
"update": {
|
||||
"updateAvailable": "Ada pembaruan tersedia!",
|
||||
"update": "Pembaruan"
|
||||
},
|
||||
"theme": {
|
||||
"dark": "Tema gelap",
|
||||
"light": "Tema terang",
|
||||
"system": "Tema sistem"
|
||||
},
|
||||
"layout": {
|
||||
"toggleCharts": "Tampilkan/sembunyikan Grafik",
|
||||
"donate": "Donasi"
|
||||
},
|
||||
"login": {
|
||||
"signIn": "Masuk",
|
||||
"rememberMe": "Ingat saya",
|
||||
"rememberMeDesc": "Tetap masuk setelah menutup browser",
|
||||
"insecure": "Anda tidak dapat masuk dengan koneksi yang tidak aman. Gunakan HTTPS.",
|
||||
"2faRequired": "Autentikasi dua faktor wajib digunakan",
|
||||
"2faWrong": "Autentikasi dua faktor salah"
|
||||
},
|
||||
"client": {
|
||||
"empty": "Belum ada klien.",
|
||||
"newShort": "Baru",
|
||||
"sort": "Urutkan",
|
||||
"create": "Buat Klien",
|
||||
"created": "Klien terbuat",
|
||||
"new": "Klien Baru",
|
||||
"name": "Nama",
|
||||
"expireDate": "Tanggal Kedaluwarsa",
|
||||
"expireDateDesc": "Tanggal klien akan dinonaktifkan. Kosongkan untuk opsi permanen.",
|
||||
"deleteClient": "Hapus Klien",
|
||||
"deleteDialog1": "Apakah anda yakin ingin menghapus",
|
||||
"deleteDialog2": "Tindakan ini tidak dapat dibatalkan.",
|
||||
"enabled": "Aktifkan",
|
||||
"address": "Alamat",
|
||||
"serverAllowedIps": "IP yang Diizinkan pada Server",
|
||||
"otlDesc": "Buat tautan sekali pakai singkat",
|
||||
"permanent": "Permanen",
|
||||
"createdOn": "Dibuat pada ",
|
||||
"lastSeen": "Terakhir terlihat pada ",
|
||||
"totalDownload": "Total Unduhan: ",
|
||||
"totalUpload": "Total Unggahan: ",
|
||||
"newClient": "Klien Baru",
|
||||
"disableClient": "Nonaktifkan Klien",
|
||||
"enableClient": "Aktifkan Klien",
|
||||
"noPrivKey": "Klien ini tidak memiliki kunci pribadi (private key) yang diketahui. Tidak dapat membuat konfigurasi.",
|
||||
"showQR": "Tampilkan Kode QR",
|
||||
"downloadConfig": "Unduh Konfigurasi",
|
||||
"allowedIpsDesc": "IP mana yang akan dialihkan melalui VPN (menggantikan konfigurasi global)",
|
||||
"serverAllowedIpsDesc": "IP mana yang akan dialihkan server ke klien",
|
||||
"mtuDesc": "Menetapkan unit transmisi maksimum (ukuran paket) untuk terowongan VPN",
|
||||
"persistentKeepaliveDesc": "Menetapkan interval (dalam detik) untuk paket keep-alive. 0 menonaktifkannya.",
|
||||
"hooks": "Hooks",
|
||||
"hooksDescription": "Hooks hanya berfungsi dengan wg-quick",
|
||||
"hooksLeaveEmpty": "Hanya untuk wg-quick. Jika tidak, biarkan kosong.",
|
||||
"dnsDesc": "Server DNS yang akan digunakan klien (menggantikan konfigurasi global)",
|
||||
"notConnected": "Klien tidak terhubung",
|
||||
"endpoint": "Titik Akhir (Endpoint)",
|
||||
"endpointDesc": "IP klien dari mana koneksi WireGuard dibuat"
|
||||
},
|
||||
"dialog": {
|
||||
"change": "Ubah",
|
||||
"cancel": "Batal",
|
||||
"create": "Buat"
|
||||
},
|
||||
"toast": {
|
||||
"success": "Berhasil",
|
||||
"saved": "Tersimpan",
|
||||
"error": "Kesalahan"
|
||||
},
|
||||
"form": {
|
||||
"actions": "Tindakan",
|
||||
"save": "Simpan",
|
||||
"revert": "Kembalikan",
|
||||
"sectionGeneral": "Umum",
|
||||
"sectionAdvanced": "Lanjutan",
|
||||
"noItems": "Tidak ada item",
|
||||
"nullNoItems": "Tidak ada item. Menggunakan konfigurasi global",
|
||||
"add": "Tambah"
|
||||
},
|
||||
"admin": {
|
||||
"general": {
|
||||
"sessionTimeout": "Waktu Sesi Habis",
|
||||
"sessionTimeoutDesc": "Durasi sesi untuk Ingat Saya (dalam detik)",
|
||||
"metrics": "Metrik",
|
||||
"metricsPassword": "Kata Sandi",
|
||||
"metricsPasswordDesc": "Kata sandi Bearer untuk endpoint metrik (kata sandi atau hash argon2)",
|
||||
"json": "JSON",
|
||||
"jsonDesc": "Rute untuk metrik dalam format JSON",
|
||||
"prometheus": "Prometheus",
|
||||
"prometheusDesc": "Rute untuk metrik Prometheus"
|
||||
},
|
||||
"config": {
|
||||
"connection": "Koneksi",
|
||||
"hostDesc": "Hostname publik yang akan digunakan klien untuk terhubung (membatalkan konfigurasi)",
|
||||
"portDesc": "Port UDP publik yang akan digunakan klien untuk terhubung (membatalkan konfigurasi, anda mungkin ingin mengubah Port Antarmuka juga)",
|
||||
"allowedIpsDesc": "IP yang diizinkan untuk digunakan klien (konfigurasi global)",
|
||||
"dnsDesc": "Server DNS yang akan digunakan klien (konfigurasi global)",
|
||||
"mtuDesc": "MTU yang akan digunakan klien (hanya untuk klien baru)",
|
||||
"persistentKeepaliveDesc": "Interval dalam detik untuk mengirim keepalive ke server. 0 = dinonaktifkan (hanya untuk klien baru)",
|
||||
"suggest": "Menyarankan",
|
||||
"suggestDesc": "Pilih alamat IP atau hostname untuk kolom Host"
|
||||
},
|
||||
"interface": {
|
||||
"cidrSuccess": "CIDR diperbarui",
|
||||
"device": "Perangkat",
|
||||
"deviceDesc": "Perangkat Ethernet tempat lalu lintas WireGuard harus diteruskan",
|
||||
"mtuDesc": "MTU yang akan digunakan oleh WireGuard",
|
||||
"portDesc": "Port UDP yang akan didengarkan oleh WireGuard (Anda mungkin ingin mengubah Port Konfigurasi juga)",
|
||||
"changeCidr": "Ubah CIDR",
|
||||
"restart": "Mulai Ulang Antarmuka",
|
||||
"restartDesc": "Mulai ulang antarmuka WireGuard",
|
||||
"restartWarn": "Apakah Anda yakin ingin memulai ulang antarmuka? Ini akan membuat semua klien menjadi terputus.",
|
||||
"restartSuccess": "Antarmuka telah dimulai ulang."
|
||||
},
|
||||
"introText": "Selamat datang di panel admin.\n\nDi sini Anda dapat mengelola pengaturan umum, konfigurasi, pengaturan antarmuka, dan hooks.\n\nMulailah dengan memilih salah satu bagian di bilah samping."
|
||||
},
|
||||
"zod": {
|
||||
"generic": {
|
||||
"required": "{0} diperlukan",
|
||||
"validNumber": "{0} harus berupa angka yang valid",
|
||||
"validString": "{0} harus berupa string yang valid",
|
||||
"validBoolean": "{0} harus berupa boolean yang valid",
|
||||
"validArray": "{0} harus berupa array yang valid",
|
||||
"stringMin": "{0} harus memiliki setidaknya {1} karakter",
|
||||
"numberMin": "{0} harus minimal {1}"
|
||||
},
|
||||
"client": {
|
||||
"id": "ID Klien",
|
||||
"name": "Nama",
|
||||
"expiresAt": "Berakhir pada",
|
||||
"address4": "Alamat IPv4",
|
||||
"address6": "Alamat IPv6",
|
||||
"serverAllowedIps": "IP yang diizinkan untuk Server"
|
||||
},
|
||||
"user": {
|
||||
"username": "Nama Pengguna",
|
||||
"password": "Kata Sandi",
|
||||
"remember": "Ingat",
|
||||
"name": "Nama",
|
||||
"email": "Email",
|
||||
"emailInvalid": "Harus berupa email yang valid",
|
||||
"passwordMatch": "Kata sandi harus cocok",
|
||||
"totpEnable": "Aktifkan TOTP",
|
||||
"totpEnableTrue": "Aktifkan TOTP harus bernilai true",
|
||||
"totpCode": "Kode TOTP"
|
||||
},
|
||||
"userConfig": {
|
||||
"host": "Host"
|
||||
},
|
||||
"general": {
|
||||
"sessionTimeout": "Waktu Sesi Habis",
|
||||
"metricsEnabled": "Metrik",
|
||||
"metricsPassword": "Kata Sandi Metrik"
|
||||
},
|
||||
"interface": {
|
||||
"cidr": "CIDR",
|
||||
"device": "Perangkat",
|
||||
"cidrValid": "CIDR harus valid"
|
||||
},
|
||||
"otl": "Tautan Sekali Pakai",
|
||||
"stringMalformed": "String tidak terformat dengan benar",
|
||||
"body": "Body harus berupa objek yang valid",
|
||||
"hook": "Hook",
|
||||
"enabled": "Diaktifkan",
|
||||
"mtu": "MTU",
|
||||
"port": "Port",
|
||||
"persistentKeepalive": "Keepalive Persisten",
|
||||
"address": "Alamat IP",
|
||||
"dns": "DNS",
|
||||
"allowedIps": "IP yang diizinkan",
|
||||
"file": "Berkas"
|
||||
},
|
||||
"hooks": {
|
||||
"preUp": "PreUp",
|
||||
"postUp": "PostUp",
|
||||
"preDown": "PreDown",
|
||||
"postDown": "PostDown"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
{
|
||||
"pages": {
|
||||
"me": "Account",
|
||||
"clients": "Client",
|
||||
"admin": {
|
||||
"panel": "Pannello di Amministrazione",
|
||||
"general": "Generale",
|
||||
"config": "Configurazione",
|
||||
"interface": "Interfaccia",
|
||||
"hooks": "Hooks"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"email": "E-Mail"
|
||||
},
|
||||
"me": {
|
||||
"currentPassword": "Password attuale",
|
||||
"enable2fa": "Abilita Autenticazione a Due Fattori",
|
||||
"enable2faDesc": "Scansiona il codice QR con la tua app di autenticazione o inserisci manualmente la chiave.",
|
||||
"2faKey": "Chiave TOTP",
|
||||
"2faCodeDesc": "Inserisci il codice generato dalla tua app di autenticazione.",
|
||||
"disable2fa": "Disabilita Autenticazione a Due Fattori",
|
||||
"disable2faDesc": "Inserisci la tua password per disabilitare l’Autenticazione a Due Fattori."
|
||||
},
|
||||
"general": {
|
||||
"name": "Nome",
|
||||
"username": "Nome utente",
|
||||
"password": "Password",
|
||||
"newPassword": "Nuova Password",
|
||||
"updatePassword": "Aggiorna Password",
|
||||
"mtu": "MTU",
|
||||
"allowedIps": "IP consentiti",
|
||||
"dns": "DNS",
|
||||
"persistentKeepalive": "Keepalive Persistente",
|
||||
"logout": "Esci",
|
||||
"continue": "Continua",
|
||||
"host": "Host",
|
||||
"port": "Porta",
|
||||
"yes": "Sì",
|
||||
"no": "No",
|
||||
"confirmPassword": "Conferma Password",
|
||||
"loading": "Caricamento...",
|
||||
"2fa": "Autenticazione a Due Fattori",
|
||||
"2faCode": "Codice TOTP"
|
||||
},
|
||||
"setup": {
|
||||
"welcome": "Benvenuto alla tua prima configurazione di wg-easy",
|
||||
"welcomeDesc": "Hai trovato il modo più semplice per installare e gestire WireGuard su qualsiasi host Linux",
|
||||
"existingSetup": "Hai già una configurazione esistente?",
|
||||
"createAdminDesc": "Inserisci prima un nome utente admin e una password forte e sicura. Queste informazioni verranno usate per accedere al pannello di amministrazione.",
|
||||
"setupConfigDesc": "Inserisci le informazioni su host e porta. Saranno usate per la configurazione dei client quando imposteranno WireGuard sui loro dispositivi.",
|
||||
"setupMigrationDesc": "Fornisci il file di backup se vuoi migrare i tuoi dati dalla precedente versione di wg-easy alla nuova installazione.",
|
||||
"upload": "Carica",
|
||||
"migration": "Ripristina backup:",
|
||||
"createAccount": "Crea Account",
|
||||
"successful": "Configurazione completata con successo",
|
||||
"hostDesc": "Nome host pubblico a cui i client si connetteranno",
|
||||
"portDesc": "Porta UDP pubblica a cui i client si connetteranno e su cui WireGuard resterà in ascolto"
|
||||
},
|
||||
"update": {
|
||||
"updateAvailable": "È disponibile un aggiornamento!",
|
||||
"update": "Aggiorna"
|
||||
},
|
||||
"theme": {
|
||||
"dark": "Tema scuro",
|
||||
"light": "Tema chiaro",
|
||||
"system": "Tema di sistema"
|
||||
},
|
||||
"layout": {
|
||||
"toggleCharts": "Mostra/nascondi Grafici",
|
||||
"donate": "Dona"
|
||||
},
|
||||
"login": {
|
||||
"signIn": "Accedi",
|
||||
"rememberMe": "Ricordami",
|
||||
"rememberMeDesc": "Rimani connesso dopo aver chiuso il browser",
|
||||
"insecure": "Non puoi accedere con una connessione non sicura. Usa HTTPS.",
|
||||
"2faRequired": "È richiesta l’Autenticazione a Due Fattori",
|
||||
"2faWrong": "Codice di Autenticazione a Due Fattori errato"
|
||||
},
|
||||
"client": {
|
||||
"empty": "Non ci sono ancora client.",
|
||||
"newShort": "Nuovo",
|
||||
"sort": "Ordina",
|
||||
"create": "Crea Client",
|
||||
"created": "Client creato",
|
||||
"new": "Nuovo Client",
|
||||
"name": "Nome",
|
||||
"expireDate": "Data di Scadenza",
|
||||
"expireDateDesc": "Data in cui il client verrà disabilitato. Lascia vuoto per permanente",
|
||||
"deleteClient": "Elimina Client",
|
||||
"deleteDialog1": "Sei sicuro di voler eliminare",
|
||||
"deleteDialog2": "Questa azione non può essere annullata.",
|
||||
"enabled": "Abilitato",
|
||||
"address": "Indirizzo",
|
||||
"serverAllowedIps": "IP consentiti dal Server",
|
||||
"otlDesc": "Genera link temporaneo monouso",
|
||||
"permanent": "Permanente",
|
||||
"createdOn": "Creato il ",
|
||||
"lastSeen": "Ultima connessione il ",
|
||||
"totalDownload": "Download Totale: ",
|
||||
"totalUpload": "Upload Totale: ",
|
||||
"newClient": "Nuovo Client",
|
||||
"disableClient": "Disabilita Client",
|
||||
"enableClient": "Abilita Client",
|
||||
"noPrivKey": "Questo client non ha una chiave privata nota. Impossibile creare la configurazione.",
|
||||
"showQR": "Mostra Codice QR",
|
||||
"downloadConfig": "Scarica Configurazione",
|
||||
"allowedIpsDesc": "Quali IP verranno instradati attraverso la VPN (sovrascrive la config globale)",
|
||||
"serverAllowedIpsDesc": "Quali IP il server instraderà al client",
|
||||
"mtuDesc": "Imposta la dimensione massima dei pacchetti (MTU) per il tunnel VPN",
|
||||
"persistentKeepaliveDesc": "Imposta l’intervallo (in secondi) dei pacchetti keep-alive. 0 lo disabilita",
|
||||
"hooks": "Hooks",
|
||||
"hooksDescription": "Gli hooks funzionano solo con wg-quick",
|
||||
"hooksLeaveEmpty": "Solo per wg-quick. Altrimenti lascia vuoto",
|
||||
"dnsDesc": "Server DNS che i client useranno (sovrascrive la config globale)",
|
||||
"notConnected": "Client non connesso",
|
||||
"endpoint": "Endpoint",
|
||||
"endpointDesc": "IP del client da cui viene stabilita la connessione WireGuard"
|
||||
},
|
||||
"dialog": {
|
||||
"change": "Modifica",
|
||||
"cancel": "Annulla",
|
||||
"create": "Crea"
|
||||
},
|
||||
"toast": {
|
||||
"success": "Operazione riuscita",
|
||||
"saved": "Salvato",
|
||||
"error": "Errore"
|
||||
},
|
||||
"form": {
|
||||
"actions": "Azioni",
|
||||
"save": "Salva",
|
||||
"revert": "Annulla modifiche",
|
||||
"sectionGeneral": "Generale",
|
||||
"sectionAdvanced": "Avanzate",
|
||||
"noItems": "Nessun elemento",
|
||||
"nullNoItems": "Nessun elemento. Uso configurazione globale",
|
||||
"add": "Aggiungi"
|
||||
},
|
||||
"admin": {
|
||||
"general": {
|
||||
"sessionTimeout": "Timeout Sessione",
|
||||
"sessionTimeoutDesc": "Durata della sessione per 'Ricordami' (in secondi)",
|
||||
"metrics": "Metriche",
|
||||
"metricsPassword": "Password",
|
||||
"metricsPasswordDesc": "Password Bearer per l’endpoint metriche (password o hash argon2)",
|
||||
"json": "JSON",
|
||||
"jsonDesc": "Percorso per metriche in formato JSON",
|
||||
"prometheus": "Prometheus",
|
||||
"prometheusDesc": "Percorso per metriche Prometheus"
|
||||
},
|
||||
"config": {
|
||||
"connection": "Connessione",
|
||||
"hostDesc": "Nome host pubblico a cui i client si connetteranno (invalida la config)",
|
||||
"portDesc": "Porta UDP pubblica a cui i client si connetteranno (invalida la config, probabilmente vuoi cambiare anche la Porta Interfaccia)",
|
||||
"allowedIpsDesc": "IP consentiti che i client useranno (config globale)",
|
||||
"dnsDesc": "Server DNS che i client useranno (config globale)",
|
||||
"mtuDesc": "MTU usata dai client (solo per i nuovi client)",
|
||||
"persistentKeepaliveDesc": "Intervallo in secondi per inviare keepalive al server. 0 = disabilitato (solo per nuovi client)",
|
||||
"suggest": "Suggerisci",
|
||||
"suggestDesc": "Scegli un indirizzo IP o un Hostname per il campo Host"
|
||||
},
|
||||
"interface": {
|
||||
"cidrSuccess": "CIDR modificato",
|
||||
"device": "Dispositivo",
|
||||
"deviceDesc": "Dispositivo di rete attraverso cui instradare il traffico WireGuard",
|
||||
"mtuDesc": "MTU usata da WireGuard",
|
||||
"portDesc": "Porta UDP su cui WireGuard resterà in ascolto (probabilmente vuoi cambiare anche la Porta Config)",
|
||||
"changeCidr": "Modifica CIDR",
|
||||
"restart": "Riavvia Interfaccia",
|
||||
"restartDesc": "Riavvia l’interfaccia WireGuard",
|
||||
"restartWarn": "Sei sicuro di riavviare l’interfaccia? Tutti i client verranno disconnessi.",
|
||||
"restartSuccess": "Interfaccia riavviata"
|
||||
},
|
||||
"introText": "Benvenuto nel pannello di amministrazione.\n\nQui puoi gestire le impostazioni generali, la configurazione, le impostazioni dell’interfaccia e gli hooks.\n\nInizia scegliendo una delle sezioni nella barra laterale."
|
||||
},
|
||||
"zod": {
|
||||
"generic": {
|
||||
"required": "{0} è obbligatorio",
|
||||
"validNumber": "{0} deve essere un numero valido",
|
||||
"validString": "{0} deve essere una stringa valida",
|
||||
"validBoolean": "{0} deve essere un valore booleano valido",
|
||||
"validArray": "{0} deve essere un array valido",
|
||||
"stringMin": "{0} deve contenere almeno {1} carattere",
|
||||
"numberMin": "{0} deve essere almeno {1}"
|
||||
},
|
||||
"client": {
|
||||
"id": "ID Client",
|
||||
"name": "Nome",
|
||||
"expiresAt": "Scadenza",
|
||||
"address4": "Indirizzo IPv4",
|
||||
"address6": "Indirizzo IPv6",
|
||||
"serverAllowedIps": "IP consentiti dal Server"
|
||||
},
|
||||
"user": {
|
||||
"username": "Nome utente",
|
||||
"password": "Password",
|
||||
"remember": "Ricorda",
|
||||
"name": "Nome",
|
||||
"email": "Email",
|
||||
"emailInvalid": "Email deve essere un indirizzo valido",
|
||||
"passwordMatch": "Le password devono coincidere",
|
||||
"totpEnable": "Abilitazione TOTP",
|
||||
"totpEnableTrue": "TOTP deve essere abilitato",
|
||||
"totpCode": "Codice TOTP"
|
||||
},
|
||||
"userConfig": {
|
||||
"host": "Host"
|
||||
},
|
||||
"general": {
|
||||
"sessionTimeout": "Timeout Sessione",
|
||||
"metricsEnabled": "Metriche",
|
||||
"metricsPassword": "Password Metriche"
|
||||
},
|
||||
"interface": {
|
||||
"cidr": "CIDR",
|
||||
"device": "Dispositivo",
|
||||
"cidrValid": "CIDR deve essere valido"
|
||||
},
|
||||
"otl": "Link Monouso",
|
||||
"stringMalformed": "Stringa non valida",
|
||||
"body": "Body deve essere un oggetto valido",
|
||||
"hook": "Hook",
|
||||
"enabled": "Abilitato",
|
||||
"mtu": "MTU",
|
||||
"port": "Porta",
|
||||
"persistentKeepalive": "Keepalive Persistente",
|
||||
"address": "Indirizzo IP",
|
||||
"dns": "DNS",
|
||||
"allowedIps": "IP consentiti",
|
||||
"file": "File"
|
||||
},
|
||||
"hooks": {
|
||||
"preUp": "PreUp",
|
||||
"postUp": "PostUp",
|
||||
"preDown": "PreDown",
|
||||
"postDown": "PostDown"
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user