Compare commits

...

227 Commits

Author SHA1 Message Date
theis.gaedigk 8c70c24205 Merge remote-tracking branch 'upstream/master'
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
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
Lint / Check Docs (push) Has been cancelled
Edge / Merge & Deploy Docker (push) Has been cancelled
Edge / Build & Deploy 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-06-03 16:30:28 +02:00
Néstor c70ad1d08b Small code quality improvements (#2553)
* Small code quality improvements

- Fix misleading JSDoc comment in cache.ts
- Mitigate timing-based username enumeration in Basic auth
- Extract duplicated TOTP configuration into private method
- Replace manual peer counter with clients.length in Prometheus metrics
- Simplify isValidPasswordHash return expression

* reset session.ts

this is currently worked on in the dev-oauth branch

* reset password.ts

no need to change

* specify unit for cache function

* remove type assertion

---------

Co-authored-by: Anghios <Anghios@users.noreply.github.com>
Co-authored-by: Bernd Storath <bernd@berndstorath.de>
2026-06-03 14:43:32 +02:00
minhducsun2002 d0566a1df9 Support disabling version check (#2648)
* Support disabling version check

* Update docs

* Move the bypass logic back to update checking function

* fix linting

* fix linting (again)
2026-06-03 14:42:15 +02:00
Bernd Storath bc95a2851f chore: rework useSubmit (#2649)
rework useSubmit
2026-06-03 10:08:07 +02:00
Bernd Storath e03d743307 update packages 2026-06-01 08:54:27 +02:00
Bernd Storath 99357848e5 remove dependabot for npm
npm updates are handled manually to correctly dedupe
2026-05-26 08:31:07 +02:00
Ankit Agarwal c41ae0d4c5 Add Hindi translation (#2632) 2026-05-26 08:29:18 +02:00
ふぁ 66f8bde206 Add Japanese translation (#2642) 2026-05-26 08:28:48 +02:00
Bernd Storath b3afb9ac1b update packages 2026-05-26 08:21:10 +02:00
theis.gaedigk 9581e6eacb Merge remote-tracking branch 'upstream/master'
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
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
Lint / Check Docs (push) Has been cancelled
Edge / Merge & Deploy Docker (push) Has been cancelled
Edge / Build & Deploy 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-22 23:32:00 +02:00
Bernd Storath 90e2bbe0a6 patch wg-quick. fix sysctl (#2630)
no unneeded sysctl
2026-05-21 11:59:46 +02:00
theis.gaedigk 7b5ba95938 edited pngs
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
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
Lint / Check Docs (push) Has been cancelled
Edge / Merge & Deploy Docker (push) Has been cancelled
Edge / Build & Deploy 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 13:14:10 +02:00
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
Bernd Storath 3fb9adbf6f Bump version to 15.3.0-beta.2 2026-04-07 11:46:03 +02:00
Bernd Storath cd9db1563d fix: mobile UI (#2569)
* improve mobile ui

* general cleanup

* cleanup, improvements

* fix hydration mismatch
2026-04-07 11:34:49 +02:00
wuys b5c30f5dbe i18n: add Vietnamese language support (#2568)
* i18n: add Vietnamese translation

* i18n: fix lint

* i18n: import fix

---------

Co-authored-by: meow <meow@air.local>
2026-04-07 11:22:13 +02:00
adi82bdg 1eb9527175 Update Polish translations for various terms (#2566) 2026-04-02 13:25:55 +02:00
Nikolas cd890c1f0f Update uk.json (#2559) 2026-03-25 08:54:53 +01:00
Timothy Pillow 2a78b30aeb Config fix for #2208 : Document network interface fix for multi-network configuations (#2555)
* document fix for #2208

* typo fix

* prettier

* fix code block format

* fix indentation

---------

Co-authored-by: Timothy Pillow <timothy.pillow@swisscom.com>
Co-authored-by: Bernd Storath <999999bst@gmail.com>
Co-authored-by: Bernd Storath <32197462+kaaax0815@users.noreply.github.com>
2026-03-23 09:59:17 +01:00
Chiahong 9a843087c3 i18n(zh-tw): Update Traditional Chinese translation (#2558) 2026-03-23 07:49:12 +01:00
Bernd Storath 483b63bba6 update packages 2026-03-16 08:28:58 +01:00
Alexis-Loskoutoff 13942c97b2 i18n(fr): Update French translation (#2544) 2026-03-16 08:04:34 +01:00
MeCias 82c64e506e Lang(de): Added missing translations (#2543)
* Update de.json

Lang(de): Added missing translations

* Lang(de): Added missing translations and Comma

* Lang(de): Added missing translations and Comma and identations
2026-03-14 19:49:28 +01:00
Aarón Rosa Díaz 9b3d919168 i18n(es): Updated Spanish translation (#2540)
Updated Spanish translations for various UI elements

Updated and completed all remaining Spanish translations, including support for AmneziaWG 2.0. Following: https://wg-easy.github.io/wg-easy/v15.2/contributing/translation/
2026-03-13 07:47:09 +01:00
杨黄林 3eaf0d01dc i18n: improve Simplified Chinese translation (#2541)
* Fix zh-CN translate

* i18n: improve Simplified Chinese translation

---------

Co-authored-by: yanghuanglin <yanghuanglin@qq.com>
2026-03-13 07:46:25 +01:00
Bernd Storath 414e9a114b Bump version to 15.3.0-beta.1 2026-03-11 09:06:54 +01:00
Maksim M. 2d28d87c5c i18n(ru): Update Russian Lang (#2538)
* i18n(ru): Add firewall and download options

- Updated translation, added and translated all missing values from en.json

* i18n(ru): Minor changes

- Minor edits that don't change the translation

* i18n(ru): Terminology changes

- "Интерфейс" (Interface) has been renamed to "Сетевой интерфейс" (Network Interface) to avoid confusion with the Web UI, which is often referred to simply as "interface" in Russian.
2026-03-11 08:51:48 +01:00
Bernd Storath 76c2233e46 chore: update node to krypton (24) (#2536)
update to node krypton
2026-03-10 10:45:10 +01:00
Stephan Paternotte abedf9f38e Dutch translation updated (#2535)
* Dutch translation updated

Update nl.json updated re. "copy & download qr code as png"

* Update nl.json

Couple of trailing comma's added. Valid json now.
2026-03-10 10:42:33 +01:00
Kayhan 25f3fa3c0f Update Turkish localization strings (#2523)
* Update Turkish localization strings

* Update src/i18n/locales/tr.json

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/i18n/locales/tr.json

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/i18n/locales/tr.json

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/i18n/locales/tr.json

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/i18n/locales/tr.json

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/i18n/locales/tr.json

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-10 09:36:46 +01:00
Bernd Storath c3c51f8088 update packages 2026-03-09 08:01:17 +01:00
Bernd Storath 8ea2b635c1 feat: change hooks to textareas (#2522)
* hooks are now textareas

* remove newlines in client config
2026-03-05 14:52:55 +01:00
Bernd Storath bc4dfd03df feat: copy & download qr code as png (#2521)
* copy & download qr code as png

* i18n, accessibility

* improve error handling
2026-03-05 13:39:31 +01:00
Bernd Storath 7cde04de81 docs: fix faq urls
introduced in #2418
2026-03-05 13:38:12 +01:00
Bernd Storath 5228734c98 feat(cli): add command to show qr code (#2518)
* refactor cli, add commands

* add docs

* improve

* fix ec mode order
2026-03-05 11:53:27 +01:00
Ian Foster 47f81dd66a Feature/client firewall filtering (#2418)
* Add per-client firewall filtering

Implement server-side firewall rules to restrict client network access,
allowing administrators to enforce security policies that cannot be
bypassed by clients modifying their local configuration.

This feature addresses the limitation where "Allowed IPs" only controls
client-side routing but doesn't prevent clients from accessing networks
they shouldn't reach. The firewall rules are enforced on the server
using iptables/ip6tables and provide true access control.

Features:
- Opt-in via "Enable Per-Client Firewall" toggle in admin interface
- Per-client "Firewall Allowed IPs" field for granular control
- Support for IPs, CIDRs, and port-based filtering
- Protocol specification: TCP, UDP, or both (default)
- IPv4 and IPv6 dual-stack support
- Falls back to client's allowedIps when firewallIps is empty
- Clean separation of routing (allowedIps) from security (firewallIps)

Supported formats:
- 10.10.0.3 (single IP)
- 10.10.0.0/24 (CIDR range)
- 192.168.1.5:443 (IP with port, both TCP+UDP)
- 192.168.1.5:443/tcp (IP with specific protocol)
- [2001:db8::1]:443 (IPv6 with port)

Implementation:
- New database columns: firewall_enabled (interfaces), firewall_ips (clients)
- Migration 0003_add_firewall_filtering for schema updates
- firewall.ts utility for iptables chain management (WG_CLIENTS chain)
- Integration into WireGuard.ts for automatic rule application
- UI components with conditional rendering based on firewall toggle

Technical details:
- Uses custom WG_CLIENTS iptables chain for isolation
- Rebuild strategy: flush and recreate all rules on config save
- Mutex protection via rebuildInProgress/rebuildQueued flags
- Graceful cleanup when firewall is disabled
- No new dependencies (uses existing is-ip, is-cidr packages)

* added Comprehensive documentation in README and docs/ for firewall
filtering

* validate firewall IPs

* check for iptables before enabling the firewall and inform the user if
it is missing

* updated firewall docs

* fix imports

* remove extra import

* Document all allowed IP/cidr/port/proto combinations that are allowed
and check on save

* add note on firewall being experimental and how to opt a single client
out of the firewall.

* cleanup more imports

* add tests

* Fix firewall IPv6 validation and test expectations

Updated validation to correctly handle plain and bracketed IPv6 addresses, and fixed test to expect string from schema instead of object.

* added comments to firewall rules and updated tests

* fix auto-import

* fix typescript errors

* recreate sql migrations and rebase

* improve tests, typechecking, documentation

* fix formatting, fix types

* improve type

* added note for including host's IP in client firewall

* updated language to include cidr and protocol options

* another language update

* refer to docs for firewall allowed IPs

---------

Co-authored-by: Bernd Storath <999999bst@gmail.com>
2026-03-05 08:47:46 +01:00
Filip Richter e5b2c3d10b Add Czech language support (#2487)
* Add Czech localization file for i18n

* Add Czech locale support to i18n configuration

* Add Czech language support to nuxt.config.ts

* Update Czech translation for 'hooks' key
2026-03-03 11:30:06 +01:00
Bernd Storath 059a0ccffc update packages 2026-03-02 08:18:59 +01:00
Maksim M. 8c9c54c8b2 i18n(ru): Add missing validNumberRange and update H1-H4 descriptions (#2501)
Update Russian translations - H Descriptions
2026-03-02 08:09:12 +01:00
Stephan Paternotte 02ce6f0a65 Update nl.json - hxDescriptions updated (#2498)
AmneziaWG 2.0: support for H1-H4 ranges (#2480)
2026-02-27 07:47:37 +01:00
Bernd Storath 48682e1abd update packages 2026-02-23 08:12:21 +01:00
Stephan Paternotte 044dd34dcc Update nl.json (#2490)
AmneziaWG 2.0: support for H1-H4 ranges (#2480) TRANSLATED
2026-02-21 20:34:09 +01:00
CthulhuVRN a469ac6897 AmneziaWG 2.0: support for H1-H4 ranges (#2480)
* AmneziaWG 2.0: support for H1-H4 ranges

## Changes:
```
- [+] Added support for H1-H4 ranges
- [!] Fixed interface fields order (H1-H4 goes before I1-I5)
```

## Known issues:
```
- [!] no check for unique/overlap of H1-H4 values on settings apply:
  settings will be applied but wg interface will crash with "Invalid argument" error
```

* AmneziaWG 2.0: support for H1-H4 ranges

## Changes:
```
- [+] Added support for H1-H4 ranges
- [!] Fixed interface fields order (H1-H4 goes before I1-I5)
```

## Known issues:
```
- [!] no check for unique/overlap of H1-H4 values on settings apply:
  settings will be applied but wg interface will crash with "Invalid argument" error
```

* AmneziaWG 2.0: support for H1-H4 ranges

## Changes:
```
- [+] Added support for H1-H4 ranges
- [!] Fixed interface fields order (H1-H4 goes before I1-I5)
```

## Known issues:
```
- [!] no check for unique/overlap of H1-H4 values on settings apply:
  settings will be applied but wg interface will crash with "Invalid argument" error
```

* Update types.ts

Lint fixes

---------

Co-authored-by: CthulhuVRN <alexander@ptitsyn.info>
2026-02-17 13:27:22 +01:00
Bernd Storath 1178d23659 update packages 2026-02-16 09:11:19 +01:00
Bernd Storath b3cc1ce839 reduce container size
from around 700mb to 400mb
2026-02-11 15:36:29 +01:00
Bernd Storath 71aaec93ef add unit tests
this adds the groundwork to start unit testing some modules
2026-02-11 15:23:04 +01:00
HackingAll 7a219b73d4 Adding galician (#2473)
* Adding galego -> gl.json

* Update i18n.config.ts

* Update nuxt.config.ts
2026-02-10 11:18:02 +01:00
Bare7a c456c5e7dd Add Bulgarian translation (#2466) 2026-02-09 08:00:38 +01:00
Bernd Storath a5880cc0b8 update packages 2026-02-09 08:00:07 +01:00
Bernd Storath 5fca628ebd change date in copyright footer 2026-02-06 15:32:39 +01:00
Bernd Storath 7ab297c366 Bump version to 15.2.2 2026-02-06 14:55:20 +01:00
Bernd Storath c5de8f0f44 cli: reset 2fa on admin reset (#2461)
reset 2fa on cli reset
2026-02-06 12:28:13 +01:00
Bernd Storath c0641889cf replace watchtower (#2456)
update to maintained fork
2026-02-04 08:24:48 +01:00
Bernd Storath 9141562f91 update tagging convention for latest tag 2026-02-04 08:17:58 +01:00
thebigpotatoe d21af70df1 fix: correct help text for prometheus metrics (#2453) 2026-02-03 11:27:03 +01:00
Bernd Storath 56ee86cc1c update packages 2026-02-02 08:27:05 +01:00
Runar Ingebrigtsen f017b4968c Add Norwegian bokmål translation (#2447)
* add Norwegian bokmål translation

Signed-off-by: Runar Ingebrigtsen <runar@rin.no>

* add nb config, available in UI

Signed-off-by: Runar Ingebrigtsen <runar@rin.no>

---------

Signed-off-by: Runar Ingebrigtsen <runar@rin.no>
2026-02-02 08:18:31 +01:00
Alexander Chepurnoy 6004457666 feat: update client and server interface parameters (#2440) 2026-01-28 09:34:11 +01:00
Bernd Storath 1a5a0180ea Fix LangSelector and ClientCard z-index (#2434)
fix client card
2026-01-27 08:24:53 +01:00
Ayush Chothe c732f149e6 feat: Add wireguard-go package in Dockerfile (#2419)
* feat: Add `wireguard-go` package in `Dockerfile`

* feat: add amneziawg-go
Co-authored-by: cany748 <cany748@gmail.com>
2026-01-27 08:08:24 +01:00
Bernd Storath 4819480eb0 update packages 2026-01-26 09:02:25 +01:00
Alexander Chepurnoy fc7ab0dc21 feat(docs): edit amnezia.md (#2430)
* feat(docs): edit amnezia.md

* Update docs/content/advanced/config/amnezia.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update docs/content/advanced/config/amnezia.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-26 08:43:09 +01:00
Bernd Storath eb6b96c0f1 lang: Add dutch language (#2413)
add dutch language

Co-authored-by: Stephan Paternotte <Stephan-P@users.noreply.github.com>
2026-01-21 09:39:18 +01:00
Bernd Storath f62fad9c40 update packages 2026-01-20 08:51:19 +01:00
Maksim M. e9a472c8f7 fix(i18n/ru): use "мусор" for AmneziaWG junk packets (#2402)
The RU translation changed "junk" from "мусорный" to "шумовой".
Amnezia authors consistently describe these packets/bytes as "мусор" in their docs and Habr posts. Align wording with upstream terminology to avoid confusion for RU users.
2026-01-15 08:14:38 +01:00
Bernd Storath 552e2b8cbf update screenshot 2026-01-14 09:53:04 +01:00
Bernd Storath a0b4192cbd Bump version to 15.2.1 2026-01-14 08:51:01 +01:00
Bernd Storath 32a055093a exclude i18n from setup middleware 2026-01-13 13:15:46 +01:00
Bernd Storath 51558c7027 refactor: session handling (#2398)
* refactor session handling

* simplify
2026-01-13 10:11:13 +01:00
binnichtaktiv b85286f0ab Lang(de): Improve and add missing translations (#2393)
* add missing german translations

* fix typos and improve existing translations

* fix punctuation

* fix last overlooked typos
2026-01-13 08:21:08 +01:00
Bernd Storath 48f3fbd715 always generate awg h1-h5 2026-01-13 08:07:13 +01:00
Bernd Storath 458f66818a fix search magnifying icon 2026-01-12 12:04:50 +01:00
Bernd Storath 7964dc7993 Bump version to 15.2.0 2026-01-12 08:28:23 +01:00
RaffaelHold 0ac5d7d461 feat(docs): Extend docs for routed setup with nftables (#2380)
* Extend docs for routed setup with nftables

When using nftables in a routed setup different up and down hooks need to be used. 
To limit interaction with docker managed chains a custom WG_EASY chain is added as a jump target.
Since nft only supports deletion via handles awk is needed to get the handle of the jump rule for deletion

* Remove link to podman-nft

* Fix formatting according to prettier rules

* Add additional whitespace
2026-01-12 08:21:18 +01:00
Bernd Storath 826914a4f3 update packages 2026-01-12 08:19:01 +01:00
Bernd Storath 261da431e7 Fix: reset onetimelink expiration instead of failing (#2370)
* update expiresAt instead of failing

* add changelog
2025-12-29 19:12:53 +01:00
Bernd Storath 94b33abf5e Remove armv7 support (#2369)
remove armv7 support
2025-12-29 18:54:47 +01:00
Bernd Storath 8325056ccc update lockfile 2025-12-29 18:47:48 +01:00
Bernd Storath 81a1b2c907 update packages 2025-12-29 18:36:51 +01:00
dependabot[bot] fc8f89fb83 build(deps): bump actions/download-artifact from 6 to 7 (#2343)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 6 to 7.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '7'
  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>
2025-12-18 07:58:23 +01:00
dependabot[bot] d846c7745f build(deps): bump actions/upload-artifact from 5 to 6 (#2344)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5 to 6.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  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>
2025-12-18 07:58:12 +01:00
Bernd Storath 61c6fd6c02 update packages 2025-12-15 08:03:25 +01:00
David DIVERRES abe5708058 Update fr.json (#2326)
Update fr.json - Add missing translations and improve existing ones

- Add missing keys: client.delete, client.search, client.config, client.viewConfig
- Add complete copy section (4 keys)
- Add complete awg section (27 keys for AmneziaWG parameters)
- Fix terminology: "double facteur" → "deux facteurs" (standard French)
- Fix zod.generic.validBoolean translation
- Fix zod.generic.stringMin capitalization and wording
- Translate zod.client.id to French
- Improve admin.general route descriptions
2025-12-08 08:51:38 +01:00
Bernd Storath 626339bddb update packages 2025-12-08 08:30:53 +01:00
Danya 381ae23c07 Update ru (#2324)
* Update ru.json

Full revision of the Russian localization for the AWG configuration block, including terminology corrections, improved consistency, and clarified parameter descriptions.

* Update ru.json

* fix formatting

---------

Co-authored-by: Bernd Storath <999999bst@gmail.com>
2025-12-05 15:59:12 +01:00
Nikolas 52382d1d7a Update uk.json (#2323) 2025-12-05 07:35:15 +01:00
Mike 68e5216d4b Update ru.json (#2319)
* Update ru.json

* format

---------

Co-authored-by: Bernd Storath <999999bst@gmail.com>
2025-12-02 12:26:04 +01:00
Bernd Storath ceff95b336 Bump version to 15.2.0-beta.3 2025-12-01 10:12:01 +01:00
Alexander Chepurnoy 782d1c215f feat(docs): edit amnezia page (#2292)
* feat(docs): edit amnezia page

* Fix AmneziaWG documentation link

* Update AmneziaWG client compatibility information
2025-12-01 08:23:01 +01:00
Bernd Storath e8e26cfe10 update packages 2025-12-01 07:54:59 +01:00
Bernd Storath 400d4d992e Fix light mode admin menu active text color (#2307)
* fix color

* remove duplicates
2025-11-25 08:26:18 +01:00
Bernd Storath b08df55321 fix build 2025-11-24 08:03:37 +01:00
Bernd Storath b26a8110e0 update packages 2025-11-24 08:01:11 +01:00
杨黄林 692f550596 Improve zh-CN translate (#2298)
Fix zh-CN translate

Co-authored-by: yanghuanglin <yanghuanglin@qq.com>
2025-11-24 07:57:17 +01:00
Chiahong badae8b8e4 fix(i18n): Add missing translation for delete action (#2295) 2025-11-21 17:55:11 +01:00
Nikolas 7f89bde99e Update uk.json (#2293) 2025-11-21 08:02:47 +01:00
Bernd Storath 326717444b Bump version to 15.2.0-beta.2 2025-11-18 15:05:42 +01:00
Bernd Storath 4e4bfc75e3 feat: add config btn and modal to view and copy config (#2289)
* add view config btn and modal

* show loading state

* add note about keyboard
2025-11-18 11:36:46 +01:00
Bernd Storath 5c97a8ba73 try all qr ecc levels (#2288)
try ecc levels
2025-11-18 09:25:57 +01:00
Bernd Storath cba7a160ea intellicode deprecated 2025-11-17 08:04:10 +01:00
Nikolas 4a75e1379d Update uk.json (#2286)
* Update uk.json

* fix formatting

---------

Co-authored-by: Bernd Storath <999999bst@gmail.com>
2025-11-17 07:54:50 +01:00
Chiahong 10a140d188 feat(i18n): Add Traditional Chinese (Taiwan, zh-TW) support (#2285) 2025-11-17 07:53:03 +01:00
Bernd Storath edc3c5af57 Bump version to 15.2.0-beta.1 2025-11-12 08:35:19 +01:00
Bernd Storath 26708305d6 add script to calculate i18n progress 2025-11-12 08:23:44 +01:00
Alexander Chepurnoy 6a282e6ab9 AmneziaWG 2.0 (#2226)
* feat!: awg

* feat: add description to fields, add I5

* fix: awg i18n

* fix: types

* minor fixes

* Remove TODO comment from types.ts

Removed TODO comment for more validation.

---------

Co-authored-by: Bernd Storath <999999bst@gmail.com>
2025-11-12 07:46:16 +01:00
Badri Isiani a8ba7f7247 Fixed a bug causing .conf.txt download on Android affecting Firefox based Android browsers. (#2269)
* Fixed a bug causing .conf.txt download on Android
... affecting Firefox based Android browsers.

* change content-type

---------

Co-authored-by: Badri Isiani <badri@loonartech.net>
Co-authored-by: Bernd Storath <999999bst@gmail.com>
2025-11-11 08:02:03 +01:00
Bernd Storath 502fe718d5 update packages 2025-11-10 10:10:24 +01:00
Bernd Storath 5c7aac9fd2 update packages 2025-11-03 08:49:11 +01:00
YuWorm 2f96d9934b add filename cleaning at OneTimeLink download (#2253)
* add filename cleaning at OneTimeLink download

* add cleanConfigFilename function in utils/WireGuard
2025-10-31 09:28:46 +01:00
Bernd Storath daff15463d switch to node v22
https://github.com/wg-easy/wg-easy/discussions/2254
2025-10-31 09:24:45 +01:00
Bernd Storath 5f68d261c0 update packages 2025-10-27 08:41:09 +01:00
Bernd Storath 013ea6dba9 add exemptions stale action 2025-10-22 10:08:50 +02:00
Bernd Storath ab9d75757f fix types 2025-10-21 12:10:53 +02:00
Daniel Vos 9be20109af Add routed (no NAT) example setup (#2181)
* Add routed.md example

* remove bad example

* Add note about 2001:db8::/32

* Add note about OPNSense/PFSense outbound NAT rule.

* add SYS_MODULE capability

* remove whitelines

* fix formatting

* typo LUA -> ULA

* minor fix

---------

Co-authored-by: Bernd Storath <999999bst@gmail.com>
2025-10-21 12:06:55 +02:00
Bernd Storath 9430b76258 update packages 2025-10-21 11:43:39 +02:00
Bernd Storath 99f1a004d5 Fix: Allow lower MTU (#2228)
allow lower mtu
2025-10-21 11:38:39 +02:00
YuWorm 2b42b639ea Add search / filter box (#2170)
* feat: Add search client based on #1978

* moved the filtering to the DB level using zod and tidied up some imports.

* minor fix

* minor fix

* fix typo

---------

Co-authored-by: Bernd Storath <999999bst@gmail.com>
2025-10-20 08:04:21 +02:00
Bernd Storath 76d5944726 Fix: Client Address Cidr (#2217)
fix client cidr
2025-10-14 11:31:41 +02:00
Bernd Storath 81bd19cfb6 update packages 2025-10-13 08:17:17 +02:00
Edgar R.N 0365ca7fb6 docs: Add AdGuard Home tutorial (#2175)
* docs: Add AdGuard Home tutorial

Signed-off-by: Edgar R.N <ernvk23@gmail.com>

* docs: Update AdGuard Home tutorial to use multi-network architecure

Signed-off-by: Edgar R.N <ernvk23@gmail.com>

* docs: Refine AdGuard Home tutorial based on feedback

Signed-off-by: Edgar R.N <ernvk23@gmail.com>

* docs: Temporary fix multi-network iptables

Signed-off-by: Edgar R.N <ernvk23@gmail.com>

* docs: AdGuard Home tutorial compatible with wg-easy v15

Signed-off-by: Edgar R.N <ernvk23@gmail.com>

---------

Signed-off-by: Edgar R.N <ernvk23@gmail.com>
2025-10-08 15:37:48 +02:00
Bernd Storath 529d65b3fb Fix: don't expect dump to contain client
Fixes: #2200
Fixes Bug introduced in: #2058
2025-10-08 13:43:07 +02:00
Bernd Storath cbbf5d3d25 Feat: Return client id of newly created client (#2190)
return client id of newly created client
2025-09-29 08:20:00 +02:00
Bernd Storath 7b2d234ea5 add link to codeberg registry 2025-09-25 15:13:33 +02:00
lenny76 a282ca35f1 feat(i18n): Add Italian language support (#2185)
* feat(i18n): Add Italian language support

This commit introduces Italian (it) language support to the application.
The `it` locale has been added to the `messages` object in `i18n.config.ts`, enabling the application to load and display content in Italian.

* little fix for italian translation

* Update nuxt.config.ts for italian language

---------

Co-authored-by: LucaS <l.scrigna@eoscaffe.it>
2025-09-25 14:42:04 +02:00
Danya 0792862c0d Fix minor issues in Russian translations (#2177)
fix-russian-ui

Update Russian translation
2025-09-22 14:31:01 +02:00
Németh Bálint 6c0d8e91fa Add INIT_ALLOWED_IPS for unattended setup (#2164)
* Add INIT_ALLOWED_IPS env var

Implement INIT_ALLOWED_IPS env var like the INIT_DNS to preset the global Allowed IPs field.

* Docs: Add INIT_ALLOWED_IPS var to unattended setup table

* Make UserConfigService.update param partial

Update UserConfigService.update() to accept any subset of the updatable fields.
Remove the unnecessary userConfig object from  DBService.initialSetup()

* formatting fix

* format on linux

On windows prettier get confused by global conf... common windows things
2025-09-16 12:16:31 +02:00
Bernd Storath 8892c43a7d Feat: Publish package on Codeberg (#2160)
* test codeberg build

* copy container to codeberg
2025-09-13 20:03:38 +02:00
Bernd Storath 7cfe04286a update packages 2025-09-13 18:35:55 +02:00
Bernd Storath 000513f212 fix build
TODO: investigate the root cause
2025-09-08 23:25:34 +02:00
dependabot[bot] 6ca3da1b80 build(deps): bump actions/checkout from 4 to 5 (#2120)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  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>
2025-09-08 22:53:49 +02:00
Bernd Storath fe394ecbe4 update actions 2025-09-08 22:17:00 +02:00
Bernd Storath ec6f0423ca fix typo 2025-09-08 22:10:16 +02:00
Bernd Storath e12208af75 update packages 2025-09-08 22:08:40 +02:00
TaeHyeon Jo 2d9c75fd81 fix(i18n-ko): unify 2FA terminology; add notConnected/endpoint/endpointDesc; keep PreUp (#2140)
fix(i18n-ko): unify 2FA terminology, add notConnected/endpoint/endpointDesc, keep PreUp
2025-08-30 13:42:57 +02:00
Bahri Rizaldi 0c54b1c3da Add Bahasa Indonesia Translations (#2142)
Add Bahasa Indonesia
2025-08-30 13:33:57 +02:00
xGhostxDev be7943dc9b Add polish language (#2126)
* Update i18n.config.ts

* Update nuxt.config.ts

* Add files via upload
2025-08-26 09:40:16 +02:00
Bernd Storath 303c2f1e39 Docs: Add amneziawg (#2108)
add amneziawg docs
2025-08-16 16:02:17 +02:00
WebX Beyond 0b32ab899c Add Bangla (বাংলা) translation and language support to wg-easy (#2112)
Bangla translation added
2025-08-16 16:01:57 +02:00
Alexander Chepurnoy ef463d3d85 feat: add amneziawg support (#2102)
* feat: detect wireguard executable

* feat: add amneziawg-tools to container

* feat: enhance AWG detection and configuration handling

* refactor: change env name

* refactor: change env values
2025-08-14 09:10:18 +02:00
dependabot[bot] c10daa2fd4 build(deps): bump actions/download-artifact from 4 to 5 (#2096)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  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>
2025-08-12 09:19:57 +02:00
Kayhan cb8aa45cde Added Turkish language (#2099) 2025-08-12 08:14:05 +02:00
Bernd Storath 54e0a1e886 update packages 2025-08-12 08:13:10 +02:00
Ezmana 71a452080e French language update (#2090) 2025-08-10 20:57:17 +02:00
Ciro Mota 5be7fb3038 feat: Add Português Brasil translation (#2077) 2025-08-08 19:30:22 +02:00
Aleksandr Fedotov 59f0c8b0d2 Update ru.json (#2078) 2025-08-06 07:52:38 +02:00
Bernd Storath e1ed93674d update packages, fix es, remove nuxtr 2025-08-05 08:43:30 +02:00
Néstor 6b65a8099b Create es.json (spanish language) (#2070)
Create es.json

Added a preview of spanish translation.
2025-08-05 08:28:12 +02:00
Nikolas c1dd494d0f Update uk.json (#2065) 2025-07-30 10:10:07 +02:00
Bernd Storath bf9e8a6e21 fix lint 2025-07-25 12:39:13 +02:00
Bernd Storath 371d7617ff fix styling 2025-07-25 12:37:46 +02:00
Bernd Storath 0b435d9ed8 update packages 2025-07-25 12:32:25 +02:00
Bernd Storath 07f89d15a9 Feat: show client endpoint (#2058)
* show client endpoint

* improve

* fix status code
2025-07-25 12:00:42 +02:00
Bernd Storath b5318086d2 Fix: Remove dns if empty (#2057)
remove dns if empty
2025-07-25 11:26:16 +02:00
Bernd Storath b7f9b7c830 Feat: Allow empty dns (#2052)
* allow empty dns

* remove log
2025-07-24 10:58:40 +02:00
mnaray 2e4f386f49 Improve incomplete setup docs in README (#2031)
* Update setup docs in README

* remove redundant information
2025-07-15 10:02:45 +02:00
Astesana 9ead985798 Fixes network creation in docker-run.md (#2018)
* Fixes network creation in docker-run.md

* Update docker-run.md as suggested by @kaaax0815
2025-07-14 09:58:34 +02:00
Bernd Storath 6326ee31c4 migrate to zod v4 2025-07-14 08:19:23 +02:00
Bernd Storath 984dc95550 update packages 2025-07-14 07:51:35 +02:00
Maksim M. cd0a9b8e33 Add Russian translation (#2014)
* feat(i18n): add Russian translation

* Corrected the AI translation

Подправил AI перевод

* fix: format ru.json
2025-07-10 09:53:03 +02:00
Bernd Storath 90b9ba15ec feat: make api more secure (#2015)
make api more secure
2025-07-09 15:42:29 +02:00
Bernd Storath 0abc419db7 fix lint errors 2025-07-07 11:19:45 +02:00
Daeho Ro b185d7a63d add Korean translation (#2003)
Added translation using Weblate (Korean)




Translate-URL: https://mini-i18n.daeho.ro/projects/wg-easy/main/ko/
Translation: wg-easy/main

Co-authored-by: OpenAI <noreply-mt-openai@weblate.org>
2025-07-07 10:59:54 +02:00
Bernd Storath 4bb880c4b7 update packages 2025-07-07 10:06:23 +02:00
SebastiaanTheCoder b0ba9e43f9 Updated traefik documentation (#2005) 2025-07-07 09:53:36 +02:00
杨黄林 ddb01fb968 Improve Chinese translation and fix 4-character Chinese suggestion button (#1997)
* Improve Chinese Simplified translate

* Use whitespace-nowrap class to fix Suggest button

* fix formatting

---------

Co-authored-by: Bernd Storath <999999bst@gmail.com>
2025-07-02 20:15:19 +02:00
Bernd Storath 22812e0632 Fix: Admin menu text (#1999)
make admin menu reactive
2025-07-02 09:42:09 +02:00
Bernd Storath 4d84e1d9d3 fix changelog 2025-07-01 08:51:35 +02:00
Bernd Storath 9368b857e8 Bump version to 15.1.0 2025-07-01 08:50:21 +02:00
Bernd Storath 0f663df7f6 Add option to disable ipv6 (#1951)
* add option to disable ipv6

* don't add ipv6 address

* update docs
2025-07-01 07:57:14 +02:00
Kirill Dvoretskov 68fde7d165 Updated container launch commands (#1989)
* Updated container launch commands

* one more occurrence

---------

Co-authored-by: Bernd Storath <999999bst@gmail.com>
2025-06-30 08:01:26 +02:00
杨黄林 501a784264 Add Chinese Simplified (#1990)
* Add Chinese Simplified

* fix formatting

---------

Co-authored-by: Bernd Storath <999999bst@gmail.com>
2025-06-30 07:55:00 +02:00
Bernd Storath 629c184195 update packages 2025-06-30 07:54:45 +02:00
Rayyamhk 76b8818a33 feat: Add Traditional Chinese (zh-HK) i18n Support (#1988)
* feat:add translation for zh-hk

* fix formatting issues
2025-06-27 08:25:02 +02:00
Pascal Dietrich 6c52301a64 Add german translations (#1889) 2025-06-26 16:05:44 +02:00
Bernd Storath be26db63ca add docs on how to add/update translation 2025-06-26 15:27:27 +02:00
Bernd Storath 962bfa213f update screenshot 2025-06-26 14:56:15 +02:00
Bernd Storath ee00e5c914 update packages 2025-06-23 09:53:30 +02:00
Bernd Storath 6343213538 v14 migration env vars
make note that the env vars from v14 won't be migrated
2025-06-11 11:14:13 +02:00
Bernd Storath 187bdc0836 update packages 2025-06-11 07:42:21 +02:00
wh!le f2520f0481 docs for caddy example (#1939) 2025-06-09 16:04:59 +02:00
Ezmana 5e9a73645b Add French language (#1924)
* French translation file creation

* Add French language
2025-06-09 15:55:12 +02:00
Bernd Storath 783fa3286c update packages
removed override as every package updated
2025-06-02 08:23:26 +02:00
Nikolas 77b4f9db65 Added Ukrainian language (#1906)
* Add files via upload

Ukrainian language

* Update ua.json

* Update i18n.config.ts

* Update i18n.config.ts

* Rename ua.json to uk.json

* Update i18n.config.ts

* Update nuxt.config.ts

* Update uk.json
2025-06-01 16:40:31 +02:00
Bernd Storath 0f6f07161b update docker compose 2025-05-28 13:50:13 +02:00
Bernd Storath d75a836de9 update changelog 2025-05-28 12:33:44 +02:00
Bernd Storath f79b0fd025 Bump version to 15.0.0 2025-05-28 12:22:42 +02:00
Bernd Storath d2ce82241b Bump version to 15.0.0-beta.13 2025-05-28 12:11:03 +02:00
Bernd Storath 8c395ec275 fix pre-release 2025-05-28 12:10:47 +02:00
Bernd Storath 10f42170f3 Bump version to 15.0.0-beta.13 2025-05-28 11:59:38 +02:00
Bernd Storath a8aa85bdaa Feat: map client to interface (#1886)
map client to interface
2025-05-28 11:58:33 +02:00
Bernd Storath 7e1aa5807d Feat: variants (#1885)
add primary & secondary button & actionfield
2025-05-28 11:44:16 +02:00
Bernd Storath df57921b8e update packages 2025-05-27 09:39:37 +02:00
Bernd Storath 4fbf059e61 add armv7 support
override until
- https://github.com/nuxt/nuxt/issues/32124
- https://github.com/nuxt-modules/i18n/issues/3605
are fixed
2025-05-16 09:11:50 +02:00
Bernd Storath b150e3f3b4 update packages 2025-05-16 08:54:52 +02:00
Bernd Storath aed10ab0bd update packages 2025-05-12 07:41:40 +02:00
Bernd Storath 478e7207b2 don't hoist libsql
not needed anymore
2025-05-05 11:15:24 +02:00
Bernd Storath c8dc710435 update packages 2025-05-05 10:25:28 +02:00
Bernd Storath 19d9e3b7d7 update packages
this greatly improves qr code size and quality
2025-04-26 16:49:51 +02:00
Bernd Storath c4efb1d03a update packages
argon2 now prebuilds for armv7
only libsql is missing: https://github.com/tursodatabase/libsql-js/pull/169
2025-04-24 11:50:35 +02:00
Bernd Storath 529b9eeb88 improve image size 2025-04-22 16:09:18 +02:00
Bernd Storath 69ee741d7e Feat: distributed build (#1829)
* distribute build across runners

* better formatting

* fix issues

* fix matrix

* retrigger build
2025-04-22 14:11:28 +02:00
Bernd Storath f2dc38e91b add setup guide 2025-04-22 10:47:55 +02:00
Bernd Storath 64e9484331 update packages 2025-04-22 07:46:33 +02:00
Bernd Storath c777fa30b3 publish stable docker compose (#1828) 2025-04-22 07:44:28 +02:00
Bernd Storath 734d91fd98 replace nightly with edge (#1819) 2025-04-17 15:58:45 +02:00
Bernd Storath 84ed7b299f Feat: Cli (#1818)
* add cli

* fix lint

* add docs, include cli packages

* fix docs, username instead of name
2025-04-16 14:17:02 +02:00
Bernd Storath 1cfe6404b2 Feat docs (#1814)
* improve docs and formatting

* lint in ci

avoid using bundled prettier from vscode extension

* fix action, typos

* remove header

* remove unused deps
2025-04-15 12:43:57 +02:00
Bernd Storath 2a32c1b9c0 remove unused dependencies 2025-04-14 08:36:07 +02:00
217 changed files with 20843 additions and 5027 deletions
-23
View File
@@ -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
-10
View File
@@ -5,13 +5,3 @@ updates:
schedule:
interval: "weekly"
rebase-strategy: "auto"
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
rebase-strategy: "auto"
- package-ecosystem: "npm"
directory: "/src/"
schedule:
interval: "weekly"
rebase-strategy: "auto"
+4 -4
View File
@@ -27,17 +27,17 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- 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}}"
+113 -20
View File
@@ -4,38 +4,131 @@ 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@v6
- 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@v6
with:
images: |
ghcr.io/wg-easy/wg-easy
flavor: |
latest=false
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build & Publish Docker Image
uses: docker/build-push-action@v6
- name: Set up QEMU
uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Build and push by digest
id: build
uses: docker/build-push-action@v7
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@v7
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@v8
with:
path: ${{ runner.temp }}/digests
pattern: digests-*
merge-multiple: true
- name: Login to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to Codeberg
uses: docker/login-action@v4
with:
registry: codeberg.org
username: ${{ secrets.CODEBERG_USER }}
password: ${{ secrets.CODEBERG_PASS }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Docker meta
id: meta
uses: docker/metadata-action@v6
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@v6
- name: Setup Python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: 3.11.9
cache: "pip"
+175
View File
@@ -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@v6
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@v6
with:
images: |
ghcr.io/wg-easy/wg-easy
flavor: |
latest=false
- name: Login to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Build and push by digest
id: build
uses: docker/build-push-action@v7
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@v7
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@v8
with:
path: ${{ runner.temp }}/digests
pattern: digests-*
merge-multiple: true
- name: Login to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to Codeberg
uses: docker/login-action@v4
with:
registry: codeberg.org
username: ${{ secrets.CODEBERG_USER }}
password: ${{ secrets.CODEBERG_PASS }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Docker meta
id: meta
uses: docker/metadata-action@v6
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@v6
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
-77
View File
@@ -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
+23 -11
View File
@@ -11,33 +11,45 @@ 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@v6
- name: Prepare
run: |
platform=${{ matrix.arch.platform }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v4
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build Docker Image
uses: docker/build-push-action@v6
uses: docker/build-push-action@v7
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 }}
+110 -27
View File
@@ -10,30 +10,121 @@ 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@v6
- name: Prepare
run: |
platform=${{ matrix.arch.platform }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Docker meta
id: meta
uses: docker/metadata-action@v6
with:
images: |
ghcr.io/wg-easy/wg-easy
flavor: |
latest=false
- name: Login to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Build and push by digest
id: build
uses: docker/build-push-action@v7
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@v7
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@v8
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@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to Codeberg
uses: docker/login-action@v4
with:
registry: codeberg.org
username: ${{ secrets.CODEBERG_USER }}
password: ${{ secrets.CODEBERG_PASS }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v4
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
uses: docker/metadata-action@v6
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@v6
- name: Setup Python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: 3.11.9
cache: "pip"
+31 -4
View File
@@ -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@v6
- uses: pnpm/action-setup@v6
name: Install pnpm
with:
run_install: false
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: "lts/krypton"
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,17 +47,17 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: pnpm/action-setup@v6
name: Install pnpm
with:
run_install: false
- name: Setup Node
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: "lts/*"
node-version: "lts/krypton"
check-latest: true
cache: "pnpm"
+10 -1
View File
@@ -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
View File
@@ -1,2 +1,3 @@
.DS_Store
*.swp
node_modules
+2 -3
View File
@@ -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"
]
}
+5 -3
View File
@@ -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"],
+107 -3
View File
@@ -7,14 +7,116 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- AWG: support for H1-H4 ranges (https://github.com/wg-easy/wg-easy/pull/2480)
- Client Firewall (https://github.com/wg-easy/wg-easy/pull/2418)
- 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)
### 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
- 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)
- 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
### Added
- Added Userspace WireGuard support (https://github.com/wg-easy/wg-easy/pull/2419)
### Fixed
- LangSelector overlapping with Buttons (https://github.com/wg-easy/wg-easy/pull/2434)
- AmnzeziaWG config parameters (https://github.com/wg-easy/wg-easy/pull/2440)
- OpenMetrics help string format (https://github.com/wg-easy/wg-easy/pull/2453)
- Reset 2fa when resetting admin password (https://github.com/wg-easy/wg-easy/pull/2461)
### Docs
- Replace Watchtower with maintained fork (https://github.com/wg-easy/wg-easy/pull/2456)
## [15.2.1] - 2026-01-14
### Fixed
- Icon in Searchbar (https://github.com/wg-easy/wg-easy/commit/458f66818a400f181e2c6326ede077c8793d71f2)
- Interface save not working (https://github.com/wg-easy/wg-easy/commit/48f3fbd715a889e2425702a8a46332f2752aef91)
- Error Messages in Setup (https://github.com/wg-easy/wg-easy/commit/32a055093a76342c40858d8dcf563b0700a8bd48)
## [15.2.0] - 2026-01-12
### 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)
- Text color in admin menu in light mode (https://github.com/wg-easy/wg-easy/pull/2307)
### 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)
- Update OneTimeLink expiry on reuse (https://github.com/wg-easy/wg-easy/pull/2370)
- Removed ARMv7 support (https://github.com/wg-easy/wg-easy/pull/2369)
### 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, https://github.com/wg-easy/wg-easy/pull/2380)
- 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
### 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 +129,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
+38 -7
View File
@@ -1,4 +1,4 @@
FROM docker.io/library/node:lts-alpine AS build
FROM docker.io/library/node:krypton-alpine AS build
WORKDIR /app
# update corepack
@@ -7,16 +7,30 @@ RUN npm install --global corepack@latest
RUN corepack enable pnpm
# 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
# Build UI
COPY src ./
RUN pnpm build
# Build amneziawg-tools
RUN apk add linux-headers build-base go git && \
git clone https://github.com/amnezia-vpn/amneziawg-tools.git && \
git clone https://github.com/amnezia-vpn/amneziawg-go && \
cd amneziawg-go && \
make && \
cd ../amneziawg-tools/src && \
make && \
sed -i 's|\[\[ $proto == -4 \]\] && cmd sysctl -q net\.ipv4\.conf\.all\.src_valid_mark=1|[[ $proto == -4 ]] \&\& [[ $(sysctl -n net.ipv4.conf.all.src_valid_mark) != 1 ]] \&\& cmd sysctl -q net.ipv4.conf.all.src_valid_mark=1|' ./wg-quick/linux.bash
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.
# This saves a lot of disk space.
FROM docker.io/library/node:lts-alpine
FROM docker.io/library/node:krypton-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 +39,19 @@ 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)
COPY --from=build-libsql /app/node_modules /app/server/node_modules
# cli
COPY --from=build /app/cli/cli.sh /usr/local/bin/cli
RUN chmod +x /usr/local/bin/cli
# Copy amneziawg-go
COPY --from=build /app/amneziawg-go/amneziawg-go /usr/bin/amneziawg-go
RUN chmod +x /usr/bin/amneziawg-go
# 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 \
@@ -37,18 +62,24 @@ RUN apk add --no-cache \
nftables \
kmod \
iptables-legacy \
wireguard-tools
wireguard-go \
wireguard-tools && \
sed -i 's|\[\[ $proto == -4 \]\] && cmd sysctl -q net\.ipv4\.conf\.all\.src_valid_mark=1|[[ $proto == -4 ]] \&\& [[ $(sysctl -n net.ipv4.conf.all.src_valid_mark) != 1 ]] \&\& cmd sysctl -q net.ipv4.conf.all.src_valid_mark=1|' /usr/bin/wg-quick
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
# Set Environment
ENV DEBUG=Server,WireGuard,Database,CMD
ENV DEBUG=Server,WireGuard,Database,CMD,Firewall
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
+6 -3
View File
@@ -1,4 +1,4 @@
FROM docker.io/library/node:lts-alpine
FROM docker.io/library/node:krypton-alpine
WORKDIR /app
# update corepack
@@ -16,6 +16,7 @@ RUN apk add --no-cache \
ip6tables \
kmod \
iptables-legacy \
wireguard-go \
wireguard-tools
# Use iptables-legacy
@@ -23,16 +24,18 @@ 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
# Set Environment
ENV DEBUG=Server,WireGuard,Database,CMD
ENV DEBUG=Server,WireGuard,Database,CMD,Firewall
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 ./
COPY src/package.json src/pnpm-lock.yaml src/pnpm-workspace.yaml ./
RUN pnpm install
# Copy Project
COPY src ./
ENTRYPOINT [ "pnpm", "run" ]
+16 -19
View File
@@ -1,24 +1,16 @@
# 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)
[![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)
[![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)
<!-- 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)
[![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!
<!-- 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
@@ -39,15 +31,11 @@ You have found the easiest way to install & manage WireGuard on any Linux host!
- IPv6 support
- CIDR support
- 2FA support
- Per-client firewall filtering (requires iptables)
> [!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 +67,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 +104,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.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

@@ -0,0 +1,63 @@
{
"fill" : {
"automatic-gradient" : "display-p3:0.48853,0.13220,0.12335,1.00000"
},
"groups" : [
{
"layers" : [
{
"fill" : {
"automatic-gradient" : "srgb:1.00000,1.00000,1.00000,1.00000"
},
"image-name" : "wireguard-logo.png",
"name" : "wireguard-logo",
"position" : {
"scale" : 0.5,
"translation-in-points" : [
255.828125,
-225.5
]
}
},
{
"fill-specializations" : [
{
"value" : {
"automatic-gradient" : "srgb:1.00000,1.00000,1.00000,1.00000"
}
},
{
"appearance" : "dark",
"value" : {
"automatic-gradient" : "display-p3:0.48853,0.13220,0.12335,1.00000"
}
}
],
"image-name" : "ticket.png",
"name" : "ticket",
"position" : {
"scale" : 1.2,
"translation-in-points" : [
-119.91562499999998,
165.65625
]
}
}
],
"shadow" : {
"kind" : "neutral",
"opacity" : 0.5
},
"translucency" : {
"enabled" : true,
"value" : 0.5
}
}
],
"supported-platforms" : {
"circles" : [
"watchOS"
],
"squares" : "shared"
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 167 KiB

+1 -1
View File
@@ -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
View File
@@ -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
+5
View File
@@ -0,0 +1,5 @@
{
"tabWidth": 4,
"semi": true,
"singleQuote": true
}
+5
View File
@@ -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.
+79
View File
@@ -0,0 +1,79 @@
---
title: AmneziaWG
---
## 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:
- 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.
## 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.
Possible values:
- `awg` — Force use of AmneziaWG
- `wg` — Force use of standard WireGuard
## 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. Where an AmneziaWG app is available for your platform, it is recommended to use it rather than Amnezia VPN.
Android:
- [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
- [Amnezia VPN](https://play.google.com/store/apps/details?id=org.amnezia.vpn) - Amnezia VPN Official Client
iOS and macOS:
- [AmneziaWG](https://apps.apple.com/us/app/amneziawg/id6478942365) - AmneziaWG Official Client
- [Amnezia VPN](https://apps.apple.com/us/app/amneziavpn/id1600529900) - Amnezia VPN Official Client
Windows:
- [AmneziaWG](https://github.com/amnezia-vpn/amneziawg-windows-client/releases) - AmneziaWG Official Client (Requires building from source code)
- [Amnezia VPN](https://amnezia.org/downloads) - Amnezia VPN 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
@@ -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,19 @@ 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 |
| `DISABLE_VERSION_CHECK` | `false` | `true` | If wg-easy should check for new updates |
/// 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`.
+3 -3
View File
@@ -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
+14 -8
View File
@@ -6,22 +6,24 @@ 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 or armv7, 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
### Backup
Before you start the migration, make sure to backup your existing configuration files.
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`.
+1 -1
View File
@@ -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
+27
View File
@@ -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.
+171 -3
View File
@@ -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)
@@ -20,7 +20,7 @@ File: `/etc/docker/containers/watchtower/docker-compose.yml`
```yaml
services:
watchtower:
image: containrrr/watchtower:latest
image: nickfedor/watchtower:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock
env_file:
@@ -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
+96 -3
View File
@@ -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.
+111
View File
@@ -0,0 +1,111 @@
---
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 servers 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 were 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
```
/// warning | Important: When using nftables use the following hooks instead.
PostUp
```shell
nft add chain ip filter WG_EASY; nft add rule ip filter DOCKER-USER jump WG_EASY; nft add rule ip filter WG_EASY iifname {{device}} accept; nft add rule ip filter WG_EASY oifname {{device}} accept; nft add chain ip6 filter WG_EASY; nft add rule ip6 filter DOCKER-USER jump WG_EASY; nft add rule ip6 filter WG_EASY iifname {{device}} accept; nft add rule ip6 filter WG_EASY oifname {{device}} accept;
```
PostDown
```shell
nft delete rule ip filter DOCKER-USER handle $(nft -a list chain ip filter DOCKER-USER | awk '/jump WG_EASY/ {print $NF}'); nft flush chain ip filter WG_EASY; nft delete chain ip filter WG_EASY; nft delete rule ip6 filter DOCKER-USER handle $(nft -a list chain ip6 filter DOCKER-USER | awk '/jump WG_EASY/ {print $NF}'); nft flush chain ip6 filter WG_EASY; nft delete chain ip6 filter WG_EASY
```
///
+10 -9
View File
@@ -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.
+52 -10
View File
@@ -6,6 +6,20 @@ hide:
Here are some frequently asked questions or errors about `wg-easy`. If you have a question that is not answered here, please feel free to open a discussion on GitHub.
## How do I restrict client access to specific networks or servers?
Use the **Per-Client Firewall** feature to enforce server-side restrictions on what each client can access.
**Requirements:** This feature requires `iptables` (and `ip6tables` for IPv6) to be installed on the host system.
1. Enable "Per-Client Firewall" in **Admin Panel → Interface**
2. Edit a client and configure "Firewall Allowed IPs"
3. Specify which destinations the client should be allowed to access
Unlike "Allowed IPs" which only controls client-side routing, firewall rules are enforced by the server and cannot be bypassed.
See the [Admin Panel Guide](./guides/admin.md#per-client-firewall) and [Client Guide](./guides/clients.md#firewall-allowed-ips) for detailed configuration.
## Error: WireGuard exited with the error: Cannot find device "wg0"
This error indicates that the WireGuard interface `wg0` does not exist. This can happen if the WireGuard kernel module is not loaded or if the interface was not created properly.
@@ -14,13 +28,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 +46,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 +64,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 +82,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 +100,40 @@ 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
```
## Clients lose connectivity after restarting the container when using multiple networks?
When you attach multiple Docker networks (e.g., `wg` and a reverse proxy network like `traefik` or `nginx`) to the `wg-easy` container, Docker might assign the network interfaces randomly (e.g., swapping `eth0` and `eth1`). Since `wg-easy` expects the wireguard interface to act as `eth0` and configures `POSTROUTING` rules for it, connectivity will break if the interfaces are swapped upon container restart.
To solve this, specify the `interface_name` and `gw_priority` explicitly in your `docker-compose.yml` file to guarantee that the `wg` network always binds to `eth0` and acts as the default gateway.
**Example `docker-compose.yml`:**
```yaml
services:
wg-easy:
# ... other configuration ...
networks:
wg:
interface_name: eth0
gw_priority: 1
ipv4_address: 10.42.42.42
nginx:
interface_name: eth1
gw_priority: 0
networks:
wg:
# ... wg network config ...
nginx:
external: true
```
+16 -38
View File
@@ -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 |
| `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 |
| `latest` | latest tag | `ghcr.io/wg-easy/wg-easy:latest` or `ghcr.io/wg-easy/wg-easy` | points to the v14 release, should be avoided |
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`).
<!-- ref: major version (check links too) -->
When publishing a tag we follow the [Semantic Versioning][semver] specification. Pin to the latest major version to avoid breaking changes (e.g. `15`), avoid using the `latest` tag.
[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
+22 -1
View File
@@ -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.
-5
View File
@@ -1,5 +0,0 @@
---
title: Edit Account
---
TODO
+39 -1
View File
@@ -2,4 +2,42 @@
title: Admin Panel
---
TODO
## Interface Settings
### Per-Client Firewall
Enable server-side firewall filtering to enforce network access restrictions per client.
When enabled, each client can have custom "Firewall Allowed IPs" configured that restrict which destinations they can access through the VPN. These restrictions are enforced by the server using iptables/ip6tables and cannot be bypassed by the client.
/// warning | Experimental Feature
This feature is currently experimental. While functional, it should be thoroughly tested in your environment before relying on it for production security requirements. Always verify that firewall rules are working as expected using test traffic or by manually inspecting the rules.
///
**Requirements:**
- `iptables` must be installed on the host system
- `ip6tables` must be installed if IPv6 is enabled (default)
- The feature cannot be enabled if these tools are not available
/// note
Most Linux distributions include iptables by default. If you're running in a minimal container environment, you may need to install the `iptables` package on the host system.
///
**Enable this feature if you want to:**
- Restrict certain clients to only access specific servers or networks
- Prevent clients from accessing the internet while allowing LAN access
- Enforce port-based restrictions (e.g., only allow HTTP/HTTPS)
- Separate routing configuration from security enforcement
**How it works:**
1. Enable "Per-Client Firewall" in Admin Panel → Interface
2. Edit any client to see the new "Firewall Allowed IPs" field
3. Specify allowed destinations (IPs, subnets, ports) for that client
4. Server enforces these rules automatically
See [Edit Client → Firewall Allowed IPs](./clients.md#firewall-allowed-ips) for detailed configuration syntax and examples.
+71
View File
@@ -0,0 +1,71 @@
---
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.
### Show Clients
List all clients that are currently configured with details such as client ID, Name, Public Key, and enabled status.
```shell
cli clients:list
```
### Show Client QR Code
Display the QR code for a specific client, which can be scanned by a compatible app to import the client's configuration.
```shell
cli clients:qr <client_id>
```
Replace `<client_id>` with the actual client ID you want to show the QR code for.
/// warning | IPv6 Support
IPv6 support is enabled by default, even if you disabled it using environment variables. To disable it pass the `--no-ipv6` flag when running the CLI.
```shell
cli clients:qr <client_id> --no-ipv6
```
///
+97 -1
View File
@@ -2,4 +2,100 @@
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 the Firewall Allowed IPs feature to prevent access to IP ranges that the user should not be able to access.
## Firewall Allowed IPs
/// note | Attention
This field only appears when **Per-Client Firewall** is enabled in the Admin Panel → Interface settings.
///
Server-side firewall rules that restrict which destinations the client can access, regardless of their local configuration.
Unlike "Allowed IPs" which only controls routing on the client side, these rules are enforced by the server using iptables/ip6tables and cannot be bypassed by the client.
**Supported Formats:**
- `10.10.0.3`, `2001:db8::1` - Allow access to a single IP address
- `10.10.0.0/24`, `2001:db8::/32` - Allow access to an entire subnet
- `192.168.1.5:443` - Allow access to specific port (TCP+UDP)
- `192.168.1.5:443/tcp` - Allow access to specific port (TCP only)
- `192.168.1.5:443/udp` - Allow access to specific port (UDP only)
- `10.10.0.0/24:443` - Allow access to an entire subnet on a specific port (TCP+UDP)
- `10.10.0.0/24:443/tcp` - Allow access to an entire subnet on a specific port (TCP only)
- `10.10.0.0/24:443/udp` - Allow access to an entire subnet on a specific port (UDP only)
- `[2001:db8::1]:443` - IPv6 address with port (brackets required)
- `[2001:db8::/32]:443/tcp` - IPv6 CIDR with port and protocol
/// warning | Invalid Formats
Protocol specifiers (`/tcp` or `/udp`) require a port number. The following formats are **not supported** and will result in an error:
- `10.10.0.3/tcp` (use `10.10.0.3:443/tcp` instead)
- `10.10.0.0/24/udp` (use `10.10.0.0/24:53/udp` instead)
///
**Behavior:**
- **Empty**: Falls back to the client's "Allowed IPs" setting
- **Specified**: Only listed destinations are accessible (allow-only, everything else is blocked)
- **Disable for specific client**: To disable firewall filtering for a single client while keeping it enabled for others, add `0.0.0.0/0, ::/0` to allow all traffic
/// note
To allow clients to reach the VPN server itself (e.g. for DNS), include the server's VPN address in the firewall allowed IPs.
///
**Use Case Examples**:
- Allow only specific servers: `10.10.0.5`
- Allow only internal network: `10.10.0.0/24, 192.168.1.0/24`
- Allow only web browsing: `0.0.0.0/0:80, 0.0.0.0/0:443, [::/0]:80, [::/0]:443`
- Block internet, allow LAN: Leave "Allowed IPs" as `0.0.0.0/0, ::/0` but set Firewall IPs to `10.0.0.0/8, 192.168.0.0/16`
## 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.
-5
View File
@@ -1,5 +0,0 @@
---
title: Login
---
TODO
+20 -1
View File
@@ -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.
+1 -1
View File
@@ -8,7 +8,7 @@ hide:
/// info | This Documentation is Versioned
**Make sure** to select the correct version of this documentation! It should match the version of the image you are using. The default version corresponds to the `:latest` image tag - [the most recent stable release][docs-tagging].
**Make sure** to select the correct version of this documentation! It should match the version of the image you are using. The default version corresponds to [the most recent stable release][docs-tagging].
///
This documentation provides you not only with the basic setup and configuration of `wg-easy` but also with advanced configuration, elaborate usage scenarios, detailed examples, hints and more.
+7 -7
View File
@@ -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>
&copy <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
+9 -3
View File
@@ -2,10 +2,16 @@
"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",
"scripts:i18n": "bash scripts/i18n.sh",
"format:check:docs": "prettier --check docs"
},
"packageManager": "pnpm@10.8.0"
"devDependencies": {
"prettier": "^3.8.3"
},
"packageManager": "pnpm@11.5.0"
}
+16 -1
View File
@@ -6,4 +6,19 @@ settings:
importers:
.: {}
.:
devDependencies:
prettier:
specifier: ^3.8.3
version: 3.8.3
packages:
prettier@3.8.3:
resolution: {integrity: sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==}
engines: {node: '>=14'}
hasBin: true
snapshots:
prettier@3.8.3: {}
+19
View File
@@ -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
View File
@@ -30,6 +30,7 @@ echo "Updated package.json to version $new_version"
echo "----"
echo "If you changed the major version, remember to update the docker-compose.yml file and docs (search for: ref: major version)"
echo "Make sure to stage any changes before proceeding (e.g. Changelog updates)."
echo "----"
echo "If you did everything press 'y' to commit the changes and create a new tag"
+2
View File
@@ -23,4 +23,6 @@ logs
.env.*
!.env.example
coverage/
wg-easy.db
-1
View File
@@ -1 +0,0 @@
public-hoist-pattern[]=@libsql/linux*
+1
View File
@@ -0,0 +1 @@
setups.@nuxt/test-utils="4.0.3"
+7
View File
@@ -0,0 +1,7 @@
:root {
color-scheme: light;
}
.dark {
color-scheme: dark;
}
+3 -3
View File
@@ -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>
+3 -3
View File
@@ -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>
+3 -3
View File
@@ -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>
+29
View File
@@ -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>
+1 -1
View File
@@ -18,7 +18,7 @@
>
<slot name="description" />
</DialogDescription>
<div class="mt-6 flex justify-end gap-2">
<div class="mt-6 flex flex-wrap justify-end gap-2">
<slot name="actions" />
</div>
</DialogContent>
+26
View File
@@ -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>
+10
View File
@@ -0,0 +1,10 @@
<template>
<textarea
v-model="data"
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"
/>
</template>
<script lang="ts" setup>
const data = defineModel<string>();
</script>
+1 -1
View File
@@ -16,7 +16,7 @@
</template>
<script setup lang="ts">
import type { ApexOptions } from 'apexcharts';
import type { ApexChart, ApexOptions } from 'apexcharts';
defineProps<{
client: LocalClient;
+2 -2
View File
@@ -1,7 +1,7 @@
<template>
<ClientCardCharts :client="client" />
<div
class="relative z-10 flex flex-col justify-between gap-3 px-3 py-3 sm:flex-row md:py-5"
class="relative flex flex-col justify-between gap-3 px-3 py-3 sm:flex-row md:py-5"
>
<div class="flex w-full items-center gap-3 md:gap-4">
<ClientCardAvatar :client="client" />
@@ -9,7 +9,7 @@
<div class="flex flex-grow flex-col gap-1">
<ClientCardName :client="client" />
<div
class="flex flex-col pb-1 text-xs text-gray-500 md:inline-block md:pb-0 dark:text-neutral-400"
class="flex flex-col text-xs text-gray-500 dark:text-neutral-400"
>
<div>
<ClientCardAddress :client="client" />
+1 -3
View File
@@ -1,7 +1,5 @@
<template>
<div
class="block pb-1 text-xs text-gray-500 md:inline-block md:pb-0 dark:text-neutral-400"
>
<div class="block text-xs text-gray-500 dark:text-neutral-400">
<span class="inline-block">{{ expiredDateFormat(client.expiresAt) }}</span>
</div>
</template>
+1 -3
View File
@@ -1,11 +1,9 @@
<template>
<div
class="text-sm text-gray-700 md:text-base dark:text-neutral-200"
class="break-all text-sm text-gray-700 md:text-base dark:text-neutral-200"
:title="$t('client.createdOn') + $d(new Date(client.createdAt))"
>
<span class="border-b-2 border-t-2 border-transparent">
{{ client.name }}
</span>
</div>
</template>
@@ -14,10 +14,11 @@ const props = defineProps<{ client: LocalClient }>();
const clientsStore = useClientsStore();
const _showOneTimeLink = useSubmit(
`/api/client/${props.client.id}/generateOneTimeLink`,
{
(data) =>
$fetch(`/api/client/${props.client.id}/generateOneTimeLink`, {
method: 'post',
},
body: data,
}),
{
revert: async () => {
await clientsStore.refresh();
+8 -6
View File
@@ -18,10 +18,11 @@ const enabled = ref(props.client.enabled);
const clientsStore = useClientsStore();
const _disableClient = useSubmit(
`/api/client/${props.client.id}/disable`,
{
(data) =>
$fetch(`/api/client/${props.client.id}/disable`, {
method: 'post',
},
body: data,
}),
{
revert: async () => {
await clientsStore.refresh();
@@ -31,10 +32,11 @@ const _disableClient = useSubmit(
);
const _enableClient = useSubmit(
`/api/client/${props.client.id}/enable`,
{
(data) =>
$fetch(`/api/client/${props.client.id}/enable`, {
method: 'post',
},
body: data,
}),
{
revert: async () => {
await clientsStore.refresh();
@@ -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>
+8 -5
View File
@@ -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>
@@ -41,10 +43,11 @@ function createClient() {
}
const _createClient = useSubmit(
'/api/client',
{
(data) =>
$fetch('/api/client', {
method: 'post',
},
body: data,
}),
{
revert: () => clientsStore.refresh(),
successMsg: t('client.created'),
+4 -4
View File
@@ -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>
+3 -3
View File
@@ -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">
<IconsPlus class="w-4 md:mr-2" />
<BaseSecondaryButton as="span">
<IconsPlus class="mr-2 w-4" />
<span class="text-sm">{{ $t('client.new') }}</span>
</BaseButton>
</BaseSecondaryButton>
</ClientsCreateDialog>
</p>
</template>
+4 -4
View File
@@ -1,8 +1,8 @@
<template>
<ClientsCreateDialog>
<BaseButton as="span">
<IconsPlus class="w-4 md:mr-2" />
<span class="text-sm max-md:hidden">{{ $t('client.newShort') }}</span>
</BaseButton>
<BaseSecondaryButton as="span">
<IconsPlus class="mr-2 w-4" />
<span class="text-sm">{{ $t('client.newShort') }}</span>
</BaseSecondaryButton>
</ClientsCreateDialog>
</template>
+100 -3
View File
@@ -5,12 +5,26 @@
</template>
<template #description>
<div class="bg-white">
<img :src="qrCode" />
<img ref="img" :src="qrCode" />
</div>
</template>
<template #actions>
<DialogClose>
<BaseButton>{{ $t('dialog.cancel') }}</BaseButton>
<BaseSecondaryButton
class="flex items-center gap-2"
:title="$t('client.copyPng')"
@click="copyPng"
>
<IconsCopy class="size-5" /> PNG
</BaseSecondaryButton>
<BaseSecondaryButton
class="flex items-center gap-2"
:title="$t('client.downloadPng')"
@click="downloadPng"
>
<IconsDownload class="size-5" /> PNG
</BaseSecondaryButton>
<DialogClose as-child>
<BaseSecondaryButton>{{ $t('dialog.cancel') }}</BaseSecondaryButton>
</DialogClose>
</template>
</BaseDialog>
@@ -18,4 +32,87 @@
<script setup lang="ts">
defineProps<{ qrCode: string }>();
const toast = useToast();
const img = useTemplateRef('img');
async function svgToPng() {
if (!img.value || !img.value.complete || img.value.naturalWidth === 0) {
throw new Error('image is not loaded');
}
const width = 1000;
const height = 1000;
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
if (!ctx) {
throw new Error('was not able to create 2d context');
}
ctx.drawImage(img.value!, 0, 0, width, height);
return new Promise<Blob>((res, rej) => {
canvas.toBlob((blob) => {
if (!blob) {
return rej(new Error('was not able to create blob'));
}
return res(blob);
}, 'image/png');
});
}
async function downloadPng() {
try {
const blob = await svgToPng();
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'client-config.png';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
} catch (e) {
console.error('failed to download png', e);
toast.showToast({
type: 'error',
message: $t('toast.unknown'),
});
}
}
async function copyPng() {
const blob = await svgToPng().catch((e) => {
console.error('failed to convert svg to png', e);
toast.showToast({
type: 'error',
message: $t('toast.unknown'),
});
});
if (!blob) {
return;
}
try {
await navigator.clipboard.write([
new ClipboardItem({
[blob.type]: blob,
}),
]);
toast.showToast({
type: 'success',
message: $t('copy.copied'),
});
} catch (e) {
console.error('failed to copy png', e);
toast.showToast({
type: 'error',
message: $t('copy.failed'),
});
}
}
</script>
+38
View File
@@ -0,0 +1,38 @@
<template>
<div class="relative">
<div class="relative flex h-full items-center">
<IconsMagnifyingGlass
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 px-8 py-2 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>
+5 -8
View File
@@ -1,12 +1,9 @@
<template>
<BaseButton @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 @click="toggleSort">
<IconsArrowDown v-if="globalStore.sortClient === true" class="mr-2 w-4" />
<IconsArrowUp v-else class="mr-2 w-4" />
<span class="text-sm">{{ $t('client.sort') }}</span>
</BasePrimaryButton>
</template>
<script setup lang="ts">
+6 -14
View File
@@ -12,23 +12,15 @@
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
as="input"
type="button"
class="rounded-lg"
value="-"
@click="del(i)"
/>
<BaseSecondaryButton type="button" class="rounded-lg" @click="del(i)">
{{ '-' }}
</BaseSecondaryButton>
</div>
</div>
<div class="mt-2">
<BaseButton
as="input"
type="button"
class="rounded-lg"
:value="$t('form.add')"
@click="add"
/>
<BasePrimaryButton type="button" class="rounded-lg" @click="add">
{{ $t('form.add') }}
</BasePrimaryButton>
</div>
</div>
</template>
+2 -2
View File
@@ -1,10 +1,10 @@
<template>
<h4 class="col-span-full flex items-center py-6 text-2xl">
<h3 class="col-span-full flex items-center py-6 text-2xl">
<slot />
<BaseTooltip v-if="description" :text="description">
<IconsInfo class="size-4" />
</BaseTooltip>
</h4>
</h3>
</template>
<script lang="ts" setup>
+6 -4
View File
@@ -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>
+20
View File
@@ -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>
+3 -3
View File
@@ -1,7 +1,7 @@
<template>
<RLabel :for="props.for" class="md:align-middle md:leading-10"
><slot
/></RLabel>
<RLabel :for="props.for" class="md:leading-[2.75rem]">
<slot />
</RLabel>
</template>
<script lang="ts" setup>
+6 -14
View File
@@ -12,23 +12,15 @@
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
as="input"
type="button"
class="rounded-lg"
value="-"
@click="del(i)"
/>
<BaseSecondaryButton type="button" class="rounded-lg" @click="del(i)">
{{ '-' }}
</BaseSecondaryButton>
</div>
</div>
<div class="mt-2">
<BaseButton
as="input"
type="button"
class="rounded-lg"
:value="$t('form.add')"
@click="add"
/>
<BasePrimaryButton type="button" class="rounded-lg" @click="add">
{{ $t('form.add') }}
</BasePrimaryButton>
</div>
</div>
</template>
@@ -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>
+2
View File
@@ -7,7 +7,9 @@
<IconsInfo class="size-4" />
</BaseTooltip>
</div>
<div class="my-auto">
<BaseSwitch :id="id" v-model="data" />
</div>
</template>
<script lang="ts" setup>
+29
View File
@@ -0,0 +1,29 @@
<template>
<div class="flex items-center">
<FormLabel :for="id">
{{ label }}
</FormLabel>
<BaseTooltip v-if="description" :text="description">
<IconsInfo class="size-4" />
</BaseTooltip>
</div>
<BaseTextArea
:id="id"
v-model.trim="data"
:name="id"
:autocomplete="autocomplete"
:disabled="disabled"
/>
</template>
<script lang="ts" setup>
defineProps<{
id: string;
label: string;
description?: string;
autocomplete?: string;
disabled?: boolean;
}>();
const data = defineModel<string>();
</script>
+2 -2
View File
@@ -1,12 +1,12 @@
<template>
<Toggle
:pressed="globalStore.uiShowCharts"
class="group inline-flex h-8 w-8 cursor-pointer items-center justify-center whitespace-nowrap rounded-full bg-gray-200 transition hover:bg-gray-300 dark:bg-neutral-700 dark:hover:bg-neutral-600"
class="group flex h-8 w-8 items-center justify-center rounded-full bg-gray-200 transition hover:bg-gray-300 dark:bg-neutral-700 dark:hover:bg-neutral-600"
:title="$t('layout.toggleCharts')"
@update:pressed="globalStore.toggleCharts"
>
<IconsChart
class="h-5 w-5 fill-gray-400 transition group-data-[state=on]:fill-gray-600 dark:fill-neutral-600 dark:group-data-[state=on]:fill-neutral-400"
class="h-5 w-5 transition group-data-[state=on]:fill-gray-600 dark:text-neutral-400 dark:group-data-[state=on]:fill-gray-300"
/>
</Toggle>
</template>
+1 -1
View File
@@ -1,5 +1,5 @@
<template>
<NuxtLink to="/" class="mb-4">
<NuxtLink to="/" class="max-sm:mb-4">
<h1 class="text-4xl font-medium dark:text-neutral-200">
<img
src="/logo.png"
+1 -1
View File
@@ -5,7 +5,7 @@
authStore.userData &&
hasPermissions(authStore.userData, 'admin', 'any')
"
class="font-small mb-10 rounded-md bg-red-800 p-4 text-sm text-white shadow-lg dark:bg-red-100 dark:text-red-600"
class="font-small rounded-md bg-red-800 p-4 text-sm text-white shadow-lg dark:bg-red-100 dark:text-red-600"
:title="`v${globalStore.information.currentRelease} → v${globalStore.information.latestRelease.version}`"
>
<div class="container mx-auto flex flex-auto flex-row items-center">
+7
View File
@@ -0,0 +1,7 @@
<template>
<ClipboardDocumentIcon />
</template>
<script lang="ts" setup>
import ClipboardDocumentIcon from '@heroicons/vue/24/outline/esm/ClipboardDocumentIcon';
</script>
@@ -0,0 +1,7 @@
<template>
<MagnifyingGlassIcon />
</template>
<script lang="ts" setup>
import MagnifyingGlassIcon from '@heroicons/vue/24/outline/esm/MagnifyingGlassIcon';
</script>

Some files were not shown because too many files have changed in this diff Show More