Compare commits

...

20 Commits

Author SHA1 Message Date
theis.gaedigk da90d67cc0 changed icons
Edge / Build Docker (map[os:ubuntu-24.04-arm platform:linux/arm64]) (push) Has been cancelled
Edge / Build Docker (map[os:ubuntu-latest platform:linux/amd64]) (push) Has been cancelled
Edge / Merge & Deploy Docker (push) Has been cancelled
Edge / Build & Deploy Docs (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
Lint / Check Docs (push) Has been cancelled
Lint / Lint (format:check) (push) Has been cancelled
Lint / Lint (lint) (push) Has been cancelled
Lint / Lint (typecheck) (push) Has been cancelled
2026-05-20 10:37:40 +02:00
HackingAll a52da67b38 Adding new translated lines (#2624) 2026-05-19 14:24:37 +02:00
Bernd Storath e513090074 update badges 2026-05-18 12:36:21 +02:00
Bernd Storath 2dc8ba7792 Bump version to 15.3.0 2026-05-18 12:19:51 +02:00
Bernd Storath 4e8cccb4c7 replace debug with obug (#2619)
* patch unenv

* replaces debug with obug

reverts unenv patch
2026-05-18 09:40:36 +02:00
Bernd Storath e57b0977d3 update packages 2026-05-18 08:14:09 +02:00
Bernd Storath b8be53c3f7 fix build 2026-05-13 09:16:24 +02:00
Bernd Storath 0794413191 update packages 2026-05-13 09:02:56 +02:00
Bernd Storath 261b0d6b8f Bump version to 15.3.0-beta.3 2026-05-07 13:59:19 +02:00
Ming Mak f656d57d20 Translation: update Traditional Chinese (zh-HK) localization (#2603)
* Update Chinese (HK) translations for clarity and completion

* Fix spacing

* Update zh-HK.json

Taking reference from zh_TW, making awg clearer

* Remove duplicate 'search' entry in zh-HK.json

* Fix translation typos in zh-HK locale

Replaced "户" with "戶".
2026-05-07 13:51:37 +02:00
Bernd Storath 46074fea1c update packages 2026-05-05 10:57:30 +02:00
Felipe Cordova Huenupil 05c655ede9 fix(ui):Error pops up when enabling disabled client (#2594)
* fix(ui):Error pops up when enabling disabled client

Action is prevented and a clear message is displayed in Web UI

* fix formatting

* fix type issue

* fix formatting

---------

Co-authored-by: Bernd Storath <999999bst@gmail.com>
2026-05-04 09:21:18 +02:00
Bernd Storath ebcc42cc49 fix build 2026-04-27 09:07:21 +02:00
Evgeniy be8d24e492 Fix: Add trailing newline to Prometheus metrics output (#2573)
Fix prometheus metrics
2026-04-27 08:30:03 +02:00
Bernd Storath 9682dedea7 update packages 2026-04-27 08:28:17 +02:00
Bernd Storath 5eb80fe3c1 update packages 2026-04-13 14:16:05 +02:00
dependabot[bot] dd9da2a067 build(deps): bump pnpm/action-setup from 5 to 6 (#2574)
Bumps [pnpm/action-setup](https://github.com/pnpm/action-setup) from 5 to 6.
- [Release notes](https://github.com/pnpm/action-setup/releases)
- [Commits](https://github.com/pnpm/action-setup/compare/v5...v6)

---
updated-dependencies:
- dependency-name: pnpm/action-setup
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-13 13:35:40 +02:00
Bernd Storath 15111ecd62 update packages 2026-04-13 13:02:53 +02:00
dependabot[bot] e9f4b4650b build(deps): bump pnpm/action-setup from 4 to 5 (#2570)
Bumps [pnpm/action-setup](https://github.com/pnpm/action-setup) from 4 to 5.
- [Release notes](https://github.com/pnpm/action-setup/releases)
- [Commits](https://github.com/pnpm/action-setup/compare/v4...v5)

---
updated-dependencies:
- dependency-name: pnpm/action-setup
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-08 16:06:15 +02:00
Bernd Storath e3e4049f8e update packages 2026-04-08 15:51:37 +02:00
23 changed files with 3603 additions and 2837 deletions
+2 -2
View File
@@ -16,7 +16,7 @@ jobs:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v6
- uses: pnpm/action-setup@v4 - uses: pnpm/action-setup@v6
name: Install pnpm name: Install pnpm
with: with:
run_install: false run_install: false
@@ -49,7 +49,7 @@ jobs:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v6
- uses: pnpm/action-setup@v4 - uses: pnpm/action-setup@v6
name: Install pnpm name: Install pnpm
with: with:
run_install: false run_install: false
+6
View File
@@ -14,11 +14,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- CLI: Show QR code (https://github.com/wg-easy/wg-easy/pull/2518) - CLI: Show QR code (https://github.com/wg-easy/wg-easy/pull/2518)
- Copy QR code to clipboard / save as png (https://github.com/wg-easy/wg-easy/pull/2521) - Copy QR code to clipboard / save as png (https://github.com/wg-easy/wg-easy/pull/2521)
### Fixed
- Add trailing newline to Prometheus metrics output (https://github.com/wg-easy/wg-easy/pull/2573)
- Correctly use DEBUG env var (https://github.com/wg-easy/wg-easy/pull/2619)
### Changed ### Changed
- Hooks are now Textareas (https://github.com/wg-easy/wg-easy/pull/2522) - Hooks are now Textareas (https://github.com/wg-easy/wg-easy/pull/2522)
- Update to Node Krypton (24) (https://github.com/wg-easy/wg-easy/pull/2536) - Update to Node Krypton (24) (https://github.com/wg-easy/wg-easy/pull/2536)
- Mobile UI (https://github.com/wg-easy/wg-easy/pull/2569) - Mobile UI (https://github.com/wg-easy/wg-easy/pull/2569)
- Prevent enabling client when expired (https://github.com/wg-easy/wg-easy/pull/2594)
## [15.2.2] - 2026-02-06 ## [15.2.2] - 2026-02-06
+8 -5
View File
@@ -7,7 +7,7 @@ RUN npm install --global corepack@latest
RUN corepack enable pnpm RUN corepack enable pnpm
# Copy Web UI # Copy Web UI
COPY src/package.json src/pnpm-lock.yaml ./ COPY src/package.json src/pnpm-lock.yaml src/pnpm-workspace.yaml ./
RUN pnpm install RUN pnpm install
# Build UI # Build UI
@@ -23,6 +23,10 @@ RUN apk add linux-headers build-base go git && \
cd ../amneziawg-tools/src && \ cd ../amneziawg-tools/src && \
make make
FROM docker.io/library/node:krypton-alpine AS build-libsql
WORKDIR /app
RUN npm install --no-save --omit=dev libsql
# Copy build result to a new image. # Copy build result to a new image.
# This saves a lot of disk space. # This saves a lot of disk space.
FROM docker.io/library/node:krypton-alpine FROM docker.io/library/node:krypton-alpine
@@ -35,9 +39,8 @@ COPY --from=build /app/.output /app
# Copy migrations # Copy migrations
COPY --from=build /app/server/database/migrations /app/server/database/migrations COPY --from=build /app/server/database/migrations /app/server/database/migrations
# libsql (https://github.com/nitrojs/nitro/issues/3328) # libsql (https://github.com/nitrojs/nitro/issues/3328)
RUN cd /app/server && \ COPY --from=build-libsql /app/node_modules /app/server/node_modules
npm install --no-save --omit=dev libsql && \
npm cache clean --force
# cli # cli
COPY --from=build /app/cli/cli.sh /usr/local/bin/cli COPY --from=build /app/cli/cli.sh /usr/local/bin/cli
RUN chmod +x /usr/local/bin/cli RUN chmod +x /usr/local/bin/cli
@@ -69,7 +72,7 @@ RUN update-alternatives --install /usr/sbin/iptables iptables /usr/sbin/iptables
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 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
# Set Environment # Set Environment
ENV DEBUG=Server,WireGuard,Database,CMD ENV DEBUG=Server,WireGuard,Database,CMD,Firewall
ENV PORT=51821 ENV PORT=51821
ENV HOST=0.0.0.0 ENV HOST=0.0.0.0
ENV INSECURE=false ENV INSECURE=false
+2 -2
View File
@@ -24,7 +24,7 @@ RUN update-alternatives --install /usr/sbin/iptables iptables /usr/sbin/iptables
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 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
# Set Environment # Set Environment
ENV DEBUG=Server,WireGuard,Database,CMD ENV DEBUG=Server,WireGuard,Database,CMD,Firewall
ENV PORT=51821 ENV PORT=51821
ENV HOST=0.0.0.0 ENV HOST=0.0.0.0
ENV INSECURE=true ENV INSECURE=true
@@ -32,7 +32,7 @@ ENV INIT_ENABLED=false
ENV DISABLE_IPV6=false ENV DISABLE_IPV6=false
# Install Dependencies # Install Dependencies
COPY src/package.json src/pnpm-lock.yaml ./ COPY src/package.json src/pnpm-lock.yaml src/pnpm-workspace.yaml ./
RUN pnpm install RUN pnpm install
# Copy Project # Copy Project
+2 -4
View File
@@ -1,16 +1,14 @@
# WireGuard Easy # WireGuard Easy
[![Build & Publish latest Image](https://github.com/wg-easy/wg-easy/actions/workflows/deploy.yml/badge.svg?branch=production)](https://github.com/wg-easy/wg-easy/actions/workflows/deploy.yml) [![Build & Publish latest Image](https://github.com/wg-easy/wg-easy/actions/workflows/deploy.yml/badge.svg)](https://github.com/wg-easy/wg-easy/actions/workflows/deploy.yml)
[![Lint](https://github.com/wg-easy/wg-easy/actions/workflows/lint.yml/badge.svg?branch=master)](https://github.com/wg-easy/wg-easy/actions/workflows/lint.yml) [![Lint](https://github.com/wg-easy/wg-easy/actions/workflows/lint.yml/badge.svg?branch=master)](https://github.com/wg-easy/wg-easy/actions/workflows/lint.yml)
[![GitHub Stars](https://img.shields.io/github/stars/wg-easy/wg-easy)](https://github.com/wg-easy/wg-easy/stargazers) [![GitHub Stars](https://img.shields.io/github/stars/wg-easy/wg-easy)](https://github.com/wg-easy/wg-easy/stargazers)
[![License](https://img.shields.io/github/license/wg-easy/wg-easy)](LICENSE) [![License](https://img.shields.io/github/license/wg-easy/wg-easy)](LICENSE)
[![GitHub Release](https://img.shields.io/github/v/release/wg-easy/wg-easy)](https://github.com/wg-easy/wg-easy/releases/latest) [![GitHub Release](https://img.shields.io/github/v/release/wg-easy/wg-easy)](https://github.com/wg-easy/wg-easy/releases/latest)
[![Image Pulls](https://img.shields.io/badge/image_pulls-12M+-blue)](https://github.com/wg-easy/wg-easy/pkgs/container/wg-easy) [![Image Pulls](https://img.shields.io/badge/image_pulls-28M+-blue)](https://github.com/wg-easy/wg-easy/pkgs/container/wg-easy)
You have found the easiest way to install & manage WireGuard on any Linux host! You have found the easiest way to install & manage WireGuard on any Linux host!
<!-- TOOD: update screenshot -->
<p align="center"> <p align="center">
<img src="./assets/screenshot.png" width="802" alt="wg-easy Screenshot" /> <img src="./assets/screenshot.png" width="802" alt="wg-easy Screenshot" />
</p> </p>
+2 -2
View File
@@ -11,7 +11,7 @@
"format:check:docs": "prettier --check docs" "format:check:docs": "prettier --check docs"
}, },
"devDependencies": { "devDependencies": {
"prettier": "^3.8.1" "prettier": "^3.8.3"
}, },
"packageManager": "pnpm@10.32.1" "packageManager": "pnpm@11.1.2"
} }
+5 -5
View File
@@ -9,16 +9,16 @@ importers:
.: .:
devDependencies: devDependencies:
prettier: prettier:
specifier: ^3.8.1 specifier: ^3.8.3
version: 3.8.1 version: 3.8.3
packages: packages:
prettier@3.8.1: prettier@3.8.3:
resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==} resolution: {integrity: sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==}
engines: {node: '>=14'} engines: {node: '>=14'}
hasBin: true hasBin: true
snapshots: snapshots:
prettier@3.8.1: {} prettier@3.8.3: {}
+1 -1
View File
@@ -1 +1 @@
setups.@nuxt/test-utils="4.0.0" setups.@nuxt/test-utils="4.0.3"
+1 -1
View File
@@ -16,7 +16,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import type { ApexOptions } from 'apexcharts'; import type { ApexChart, ApexOptions } from 'apexcharts';
defineProps<{ defineProps<{
client: LocalClient; client: LocalClient;
+152 -1
View File
@@ -120,7 +120,11 @@
"endpointDesc": "IP do cliente desde a que se establece a conexión WireGuard", "endpointDesc": "IP do cliente desde a que se establece a conexión WireGuard",
"search": "Buscar clientes...", "search": "Buscar clientes...",
"config": "Configuración", "config": "Configuración",
"viewConfig": "Ver configuración" "viewConfig": "Ver configuración",
"firewallIps": "IPs permitidas do cortalumes",
"firewallIpsDesc": "IPs/CIDRs de destino aos que este cliente pode acceder (aplicado no lado do servidor). Déixao baleiro para usar as IPs permitidas. Admite filtrado opcional por porto e protocolo. Consulta a documentación para a sintaxe.",
"downloadPng": "Descargar PNG",
"copyPng": "Copiar PNG"
}, },
"dialog": { "dialog": {
"change": "Cambiar", "change": "Cambiar",
@@ -141,5 +145,152 @@
"noItems": "Sen elementos", "noItems": "Sen elementos",
"nullNoItems": "Sen elementos. Usando a configuración global", "nullNoItems": "Sen elementos. Usando a configuración global",
"add": "Engadir" "add": "Engadir"
},
"admin": {
"general": {
"sessionTimeout": "Tempo límite da sesión",
"sessionTimeoutDesc": "Duración da sesión para Lembrarme (segundos)",
"metrics": "Métricas",
"metricsPassword": "Contrasinal",
"metricsPasswordDesc": "Contrasinal Bearer para o endpoint de métricas (contrasinal ou hash argon2)",
"json": "JSON",
"jsonDesc": "Ruta para as métricas en formato JSON",
"prometheus": "Prometheus",
"prometheusDesc": "Ruta para as métricas de Prometheus"
},
"config": {
"connection": "Conexión",
"hostDesc": "Nome de host público ao que se conectarán os clientes (invalida a configuración)",
"portDesc": "Porto UDP público ao que se conectarán os clientes (invalida a configuración; probablemente tamén queiras cambiar o Porto da interface)",
"allowedIpsDesc": "IPs permitidas que usarán os clientes (configuración global)",
"dnsDesc": "Servidor DNS que usarán os clientes (configuración global)",
"mtuDesc": "MTU que usarán os clientes (só para clientes novos)",
"persistentKeepaliveDesc": "Intervalo en segundos para enviar keepalives ao servidor. 0 = desactivado (só para clientes novos)",
"suggest": "Suxerir",
"suggestDesc": "Escolle un enderezo IP ou nome de host para o campo Host"
},
"interface": {
"cidrSuccess": "CIDR cambiado",
"device": "Dispositivo",
"deviceDesc": "Dispositivo Ethernet polo que se redirixirá o tráfico de WireGuard",
"mtuDesc": "MTU que usará WireGuard",
"portDesc": "Porto UDP no que WireGuard escoitará (probablemente tamén queiras cambiar o Porto da configuración)",
"changeCidr": "Cambiar CIDR",
"restart": "Reiniciar interface",
"restartDesc": "Reiniciar a interface de WireGuard",
"restartWarn": "Seguro que queres reiniciar a interface? Isto desconectará todos os clientes.",
"restartSuccess": "Interface reiniciada",
"firewall": "Filtrado de tráfico",
"firewallEnabled": "Activar devasa por cliente",
"firewallEnabledDesc": "Restrinxir o tráfico dos clientes a IPs de destino específicas usando iptables. Cando está activado, cada cliente pode configurarse con destinos permitidos."
},
"introText": "Benvido ao panel de administración.\n\nAquí podes xestionar a configuración xeral, a configuración, os axustes da interface e os hooks.\n\nComeza escollendo unha das seccións na barra lateral."
},
"zod": {
"generic": {
"required": "{0} é obrigatorio",
"validNumber": "{0} debe ser un número válido",
"validNumberRange": "{0} debe ser un número válido ou un rango de números",
"validString": "{0} debe ser unha cadea válida",
"validBoolean": "{0} debe ser un booleano válido",
"validArray": "{0} debe ser un array válido",
"stringMin": "{0} debe ter polo menos {1} carácter",
"numberMin": "{0} debe ser polo menos {1}"
},
"client": {
"id": "ID do cliente",
"name": "Nome",
"expiresAt": "Caduca o",
"address4": "Enderezo IPv4",
"address6": "Enderezo IPv6",
"serverAllowedIps": "IPs permitidas do servidor",
"firewallIps": "IPs permitidas da devasa",
"firewallIpsInvalid": "Entrada de IP da devasa non válida. Consulta a documentación para a sintaxe admitida."
},
"user": {
"username": "Nome de usuario",
"password": "Contrasinal",
"remember": "Lembrar",
"name": "Nome",
"email": "Correo electrónico",
"emailInvalid": "O correo electrónico debe ser válido",
"passwordMatch": "Os contrasinais deben coincidir",
"totpEnable": "Activar TOTP",
"totpEnableTrue": "Activar TOTP debe ser verdadeiro",
"totpCode": "Código TOTP"
},
"userConfig": {
"host": "Host"
},
"general": {
"sessionTimeout": "Tempo límite da sesión",
"metricsEnabled": "Métricas",
"metricsPassword": "Contrasinal das métricas"
},
"interface": {
"cidr": "CIDR",
"device": "Dispositivo",
"cidrValid": "O CIDR debe ser válido"
},
"otl": "Ligazón dun só uso",
"stringMalformed": "A cadea está mal formada",
"body": "O corpo debe ser un obxecto válido",
"hook": "Hook",
"enabled": "Activado",
"mtu": "MTU",
"port": "Porto",
"persistentKeepalive": "Keepalive persistente",
"address": "Enderezo IP",
"dns": "DNS",
"allowedIps": "IPs permitidas",
"file": "Ficheiro"
},
"hooks": {
"preUp": "PreUp",
"postUp": "PostUp",
"preDown": "PreDown",
"postDown": "PostDown"
},
"copy": {
"notSupported": "Copiar non é compatible",
"copied": "Copiado!",
"failed": "Erro ao copiar",
"copy": "Copiar"
},
"awg": {
"jCLabel": "Número de paquetes lixo (Jc)",
"jCDescription": "Número de paquetes lixo para enviar (1-128, recomendado: 4-12)",
"jMinLabel": "Tamaño mínimo dos paquetes lixo (Jmin)",
"jMinDescription": "Tamaño mínimo dos paquetes lixo (0-1279*, recomendado: 8, debe ser < Jmax)",
"jMaxLabel": "Tamaño máximo dos paquetes lixo (Jmax)",
"jMaxDescription": "Tamaño máximo dos paquetes lixo (1-1280*, recomendado: 80, debe ser > Jmin)",
"s1Label": "Tamaño lixo do paquete de inicio (S1)",
"s1Description": "Tamaño lixo do paquete de inicio (0-1132[1280* - 148 = 1132], recomendado: 15-150, S1+56 ≠ S2)",
"s2Label": "Tamaño lixo do paquete de resposta (S2)",
"s2Description": "Tamaño lixo do paquete de resposta (0-1188[1280* - 92 = 1188], recomendado: 15-150)",
"s3Label": "Tamaño lixo do paquete de resposta cookie (S3)",
"s3Description": "Tamaño lixo do paquete de resposta cookie",
"s4Label": "Tamaño lixo do paquete de transporte (S4)",
"s4Description": "Tamaño lixo do paquete de transporte",
"h1Label": "Cabeceira máxica de inicio (H1)",
"h1Description": "Valor ou rango da cabeceira do paquete de inicio (X ou X-Y, onde X<Y. Mín 5, máx 2147483647. O valor ou rango non debe solaparse con outras cabeceiras)",
"h2Label": "Cabeceira máxica de resposta (H2)",
"h2Description": "Valor ou rango da cabeceira do paquete de resposta (X ou X-Y, onde X<Y. Mín 5, máx 2147483647. O valor ou rango non debe solaparse con outras cabeceiras)",
"h3Label": "Cabeceira máxica da resposta cookie (H3)",
"h3Description": "Valor ou rango da cabeceira do paquete de resposta cookie (X ou X-Y, onde X<Y. Mín 5, máx 2147483647. O valor ou rango non debe solaparse con outras cabeceiras)",
"h4Label": "Cabeceira máxica de transporte (H4)",
"h4Description": "Valor ou rango da cabeceira do paquete de transporte (X ou X-Y, onde X<Y. Mín 5, máx 2147483647. O valor ou rango non debe solaparse con outras cabeceiras)",
"i1Label": "Paquete lixo especial 1 (I1)",
"i1Description": "Paquete de simulación de protocolo en formato hexadecimal: <b 0x...>",
"i2Label": "Paquete lixo especial 2 (I2)",
"i2Description": "Paquete de simulación de protocolo en formato hexadecimal: <b 0x...>",
"i3Label": "Paquete lixo especial 3 (I3)",
"i3Description": "Paquete de simulación de protocolo en formato hexadecimal: <b 0x...>",
"i4Label": "Paquete lixo especial 4 (I4)",
"i4Description": "Paquete de simulación de protocolo en formato hexadecimal: <b 0x...>",
"i5Label": "Paquete lixo especial 5 (I5)",
"i5Description": "Paquete de simulación de protocolo en formato hexadecimal: <b 0x...>",
"mtuNote": "Os valores dependen da MTU",
"obfuscationParameters": "Parámetros de ofuscación de AmneziaWG"
} }
} }
+68 -9
View File
@@ -45,9 +45,9 @@
}, },
"setup": { "setup": {
"welcome": "歡迎首次設定wg-easy", "welcome": "歡迎首次設定wg-easy",
"welcomeDesc": "這是最簡單的方法讓你在任何Linux主機上安裝管理WireGuard", "welcomeDesc": "這是在任何Linux主機上安裝管理WireGuard最簡單的方法。",
"existingSetup": "你有現存設定嗎?", "existingSetup": "你有現存設定嗎?",
"createAdminDesc": "請輸入管理員用戶名及一個高強度密碼。這些資料將用於登入管理員版面。", "createAdminDesc": "請輸入管理員用戶名及高強度密碼。這些資料將用於登入管理員版面。",
"setupConfigDesc": "請輸入主機及連接埠資料。這將用於客戶端設定,以便在他們的裝置上設定WireGuard。", "setupConfigDesc": "請輸入主機及連接埠資料。這將用於客戶端設定,以便在他們的裝置上設定WireGuard。",
"setupMigrationDesc": "如果你想將舊版wg-easy的資料轉移到新設定,請提供備份檔案。", "setupMigrationDesc": "如果你想將舊版wg-easy的資料轉移到新設定,請提供備份檔案。",
"upload": "上傳", "upload": "上傳",
@@ -88,6 +88,7 @@
"name": "名稱", "name": "名稱",
"expireDate": "有效期", "expireDate": "有效期",
"expireDateDesc": "客戶端將於此日期停用。留空則為永久有效", "expireDateDesc": "客戶端將於此日期停用。留空則為永久有效",
"delete": "刪除",
"deleteClient": "刪除客戶端", "deleteClient": "刪除客戶端",
"deleteDialog1": "你確定要刪除", "deleteDialog1": "你確定要刪除",
"deleteDialog2": "此操作無法還原。", "deleteDialog2": "此操作無法還原。",
@@ -114,21 +115,31 @@
"hooksDescription": "掛鉤僅適用於wg-quick", "hooksDescription": "掛鉤僅適用於wg-quick",
"hooksLeaveEmpty": "僅適用於wg-quick,否則請留空", "hooksLeaveEmpty": "僅適用於wg-quick,否則請留空",
"dnsDesc": "客戶端使用的域名系統伺服器(取代全局配置)", "dnsDesc": "客戶端使用的域名系統伺服器(取代全局配置)",
"search": "搜尋客戶端..." "notConnected": "客戶端尚未連接",
"endpoint": "端點",
"endpointDesc": "建立WireGuard連線的客戶端IP地址",
"search": "搜尋客戶端...",
"config": "配置",
"viewConfig": "檢視配置",
"firewallIps": "防火牆IP白名單",
"firewallIpsDesc": "此客戶端可存取的目的地IP/CIDR(伺服器端強制執行)。留空則使用「IP白名單」。支援選填的連接埠及協定過濾,語法請參閱說明文件。",
"downloadPng": "正在下載PNG",
"copyPng": "複製PNG"
}, },
"dialog": { "dialog": {
"change": "更改", "change": "更改",
"cancel": "取消", "cancel": "取消",
"create": "建" "create": "建"
}, },
"toast": { "toast": {
"success": "成功", "success": "成功",
"saved": "已存", "saved": "已存",
"error": "錯誤" "error": "錯誤",
"unknown": "未知錯誤。詳情請參閱主控台。"
}, },
"form": { "form": {
"actions": "操作", "actions": "操作",
"save": "存", "save": "存",
"revert": "重置", "revert": "重置",
"sectionGeneral": "一般設定", "sectionGeneral": "一般設定",
"sectionAdvanced": "進階", "sectionAdvanced": "進階",
@@ -169,7 +180,10 @@
"restart": "重啟介面", "restart": "重啟介面",
"restartDesc": "重啟WireGuard介面", "restartDesc": "重啟WireGuard介面",
"restartWarn": "你確定要重新啟動介面嗎?這將中斷所有客戶端連線。", "restartWarn": "你確定要重新啟動介面嗎?這將中斷所有客戶端連線。",
"restartSuccess": "介面已重啟" "restartSuccess": "介面已重啟",
"firewall": "流量過濾",
"firewallEnabled": "啟用獨立客戶端防火牆",
"firewallEnabledDesc": "使用iptables以限制客戶端流量至特定的目的地IP。啟用後,即可為每個客戶端單獨設定允許目的地。"
}, },
"introText": "歡迎來到管理員版面。\n\n你可以在側邊欄中管理一般、配置、介面和掛鉤設定。" "introText": "歡迎來到管理員版面。\n\n你可以在側邊欄中管理一般、配置、介面和掛鉤設定。"
}, },
@@ -177,6 +191,7 @@
"generic": { "generic": {
"required": "{0}為必填項", "required": "{0}為必填項",
"validNumber": "{0}必須是有效的數字", "validNumber": "{0}必須是有效的數字",
"validNumberRange": "{0}必須是有效的數字或數字範圍",
"validString": "{0}必須是有效的字串", "validString": "{0}必須是有效的字串",
"validBoolean": "{0}必須是有效的布林值", "validBoolean": "{0}必須是有效的布林值",
"validArray": "{0}必須是有效的陣列", "validArray": "{0}必須是有效的陣列",
@@ -189,7 +204,9 @@
"expiresAt": "到期於", "expiresAt": "到期於",
"address4": "IPv4地址", "address4": "IPv4地址",
"address6": "IPv6地址", "address6": "IPv6地址",
"serverAllowedIps": "伺服器IP白名單" "serverAllowedIps": "伺服器IP白名單",
"firewallIps": "防火牆IP白名單",
"firewallIpsInvalid": "防火牆IP輸入格式無效。請參閱說明文件以查看支援語法。"
}, },
"user": { "user": {
"username": "用戶名", "username": "用戶名",
@@ -234,5 +251,47 @@
"postUp": "PostUp", "postUp": "PostUp",
"preDown": "PreDown", "preDown": "PreDown",
"postDown": "PostDown" "postDown": "PostDown"
},
"copy": {
"notSupported": "不支援複製",
"copied": "已複製!",
"failed": "複製失敗",
"copy": "複製"
},
"awg": {
"jCLabel": "填充封包數量 (Jc)",
"jCDescription": "要傳送的填充封包數量 (1-128,建議: 4-12)",
"jMinLabel": "填充封包最小大小 (Jmin)",
"jMinDescription": "填充封包的最小大小 (0-1279*,建議: 8,必須<Jmax)",
"jMaxLabel": "填充封包最大大小 (Jmax)",
"jMaxDescription": "填充封包的最大大小 (1-1280*,建議: 80,必須>Jmin)",
"s1Label": "初始封包填充大小 (S1)",
"s1Description": "初始封包填充大小 (0-1132 [1280* - 148 = 1132],建議: 15-150S1+56 ≠ S2)",
"s2Label": "回應封包填充大小 (S2)",
"s2Description": "回應封包填充大小 (0-1188 [1280* - 92 = 1188],建議: 15-150)",
"s3Label": "Cookie 回覆封包填充大小 (S3)",
"s3Description": "Cookie 回覆封包填充大小",
"s4Label": "傳輸封包填充大小 (S4)",
"s4Description": "傳輸封包填充大小",
"h1Label": "初始特徵標頭 (H1)",
"h1Description": "初始封包標頭值 (5-2147483647,必須與 H2-H4 不同)",
"h2Label": "回應特徵標頭 (H2)",
"h2Description": "回應封包標頭值 (5-2147483647,必須與 H1、H3、H4 不同)",
"h3Label": "Cookie 回覆特徵標頭 (H3)",
"h3Description": "Cookie 回覆封包標頭值 (5-2147483647,必須與 H1、H2、H4 不同)",
"h4Label": "傳輸特徵標頭 (H4)",
"h4Description": "傳輸封包標頭值 (5-2147483647,必須與 H1-H3 不同)",
"i1Label": "特殊填充封包 1 (I1)",
"i1Description": "協定模仿封包 (16 進位格式): <b 0x...>",
"i2Label": "特殊填充封包 2 (I2)",
"i2Description": "協定模仿封包 (16 進位格式): <b 0x...>",
"i3Label": "特殊填充封包 3 (I3)",
"i3Description": "協定模仿封包 (16 進位格式): <b 0x...>",
"i4Label": "特殊填充封包 4 (I4)",
"i4Description": "協定模仿封包 (16 進位格式): <b 0x...>",
"i5Label": "特殊填充封包 5 (I5)",
"i5Description": "協定模仿封包 (16 進位格式): <b 0x...>",
"mtuNote": "數值取決於 MTU",
"obfuscationParameters": "AmneziaWG 混淆參數"
} }
} }
+29 -30
View File
@@ -1,6 +1,6 @@
{ {
"name": "wg-easy", "name": "wg-easy",
"version": "15.3.0-beta.2", "version": "15.3.0",
"description": "The easiest way to run WireGuard VPN + Web-based Admin UI.", "description": "The easiest way to run WireGuard VPN + Web-based Admin UI.",
"private": true, "private": true,
"type": "module", "type": "module",
@@ -23,56 +23,55 @@
"dependencies": { "dependencies": {
"@eschricht/nuxt-color-mode": "^1.2.0", "@eschricht/nuxt-color-mode": "^1.2.0",
"@heroicons/vue": "^2.2.0", "@heroicons/vue": "^2.2.0",
"@libsql/client": "^0.17.0", "@libsql/client": "^0.17.3",
"@nuxtjs/i18n": "^10.2.3", "@nuxtjs/i18n": "^10.3.0",
"@nuxtjs/tailwindcss": "^6.14.0", "@nuxtjs/tailwindcss": "^6.14.0",
"@phc/format": "^1.0.0", "@phc/format": "^1.0.0",
"@pinia/nuxt": "^0.11.3", "@pinia/nuxt": "^0.11.3",
"@tailwindcss/forms": "^0.5.11", "@tailwindcss/forms": "^0.5.11",
"@vueuse/core": "^14.2.1", "@vueuse/core": "^14.3.0",
"@vueuse/nuxt": "^14.2.1", "@vueuse/nuxt": "^14.3.0",
"apexcharts": "^5.10.4", "apexcharts": "^5.12.0",
"argon2": "^0.44.0", "argon2": "^0.44.0",
"cidr-tools": "^11.2.1", "cidr-tools": "^12.0.1",
"citty": "^0.2.1", "citty": "^0.2.2",
"consola": "^3.4.2", "consola": "^3.4.2",
"crc-32": "^1.2.2", "crc-32": "^1.2.2",
"debug": "^4.4.3", "drizzle-orm": "^0.45.2",
"drizzle-orm": "^0.45.1", "ip-bigint": "^9.0.4",
"ip-bigint": "^8.2.12", "is-cidr": "^7.0.0",
"is-cidr": "^6.0.3",
"is-ip": "^5.0.1", "is-ip": "^5.0.1",
"js-sha256": "^0.11.1", "js-sha256": "^0.11.1",
"nuxt": "^3.21.2", "nuxt": "^3.21.5",
"otpauth": "^9.5.0", "obug": "^2.1.1",
"otpauth": "^9.5.1",
"pinia": "^3.0.4", "pinia": "^3.0.4",
"qr": "^0.5.5", "qr": "^0.6.0",
"radix-vue": "^1.9.17", "radix-vue": "^1.9.17",
"semver": "^7.7.4", "semver": "^7.8.0",
"tailwindcss": "^3.4.19", "tailwindcss": "^3.4.19",
"timeago.js": "^4.0.2", "timeago.js": "^4.0.2",
"vue": "latest", "vue": "latest",
"vue3-apexcharts": "^1.11.1", "vue3-apexcharts": "^1.11.1",
"zod": "^4.3.6" "zod": "^4.4.3"
}, },
"devDependencies": { "devDependencies": {
"@nuxt/eslint": "^1.15.2", "@nuxt/eslint": "^1.15.2",
"@nuxt/test-utils": "^4.0.0", "@nuxt/test-utils": "^4.0.3",
"@types/debug": "^4.1.12",
"@types/phc__format": "^1.0.1", "@types/phc__format": "^1.0.1",
"@types/semver": "^7.7.1", "@types/semver": "^7.7.1",
"@vitest/coverage-v8": "^4.1.0", "@vitest/coverage-v8": "^4.1.6",
"@vitest/ui": "^4.1.0", "@vitest/ui": "^4.1.6",
"drizzle-kit": "^0.31.9", "drizzle-kit": "^0.31.10",
"esbuild": "^0.27.4", "esbuild": "^0.28.0",
"eslint": "^9.39.4", "eslint": "^9.39.4",
"eslint-config-prettier": "^10.1.8", "eslint-config-prettier": "^10.1.8",
"prettier": "^3.8.1", "prettier": "^3.8.3",
"prettier-plugin-tailwindcss": "^0.7.2", "prettier-plugin-tailwindcss": "^0.8.0",
"tsx": "^4.21.0", "tsx": "^4.22.1",
"typescript": "^5.9.3", "typescript": "^6.0.3",
"vitest": "^4.1.0", "vitest": "^4.1.6",
"vue-tsc": "^3.2.5" "vue-tsc": "^3.2.9"
}, },
"packageManager": "pnpm@10.32.1" "packageManager": "pnpm@11.1.2"
} }
+3295 -2765
View File
File diff suppressed because it is too large Load Diff
+6
View File
@@ -0,0 +1,6 @@
allowBuilds:
'@parcel/watcher': false
argon2: false
esbuild: false
unrs-resolver: false
vue-demi: false
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 1.9 MiB

@@ -12,6 +12,19 @@ export default definePermissionEventHandler(
const client = await Database.clients.get(clientId); const client = await Database.clients.get(clientId);
checkPermissions(client); checkPermissions(client);
if (
client &&
client.expiresAt !== null &&
new Date() > new Date(client.expiresAt)
) {
throw createError({
statusCode: 422,
statusMessage:
'Client is expired. Please update the expiration date first.',
message: 'Client is expired. Please update the expiration date first.',
});
}
await Database.clients.toggle(clientId, true); await Database.clients.toggle(clientId, true);
await WireGuard.saveConfig(); await WireGuard.saveConfig();
return { success: true }; return { success: true };
+2 -2
View File
@@ -1,7 +1,7 @@
import { drizzle } from 'drizzle-orm/libsql'; import { drizzle } from 'drizzle-orm/libsql';
import { migrate as drizzleMigrate } from 'drizzle-orm/libsql/migrator'; import { migrate as drizzleMigrate } from 'drizzle-orm/libsql/migrator';
import { createClient } from '@libsql/client'; import { createClient } from '@libsql/client';
import debug from 'debug'; import { createDebug } from 'obug';
import { eq } from 'drizzle-orm'; import { eq } from 'drizzle-orm';
import * as schema from './schema'; import * as schema from './schema';
@@ -13,7 +13,7 @@ import { InterfaceService } from './repositories/interface/service';
import { HooksService } from './repositories/hooks/service'; import { HooksService } from './repositories/hooks/service';
import { OneTimeLinkService } from './repositories/oneTimeLink/service'; import { OneTimeLinkService } from './repositories/oneTimeLink/service';
const DB_DEBUG = debug('Database'); const DB_DEBUG = createDebug('Database');
const client = createClient({ url: 'file:/etc/wireguard/wg-easy.db' }); const client = createClient({ url: 'file:/etc/wireguard/wg-easy.db' });
const db = drizzle({ client, schema }); const db = drizzle({ client, schema });
@@ -62,6 +62,7 @@ async function getPrometheusResponse() {
'# HELP wireguard_latest_handshake_seconds UNIX timestamp seconds of the last handshake', '# HELP wireguard_latest_handshake_seconds UNIX timestamp seconds of the last handshake',
'# TYPE wireguard_latest_handshake_seconds gauge', '# TYPE wireguard_latest_handshake_seconds gauge',
`${wireguardLatestHandshakeSeconds.join('\n')}`, `${wireguardLatestHandshakeSeconds.join('\n')}`,
'',
]; ];
return returnText.join('\n'); return returnText.join('\n');
+2 -2
View File
@@ -1,8 +1,8 @@
import fs from 'node:fs/promises'; import fs from 'node:fs/promises';
import debug from 'debug'; import { createDebug } from 'obug';
import type { InterfaceType } from '#db/repositories/interface/types'; import type { InterfaceType } from '#db/repositories/interface/types';
const WG_DEBUG = debug('WireGuard'); const WG_DEBUG = createDebug('WireGuard');
const generateRandomHeaderValue = () => const generateRandomHeaderValue = () =>
Math.floor(Math.random() * 2147483642) + 5; Math.floor(Math.random() * 2147483642) + 5;
+2 -2
View File
@@ -1,7 +1,7 @@
import childProcess from 'child_process'; import childProcess from 'child_process';
import debug from 'debug'; import { createDebug } from 'obug';
const CMD_DEBUG = debug('CMD'); const CMD_DEBUG = createDebug('CMD');
export function exec( export function exec(
cmd: string, cmd: string,
+2 -2
View File
@@ -1,9 +1,9 @@
import debug from 'debug'; import { createDebug } from 'obug';
import packageJson from '@@/package.json'; import packageJson from '@@/package.json';
export const RELEASE = 'v' + packageJson.version; export const RELEASE = 'v' + packageJson.version;
export const SERVER_DEBUG = debug('Server'); export const SERVER_DEBUG = createDebug('Server');
export const OLD_ENV = { export const OLD_ENV = {
/** @deprecated Only for migration purposes */ /** @deprecated Only for migration purposes */
+2 -2
View File
@@ -1,11 +1,11 @@
import debug from 'debug'; import { createDebug } from 'obug';
import { isIPv6 } from 'is-ip'; import { isIPv6 } from 'is-ip';
import type { ClientType } from '#db/repositories/client/types'; import type { ClientType } from '#db/repositories/client/types';
import type { InterfaceType } from '#db/repositories/interface/types'; import type { InterfaceType } from '#db/repositories/interface/types';
import type { UserConfigType } from '#db/repositories/userConfig/types'; import type { UserConfigType } from '#db/repositories/userConfig/types';
const FW_DEBUG = debug('Firewall'); const FW_DEBUG = createDebug('Firewall');
const CHAIN_NAME = 'WG_CLIENTS'; const CHAIN_NAME = 'WG_CLIENTS';
// Mutex to prevent concurrent rule rebuilds // Mutex to prevent concurrent rule rebuilds