Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ceff95b336 | |||
| 782d1c215f | |||
| e8e26cfe10 | |||
| 400d4d992e | |||
| b08df55321 | |||
| b26a8110e0 | |||
| 692f550596 | |||
| badae8b8e4 | |||
| 7f89bde99e | |||
| 326717444b | |||
| 4e4bfc75e3 | |||
| 5c97a8ba73 | |||
| cba7a160ea | |||
| 4a75e1379d | |||
| 10a140d188 |
@@ -27,7 +27,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v4
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
- platform: linux/arm/v7
|
||||
os: ubuntu-24.04-arm
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Prepare
|
||||
run: |
|
||||
@@ -138,7 +138,7 @@ jobs:
|
||||
contents: write
|
||||
needs: docker-merge
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
- platform: linux/arm/v7
|
||||
os: ubuntu-24.04-arm
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: master
|
||||
|
||||
@@ -147,7 +147,7 @@ jobs:
|
||||
contents: write
|
||||
needs: docker-merge
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: master
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
- platform: linux/arm/v7
|
||||
os: ubuntu-24.04-arm
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Prepare
|
||||
run: |
|
||||
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
- platform: linux/arm/v7
|
||||
os: ubuntu-24.04-arm
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Prepare
|
||||
run: |
|
||||
@@ -152,7 +152,7 @@ jobs:
|
||||
contents: write
|
||||
needs: docker-merge
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
@@ -47,7 +47,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
|
||||
Vendored
-1
@@ -3,7 +3,6 @@
|
||||
"aaron-bond.better-comments",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"antfu.goto-alias",
|
||||
"visualstudioexptteam.vscodeintellicode",
|
||||
"esbenp.prettier-vscode",
|
||||
"yoavbls.pretty-ts-errors",
|
||||
"bradlc.vscode-tailwindcss",
|
||||
|
||||
@@ -13,11 +13,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- 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)
|
||||
- Text color in admin menu in light mode (https://github.com/wg-easy/wg-easy/pull/2307)
|
||||
|
||||
## Changed
|
||||
|
||||
@@ -27,11 +29,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- 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)
|
||||
- Add AmneziaWG docs (https://github.com/wg-easy/wg-easy/pull/2108, https://github.com/wg-easy/wg-easy/pull/2292)
|
||||
|
||||
## [15.1.0] - 2025-07-01
|
||||
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
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.
|
||||
## Introduction
|
||||
|
||||
**AmneziaWG** is a modified version of the WireGuard protocol with enhanced traffic obfuscation capabilities. AmneziaWG's primary goal is to counter deep packet inspection (DPI) systems and bypass VPN blocking.
|
||||
|
||||
AmneziaWG adds multi-level transport-layer obfuscation by:
|
||||
|
||||
@@ -12,6 +14,12 @@ AmneziaWG adds multi-level transport-layer obfuscation by:
|
||||
|
||||
These measures make it harder for third parties to analyze or identify your traffic, enhancing both privacy and security.
|
||||
|
||||
## Activating AmneziaWG
|
||||
|
||||
You must install the [AmneziaWG kernel module](https://github.com/amnezia-vpn/amneziawg-linux-kernel-module) on the host system.
|
||||
|
||||
Experimental support for AmneziaWG can be enabled by setting the `EXPERIMENTAL_AWG` environment variable to `true`. Starting from wg-easy version 16, this setting will be enabled by default. This feature is still under development and may change in future releases.
|
||||
|
||||
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.
|
||||
@@ -21,17 +29,51 @@ 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.
|
||||
## AmneziaWG Parameters
|
||||
|
||||
Parameter descriptions can be found in the [AmneziaWG documentation](https://docs.amnezia.org/documentation/amnezia-wg) and on the [kernel module page](https://github.com/amnezia-vpn/amneziawg-linux-kernel-module).
|
||||
|
||||
All parameters except I1-I5 will be set at first startup. For information on how to set I1-I5 parameters, refer to the [AmneziaWG documentation](https://docs.amnezia.org/documentation/instructions/new-amneziawg-selfhosted/#how-to-extract-a-protocol-signature-for-amneziawg-15-manually).
|
||||
|
||||
If a parameter is not set, it will not be added to the configuration. If all AmneziaWG-specific parameters are absent, AmneziaWG will be fully compatible with standard WireGuard.
|
||||
|
||||
### Parameter Compatibility Table
|
||||
|
||||
| Parameter | Can differ between server and client | Configurable on server | Configurable on client |
|
||||
| --------- | ------------------------------------ | ---------------------- | ----------------------- |
|
||||
| Jc | ✅ Yes | ✅ | ✅ |
|
||||
| Jmin | ✅ Yes | ✅ | ✅ |
|
||||
| Jmax | ✅ Yes | ✅ | ✅ |
|
||||
| S1-S4 | ❌ No, must match | ✅ | ❌ (copied from server) |
|
||||
| H1-H4 | ❌ No, must match | ✅ | ❌ (copied from server) |
|
||||
| I1-I5 | ✅ Yes | ✅ | ✅ |
|
||||
|
||||
## Client Applications
|
||||
|
||||
To be able to connect to wg-easy if AmneziaWG is enabled, you must have an AmneziaWG-compatible client. Currently, only WG Tunnel and Amnezia VPN supports AmneziaWG 1.5/2.0! AmneziaWG clients require building from source code.
|
||||
|
||||
Android:
|
||||
|
||||
- [AmneziaWG](https://play.google.com/store/apps/details?id=org.amnezia.awg) - Official Client
|
||||
- [Amnezia VPN](https://play.google.com/store/apps/details?id=org.amnezia.vpn) - Amnezia VPN Official Client
|
||||
- [AmneziaWG](https://play.google.com/store/apps/details?id=org.amnezia.awg) - AmneziaWG 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
|
||||
- [Amnezia VPN](https://apps.apple.com/us/app/amneziavpn/id1600529900) - Amnezia VPN Official Client
|
||||
- [AmneziaWG](https://apps.apple.com/us/app/amneziawg/id6478942365) - AmneziaWG Official Client
|
||||
|
||||
Windows:
|
||||
|
||||
- [AmneziaWG](https://github.com/amnezia-vpn/amneziawg-windows-client/releases) - Official Client
|
||||
- [Amnezia VPN](https://amnezia.org/downloads) - Amnezia VPN Official Client
|
||||
- [AmneziaWG](https://github.com/amnezia-vpn/amneziawg-windows-client/releases) - AmneziaWG Official Client
|
||||
|
||||
Linux:
|
||||
|
||||
- [Amnezia VPN](https://amnezia.org/downloads) - Amnezia VPN Official Client
|
||||
- [amneziawg-tools](https://github.com/amnezia-vpn/amneziawg-tools) - AmneziaWG Tools
|
||||
|
||||
OpenWRT:
|
||||
|
||||
- [AmneziaWG OpenWRT](https://github.com/Slava-Shchipunov/awg-openwrt) - AmneziaWG OpenWRT Packages
|
||||
- [AmneziaWG OpenWRT](https://github.com/lolo6oT/awg-openwrt) - AmneziaWG OpenWRT Packages
|
||||
|
||||
+3
-2
@@ -7,10 +7,11 @@
|
||||
"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:i18n": "bash scripts/i18n.sh",
|
||||
"format:check:docs": "prettier --check docs"
|
||||
},
|
||||
"devDependencies": {
|
||||
"prettier": "^3.6.2"
|
||||
"prettier": "^3.7.3"
|
||||
},
|
||||
"packageManager": "pnpm@10.21.0"
|
||||
"packageManager": "pnpm@10.24.0"
|
||||
}
|
||||
|
||||
Generated
+5
-5
@@ -9,16 +9,16 @@ importers:
|
||||
.:
|
||||
devDependencies:
|
||||
prettier:
|
||||
specifier: ^3.6.2
|
||||
version: 3.6.2
|
||||
specifier: ^3.7.3
|
||||
version: 3.7.3
|
||||
|
||||
packages:
|
||||
|
||||
prettier@3.6.2:
|
||||
resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==}
|
||||
prettier@3.7.3:
|
||||
resolution: {integrity: sha512-QgODejq9K3OzoBbuyobZlUhznP5SKwPqp+6Q6xw6o8gnhr4O85L2U915iM2IDcfF2NPXVaM9zlo9tdwipnYwzg==}
|
||||
engines: {node: '>=14'}
|
||||
hasBin: true
|
||||
|
||||
snapshots:
|
||||
|
||||
prettier@3.6.2: {}
|
||||
prettier@3.7.3: {}
|
||||
|
||||
@@ -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,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>
|
||||
@@ -9,7 +9,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<template #actions>
|
||||
<DialogClose>
|
||||
<DialogClose as-child>
|
||||
<BaseSecondaryButton>{{ $t('dialog.cancel') }}</BaseSecondaryButton>
|
||||
</DialogClose>
|
||||
</template>
|
||||
|
||||
@@ -13,11 +13,12 @@
|
||||
v-for="(item, index) in menuItems"
|
||||
:key="index"
|
||||
:to="`/admin/${item.id}`"
|
||||
active-class="bg-red-800 rounded"
|
||||
class="group rounded"
|
||||
active-class="bg-red-800 active"
|
||||
>
|
||||
<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"
|
||||
class="w-full font-medium group-[.active]:text-white"
|
||||
>
|
||||
{{ item.name }}
|
||||
</BaseSecondaryButton>
|
||||
|
||||
@@ -179,13 +179,25 @@
|
||||
@delete="deleteClient"
|
||||
>
|
||||
<FormSecondaryActionField
|
||||
label="Delete"
|
||||
:label="$t('client.delete')"
|
||||
class="w-full"
|
||||
type="button"
|
||||
tabindex="-1"
|
||||
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>
|
||||
|
||||
@@ -7,6 +7,7 @@ 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';
|
||||
@@ -27,6 +28,7 @@ export default defineI18nConfig(() => ({
|
||||
ru,
|
||||
'zh-HK': zhhk,
|
||||
'zh-CN': zhcn,
|
||||
'zh-TW': zhtw,
|
||||
ko,
|
||||
es,
|
||||
'pt-BR': ptbr,
|
||||
|
||||
@@ -88,6 +88,7 @@
|
||||
"name": "Name",
|
||||
"expireDate": "Expire Date",
|
||||
"expireDateDesc": "Date the client will be disabled. Blank for permanent",
|
||||
"delete": "Delete",
|
||||
"deleteClient": "Delete Client",
|
||||
"deleteDialog1": "Are you sure you want to delete",
|
||||
"deleteDialog2": "This action cannot be undone.",
|
||||
@@ -117,7 +118,9 @@
|
||||
"notConnected": "Client not connected",
|
||||
"endpoint": "Endpoint",
|
||||
"endpointDesc": "IP of the client from which the WireGuard connection is established",
|
||||
"search": "Search clients..."
|
||||
"search": "Search clients...",
|
||||
"config": "Configuration",
|
||||
"viewConfig": "View Configuration"
|
||||
},
|
||||
"dialog": {
|
||||
"change": "Change",
|
||||
@@ -238,6 +241,12 @@
|
||||
"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)",
|
||||
|
||||
@@ -116,7 +116,10 @@
|
||||
"dnsDesc": "DNS сервер, який використовуватимуть клієнти (перевизначає глобальну конфігурацію)",
|
||||
"notConnected": "Клієнт не підключений",
|
||||
"endpoint": "Кінцева точка",
|
||||
"endpointDesc": "IP-адреса клієнта, з якої встановлюється з’єднання WireGuard"
|
||||
"endpointDesc": "IP-адреса клієнта, з якої встановлюється з’єднання WireGuard",
|
||||
"search": "Пошук клієнтів...",
|
||||
"config": "Конфігурація",
|
||||
"viewConfig": "Переглянути конфігурацію"
|
||||
},
|
||||
"dialog": {
|
||||
"change": "Змінити",
|
||||
@@ -236,5 +239,47 @@
|
||||
"postUp": "PostUp",
|
||||
"preDown": "PreDown",
|
||||
"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–150, S1+56 ≠ S2)",
|
||||
"s2Label": "Розмір сміттєвих даних у пакеті відповіді (S2)",
|
||||
"s2Description": "Розмір сміттєвих даних у пакеті відповіді (0–1188 [1280* - 92 = 1188], рекомендовано: 15–150)",
|
||||
"s3Label": "Розмір сміттєвих даних у пакеті «cookie reply» (S3)",
|
||||
"s3Description": "Розмір сміттєвих даних у пакеті «cookie reply»",
|
||||
"s4Label": "Розмір сміттєвих даних у транспортному пакеті (S4)",
|
||||
"s4Description": "Розмір сміттєвих даних у транспортному пакеті",
|
||||
"i1Label": "Спеціальний сміттєвий пакет 1 (I1)",
|
||||
"i1Description": "Пакет-імітація протоколу у hex-форматі: <b 0x...>",
|
||||
"i2Label": "Спеціальний сміттєвий пакет 2 (I2)",
|
||||
"i2Description": "Пакет-імітація протоколу у hex-форматі: <b 0x...>",
|
||||
"i3Label": "Спеціальний сміттєвий пакет 3 (I3)",
|
||||
"i3Description": "Пакет-імітація протоколу у hex-форматі: <b 0x...>",
|
||||
"i4Label": "Спеціальний сміттєвий пакет 4 (I4)",
|
||||
"i4Description": "Пакет-імітація протоколу у hex-форматі: <b 0x...>",
|
||||
"i5Label": "Спеціальний сміттєвий пакет 5 (I5)",
|
||||
"i5Description": "Пакет-імітація протоколу у hex-форматі: <b 0x...>",
|
||||
"h1Label": "Початковий магічний заголовок (H1)",
|
||||
"h1Description": "Значення заголовка початкового пакета (5–2147483647, має бути унікальним від H2–H4)",
|
||||
"h2Label": "Магічний заголовок відповіді (H2)",
|
||||
"h2Description": "Значення заголовка пакета відповіді (5–2147483647, має бути унікальним від H1, H3, H4)",
|
||||
"h3Label": "Магічний заголовок «cookie reply» (H3)",
|
||||
"h3Description": "Значення заголовка пакета «cookie reply» (5–2147483647, має бути унікальним від H1, H2, H4)",
|
||||
"h4Label": "Магічний заголовок транспортного пакета (H4)",
|
||||
"h4Description": "Значення заголовка транспортного пакета (5–2147483647, має бути унікальним від H1–H3)",
|
||||
"mtuNote": "Значення залежать від MTU",
|
||||
"obfuscationParameters": "Параметри обфускації AmneziaWG"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,8 +88,9 @@
|
||||
"name": "客户端名称",
|
||||
"expireDate": "过期日期",
|
||||
"expireDateDesc": "客户端将被自动禁用的日期。留空表示永久有效",
|
||||
"delete": "删除客户端",
|
||||
"deleteClient": "删除客户端",
|
||||
"deleteDialog1": "您确定要删除此客户端吗?",
|
||||
"deleteDialog1": "您确定要删除客户端",
|
||||
"deleteDialog2": "此操作无法撤销。",
|
||||
"enabled": "已启用",
|
||||
"address": "IP地址",
|
||||
@@ -113,7 +114,13 @@
|
||||
"hooks": "钩子脚本",
|
||||
"hooksDescription": "钩子脚本仅在使用wg-quick时有效",
|
||||
"hooksLeaveEmpty": "如果不使用wg-quick,请留空此字段",
|
||||
"search": "搜索客户端..."
|
||||
"dnsDesc": "客户端将使用的 DNS 服务器(将覆盖全局配置)",
|
||||
"notConnected": "客户端未连接",
|
||||
"endpoint": "端点",
|
||||
"endpointDesc": "建立 WireGuard 连接时客户端的 IP 地址",
|
||||
"search": "搜索客户端...",
|
||||
"config": "配置",
|
||||
"viewConfig": "查看配置文本"
|
||||
},
|
||||
"dialog": {
|
||||
"change": "确认修改",
|
||||
@@ -233,5 +240,47 @@
|
||||
"postUp": "启动后脚本",
|
||||
"preDown": "停止前脚本",
|
||||
"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-150,S1+56 ≠ S2)",
|
||||
"s2Label": "响应数据包垃圾数据大小(S2)",
|
||||
"s2Description": "响应数据包中垃圾数据的大小(范围:0-1188[1280* - 92 = 1188],推荐值:15-150)",
|
||||
"s3Label": "Cookie 回复数据包垃圾数据大小(S3)",
|
||||
"s3Description": "Cookie 回复数据包中垃圾数据的大小",
|
||||
"s4Label": "传输数据包垃圾数据大小(S4)",
|
||||
"s4Description": "传输数据包中垃圾数据的大小",
|
||||
"i1Label": "特殊垃圾数据包 1(I1)",
|
||||
"i1Description": "协议模拟数据包(十六进制格式):<b 0x...>",
|
||||
"i2Label": "特殊垃圾数据包 2(I2)",
|
||||
"i2Description": "协议模拟数据包(十六进制格式):<b 0x...>",
|
||||
"i3Label": "特殊垃圾数据包 3(I3)",
|
||||
"i3Description": "协议模拟数据包(十六进制格式):<b 0x...>",
|
||||
"i4Label": "特殊垃圾数据包 4(I4)",
|
||||
"i4Description": "协议模拟数据包(十六进制格式):<b 0x...>",
|
||||
"i5Label": "特殊垃圾数据包 5(I5)",
|
||||
"i5Description": "协议模拟数据包(十六进制格式):<b 0x...>",
|
||||
"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 不同)",
|
||||
"mtuNote": "具体数值取决于 MTU(最大传输单元)",
|
||||
"obfuscationParameters": "AmneziaWG 混淆参数"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,286 @@
|
||||
{
|
||||
"pages": {
|
||||
"me": "帳戶",
|
||||
"clients": "用戶端",
|
||||
"admin": {
|
||||
"panel": "管理面板",
|
||||
"general": "一般設定",
|
||||
"config": "組態設定",
|
||||
"interface": "介面設定",
|
||||
"hooks": "Hook 設定"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"email": "電子郵件"
|
||||
},
|
||||
"me": {
|
||||
"currentPassword": "目前密碼",
|
||||
"enable2fa": "啟用兩步驟驗證",
|
||||
"enable2faDesc": "請使用您的驗證碼應用程式掃描 QR Code,或手動輸入金鑰。",
|
||||
"2faKey": "TOTP 金鑰",
|
||||
"2faCodeDesc": "請輸入驗證碼應用程式提供的驗證碼。",
|
||||
"disable2fa": "停用兩步驟驗證",
|
||||
"disable2faDesc": "請輸入您的密碼以停用兩步驟驗證。"
|
||||
},
|
||||
"general": {
|
||||
"name": "名稱",
|
||||
"username": "使用者名稱",
|
||||
"password": "密碼",
|
||||
"newPassword": "新密碼",
|
||||
"updatePassword": "更新密碼",
|
||||
"mtu": "MTU",
|
||||
"allowedIps": "允許的 IP",
|
||||
"dns": "DNS",
|
||||
"persistentKeepalive": "保持連線",
|
||||
"logout": "登出",
|
||||
"continue": "繼續",
|
||||
"host": "主機",
|
||||
"port": "連接埠",
|
||||
"yes": "是",
|
||||
"no": "否",
|
||||
"confirmPassword": "確認密碼",
|
||||
"loading": "正在載入...",
|
||||
"2fa": "兩步驟驗證",
|
||||
"2faCode": "TOTP 驗證碼"
|
||||
},
|
||||
"setup": {
|
||||
"welcome": "歡迎首次設定您的 wg-easy",
|
||||
"welcomeDesc": "這是您在任何 Linux 主機上安裝與管理 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": "用戶端將被停用的日期。留白表示永久有效",
|
||||
"delete": "刪除",
|
||||
"deleteClient": "刪除用戶端",
|
||||
"deleteDialog1": "您確定要刪除",
|
||||
"deleteDialog2": "此動作無法復原。",
|
||||
"enabled": "啟用",
|
||||
"address": "位址",
|
||||
"serverAllowedIps": "伺服器允許的 IP",
|
||||
"otlDesc": "產生暫時性單次連結",
|
||||
"permanent": "永久",
|
||||
"createdOn": "建立於 ",
|
||||
"lastSeen": "上次連線於 ",
|
||||
"totalDownload": "總下載量: ",
|
||||
"totalUpload": "總上傳量: ",
|
||||
"newClient": "新增用戶端",
|
||||
"disableClient": "停用用戶端",
|
||||
"enableClient": "啟用用戶端",
|
||||
"noPrivKey": "此用戶端沒有已知的私密金鑰,無法建立設定。",
|
||||
"showQR": "顯示 QR Code",
|
||||
"downloadConfig": "下載組態設定檔",
|
||||
"allowedIpsDesc": "將透過 VPN 路由的 IP (會覆寫全域設定)",
|
||||
"serverAllowedIpsDesc": "伺服器將路由至用戶端的 IP",
|
||||
"mtuDesc": "設定 VPN 通道的最大傳輸單位 (封包大小)",
|
||||
"persistentKeepaliveDesc": "Keep-alive 封包的間隔秒數。0 表示停用",
|
||||
"hooks": "Hook 設定",
|
||||
"hooksDescription": "Hook 設定僅適用於 wg-quick",
|
||||
"hooksLeaveEmpty": "僅適用於 wg-quick,否則請保持空白",
|
||||
"dnsDesc": "用戶端使用的 DNS 伺服器 (會覆寫全域設定)",
|
||||
"notConnected": "用戶端未連線",
|
||||
"endpoint": "端點",
|
||||
"endpointDesc": "用戶端建立 WireGuard 連線的來源 IP",
|
||||
"search": "搜尋用戶端...",
|
||||
"config": "組態設定",
|
||||
"viewConfig": "檢視組態設定"
|
||||
},
|
||||
"dialog": {
|
||||
"change": "變更",
|
||||
"cancel": "取消",
|
||||
"create": "建立"
|
||||
},
|
||||
"toast": {
|
||||
"success": "成功",
|
||||
"saved": "已儲存",
|
||||
"error": "錯誤"
|
||||
},
|
||||
"form": {
|
||||
"actions": "操作",
|
||||
"save": "儲存",
|
||||
"revert": "還原",
|
||||
"sectionGeneral": "一般設定",
|
||||
"sectionAdvanced": "進階設定",
|
||||
"noItems": "沒有項目",
|
||||
"nullNoItems": "沒有項目。使用全域設定",
|
||||
"add": "新增"
|
||||
},
|
||||
"admin": {
|
||||
"general": {
|
||||
"sessionTimeout": "工作階段逾時",
|
||||
"sessionTimeoutDesc": "「記住我」的工作階段持續時間 (秒)",
|
||||
"metrics": "計量",
|
||||
"metricsPassword": "密碼",
|
||||
"metricsPasswordDesc": "計量端點的 Bearer 密碼 (密碼或 argon2 雜湊)",
|
||||
"json": "JSON",
|
||||
"jsonDesc": "提供 JSON 格式計量的路由",
|
||||
"prometheus": "Prometheus",
|
||||
"prometheusDesc": "提供 Prometheus 計量的路由"
|
||||
},
|
||||
"config": {
|
||||
"connection": "連線",
|
||||
"hostDesc": "用戶端將連線的公開主機名稱 (變更後會使目前組態設定檔失效)",
|
||||
"portDesc": "用戶端將連線的公開 UDP 連接埠 (變更後會使目前組態設定檔失效,您可能也需要變更介面連接埠)",
|
||||
"allowedIpsDesc": "用戶端將使用的允許 IP (全域設定)",
|
||||
"dnsDesc": "用戶端將使用的 DNS 伺服器 (全域設定)",
|
||||
"mtuDesc": "用戶端使用的 MTU (僅適用於新用戶端)",
|
||||
"persistentKeepaliveDesc": "傳送 keepalive 的間隔秒數。以 0 表示停用 (僅適用於新用戶端)",
|
||||
"suggest": "建議",
|
||||
"suggestDesc": "為主機欄位選擇 IP 位址或主機名稱"
|
||||
},
|
||||
"interface": {
|
||||
"cidrSuccess": "已變更 CIDR",
|
||||
"device": "裝置",
|
||||
"deviceDesc": "用於轉送 WireGuard 流量的乙太網路裝置",
|
||||
"mtuDesc": "WireGuard 將使用的 MTU",
|
||||
"portDesc": "WireGuard 監聽的 UDP 連接埠 (您可能也需要變更連接埠組態設定檔)",
|
||||
"changeCidr": "變更 CIDR",
|
||||
"restart": "重新啟動介面",
|
||||
"restartDesc": "重新啟動 WireGuard 介面",
|
||||
"restartWarn": "您確定要重新啟動介面嗎? 所有用戶端將被中斷連線。",
|
||||
"restartSuccess": "介面已重新啟動"
|
||||
},
|
||||
"introText": "歡迎使用管理面板。\n\n您可在此管理一般、組態、介面與 Hook 設定。\n\n請從側邊欄選擇任一項目開始。"
|
||||
},
|
||||
"zod": {
|
||||
"generic": {
|
||||
"required": "{0} 為必填項目",
|
||||
"validNumber": "{0} 必須為有效的數字",
|
||||
"validString": "{0} 必須為有效的字串",
|
||||
"validBoolean": "{0} 必須為有效的布林值",
|
||||
"validArray": "{0} 必須為有效的陣列",
|
||||
"stringMin": "{0} 至少需要 {1} 個字元",
|
||||
"numberMin": "{0} 不能小於 {1}"
|
||||
},
|
||||
"client": {
|
||||
"id": "用戶端 ID",
|
||||
"name": "名稱",
|
||||
"expiresAt": "到期時間",
|
||||
"address4": "IPv4 位址",
|
||||
"address6": "IPv6 位址",
|
||||
"serverAllowedIps": "伺服器允許的 IP"
|
||||
},
|
||||
"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": "Body 必須為有效的物件",
|
||||
"hook": "Hook",
|
||||
"enabled": "啟用",
|
||||
"mtu": "MTU",
|
||||
"port": "連接埠",
|
||||
"persistentKeepalive": "保持連線",
|
||||
"address": "IP 位址",
|
||||
"dns": "DNS",
|
||||
"allowedIps": "允許的 IP",
|
||||
"file": "檔案"
|
||||
},
|
||||
"hooks": {
|
||||
"preUp": "PreUp",
|
||||
"postUp": "PostUp",
|
||||
"preDown": "PreDown",
|
||||
"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-150,S1+56 ≠ S2)",
|
||||
"s2Label": "回應封包填充大小 (S2)",
|
||||
"s2Description": "回應封包填充大小 (0-1188 [1280* - 92 = 1188],建議: 15-150)",
|
||||
"s3Label": "Cookie 回覆封包填充大小 (S3)",
|
||||
"s3Description": "Cookie 回覆封包填充大小",
|
||||
"s4Label": "傳輸封包填充大小 (S4)",
|
||||
"s4Description": "傳輸封包填充大小",
|
||||
"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...>",
|
||||
"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 不同)",
|
||||
"mtuNote": "數值取決於 MTU",
|
||||
"obfuscationParameters": "AmneziaWG 混淆參數"
|
||||
}
|
||||
}
|
||||
@@ -79,6 +79,11 @@ export default defineNuxtConfig({
|
||||
language: 'zh-HK',
|
||||
name: '繁體中文(香港)',
|
||||
},
|
||||
{
|
||||
code: 'zh-TW',
|
||||
language: 'zh-TW',
|
||||
name: '正體中文 (台灣)',
|
||||
},
|
||||
{
|
||||
code: 'pl',
|
||||
language: 'pl-PL',
|
||||
|
||||
+12
-12
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "wg-easy",
|
||||
"version": "15.2.0-beta.1",
|
||||
"version": "15.2.0-beta.3",
|
||||
"description": "The easiest way to run WireGuard VPN + Web-based Admin UI.",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
@@ -23,13 +23,13 @@
|
||||
"@eschricht/nuxt-color-mode": "^1.2.0",
|
||||
"@heroicons/vue": "^2.2.0",
|
||||
"@libsql/client": "^0.15.15",
|
||||
"@nuxtjs/i18n": "^10.2.0",
|
||||
"@nuxtjs/i18n": "^10.2.1",
|
||||
"@nuxtjs/tailwindcss": "^6.14.0",
|
||||
"@phc/format": "^1.0.0",
|
||||
"@pinia/nuxt": "^0.11.3",
|
||||
"@tailwindcss/forms": "^0.5.10",
|
||||
"@vueuse/core": "^14.0.0",
|
||||
"@vueuse/nuxt": "^14.0.0",
|
||||
"@vueuse/core": "^14.1.0",
|
||||
"@vueuse/nuxt": "^14.1.0",
|
||||
"apexcharts": "^5.3.6",
|
||||
"argon2": "^0.44.0",
|
||||
"cidr-tools": "^11.0.3",
|
||||
@@ -45,29 +45,29 @@
|
||||
"nuxt": "^3.20.1",
|
||||
"otpauth": "^9.4.1",
|
||||
"pinia": "^3.0.4",
|
||||
"qr": "^0.5.2",
|
||||
"qr": "^0.5.3",
|
||||
"radix-vue": "^1.9.17",
|
||||
"semver": "^7.7.3",
|
||||
"tailwindcss": "^3.4.18",
|
||||
"timeago.js": "^4.0.2",
|
||||
"vue": "latest",
|
||||
"vue3-apexcharts": "^1.10.0",
|
||||
"zod": "^4.1.12"
|
||||
"zod": "^4.1.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxt/eslint": "^1.10.0",
|
||||
"@nuxt/eslint": "^1.11.0",
|
||||
"@types/debug": "^4.1.12",
|
||||
"@types/phc__format": "^1.0.1",
|
||||
"@types/semver": "^7.7.1",
|
||||
"drizzle-kit": "^0.31.6",
|
||||
"drizzle-kit": "^0.31.7",
|
||||
"esbuild": "^0.27.0",
|
||||
"eslint": "^9.39.1",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"prettier": "^3.6.2",
|
||||
"prettier": "^3.7.3",
|
||||
"prettier-plugin-tailwindcss": "^0.7.1",
|
||||
"tsx": "^4.20.6",
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "^5.9.3",
|
||||
"vue-tsc": "^3.1.3"
|
||||
"vue-tsc": "^3.1.5"
|
||||
},
|
||||
"packageManager": "pnpm@10.21.0"
|
||||
"packageManager": "pnpm@10.24.0"
|
||||
}
|
||||
|
||||
Generated
+853
-904
File diff suppressed because it is too large
Load Diff
@@ -166,11 +166,24 @@ class WireGuard {
|
||||
|
||||
async getClientQRCodeSVG({ clientId }: { clientId: ID }) {
|
||||
const config = await this.getClientConfiguration({ clientId });
|
||||
return encodeQR(config, 'svg', {
|
||||
ecc: 'high',
|
||||
scale: 2,
|
||||
encoding: 'byte',
|
||||
});
|
||||
const ECMode = ['high', 'quartile', 'medium', 'low'] as const;
|
||||
for (const ecc of ECMode) {
|
||||
try {
|
||||
return encodeQR(config, 'svg', {
|
||||
ecc,
|
||||
scale: 2,
|
||||
encoding: 'byte',
|
||||
});
|
||||
} catch (err) {
|
||||
if (!(err instanceof Error && err.message === 'Capacity overflow')) {
|
||||
throw err;
|
||||
}
|
||||
// retry with lower ecc
|
||||
}
|
||||
}
|
||||
throw new Error(
|
||||
'Failed to generate QR code: Capacity overflow at all ECC levels'
|
||||
);
|
||||
}
|
||||
|
||||
cleanClientFilename(name: string): string {
|
||||
|
||||
Reference in New Issue
Block a user