Compare commits

130 Commits

Author SHA1 Message Date
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
Bernd Storath
3ef258a28a Bump version to 15.0.0-beta.12 2025-04-11 23:35:51 +02:00
Bernd Storath
ff783fd4d1 Feat: Improve Docs (#1791)
* improve docs

* preplan guides

* fix spelling

* fix nftables rules

* consistent wg-easy code block

* fix grammar
2025-04-11 23:25:58 +02:00
Bernd Storath
65aa067100 update packages 2025-04-11 22:46:03 +02:00
Edgars
48e6949a4d Update docker-run.md (#1804)
Small fixes were made to:

- adjust indentation for the first line in code blocks;
- fix backticks;
- make the URL interactive.
2025-04-09 11:25:30 +02:00
Bernd Storath
9ebf2c1d33 chore: disable latest tag for docker
so old installations wont break until v15 is stable enough
2025-04-02 09:32:52 +02:00
Bernd Storath
d0f85316a6 chore: delete changelog from docs 2025-04-02 09:20:18 +02:00
Bernd Storath
ff9fd553c5 Fix: sync / rekey issue (#1789)
only sync if needed
2025-04-02 08:49:04 +02:00
Bernd Storath
e92ee0464e Feat: Server Endpoint (#1785)
* add server endpoint to client

* be able to update endpoint over api
2025-04-01 16:13:51 +02:00
Bernd Storath
9df049d3f4 Feat: startup info (#1784)
add startup info
2025-04-01 15:18:13 +02:00
Bernd Storath
32b73b850a Feat: 2fa (#1783)
* preplan otp, better qrcode library

* add 2fa as feature

* add totp generation

* working totp lifecycle

* don't allow disabled user to log in

not a security issue as permission handler would fail anyway

* require 2fa on login

if enabled

* update packages

* fix typo

* remove console.logs
2025-04-01 14:43:48 +02:00
Bernd Storath
1c7f64ebd5 Bump version to 15.0.0-beta.11 2025-03-31 10:32:27 +02:00
Bernd Storath
589ec1fe9a Feat: Show insecure warning (#1779)
show insecure warning
2025-03-31 10:29:22 +02:00
Bernd Storath
6e0d758e36 Feat: Hash metrics password (#1778)
hash the metrics password

if it is not already hashed
2025-03-31 09:58:02 +02:00
Pokydko Oleksandr
940edb2b0c Improvements to username and password validations (#1745)
* Fix: Improve special character regex (#1744)
* update password special character regex to support
( `-` `_` `=` `+` `[` `]` `;` `'` `\` `/` )

* Fix: Allow usernames starting from 2 characters (#1744)
* update username validation to support short usernames starting from 2 characters

* remove char validation altogether

---------

Co-authored-by: Bernd Storath <999999bst@gmail.com>
2025-03-31 08:58:24 +02:00
Bernd Storath
d51f12a82f update packages 2025-03-31 07:51:21 +02:00
Bernd Storath
4a3747fa12 update packages 2025-03-24 07:53:02 +01:00
Bernd Storath
499fb096b6 Fix: button triggering form (#1750)
prevent button from triggering form
2025-03-19 11:50:48 +01:00
Bernd Storath
c5c3a65bbf Fix: slow suggest Host Address (#1746)
load on client instead of block
2025-03-17 14:28:03 +01:00
Bernd Storath
c133446f9c Bump version to 15.0.0-beta.10 2025-03-14 14:28:12 +01:00
Bernd Storath
e8b3e54228 fix libsql bundling issue 2025-03-14 14:26:08 +01:00
Bernd Storath
a9a51337da Bump version to 15.0.0-beta.9 2025-03-14 12:22:43 +01:00
Bernd Storath
bbee7e04ed Feat: add ability to restart interface (#1740)
add ability to restart interface
2025-03-14 12:19:26 +01:00
Bernd Storath
198b240755 Feat: Suggest IP or Hostname (#1739)
* get ip and hostnames

* use heroicons

* add host field

* get private info

* unstyled prototype

* styled select

* add to setup

* fix types
2025-03-14 10:33:02 +01:00
Bernd Storath
86bdbe4c3d Feat: Initial Setup through env vars (#1736)
* initial support for initial setup

* improve setup

* improve mobile view

* move base admin route

* admin panel mobile view

* set initial host and port

* add docs

* properly setup everything, use for dev env

* change userconfig and interface port on setup, note users afterwards
2025-03-13 11:28:05 +01:00
Bernd Storath
4890bb28e5 Bump version to 15.0.0-beta.8 2025-03-12 13:47:46 +01:00
Bernd Storath
c3dbd3a815 Fix: Add ui port to template (#1735)
* add ui port to template

* update changelog
2025-03-12 13:44:45 +01:00
Bernd Storath
fc480df910 Fix: Update ip outside of cidr (#1733)
* update packages

* check if ip is included on update

* update package manager
2025-03-12 12:47:12 +01:00
dependabot[bot]
b3bd2502af build(deps): bump nuxt from 3.15.4 to 3.16.0 in /src (#1727)
Bumps [nuxt](https://github.com/nuxt/nuxt/tree/HEAD/packages/nuxt) from 3.15.4 to 3.16.0.
- [Release notes](https://github.com/nuxt/nuxt/releases)
- [Commits](https://github.com/nuxt/nuxt/commits/v3.16.0/packages/nuxt)

---
updated-dependencies:
- dependency-name: nuxt
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-10 12:26:21 +01:00
dependabot[bot]
eb5ad91022 build(deps-dev): bump eslint from 9.21.0 to 9.22.0 in /src (#1726)
Bumps [eslint](https://github.com/eslint/eslint) from 9.21.0 to 9.22.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v9.21.0...v9.22.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-10 08:11:31 +01:00
dependabot[bot]
f2955a1278 build(deps): bump @nuxtjs/i18n from 9.2.1 to 9.3.1 in /src (#1728)
Bumps [@nuxtjs/i18n](https://github.com/nuxt-modules/i18n) from 9.2.1 to 9.3.1.
- [Release notes](https://github.com/nuxt-modules/i18n/releases)
- [Changelog](https://github.com/nuxt-modules/i18n/blob/main/CHANGELOG.md)
- [Commits](https://github.com/nuxt-modules/i18n/compare/v9.2.1...v9.3.1)

---
updated-dependencies:
- dependency-name: "@nuxtjs/i18n"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-10 07:34:37 +01:00
dependabot[bot]
1b76c066e0 build(deps-dev): bump @nuxt/eslint from 1.1.0 to 1.2.0 in /src (#1729)
Bumps [@nuxt/eslint](https://github.com/nuxt/eslint/tree/HEAD/packages/module) from 1.1.0 to 1.2.0.
- [Release notes](https://github.com/nuxt/eslint/releases)
- [Commits](https://github.com/nuxt/eslint/commits/v1.2.0/packages/module)

---
updated-dependencies:
- dependency-name: "@nuxt/eslint"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-10 07:27:29 +01:00
Bernd Storath
5b68cc7311 Feat: Confirm setup password (#1722)
confirm setup password
2025-03-07 16:05:40 +01:00
Bernd Storath
0597470f4c Bump version to 15.0.0-beta.7 2025-03-07 15:00:12 +01:00
Bernd Storath
159a51cff4 Feat: Global config override (#1720)
* be able to change dns. implement global override

* link donate to readme

* implement global config for allowed ips

* change translations, fix generation

* improve docs
2025-03-07 14:59:06 +01:00
Bernd Storath
9fc6ebafb3 Bump version to 15.0.0-beta.6 2025-03-07 12:02:03 +01:00
Bernd Storath
9a029eeb23 improve docs, add version script 2025-03-07 12:02:02 +01:00
Bernd Storath
e5fb6ff3a6 Fix: OneTimeLinks (#1719)
* fix otls

* one otl per client

* revert some code

* revert some more code, add comments

* adjust migration
2025-03-07 09:16:24 +01:00
杨黄林
fcb5049dab Add PreUp, PostUp, PreDown, PostDown for client (#1714)
* Fix create client popup background is not white

* Fix no Add button when client Allowed Ips or Server Allowed Ips is empty

* Add preUp preDown postUp postDown for client

* Add description of hooks for client config

* Move hooks's label text into 'hooks' in en.json

---------

Co-authored-by: yanghuanglin <yanghuanglin@qq.com>
Co-authored-by: Bernd Storath <999999bst@gmail.com>
2025-03-07 08:17:33 +01:00
Bernd Storath
93db67bab6 fix: only require metrics password if set (#1715) 2025-03-06 11:45:03 +01:00
Bernd Storath
842475f799 Fix: Cidr Change (#1712)
* only calculate ip if cidr changed

if the cidr did not change, the ip will not change to prevent ip shifts

* fix lint
2025-03-06 10:04:49 +01:00
Bernd Storath
f4d3608da7 Fix: Various (#1711)
* fix docs

* fix migration
2025-03-06 08:15:18 +01:00
Bernd Storath
84ee8c35f0 fix labels, date field, enable 2025-03-05 15:38:09 +01:00
Bernd Storath
ded148a0f5 fix docs action 2025-03-05 13:37:16 +01:00
Bernd Storath
9b29d72991 Version 15.0.0-beta.1: Rewrite in Nuxt and Typescript, Move to UI (#1333)
* Add Nuxt, ESM, Typescript (#1244)

* wip: add nuxt

* basic implementation

* add changes from c9ff248

* update workflow, add eslint

* add types, fix wrong error message

* install correct bcrypt, move eslint to dev modules

* add docker dev script

* fix styling

* add wireguard routes

* typescript, vendors

* fix lint workflow

* lint fixes

* add prettier, format

* fix lint, add vscode settings

* better typescript

* use auto imports

* add prettier eslint config

* cache config

* fix styling issue, fix formatting

* fix tailwind problems

* fix logout not showing

* fix lint action

* Fix session middleware

* split files into correct methods

* use type safe api, fix typescript errors

* better return types

not tested

* change default working directory

* update workflows

* fix error

* correct session middleware, type safe session

* convert undefined to boolean

* correct key for api errors

* use zod to validate input

* add more jobs to check for good code

* add pinia

Co-authored-by: Sergei Birukov <suxscribe@gmail.com>
Co-authored-by: Bernd Storath <bernd.storath@offizium.de>

* use color mode plugin

* !! use better storage key name

Breaking as if old key exists it breaks as "auto" is not compatible with new "system"

* better local dev while dev container is running

use `docker compose -f docker-compose.dev.yml up`
or after changing dockerfile
`docker compose -f docker-compose.dev.yml up --build`

* update translation to match new theme mode

* improve dx

new devs get extensions recommended to catch errors, etc directly in vscode

* reduce errors, improve typing

* Split components (#1)

* update: introduce pages & components

 fix lint

* update: starting split components

* use auto imports

* Improve workflows and docker

workflow fix step naming

simplify docker dev

simplify docker prod

revert to node 18

dockerfile naming scheme

* Split components (#2)

* update: starting split components

* upd: rebase & continue splitting components

- layouts: header & footer
- components: basic buttton
- pages: login page

* update: login page

* package.json: remove dev:pass script

* Split into Components, migrate to nuxt

fixup

shutdown wireguard properly

fix styling, fix store

split even more

clear interval

split even more

split even more

handle auth middleware on server

avoid flicker of login page

* fix: buttons spaces & move layouts to components (#3)

* update: icons into components

- fix: header login page

* fix: tailwind handle btn class

* Split into icons

fix avatar

move class to view not icon itself

fix icon

format

* invalidate cache to make restoreConfig work

* fix apexchart

* use different color mode module

other one resulted in hydration mismatch

* fix dialog

* fix bad i18n merge

* use nuxt 4

* fix typing, fix redirect, latest release on server

* start wireguard on start

* wait for shutdown

* improve zod errors, consistent server errors

* migrate to useFetch

this makes sure that there is no double fetching

* fix hydration issues, remove unnecessary state, rename function

* fetch globalstore globally

otherwise this will load on login to homepage

* migrate to useFetch

no javascript support

TODO: not properly tested

* update backend

* wip: frontend

* update frontend

* update pnpm lock

---------

Co-authored-by: Sergei Birukov <suxscribe@gmail.com>
Co-authored-by: Bernd Storath <bernd.storath@offizium.de>
Co-authored-by: tetuaoro <65575727+tetuaoro@users.noreply.github.com>

* Fix various issues

fix router param

fix max age

unit is seconds not ms

fix regressions

fix missing expire date in client create dialog
fix wrong type rules
fix wrong api endpoint

properly catch error running cron job

fix type issues

* add database (#1330)

* add: database abstraction

* update: get lang from database

* udpate: with repositories

* add: interfaces to connect a database provider

- easy swapping between database provider

* add: setup page

- add: in-memory database provider
- create a new account (signup)
- login with username and password
- first setup page to create an account
- PASSWORD_HASH was removed from environment and files was updated/removed due to that change

* update: Dockerfile

* fix: review done

- remove: REQUIRES_PASSWORD & RELEASE environment variables

* fix: i18n translation

- rename directories

* update: use database

* fix: typecheck

* fix: review

* rebase & add: persistent lowdb provider

* update: french translation

* revert: due to rebase

* remove & document

* Refactor New UI (#1342)

* refactor code

* refactor code

* add some todos

* update pnpm, start migrating to database

* add missing i18n key

* add todo

* basic setup styling

* nuxt 4 folder structure, update packages

* Feat: Migrations (#1344)

* add migrations

* improve migration runner

* improve migration runner

* document what each migration does

* Feat: Rewrite Wireguard to use Database (#1345)

* update wireguard

* update

* update

* remove all config

* move all features into one route

* improve code

* fix some issues

add wg_path, update documentation

* Feat: Cidr Support (#1347)

* cidr support

* add cidr

* fix some errors

fix server config

missing cidr block in server config

* Fix: Database Date type (#1349)

* Feat: IPv6 (#1354)

* start supporting ipv6

* add ipv6 support

* build server with es2020

es2019 doesn't support bigint

* fix issues, better naming

* Fix: Security (#1355)

* separate route for onboarding

* zse zod for validation

* use argon2id

* add build tools

* Feat: Server AllowedIPs, MTU (#1356)

* add wireguard helpers

* improve wireguard helpers

* add server mtu

* fix wg0.conf formatting

* add ipv6 support to docker compose and readme

* Feat: Docs (#1361)

* basic docs

* use semver versioning

* Feat: Migration (#1363)

* start migration

* improve migration

* remove endpoint from client

* improve docker

* Chore: Deprecate Dockerless (#1377)

* deprecate dockerless

* Feat: Improve Repository pattern (#1380)

* improve repository pattern

* fix errors

* Feat: Improve Database Handling (#1383)

* improve docker build

* build doc workflow

* Feat: Changelog, Release Notes (#1385)

* add changelog, use semver for update message

* use first line of release for short changelog

* load ipv6 iptables module

* Feat: Show Version in Footer (#1389)

update ui logic, always store release in global store.

new release logic uses rate limited github api, avoid using cache

* use i18n ally (#1391)

* improve gh actions

* Setup UI (#1392)

* update: setup ui page

* rebase

* remove script addition

* Fixed usage of Ukrainian instead of Russian in ru.json (#1414)

* Added translations for the Belarusian language (#1472)

* Install kmod from alpine repository (#1553)

Because the busybox modprobe utility is unable to load zstd compressed modules.

Co-authored-by: Matt <mmoore2012@users.noreply.github.com>

* WIP: Feat: UI, General Improvements (#1397)

* update: setup ui page

* remove script addition

* add admin panel

* basic user menu and admin page

* make usable admin panel

* add radix vue, improve ui

* fix features, add toast

* rewrite middleware logic, support basic auth

* add todo marker

* active tailwind forms

* remove some console.logs

* check if user is enabled

frontend doesn't handle this state yet, nothing will work as api routes will fail

* add email to user, basic account page

* better group database

* group even more

* basic statistics page

* update: admin ui

- add: common panel components to get same UI
- i18n: french

* update: setup page error handle

- use fetch error data to provide error message
- use translation to provider error message

* update: me page

* fix: :text props

* update: login page

* update: i18n french support

* fix: use radix toast duration

* update: reduce templates

- remake: setup page to add others step configuration (host/port/migration)

* udpate: setup page use wizard form step

* update: ui

* update: step page

- first step to choose a language
- use red color in light mode
- validate step before move toward

* update: setup page

- use radix select component to reduce boilerplate

* update: setup page

- add: database langugage method
- update: api lang & export supported languages

* update: setup page

- update ui select language
- change lang on selection

* fix: use global store

* fix: initial value

- update: sort langs by value

* fix: ui center paragraph

* fix: remove file extension & some revert

- add: script to run checks script

* update: setup page

- add: host/port section
- i18n: french
- fix: fallback translation

* refactor: split setup into files

* update: setup page

- redirect to login when the setup is done
- allow user to return to previous steps
- prompt error message
- i18n french

* add: migration UI step

- rename: components
- fix: label for & form child id
- i18n french sup

* add: migration server

* fix: use string instead of File

* improve: with zod validation

* restore: clients

* rework setup

* add client page, move api routes

* improve setup

* switch to agpl

* add step back

* update licensed under texts

cc -> agpl

* make db results readonly

avoid weird side effects, when modifying the db object as its only allowed inside e.g. lowdb.ts

* update footer links

* improve client edit page, add mtu

* reorder tailwind classes

* update packages

* update comments

* better toast, better avatar

* delete feature toggle

* remove chart, statistics from server

let user decide what he wants to display

* move into own components

* switch from AGPL-3.0-or-later to AGPL-3.0-only

AGPL-3.0-or-later is not OSI approved

* fix building source

fixes https://github.com/wg-easy/wg-easy/issues/1563

* update packages

---------

Co-authored-by: tetuaoro <65575727+tetuaoro@users.noreply.github.com>

* update readme

* Feat: Settings, UI, General Improvements (#1572)

* deprecate other languages

new ui has too many new strings

* fix wrong license in readme

* properly fetch release

* order safe data structure for migrations

* empty server allowed ips by default

* show userconfig in admin panel

* remove routes, fix config

* add ability to update clients

* handle form submit using js

avoid weird behavior with FormData

* global toast, be able to update client

* update packages

* fix date field

* delete client using radix dialog

* remove lang from backend, let users decide

* be able to change interface and general

* be able to update user config

* consistent allowedips

* fix array field

* improve avatar, code cleanup

* basic metrics support

* remove dateTime helper

* be able to change hooks

* start cidr update

* be able to update cidr

* Feat: SQLite (#1619)

* start drizzle migration

* split schema

* improve schema

* improve schema, cascade, unique

* improve structure, start migration

* migrate to sqlite

* work in prod docker

* start adding a better permission handler

* permission matrix, permission handler

* update packages

* move session timeout to session config, use new permission handler

* improve docker dev

only install dependencies if changed

* implement setup

* migrate to sqlite

* improve debug, fix custom migration

* migrate to sqlite

* regenerate migrations

* ignore autogenerated migrations from prettier

* migrate to sqlite

* migrate to sqlite

* Migrate to sqlite

* fix prod error

* move nuxt middleware from server to nuxt

* update corepack in prod dockerfile

* use correct branch for workflow

* make docker file build on armv6/v7

* fix client update

* update zod locales

* cancel pr workflow if new commit

* test concurrency

* Feat: Account (#1645)

* be able to change name, email

* be able to change password

* consistent naming

zod is a schema not a type

* use transaction instance

* add zod strings

* Feat: Prometheus (#1655)

* check metrics password

* rewrite prometheus and json metric endpoints

* move metrics to general

metrics is not per interface

* change metrics settings in admin panel

* add i18n keys

* Chore: Remove multi interface support (#1657)

* streamline references to wg0

database wg0 name makes no sense anymore
wg0 only in database, could be easily replaced, or support for custom name added

* fix default key gen

* Feat: Permission System (#1660)

* wip: add abac

* wip: add admin abac

* add me abac

* fix type issue

* move from role check

avoid authStore.userData?.role === roles.ADMIN

* Feat: Zod Generic String (#1661)

* start improving zod translations

* update zod translations

* Feat: Migration (#1663)

* show error for old env vars

* reorder setup, be able to migrate

* fix type issue

* footer and header in setup, remove lang setup step

* remove backup / restore

* refactor dialog (#1665)

* fixed Dockerfile HEALTHCHECK syntax (#1686)

HEALTHCHECK options should always come before the CMD instruction

* Feat: Info (#1666)

* add tooltip info, extract strings

* multi type toast

* improve useSubmit, i18n

* better login screen

* improve

* consistent folder casing

* consistent casing

* fix even more stuff

* temp

* fix type errors

* remove armv6/7 support for now

* add information to client page

* optimize dockerfile

* update base image in Dockerfile to use node:lts-alpine

* fix build stage

* Chore: TODOs (#1695)

* verify setup step

* improve readme

* format todos

* move id

* remove objectMessage

* style array field

* Chore: TODOs (#1696)

* fix chart

* replace localstorage with cookies

* Chore: Improvments (#1697)

* update packages

* fix tab issues

* consistent imports

* use eslint module

* update date

* improve docs

* update docs

* format

* fix docs, fix cookie

* recognize timing attack potential

* improve gh actions, issue templates (#1700)

* Feat improv (#1702)

* add insecure option, link readme to docs

* improve docs

* update version

* add warning to readme

---------

Co-authored-by: Sergei Birukov <suxscribe@gmail.com>
Co-authored-by: Bernd Storath <bernd.storath@offizium.de>
Co-authored-by: tetuaoro <65575727+tetuaoro@users.noreply.github.com>
Co-authored-by: laperuz92 <31198184+laperuz92@users.noreply.github.com>
Co-authored-by: Siomkin Alexander <siomkin.alexander@gmail.com>
Co-authored-by: Matt <102529127+mmoore2012@users.noreply.github.com>
Co-authored-by: Matt <mmoore2012@users.noreply.github.com>
Co-authored-by: Denis Kazimirov <rokiden@users.noreply.github.com>
2025-03-05 13:06:31 +01:00
Philip H.
c6dce0f6fb Dockerfile: don't update npm to latest, fix iptables paths (#1574)
* Dockerfile: don't update npm to latest

It's breaking nightly builds

* Dockerfile: path for iptables-legacy dosn't exist

* Dockerfile: fix iptables paths

* fixup: iptables-legacy

* Update Dockerfile
2024-12-22 14:14:01 +01:00
shalafi99
ddc7394e1b Document updates: docker-compose.yml / How_to_generate_an_bcrypt_hash.md (#1529)
* Update How_to_generate_an_bcrypt_hash.md

inclusion of single quotes for password with docker run command

addition of "--rm" parameter to docker run command as to cleanup the wg-easy container created with the password hash generation command

* Update docker-compose.yml

addition of missing # before the comment on PASSWORD_HASH line
2024-11-24 15:49:01 +01:00
Bernd Storath
6c752941f3 Update CODEOWNERS
Remove pheiduck
Reorder so WeeJeWel is not pinged on every PR
2024-10-18 09:02:26 +02:00
Philip H.
eda8040a03 README.md: add donation link for WireGuard 2024-10-14 18:13:04 +02:00
dependabot[bot]
bf77386ec6 build(deps): bump express-session from 1.18.0 to 1.18.1 in /src (#1470)
Bumps [express-session](https://github.com/expressjs/session) from 1.18.0 to 1.18.1.
- [Release notes](https://github.com/expressjs/session/releases)
- [Changelog](https://github.com/expressjs/session/blob/master/HISTORY.md)
- [Commits](https://github.com/expressjs/session/compare/v1.18.0...v1.18.1)

---
updated-dependencies:
- dependency-name: express-session
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-14 09:23:24 +02:00
dependabot[bot]
6f2811c637 build(deps): bump h3 from 1.12.0 to 1.13.0 in /src (#1454)
Bumps [h3](https://github.com/unjs/h3) from 1.12.0 to 1.13.0.
- [Release notes](https://github.com/unjs/h3/releases)
- [Changelog](https://github.com/unjs/h3/blob/v1.13.0/CHANGELOG.md)
- [Commits](https://github.com/unjs/h3/compare/v1.12.0...v1.13.0)

---
updated-dependencies:
- dependency-name: h3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-07 09:56:08 +02:00
dependabot[bot]
e75719b803 build(deps-dev): bump tailwindcss from 3.4.12 to 3.4.13 in /src (#1446)
Bumps [tailwindcss](https://github.com/tailwindlabs/tailwindcss) from 3.4.12 to 3.4.13.
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/v3.4.13/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/compare/v3.4.12...v3.4.13)

---
updated-dependencies:
- dependency-name: tailwindcss
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-30 13:29:41 +02:00
Gašper Spagnolo
3fc0e02798 Add slovene translation (#1440) 2024-09-28 11:37:09 +02:00
Philip H.
5aed67838c README.md: fixup urls
fix error 404
2024-09-23 12:27:57 +02:00
Edgars
99e1e15b03 Make minor improvements for ReadMe.md (#1428)
The following minor improvements were made to `README.md`:

- The notice about stable version was styled as block quote to emphasize
  it more and it was moved right after the title of the section
  "Versions", and a link to the production branch was added;
- versions table was updated to add links to branches for easier
  navigation;
- some typos were fixed;
- wording was changed in couple of places to make it simpler;
- the `docker run` command was updated to:
  - use long options to make it more self-explanatory;
  - wrap password hash value placeholder in single quotes to avoid
    variable expanding as password hashes tend to contain `$`;
  - some other very minor changes to make `docker run` options uniform.

Co-authored-by: Philip H. <47042125+pheiduck@users.noreply.github.com>
2024-09-23 12:02:33 +02:00
dependabot[bot]
6dc89257ff build(deps-dev): bump nodemon from 3.1.4 to 3.1.7 in /src (#1426)
Bumps [nodemon](https://github.com/remy/nodemon) from 3.1.4 to 3.1.7.
- [Release notes](https://github.com/remy/nodemon/releases)
- [Commits](https://github.com/remy/nodemon/compare/v3.1.4...v3.1.7)

---
updated-dependencies:
- dependency-name: nodemon
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-23 10:07:33 +02:00
dependabot[bot]
a32655992f build(deps-dev): bump tailwindcss from 3.4.11 to 3.4.12 in /src (#1425)
Bumps [tailwindcss](https://github.com/tailwindlabs/tailwindcss) from 3.4.11 to 3.4.12.
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/v3.4.12/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/compare/v3.4.11...v3.4.12)

---
updated-dependencies:
- dependency-name: tailwindcss
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-23 10:05:04 +02:00
Philip H.
2bd1dffe7e CODEOWNERS: fixup request review by all Maintainers
Emile will also be on this list, should be fine anyways.
2024-09-17 17:39:25 +02:00
Naoki Ishikawa
3c959ba3bb Add japanese translation. (#1403) 2024-09-16 08:31:28 +02:00
dependabot[bot]
0b204a6f21 build(deps-dev): bump tailwindcss from 3.4.10 to 3.4.11 in /src (#1412)
Bumps [tailwindcss](https://github.com/tailwindlabs/tailwindcss) from 3.4.10 to 3.4.11.
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/v3.4.11/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/compare/v3.4.10...v3.4.11)

---
updated-dependencies:
- dependency-name: tailwindcss
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-16 08:28:26 +02:00
Philip H.
efe5cccb02 index.html: Emile Nijssen's blog url changed 2024-09-13 18:24:41 +02:00
Tristan
31c59c1b26 Update README.md (#1390) 2024-09-11 09:05:32 +02:00
Vadim Babadzhanyan
6e891ad9fb Prometheus password fix (#1319)
* Russian translation

* Fix require prometheus password

---------

Co-authored-by: Vadim Babadzhanyan <vadim.babadzhanyan@my.games>
2024-09-10 18:46:09 +02:00
Philip H.
e2fe3b8b32 fixup: deploy.yml
echo moved by a mistake
2024-09-09 15:31:18 +02:00
Philip H.
c0fbc3f647 Update deploy.yml 2024-09-09 13:33:52 +02:00
Xiwangly
f3324cbb28 Update i18n.js, add chs & cht translations (#1374) 2024-09-08 08:21:26 +02:00
Vadim Babadzhanyan
33b4c3859d Fix onetimelink (#1367)
* Russian translation

* fix onetime link for android tv

---------

Co-authored-by: Vadim Babadzhanyan <vadim.babadzhanyan@my.games>
2024-09-07 08:43:59 +02:00
Philip H
067b7bcf84 CI: fixup dev deploy too
Signed-off-by: Philip H <47042125+pheiduck@users.noreply.github.com>
2024-09-06 16:39:34 +02:00
Philip H.
942f35916c deploy-nightly.yml: reference to master branch 2024-09-06 16:35:03 +02:00
dependabot[bot]
52d83dbf35 build(deps): bump debug from 4.3.6 to 4.3.7 in /src (#1365)
Bumps [debug](https://github.com/debug-js/debug) from 4.3.6 to 4.3.7.
- [Release notes](https://github.com/debug-js/debug/releases)
- [Commits](https://github.com/debug-js/debug/compare/4.3.6...4.3.7)

---
updated-dependencies:
- dependency-name: debug
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-06 16:29:05 +02:00
Philip H.
4dc56a0718 CI: use dependabot instead of gh action (#1364) 2024-09-06 16:26:27 +02:00
NPM Update Bot
66c64af7cf npm: package updates 2024-09-05 16:09:17 +00:00
Philip H.
f4b3d4fb6b wg-password(docu): fixup docker command 2024-09-05 18:08:49 +02:00
NPM Update Bot
3427d677f6 npm: package updates 2024-09-04 16:37:44 +00:00
Bernd Storath
4ba7dc244c early fail if old password variable (#1350) 2024-09-04 18:37:11 +02:00
NPM Update Bot
3f6b6f3c9b npm: package updates 2024-09-03 20:34:41 +00:00
Hans
11872de321 Allow wgpw to prompt for a password through stdin (#1348)
* Allow wgpw to prompt for a password through stdin

If the user does not pass the password as a parameter, they are prompted
for it through stdin.
The password is not echoed back, just like any other command-line log-in
prompt (ie. sudo).

* Fix lint errors in wgpw
2024-09-03 22:34:08 +02:00
NPM Update Bot
4758c0dddc npm: package updates 2024-09-02 00:03:22 +00:00
NPM Update Bot
96030c08f4 npm: package updates 2024-08-30 11:17:49 +00:00
Kirk1984
ba989aa829 Fix typo in How_to_generate_an_bcrypt_hash.md (#1332) 2024-08-30 13:17:20 +02:00
NPM Update Bot
78fab2b5d8 npm: package updates 2024-08-26 00:03:04 +00:00
NPM Update Bot
dd04dd285d npm: package updates 2024-08-23 10:11:05 +00:00
Vadim Babadzhanyan
7be9884aec Feat Prometheus metrics (#1299)
* Russian translation

* Add Prometheus metrics
[Feat]: Simple Stats API #1285

* Revert "Add Prometheus metrics"

This reverts commit a998f6be8a0c54a5daffe70a0fc3d8b9ed53a960.

* Add Prometheus metrics
[Feat]: Simple Stats API #1285

* Fix short link. Generate One Time Link (#1301)

Co-authored-by: Vadim Babadzhanyan <vadim.babadzhanyan@my.games>

* fix one time links (#1304)

Closes #1302
Co-authored-by: Bernd Storath <999999bst@gmail.com>

* fixup: issue templates due to labels reorg

Signed-off-by: Philip H <47042125+pheiduck@users.noreply.github.com>

* Separate port for prometheus metrics
Add Prometheus metrics [Feat]: Simple Stats API #1285

* Separate port for prometheus metrics
Add Prometheus metrics [Feat]: Simple Stats API #1285

* Fix port in Readme
Separate port for prometheus metrics
Add Prometheus metrics [Feat]: Simple Stats API #1285

* Add Prometheus port in Service
Separate port for prometheus metrics
Add Prometheus metrics [Feat]: Simple Stats API #1285

* Revert "Add Prometheus port in Service"

This reverts commit a7376abcf1fe2b729ab05ba0d49977ab5a2642ea.

* Revert "Fix port in Readme"

This reverts commit 9760bde2f2dc4428b0bf0b27d91bbded1c2ad05d.

* Revert "Separate port for prometheus metrics"

This reverts commit 58f5b6806e20c7704ff04247f384d30c2845a34e.

* Revert "Separate port for prometheus metrics"

This reverts commit 6d246ea4bda265f8b8b9e99acb336aeb26c9fa17.

* Add Prometheus metrics with Basic Auth
[Feat]: Simple Stats API #1285

* Disable by default
[Feat]: Simple Stats API #1285

* [Feat]: Simple Stats API #1285

* Update README.md

---------

Co-authored-by: Vadim Babadzhanyan <vadim.babadzhanyan@my.games>
Co-authored-by: Bernd Storath <bernd.storath@offizium.de>
Co-authored-by: Philip H <47042125+pheiduck@users.noreply.github.com>
2024-08-23 12:10:20 +02:00
Bernd Storath
41be774761 rename features, fix formatting 2024-08-23 08:32:47 +02:00
Philip H.
ed93c6c8ed CODEOWNERS: make it commplete 2024-08-22 23:46:04 +02:00
NPM Update Bot
bbffc22ae3 npm: package updates 2024-08-22 21:45:42 +00:00
Philip H.
800ec155c1 CODEOWNERS: add kaaax0815 2024-08-22 23:45:04 +02:00
Philip H
75df17476f fixup: issue templates due to labels reorg
Signed-off-by: Philip H <47042125+pheiduck@users.noreply.github.com>
2024-08-21 17:07:46 +02:00
Bernd Storath
86f968499a fix one time links (#1304)
Closes #1302
Co-authored-by: Bernd Storath <999999bst@gmail.com>
2024-08-21 15:55:35 +02:00
Vadim Babadzhanyan
968d2b90a0 Fix short link. Generate One Time Link (#1301)
Co-authored-by: Vadim Babadzhanyan <vadim.babadzhanyan@my.games>
2024-08-20 16:19:07 +02:00
Philip H
352a022f30 nodejs: use lts as version tag
Signed-off-by: Philip H <47042125+pheiduck@users.noreply.github.com>
2024-08-19 14:30:41 +02:00
Vadim Babadzhanyan
8145809e22 Feat expiration date (#1296)
Closes #1287
Co-authored-by: Vadim Babadzhanyan <vadim.babadzhanyan@my.games>
2024-08-18 23:18:09 +02:00
Vadim Babajanyan
40af030266 Update Russian translation (#1291)
Co-authored-by: Vadim Babadzhanyan <vadim.babadzhanyan@my.games>
2024-08-16 21:48:22 +02:00
Vadim Babajanyan
bb2e8d2751 Fix sort clients (#1290)
Co-authored-by: Vadim Babadzhanyan <vadim.babadzhanyan@my.games>
2024-08-16 20:47:31 +02:00
Võ Hoàng
ca7ee32052 feat(www): add sort clients by name (#1227)
Co-authored-by: Philip H. <47042125+pheiduck@users.noreply.github.com>
2024-08-16 19:40:24 +02:00
Philip H.
7c521e8733 Add Remember me (#1276) 2024-08-16 19:27:30 +02:00
Philip H.
f5885e335c Merge branch 'master' into remember-me 2024-08-16 19:25:36 +02:00
Philip H.
36b9ff60c8 Supports displaying short links (#1288) 2024-08-16 19:24:43 +02:00
Vadim Babadzhanyan
cd3d4efebf Supports displaying short links, for easy downloading on TVs and Android TVs: fix lint errors 2024-08-16 20:20:31 +03:00
Vadim Babadzhanyan
81633de07b Supports displaying short links, for easy downloading on TVs and Android TVs: Add crc-32 package 2024-08-16 20:08:30 +03:00
Vadim Babadzhanyan
0a33b1f7df Supports displaying short links, for easy downloading on TVs and Android TVs 2024-08-16 18:39:24 +03:00
NPM Update Bot
2ea37dd7ba npm: package updates 2024-08-15 15:04:36 +00:00
Philip H.
2fe5269bbe Additional Korean language updates (#1278) 2024-08-15 17:04:07 +02:00
jkh0kr
8591b35d4e Update i18n.js
Additional Korean language updates
2024-08-14 10:00:58 +09:00
Philip H.
7fbc2702a6 fix pr template location (#1277)
Co-authored-by: Bernd Storath <999999bst@gmail.com>
2024-08-13 14:54:46 +02:00
Bernd Storath
4ead4c2cc9 fix pr template location 2024-08-13 08:30:51 +02:00
Viktor Yudov
3e6ded18a5 Add Remember me 2024-08-13 02:51:05 +03:00
NPM Update Bot
4e79d0ee03 npm: package updates 2024-08-12 14:56:03 +00:00
Philip H.
6d59e16161 add better issue template (#1274)
Co-authored-by: Bernd Storath <999999bst@gmail.com>
2024-08-12 16:55:29 +02:00
Bernd Storath
0386a0da6b add better issue template
Co-authored-by: Bernd Storath <bernd.storath@offizium.de>
2024-08-12 16:34:01 +02:00
NPM Update Bot
0bf266d5cb npm: package updates 2024-08-12 08:56:59 +00:00
Philip H.
0ca39b4f34 fix(Doc): fix escaping issue for PASSWORD_HASH in docker-compose.yml (#1270)
For users using docker-compose.yml, please note that you should not wrap the generated hash password in single quotes. Instead, replace each `$` symbol with two `$$` symbols.

For example, for the password 'foobar123', use the following command to generate the hash:
`docker run ghcr.io/wg-easy/wg-easy wgpw foobar123`

The resulting hash should be used in docker-compose.yml like this:
``` yaml
- PASSWORD_HASH=$$2y$$10$$hBCoykrB95WSzuV4fafBzOHWKu9sbyVa34GJr8VV5R/pIelfEMYyG
```

Signed-off-by: cyicz123 <cyicz123@outlook.com>
2024-08-12 10:56:32 +02:00
程越
a268422e9d fix(Doc): fix escaping issue for PASSWORD_HASH in docker-compose.yml
For users using docker-compose.yml, please note that you should not wrap the generated hash password in single quotes. Instead, replace each `$` symbol with two `$$` symbols.

For example, for the password 'foobar123', use the following command to generate the hash:
`docker run ghcr.io/wg-easy/wg-easy wgpw foobar123`

The resulting hash should be used in docker-compose.yml like this:
``` yaml
- PASSWORD_HASH=$$2y$$10$$hBCoykrB95WSzuV4fafBzOHWKu9sbyVa34GJr8VV5R/pIelfEMYyG
```

Signed-off-by: cyicz123 <cyicz123@outlook.com>
2024-08-12 11:25:17 +08:00
Philip H.
8921d6c661 docker-compose.yml: reflect How_to_generate_an_bcrypt_hash.md 2024-08-10 19:33:30 +02:00
Philip H.
b18f919419 fixup How_to_generate_an_bcrypt_hash.md
Password hash has to be in single quotes
2024-08-10 10:34:46 +02:00
Philip H.
ea99f56484 Update How_to_generate_an_bcrypt_hash.md (#1262)
explain about double $ usage on password.
2024-08-10 10:28:29 +02:00
Jouy Durao
aea653a071 Update How_to_generate_an_bcrypt_hash.md
explain about double $ on password
2024-08-09 18:40:39 -03:00
310 changed files with 24444 additions and 10854 deletions

View File

@@ -1 +1,3 @@
/src/node_modules
/src/node_modules
/src/.nuxt
/src/.output

5
.github/CODEOWNERS vendored
View File

@@ -1,4 +1,5 @@
# Copyright (c) Emile Nijssen (WeeJeWel)
# Founder and Codeowner of WireGuard Easy (wg-easy)
# Maintained by Philip Heiduck (pheiduck)
* @pheiduck
# Maintained by Bernd Storath (kaaax0815)
* @WeeJeWel
* @kaaax0815

2
.github/FUNDING.yml vendored
View File

@@ -1,3 +1,3 @@
# These are supported funding model platforms
github: weejewel
github: [weejewel, kaaax0815]

View File

@@ -0,0 +1,37 @@
---
name: 🐛 Bug Report
description: Create a report to help us improve
title: "[Bug]: "
type: Bug
body:
- type: markdown
attributes:
value: |
**Thanks :heart: for taking the time to fill out this bug report!**
We kindly ask that you search to see if an issue [already exists](https://github.com/wg-easy/wg-easy/issues?q=is%3Aissue+sort%3Acreated-desc+) for the bug you encountered.
- type: textarea
id: what-happened
attributes:
label: Describe the bug
placeholder: Tell us what you see!
value: "A bug happened!"
validations:
required: true
- type: textarea
id: what-should-happen
attributes:
label: Expected behavior
placeholder: Tell us what you expected!
value: "Work just fine!"
validations:
required: true
- type: textarea
id: logs
attributes:
label: Relevant log output
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
render: shell

View File

@@ -0,0 +1,47 @@
---
name: 🛠️ Feature Request
description: Suggest an idea to help us improve
title: "[Feat]: "
type: Feature
body:
- type: markdown
attributes:
value: |
**Thanks :heart: for taking the time to fill out this feature request report!**
We kindly ask that you search to see if an issue [already exists](https://github.com/wg-easy/wg-easy/issues?q=is%3Aissue+sort%3Acreated-desc+) for your feature.
We are also happy to accept contributions from our users. For more details see [here](https://github.com/wg-easy/wg-easy/blob/master/contributing.md).
- type: textarea
attributes:
label: Description
description: |
A clear and concise description of the feature you're interested in.
validations:
required: true
- type: textarea
attributes:
label: Suggested Solution
description: |
Describe the solution you'd like. A clear and concise description of what you want to happen.
validations:
required: true
- type: textarea
attributes:
label: Alternatives
description: |
Describe alternatives you've considered.
A clear and concise description of any alternative solutions or features you've considered.
validations:
required: false
- type: textarea
attributes:
label: Additional Context
description: |
Add any other context about the problem here.
validations:
required: false

View File

@@ -1,38 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. macOS 12.1]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS 8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

5
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,5 @@
contact_links:
- name: Get Help
url: https://github.com/wg-easy/wg-easy/discussions/new?category=q-a
about: If you can't get something to work the way you expect, open a question in the discussions.
blank_issues_enabled: false

View File

@@ -5,3 +5,13 @@ 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"

View File

@@ -1,10 +1,12 @@
name: "CodeQL"
name: CodeQL
on:
push:
branches: [ "master" ]
branches:
- master
pull_request:
branches: [ "master" ]
branches:
- master
schedule:
- cron: "15 0 * * *"
@@ -21,21 +23,21 @@ jobs:
strategy:
fail-fast: false
matrix:
language: [ 'javascript-typescript' ]
language: ["javascript-typescript"]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Checkout repository
uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
- name: Autobuild
uses: github/codeql-action/autobuild@v3
- name: Autobuild
uses: github/codeql-action/autobuild@v3
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{matrix.language}}"
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{matrix.language}}"

View File

@@ -1,37 +1,154 @@
name: Build & Publish Development
name: Development
on:
workflow_dispatch:
jobs:
deploy:
name: Build & Deploy
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
steps:
- uses: actions/checkout@v4
- name: Prepare
run: |
platform=${{ matrix.arch.platform }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: |
ghcr.io/wg-easy/wg-easy
flavor: |
latest=false
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push by digest
id: build
uses: docker/build-push-action@v6
with:
context: .
platforms: ${{ matrix.arch.platform }}
labels: ${{ steps.meta.outputs.labels }}
tags: ghcr.io/wg-easy/wg-easy
outputs: type=image,push-by-digest=true,name-canonical=true,push=true
cache-from: type=gha,scope=build-${{ env.PLATFORM_PAIR }}
cache-to: type=gha,mode=min,scope=build-${{ env.PLATFORM_PAIR }}
- name: Export digest
run: |
mkdir -p ${{ runner.temp }}/digests
digest="${{ steps.build.outputs.digest }}"
touch "${{ runner.temp }}/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v4
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
contents: read
needs: docker-build
steps:
- uses: actions/checkout@v4
with:
ref: production
- name: Download digests
uses: actions/download-artifact@v4
with:
path: ${{ runner.temp }}/digests
pattern: digests-*
merge-multiple: true
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-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: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: |
ghcr.io/wg-easy/wg-easy
flavor: |
latest=false
tags: |
type=raw,value=development
- name: Build & Publish Docker Image
uses: docker/build-push-action@v6
with:
push: true
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8
tags: ghcr.io/wg-easy/wg-easy: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
runs-on: ubuntu-latest
if: github.repository_owner == 'wg-easy'
permissions:
contents: write
needs: docker-merge
steps:
- uses: actions/checkout@v4
- 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 development

165
.github/workflows/deploy-edge.yml vendored Normal file
View File

@@ -0,0 +1,165 @@
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
steps:
- uses: actions/checkout@v4
with:
ref: master
- name: Prepare
run: |
platform=${{ matrix.arch.platform }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: |
ghcr.io/wg-easy/wg-easy
flavor: |
latest=false
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push by digest
id: build
uses: docker/build-push-action@v6
with:
context: .
platforms: ${{ matrix.arch.platform }}
labels: ${{ steps.meta.outputs.labels }}
tags: ghcr.io/wg-easy/wg-easy
outputs: type=image,push-by-digest=true,name-canonical=true,push=true
cache-from: type=gha,scope=build-${{ env.PLATFORM_PAIR }}
cache-to: type=gha,mode=min,scope=build-${{ env.PLATFORM_PAIR }}
- name: Export digest
run: |
mkdir -p ${{ runner.temp }}/digests
digest="${{ steps.build.outputs.digest }}"
touch "${{ runner.temp }}/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v4
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@v4
with:
path: ${{ runner.temp }}/digests
pattern: digests-*
merge-multiple: true
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: |
ghcr.io/wg-easy/wg-easy
flavor: |
latest=false
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@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 edge

View File

@@ -1,39 +0,0 @@
name: Build & Publish Nightly
on:
workflow_dispatch:
schedule:
- cron: "0 0 * * *"
jobs:
deploy:
name: Build & Deploy
runs-on: ubuntu-latest
if: github.repository_owner == 'wg-easy'
permissions:
packages: write
contents: read
steps:
- uses: actions/checkout@v4
with:
ref: production
- 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:
push: true
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8
tags: ghcr.io/wg-easy/wg-easy:nightly

View File

@@ -1,38 +1,53 @@
name: Build Pull Request
name: Pull Request
on:
workflow_dispatch:
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
deploy:
name: Build & Deploy
runs-on: ubuntu-latest
docker:
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
steps:
- uses: actions/checkout@v4
with:
ref: production
- uses: actions/checkout@v4
- 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: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build Docker Image
uses: docker/build-push-action@v6
with:
push: false
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8
tags: ghcr.io/wg-easy/wg-easy:pr
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build Docker Image
uses: docker/build-push-action@v6
with:
context: .
push: false
platforms: ${{ matrix.arch.platform }}
tags: ghcr.io/wg-easy/wg-easy:pr
cache-from: type=gha
cache-to: type=gha,mode=min,scope=build-${{ env.PLATFORM_PAIR }}

View File

@@ -1,43 +1,179 @@
name: Build & Publish Latest
name: Production
on:
workflow_dispatch:
push:
branches:
- production
tags:
- "v*"
# This workflow does not support fixing old versions
# as this will break the latest and major tags
jobs:
deploy:
name: Build & Deploy
runs-on: ubuntu-latest
if: github.repository_owner == 'wg-easy'
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
contents: read
strategy:
fail-fast: false
matrix:
arch:
- platform: linux/amd64
os: ubuntu-latest
- platform: linux/arm64
os: ubuntu-24.04-arm
steps:
- uses: actions/checkout@v4
with:
ref: production
- uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Prepare
run: |
platform=${{ matrix.arch.platform }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: |
ghcr.io/wg-easy/wg-easy
flavor: |
latest=false
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set environment variables
run: echo RELEASE=$(cat ./src/package.json | jq -r .release | jq -r .version) >> $GITHUB_ENV
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Build & Publish Docker Image
uses: docker/build-push-action@v6
with:
push: true
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8
tags: ghcr.io/wg-easy/wg-easy:latest, ghcr.io/wg-easy/wg-easy:${{ env.RELEASE }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push by digest
id: build
uses: docker/build-push-action@v6
with:
context: .
platforms: ${{ matrix.arch.platform }}
labels: ${{ steps.meta.outputs.labels }}
tags: ghcr.io/wg-easy/wg-easy
outputs: type=image,push-by-digest=true,name-canonical=true,push=true
cache-from: type=gha,scope=build-${{ env.PLATFORM_PAIR }}
cache-to: type=gha,mode=min,scope=build-${{ env.PLATFORM_PAIR }}
- name: Export digest
run: |
mkdir -p ${{ runner.temp }}/digests
digest="${{ steps.build.outputs.digest }}"
touch "${{ runner.temp }}/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v4
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
needs: docker-build
steps:
- name: Download digests
uses: actions/download-artifact@v4
with:
path: ${{ runner.temp }}/digests
pattern: digests-*
merge-multiple: true
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: |
ghcr.io/wg-easy/wg-easy
flavor: |
latest=false
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}
type=semver,pattern={{major}}.{{minor}}
- 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' &&
startsWith(github.ref, 'refs/tags/v')
permissions:
contents: write
needs: docker-merge
steps:
- uses: actions/checkout@v4
- 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
# Extract version numbers
DOCS_VERSION=${GITHUB_REF#refs/tags/} # e.g. v1.2.3 or v1.2.3-beta
MINOR_VERSION=$(echo $DOCS_VERSION | cut -d. -f1,2) # e.g. v1.2
# Check if it's a stable release (only numbers, no '-')
if [[ "$DOCS_VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Stable release detected: $DOCS_VERSION"
mike deploy --push --update-aliases $MINOR_VERSION latest
else
echo "Pre-release detected: $DOCS_VERSION"
mike deploy --push --update-aliases Pre-release
fi

View File

@@ -4,26 +4,65 @@ on:
push:
branches:
- master
- production
pull_request:
jobs:
lint:
name: Lint
docs:
name: Check Docs
runs-on: ubuntu-latest
if: github.repository_owner == 'wg-easy'
steps:
- name: Checkout repository
uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
run_install: false
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
node-version: "lts/*"
check-latest: true
cache: 'npm'
cache: "pnpm"
- name: npm run lint
- 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:
fail-fast: false
max-parallel: 3
matrix:
command: ["lint", "typecheck", "format:check"]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
run_install: false
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "lts/*"
check-latest: true
cache: "pnpm"
- name: pnpm ${{ matrix.command }}
run: |
cd src
npm ci
npm run lint
pnpm install
pnpm ${{ matrix.command }}

View File

@@ -1,40 +0,0 @@
name: NPM Update Bot 🤖
on:
push:
branches: [ "master" ]
schedule:
- cron: "0 0 * * 1"
jobs:
npmupbot:
name: NPM Update Bot 🤖
runs-on: ubuntu-latest
if: github.repository_owner == 'wg-easy'
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
repository: wg-easy/wg-easy
ref: master
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
check-latest: true
cache: 'npm'
- name: Bot 🤖 "Updating NPM Packages..."
run: |
npm install -g --silent npm-check-updates
ncu -u
npm update
cd src
ncu -u
npm update
npm run buildcss
git config --global user.name 'NPM Update Bot'
git config --global user.email 'npmupbot@users.noreply.github.com'
git add .
git commit -am "npm: package updates" || true
git push

View File

@@ -8,11 +8,10 @@ name: Mark stale issues and pull requests
on:
workflow_dispatch:
schedule:
- cron: '*/5 * * * *'
- cron: "*/5 * * * *"
jobs:
stale:
runs-on: ubuntu-latest
if: github.repository_owner == 'wg-easy'
permissions:
@@ -20,8 +19,8 @@ jobs:
pull-requests: write
steps:
- uses: actions/stale@v9
with:
- uses: actions/stale@v9
with:
days-before-issue-stale: 30
days-before-issue-close: 14
stale-issue-label: "stale"

5
.gitignore vendored
View File

@@ -1,6 +1,3 @@
/config
/wg0.conf
/wg0.json
/src/node_modules
.DS_Store
*.swp
node_modules

15
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,15 @@
{
"recommendations": [
"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",
"DavidAnson.vscode-markdownlint"
]
}

33
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,33 @@
{
"editor.tabSize": 2,
"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"
},
"[vue]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[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"],
"i18n-ally.sortKeys": false,
"i18n-ally.keepFulfilled": false,
"i18n-ally.keystyle": "nested",
"editor.gotoLocation.multipleDefinitions": "goto"
}

42
CHANGELOG.md Normal file
View File

@@ -0,0 +1,42 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
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
As the whole setup has changed, we recommend to start from scratch. And import your existing configs.
## Major Changes
- Almost all Environment variables removed
- New and Improved UI
- API Basic Authentication
- Added Docs
- Incrementing Version -> Semantic Versioning
- CIDR Support
- IPv6 Support
- Changed API Structure
- SQLite Database
- Deprecated Dockerless Installations
- Added Docker Volume Mount (`/lib/modules`)
- Removed ARMv6 and ARMv7 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
### Major changes
- `PASSWORD` has been replaced by `PASSWORD_HASH`

View File

@@ -1,49 +1,61 @@
# As a workaround we have to build on nodejs 18
# nodejs 20 hangs on build with armv6/armv7
FROM docker.io/library/node:18-alpine AS build_node_modules
FROM docker.io/library/node:lts-alpine AS build
WORKDIR /app
# Update npm to latest
RUN npm install -g npm@latest
# update corepack
RUN npm install --global corepack@latest
# Install pnpm
RUN corepack enable pnpm
# Copy Web UI
COPY src /app
WORKDIR /app
RUN npm ci --omit=dev &&\
mv node_modules /node_modules
COPY src/package.json src/pnpm-lock.yaml ./
RUN pnpm install
# Build UI
COPY src ./
RUN pnpm build
# Copy build result to a new image.
# This saves a lot of disk space.
FROM docker.io/library/node:20-alpine
HEALTHCHECK CMD /usr/bin/timeout 5s /bin/sh -c "/usr/bin/wg show | /bin/grep -q interface || exit 1" --interval=1m --timeout=5s --retries=3
COPY --from=build_node_modules /app /app
FROM docker.io/library/node:lts-alpine
WORKDIR /app
# Move node_modules one directory up, so during development
# we don't have to mount it in a volume.
# This results in much faster reloading!
#
# Also, some node_modules might be native, and
# the architecture & OS of your development machine might differ
# than what runs inside of docker.
COPY --from=build_node_modules /node_modules /node_modules
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"
# Copy the needed wg-password scripts
COPY --from=build_node_modules /app/wgpw.sh /bin/wgpw
RUN chmod +x /bin/wgpw
# Copy build
COPY --from=build /app/.output /app
# Copy migrations
COPY --from=build /app/server/database/migrations /app/server/database/migrations
# libsql (https://github.com/nitrojs/nitro/issues/3328)
RUN cd /app/server && \
npm install --no-save libsql && \
npm cache clean --force
# cli
COPY --from=build /app/cli/cli.sh /usr/local/bin/cli
RUN chmod +x /usr/local/bin/cli
# Install Linux packages
RUN apk add --no-cache \
dpkg \
dumb-init \
iptables \
ip6tables \
nftables \
kmod \
iptables-legacy \
wireguard-tools
# Use iptables-legacy
RUN update-alternatives --install /sbin/iptables iptables /sbin/iptables-legacy 10 --slave /sbin/iptables-restore iptables-restore /sbin/iptables-legacy-restore --slave /sbin/iptables-save iptables-save /sbin/iptables-legacy-save
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
ENV DEBUG=Server,WireGuard,Database,CMD
ENV PORT=51821
ENV HOST=0.0.0.0
ENV INSECURE=false
ENV INIT_ENABLED=false
LABEL org.opencontainers.image.source=https://github.com/wg-easy/wg-easy
# Run Web UI
WORKDIR /app
CMD ["/usr/bin/dumb-init", "node", "server.js"]
CMD ["/usr/bin/dumb-init", "node", "server/index.mjs"]

39
Dockerfile.dev Normal file
View File

@@ -0,0 +1,39 @@
FROM docker.io/library/node:lts-alpine
WORKDIR /app
# update corepack
RUN npm install --global corepack@latest
# Install pnpm
RUN corepack enable pnpm
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"
# Install Linux packages
RUN apk add --no-cache \
dpkg \
dumb-init \
iptables \
ip6tables \
kmod \
iptables-legacy \
wireguard-tools
# 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 PORT=51821
ENV HOST=0.0.0.0
ENV INSECURE=true
ENV INIT_ENABLED=false
# Install Dependencies
COPY src/package.json src/pnpm-lock.yaml ./
RUN pnpm install
# Copy Project
COPY src ./
ENTRYPOINT [ "pnpm", "run" ]

View File

@@ -1,28 +0,0 @@
# wg-password
`wg-password` (wgpw) is a script that generates bcrypt password hashes for use with `wg-easy`, enhancing security by requiring passwords.
## Features
- Generate bcrypt password hashes.
- Easily integrate with `wg-easy` to enforce password requirements.
## Usage with Docker
To generate a bcrypt password hash using docker, run the following command :
```sh
docker run ghcr.io/wg-easy/wg-easy wgpw YOUR_PASSWORD
PASSWORD_HASH='$2b$12$coPqCsPtcFO.Ab99xylBNOW4.Iu7OOA2/ZIboHN6/oyxca3MWo7fW' // literally YOUR_PASSWORD
```
*Important* : make sure to enclose your password in single quotes when you run `docker run` command :
```bash
$ echo $2b$12$coPqCsPtcF
b2
$ echo "$2b$12$coPqCsPtcF"
b2
$ echo '$2b$12$coPqCsPtcF'
$2b$12$coPqCsPtcF
```

1098
LICENSE

File diff suppressed because it is too large Load Diff

205
README.md
View File

@@ -1,56 +1,75 @@
# WireGuard Easy
[![Build & Publish Docker Image to Docker Hub](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?branch=production)](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)
![Docker](https://img.shields.io/docker/pulls/weejewel/wg-easy.svg)
[![Sponsor](https://img.shields.io/github/sponsors/weejewel)](https://github.com/sponsors/WeeJeWel)
![GitHub Stars](https://img.shields.io/github/stars/wg-easy/wg-easy)
[![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)
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" />
</p>
## Features
* All-in-one: WireGuard + Web UI.
* Easy installation, simple to use.
* List, create, edit, delete, enable & disable clients.
* Show a client's QR code.
* Download a client's configuration file.
* Statistics for which clients are connected.
* Tx/Rx charts for each connected client.
* Gravatar support.
* Automatic Light / Dark Mode
* Multilanguage Support
* UI_TRAFFIC_STATS (default off)
## Requirements
- All-in-one: WireGuard + Web UI.
- Easy installation, simple to use.
- List, create, edit, delete, enable & disable clients.
- Show a client's QR code.
- Download a client's configuration file.
- Statistics for which clients are connected.
- Tx/Rx charts for each connected client.
- Gravatar support.
- Automatic Light / Dark Mode
- Multilanguage Support
- One Time Links
- Client Expiration
- Prometheus metrics support
- IPv6 support
- CIDR support
- 2FA support
* A host with a kernel that supports WireGuard (all modern kernels).
* A host with Docker installed.
> [!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)
## Versions
<!-- TODO: remove after release -->
We provide more then 1 docker image to get, this will help you decide which one is best for you. <br>
For **stable** versions instead of nightly or development please read **README** from the **production** branch!
> [!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)
| tag | Branch | Example | Description |
| - | - | - | - |
| `latest` | production | `ghcr.io/wg-easy/wg-easy:latest` or `ghcr.io/wg-easy/wg-easy` | stable as possbile get bug fixes quickly when needed, deployed against `production`. |
| `13` | production | `ghcr.io/wg-easy/wg-easy:13` | same as latest, stick to a version tag. |
| `nightly` | master | `ghcr.io/wg-easy/wg-easy:nightly` | mostly unstable gets frequent package and code updates, deployed against `master`. |
| `development` | pull requests | `ghcr.io/wg-easy/wg-easy:development` | used for development, testing code from PRs before landing into `master`. |
- [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/)
- [Traefik](https://wg-easy.github.io/wg-easy/latest/examples/tutorials/traefik/)
- [Podman](https://wg-easy.github.io/wg-easy/latest/examples/tutorials/podman-nft/)
- [AdGuard Home](https://wg-easy.github.io/wg-easy/latest/examples/tutorials/adguard/)
> [!NOTE]
> If you want to migrate from the old version to the new version, you can find the migration guide here: [Migration Guide](https://wg-easy.github.io/wg-easy/latest/advanced/migrate/)
## Installation
This is a quick start guide to get you up and running with WireGuard Easy.
For a more detailed installation guide, please refer to the [Getting Started](https://wg-easy.github.io/wg-easy/latest/getting-started/) page.
### 1. Install Docker
If you haven't installed Docker yet, install it by running:
If you haven't installed Docker yet, install it by running as root:
```bash
```shell
curl -sSL https://get.docker.com | sh
sudo usermod -aG docker $(whoami)
exit
```
@@ -58,94 +77,60 @@ And log in again.
### 2. Run WireGuard Easy
To automatically install & run wg-easy, simply run:
The easiest way to run WireGuard Easy is with Docker Compose.
```
docker run -d \
--name=wg-easy \
-e LANG=de \
-e WG_HOST=<🚨YOUR_SERVER_IP> \
-e PASSWORD_HASH=<🚨YOUR_ADMIN_PASSWORD_HASH> \
-e PORT=51821 \
-e WG_PORT=51820 \
-v ~/.wg-easy:/etc/wireguard \
-p 51820:51820/udp \
-p 51821:51821/tcp \
--cap-add=NET_ADMIN \
--cap-add=SYS_MODULE \
--sysctl="net.ipv4.conf.all.src_valid_mark=1" \
--sysctl="net.ipv4.ip_forward=1" \
--restart unless-stopped \
ghcr.io/wg-easy/wg-easy
Just download [`docker-compose.yml`](docker-compose.yml) and execute `sudo docker compose up -d`.
Now setup a reverse proxy to be able to access the Web UI securely from the internet.
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
## Donate
Are you enjoying this project? Consider donating.
Founder: [Buy Emile a beer!](https://github.com/sponsors/WeeJeWel) 🍻
Maintainer: [Buy kaaax0815 a coffee!](https://github.com/sponsors/kaaax0815) ☕
## Development
### Prerequisites
- Docker
- Node LTS & corepack enabled
- Visual Studio Code
### Dev Server
This starts the development server with docker
```shell
pnpm dev
```
> 💡 Replace `YOUR_SERVER_IP` with your WAN IP, or a Dynamic DNS hostname.
>
> 💡 Replace `YOUR_ADMIN_PASSWORD_HASH` with a bcrypt password hash to log in on the Web UI. See [How_to_generate_an_bcrypt_hash.md](./How_to_generate_an_bcrypt_hash.md) for know how generate the hash.
### Update Auto Imports
The Web UI will now be available on `http://0.0.0.0:51821`.
If you add something that should be auto-importable and VSCode complains, run:
> 💡 Your configuration files will be saved in `~/.wg-easy`
WireGuard Easy can be launched with Docker Compose as well - just download
[`docker-compose.yml`](docker-compose.yml), make necessary adjustments and
execute `docker compose up --detach`.
### 3. Sponsor
Are you enjoying this project? [Buy Emile a beer!](https://github.com/sponsors/WeeJeWel) 🍻
## Options
These options can be configured by setting environment variables using `-e KEY="VALUE"` in the `docker run` command.
| Env | Default | Example | Description |
| - | - | - |------------------------------------------------------------------------------------------------------------------------------------------------------|
| `PORT` | `51821` | `6789` | TCP port for Web UI. |
| `WEBUI_HOST` | `0.0.0.0` | `localhost` | IP address web UI binds to. |
| `PASSWORD_HASH` | - | `$2y$05$Ci...` | When set, requires a password when logging in to the Web UI. See [How to generate an bcrypt hash.md]("https://github.com/wg-easy/wg-easy/blob/master/How_to_generate_an_bcrypt_hash.md") for know how generate the hash. |
| `WG_HOST` | - | `vpn.myserver.com` | The public hostname of your VPN server. |
| `WG_DEVICE` | `eth0` | `ens6f0` | Ethernet device the wireguard traffic should be forwarded through. |
| `WG_PORT` | `51820` | `12345` | The public UDP port of your VPN server. WireGuard will listen on that (othwise default) inside the Docker container. |
| `WG_CONFIG_PORT`| `51820` | `12345` | The UDP port used on [Home Assistant Plugin](https://github.com/adriy-be/homeassistant-addons-jdeath/tree/main/wgeasy)
| `WG_MTU` | `null` | `1420` | The MTU the clients will use. Server uses default WG MTU. |
| `WG_PERSISTENT_KEEPALIVE` | `0` | `25` | Value in seconds to keep the "connection" open. If this value is 0, then connections won't be kept alive. |
| `WG_DEFAULT_ADDRESS` | `10.8.0.x` | `10.6.0.x` | Clients IP address range. |
| `WG_DEFAULT_DNS` | `1.1.1.1` | `8.8.8.8, 8.8.4.4` | DNS server clients will use. If set to blank value, clients will not use any DNS. |
| `WG_ALLOWED_IPS` | `0.0.0.0/0, ::/0` | `192.168.15.0/24, 10.0.1.0/24` | Allowed IPs clients will use. |
| `WG_PRE_UP` | `...` | - | See [config.js](https://github.com/wg-easy/wg-easy/blob/master/src/config.js#L19) for the default value. |
| `WG_POST_UP` | `...` | `iptables ...` | See [config.js](https://github.com/wg-easy/wg-easy/blob/master/src/config.js#L20) for the default value. |
| `WG_PRE_DOWN` | `...` | - | See [config.js](https://github.com/wg-easy/wg-easy/blob/master/src/config.js#L27) for the default value. |
| `WG_POST_DOWN` | `...` | `iptables ...` | See [config.js](https://github.com/wg-easy/wg-easy/blob/master/src/config.js#L28) for the default value. |
| `LANG` | `en` | `de` | Web UI language (Supports: en, ua, ru, tr, no, pl, fr, de, ca, es, ko, vi, nl, is, pt, chs, cht, it, th, hi). |
| `UI_TRAFFIC_STATS` | `false` | `true` | Enable detailed RX / TX client stats in Web UI |
| `UI_CHART_TYPE` | `0` | `1` | UI_CHART_TYPE=0 # Charts disabled, UI_CHART_TYPE=1 # Line chart, UI_CHART_TYPE=2 # Area chart, UI_CHART_TYPE=3 # Bar chart |
> If you change `WG_PORT`, make sure to also change the exposed port.
## Updating
To update to the latest version, simply run:
```bash
docker stop wg-easy
docker rm wg-easy
docker pull ghcr.io/wg-easy/wg-easy
```shell
cd src
pnpm install
cd ..
```
And then run the `docker run -d \ ...` command above again.
### Test Cli
With Docker Compose WireGuard Easy can be updated with a single command:
`docker compose up --detach --pull always` (if an image tag is specified in the
Compose file and it is not `latest`, make sure that it is changed to the desired
one; by default it is omitted and
[defaults to `latest`](https://docs.docker.com/engine/reference/run/#image-references)). \
The WireGuared Easy container will be automatically recreated if a newer image
was pulled.
This starts the cli with docker
## Common Use Cases
```shell
pnpm cli:dev
```
* [Using WireGuard-Easy with Pi-Hole](https://github.com/wg-easy/wg-easy/wiki/Using-WireGuard-Easy-with-Pi-Hole)
* [Using WireGuard-Easy with nginx/SSL](https://github.com/wg-easy/wg-easy/wiki/Using-WireGuard-Easy-with-nginx-SSL)
## License
For less common or specific edge-case scenarios, please refer to the detailed information provided in the [Wiki](https://github.com/wg-easy/wg-easy/wiki).
This project is licensed under the AGPL-3.0-only License - see the [LICENSE](LICENSE) file for details
This project is not affiliated, associated, authorized, endorsed by, or in any way officially connected with Jason A. Donenfeld, ZX2C4 or Edge Security
"WireGuard" and the "WireGuard" logo are registered trademarks of Jason A. Donenfeld

44
docker-compose.beta.yml Normal file
View File

@@ -0,0 +1,44 @@
volumes:
etc_wireguard:
services:
wg-easy:
#environment:
# Optional:
# - PORT=51821
# - HOST=0.0.0.0
# - INSECURE=false
image: ghcr.io/wg-easy/wg-easy:15
container_name: wg-easy
networks:
wg:
ipv4_address: 10.42.42.42
ipv6_address: fdcc:ad94:bacf:61a3::2a
volumes:
- etc_wireguard:/etc/wireguard
- /lib/modules:/lib/modules:ro
ports:
- "51820:51820/udp"
- "51821:51821/tcp"
restart: unless-stopped
cap_add:
- NET_ADMIN
- SYS_MODULE
# - NET_RAW # ⚠️ Uncomment if using Podman Compose
sysctls:
- 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
networks:
wg:
driver: bridge
enable_ipv6: true
ipam:
driver: default
config:
- subnet: 10.42.42.0/24
- subnet: fdcc:ad94:bacf:61a3::/64

View File

@@ -1,10 +1,13 @@
services:
wg-easy:
build:
dockerfile: ./Dockerfile
command: npm run serve
dockerfile: ./Dockerfile.dev
command: dev
volumes:
- ./src/:/app/
- temp:/app/.nuxt/
- temp1:/app/node_modules/
- /lib/modules:/lib/modules:ro
# - ./data/:/etc/wireguard
ports:
- "51820:51820/udp"
@@ -13,5 +16,15 @@ services:
- NET_ADMIN
- SYS_MODULE
environment:
# - PASSWORD=p
- WG_HOST=192.168.1.233
- INIT_ENABLED=true
- INIT_HOST=test
- INIT_PORT=51820
- INIT_USERNAME=testtest
- INIT_PASSWORD=Qweasdyxcv!2
# folders should be generated inside container
volumes:
temp:
driver: local
temp1:
driver: local

View File

@@ -12,7 +12,7 @@ services:
- WG_HOST=raspberrypi.local
# Optional:
# - PASSWORD_HASH=$$2y$$10$$hBCoykrB95WSzuV4fafBzOHWKu9sbyVa34GJr8VV5R/pIelfEMYyG (needs double $$, hash of 'foobar123'; see "How_to_generate_an_bcrypt_hash.md" for generate the hash)
# - PASSWORD_HASH=$$2y$$10$$hBCoykrB95WSzuV4fafBzOHWKu9sbyVa34GJr8VV5R/pIelfEMYyG # (needs double $$, hash of 'foobar123'; see "How_to_generate_an_bcrypt_hash.md" for generate the hash)
# - PORT=51821
# - WG_PORT=51820
# - WG_CONFIG_PORT=92820
@@ -28,7 +28,7 @@ services:
# - UI_TRAFFIC_STATS=true
# - UI_CHART_TYPE=0 # (0 Charts disabled, 1 # Line chart, 2 # Area chart, 3 # Bar chart)
image: ghcr.io/wg-easy/wg-easy
image: ghcr.io/wg-easy/wg-easy:14
container_name: wg-easy
volumes:
- etc_wireguard:/etc/wireguard
@@ -39,7 +39,7 @@ services:
cap_add:
- NET_ADMIN
- SYS_MODULE
# - NET_RAW # ⚠️ Uncomment if using Podman
# - NET_RAW # ⚠️ Uncomment if using Podman
sysctls:
- net.ipv4.ip_forward=1
- net.ipv4.conf.all.src_valid_mark=1

5
docs/.prettierrc.json Normal file
View File

@@ -0,0 +1,5 @@
{
"tabWidth": 4,
"semi": true,
"singleQuote": true
}

View File

@@ -1,16 +0,0 @@
{
"1": "Initial version. Enjoy!",
"2": "You can now rename a client & update the address. Enjoy!",
"3": "Many improvements and small changes. Enjoy!",
"4": "Now with pretty charts for client's network speed. Enjoy!",
"5": "Many small improvements & feature requests. Enjoy!",
"6": "Many small performance improvements & bug fixes. Enjoy!",
"7": "Improved the look & performance of the upload/download chart.",
"8": "Updated to Node.js v18.",
"9": "Fixed issue running on devices with older kernels.",
"10": "Added sessionless HTTP API auth & automatic dark mode.",
"11": "Multilanguage Support & various bugfixes.",
"12": "UI_TRAFFIC_STATS, Import json configurations with no PreShared-Key, allow clients with no privateKey & more.",
"13": "New framework (h3), UI_CHART_TYPE, some bugfixes & more.",
"14": "Home Assistent support, PASSWORD_HASH (inc. Helper), translation updates bugfixes & more."
}

View File

@@ -0,0 +1,43 @@
---
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.
## Authentication
To use the API, you need to authenticate using Basic Authentication. The username and password are the same as the ones you use to log in to the web application.
If you use 2FA, the API will not work. You need to disable 2FA in the web application to use the API.
### Authentication Example
```python
import requests
from requests.auth import HTTPBasicAuth
url = "https://example.com:51821/api/client"
response = requests.get(url, auth=HTTPBasicAuth('username', 'password'))
if response.status_code == 200:
data = response.json()
print(data)
else:
print(f"Error: {response.status_code}")
```
## Endpoints
The Endpoints are not yet documented. But as file-based routing is used, you can find the endpoints in the `src/server/api` folder. The method is defined in the file name.
### Endpoints Example
| File Name | Endpoint | Method |
| -------------------------------- | -------------- | ------ |
| `src/server/api/client.get.ts` | `/api/client` | GET |
| `src/server/api/setup/2.post.ts` | `/api/setup/2` | POST |

View File

@@ -0,0 +1,11 @@
---
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 |

View File

@@ -0,0 +1,32 @@
---
title: Unattended Setup
---
If you want to run the setup without any user interaction, e.g. with a tool like Ansible, you can use these environment variables to configure the setup.
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 |
| `INIT_HOST` | `vpn.example.com` | Host clients will connect to | 1 |
| `INIT_PORT` | `51820` | Port clients will connect to and wireguard will listen on | 1 |
| `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 |
/// 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`.
If you want to skip the setup process, you have to configure group `1`
///
/// note | Security
The initial username and password is not checked for complexity. Make sure to set a long enough username and password. Otherwise, the user won't be able to log in.
It's recommended to remove the variables after the setup is done to prevent the password from being exposed.
///

View File

@@ -0,0 +1,42 @@
---
title: Prometheus
---
To monitor the WireGuard server, you can use [Prometheus](https://prometheus.io/) and [Grafana](https://grafana.com/). The container exposes a `/metrics/prometheus` endpoint that can be scraped by Prometheus.
## Enable Prometheus
To enable Prometheus metrics, go to Admin Panel > General and enable Prometheus.
You can optionally set a Bearer Password for the metrics endpoints. This is useful if you want to expose the metrics endpoint to the internet.
## Configure Prometheus
You need to add a scrape config to your Prometheus configuration file. Here is an example:
```yaml
scrape_configs:
- job_name: 'wg-easy'
scrape_interval: 30s
metrics_path: /metrics/prometheus
static_configs:
- targets:
- 'localhost:51821'
authorization:
type: Bearer
credentials: 'SuperSecurePassword'
```
## Grafana Dashboard
You can use the following Grafana dashboard to visualize the metrics:
[![Grafana Dashboard](https://grafana.com/api/dashboards/21733/images/16863/image)](https://grafana.com/grafana/dashboards/21733-wireguard/)
[21733](https://grafana.com/grafana/dashboards/21733-wireguard/)
/// note | Unofficial
The Grafana dashboard is not official and is not maintained by the `wg-easy` team. If you have any issues with the dashboard, please contact the author of the dashboard.
See [#1299](https://github.com/wg-easy/wg-easy/pull/1299) for more information.
///

View File

@@ -0,0 +1,52 @@
---
title: Migrate from v14 to v15
---
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.
## Migration
### Backup
Before you start the migration, make sure to backup your existing configuration files.
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
### Remove old container
1. Stop the running container
If you are using `docker run`
```shell
docker stop wg-easy
```
If you are using `docker-compose`
```shell
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.
[docs-getting-started]: ../../getting-started.md
[docs-examples]: ../../examples/tutorials/basic-installation.md
### Done
You have now successfully migrated to `v15` of `wg-easy`.

View File

@@ -0,0 +1,7 @@
---
title: Migrate
---
If you want to migrate from an older version of `wg-easy` to the new version, you can find the migration guides listed below.
- [Migrate from v14 to v15](./from-14-to-15.md) : This guide should also work for any version before `v14`.

View File

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -0,0 +1,23 @@
---
title: General Information
---
## Coding Style
When refactoring, writing or altering files, adhere to these rules:
1. **Adjust your style of coding to the style that is already present**! Even if you do not like it, this is due to consistency. There was a lot of work involved in making all files consistent.
2. **Use `pnpm lint` to check your scripts**! Your contributions are checked by GitHub Actions too, so you will need to do this.
3. **Use the provided `.vscode/settings.json`** file.
## Documentation
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
```sh
pnpm docs:serve
```
This serves the documentation on your local machine on port `8080`. Each change will be hot-reloaded onto the page you view, just edit, save and look at the result.

View File

@@ -0,0 +1,58 @@
---
title: Issues and Pull Requests
---
This project is Open Source. That means that you can contribute on enhancements, bug fixing or improving the documentation.
## Opening an Issue
/// note | Attention
**Before opening an issue**, read the [`README`][github-file-readme] carefully, study the docs for your version (maybe [latest][docs-latest]) and your search engine you trust. The issue tracker is not meant to be used for unrelated questions!
///
When opening an issue, please provide details use case to let the community reproduce your problem.
/// note | Attention
**Use the issue templates** to provide the necessary information. Issues which do not use these templates are not worked on and closed.
///
By raising issues, I agree to these terms and I understand, that the rules set for the issue tracker will help both maintainers as well as everyone to find a solution.
Maintainers take the time to improve on this project and help by solving issues together. It is therefore expected from others to make an effort and **comply with the rules**.
### Filing a Bug Report
Thank you for participating in this project and reporting a bug. `wg-easy` is a community-driven project, and each contribution counts!
Maintainers and moderators are volunteers. We greatly appreciate reports that take the time to provide detailed information via the template, enabling us to help you in the best and quickest way. Ignoring the template provided may seem easier, but discourages receiving any support.
Markdown formatting can be used in almost all text fields (_unless stated otherwise in the description_).
Be as precise as possible, and if in doubt, it's best to add more information that too few.
When an option is marked with "not officially supported" / "unsupported", then support is dependent on availability from specific maintainers.
## Pull Requests
/// question | Motivation
You want to add a feature? Feel free to start creating an issue explaining what you want to do and how you're thinking doing it. Other users may have the same need and collaboration may lead to better results.
///
### Submit a Pull-Request
The development workflow is the following:
1. Fork the project
2. Write the code that is needed :D
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 `: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
[commit]: https://help.github.com/articles/closing-issues-via-commit-messages/
[gpg]: https://docs.github.com/en/github/authenticating-to-github/generating-a-new-gpg-key

View File

@@ -0,0 +1,9 @@
---
title: AdGuard Home
---
It seems like the Docs on how to setup AdGuard Home are not available yet.
Feel free to create a PR and add them here.
<!-- TODO -->

View File

@@ -0,0 +1,72 @@
---
title: Auto Updates
---
## Docker Compose
With Docker Compose `wg-easy` can be updated with a single command:
```shell
cd /etc/docker/containers/wg-easy
sudo docker compose up -d --pull always
```
### Watchtower
If you want the updates to be fully automatic you can install Watchtower. This will check for updates every day at 4:00 AM and update the container if a new version is available.
File: `/etc/docker/containers/watchtower/docker-compose.yml`
```yaml
services:
watchtower:
image: containrrr/watchtower:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock
env_file:
- watchtower.env
restart: unless-stopped
```
File: `/etc/docker/containers/watchtower/watchtower.env`
```env
WATCHTOWER_CLEANUP=true
WATCHTOWER_SCHEDULE=0 0 4 * * *
TZ=Europe/Berlin
# Email
# WATCHTOWER_NOTIFICATIONS_LEVEL=info
# WATCHTOWER_NOTIFICATIONS=email
# WATCHTOWER_NOTIFICATION_EMAIL_FROM=mail@example.com
# WATCHTOWER_NOTIFICATION_EMAIL_TO=mail@example.com
# WATCHTOWER_NOTIFICATION_EMAIL_SERVER=smtp.example.com
# WATCHTOWER_NOTIFICATION_EMAIL_SERVER_USER=mail@example.com
# WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PASSWORD="SuperSecurePassword"
# WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PORT=587
```
```shell
cd /etc/docker/containers/watchtower
sudo docker compose up -d
```
## Docker Run
```shell
sudo docker stop wg-easy
sudo docker rm wg-easy
sudo docker pull ghcr.io/wg-easy/wg-easy
```
And then run the `docker run -d \ ...` command from [Docker Run][docker-run] again.
[docker-run]: ./docker-run.md
## Podman
To update `wg-easy` (and every container that has auto updates enabled), you can run the following command:
```shell
sudo podman auto-update
```

View File

@@ -0,0 +1,67 @@
---
title: Basic Installation
---
<!-- TOOD: add docs for pihole, nginx, caddy, traefik -->
## Requirements
1. You need to have a host that you can manage
2. You need to have a domain name or a public IP address
3. You need a supported architecture (x86_64, arm64)
4. You need curl installed on your host
## Install Docker
Follow the Docs here: <https://docs.docker.com/engine/install/> and install Docker on your host.
## Install `wg-easy`
1. Create a directory for the configuration files (you can choose any directory you like):
```shell
sudo mkdir -p /etc/docker/containers/wg-easy
```
2. Download docker compose file
```shell
sudo curl -o /etc/docker/containers/wg-easy/docker-compose.yml https://raw.githubusercontent.com/wg-easy/wg-easy/master/docker-compose.yml
```
3. Start `wg-easy`
```shell
cd /etc/docker/containers/wg-easy
sudo docker-compose up -d
```
## Setup Firewall
If you are using a firewall, you need to open the following ports:
- UDP 51820 (WireGuard)
These ports can be changed, so if you change them you have to update your firewall rules accordingly.
## Setup Reverse Proxy
- 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`
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
```
## Auto Update
If you want to enable auto-updates, follow the instructions here: [Auto Updates][auto-updates]
[auto-updates]: ./auto-updates.md

View File

@@ -0,0 +1,9 @@
---
title: Caddy
---
It seems like the Docs on how to setup Caddy are not available yet.
Feel free to create a PR and add them here.
<!-- TODO -->

View File

@@ -0,0 +1,41 @@
---
title: Docker Run
---
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 \
```
<!-- ref: major version -->
To automatically install & run `wg-easy`, simply run:
```shell
docker run -d \
--net wg \
-e INSECURE=true \
--name wg-easy \
--ip6 fdcc:ad94:bacf:61a3::2a \
--ip 10.42.42.42 \
-v ~/.wg-easy:/etc/wireguard \
-v /lib/modules:/lib/modules:ro \
-p 51820:51820/udp \
-p 51821:51821/tcp \
--cap-add NET_ADMIN \
--cap-add SYS_MODULE \
--sysctl net.ipv4.ip_forward=1 \
--sysctl net.ipv4.conf.all.src_valid_mark=1 \
--sysctl net.ipv6.conf.all.disable_ipv6=0 \
--sysctl net.ipv6.conf.all.forwarding=1 \
--sysctl net.ipv6.conf.default.forwarding=1 \
--restart unless-stopped \
ghcr.io/wg-easy/wg-easy:15
```
The Web UI will now be available at <http://0.0.0.0:51821>.

View File

@@ -0,0 +1,7 @@
---
title: Without Docker
---
This is currently not yet supported.
<!-- TODO -->

View File

@@ -0,0 +1,108 @@
---
title: Podman + nftables
---
This guide will show you how to run `wg-easy` with rootful Podman and nftables.
## Requirements
1. Podman installed with version 4.4 or higher
## Configuration
Create a Folder for the configuration files:
```shell
sudo mkdir -p /etc/containers/systemd/wg-easy
sudo mkdir -p /etc/containers/volumes/wg-easy
```
Create a file `/etc/containers/systemd/wg-easy/wg-easy.container` with the following content:
<!-- ref: major version -->
```ini
[Container]
ContainerName=wg-easy
Image=ghcr.io/wg-easy/wg-easy:15
AutoUpdate=registry
Volume=/etc/containers/volumes/wg-easy:/etc/wireguard:Z
Network=wg-easy.network
PublishPort=51820:51820/udp
PublishPort=51821:51821/tcp
# this is used to allow access over HTTP
# remove this when using a reverse proxy
Environment=INSECURE=true
AddCapability=NET_ADMIN
AddCapability=SYS_MODULE
AddCapability=NET_RAW
Sysctl=net.ipv4.ip_forward=1
Sysctl=net.ipv4.conf.all.src_valid_mark=1
Sysctl=net.ipv6.conf.all.disable_ipv6=0
Sysctl=net.ipv6.conf.all.forwarding=1
Sysctl=net.ipv6.conf.default.forwarding=1
[Install]
# this is used to start the container on boot
WantedBy=default.target
```
Create a file `/etc/containers/systemd/wg-easy/wg-easy.network` with the following content:
```ini
[Network]
NetworkName=wg-easy
IPv6=true
```
## Load Kernel Modules
You will need to load the following kernel modules
```txt
wireguard
nft_masq
```
Create a file `/etc/modules-load.d/wg-easy.conf` with the following content:
```txt
wireguard
nft_masq
```
## Start the Container
```shell
sudo systemctl daemon-reload
sudo systemctl start wg-easy
```
## Edit Hooks
In the Admin Panel of your WireGuard server, go to the `Hooks` tab and add the following hook:
1. PostUp
```shell
nft add table inet wg_table; nft add chain inet wg_table prerouting { type nat hook prerouting priority 100 \; }; nft add chain inet wg_table postrouting { type nat hook postrouting priority 100 \; }; nft add rule inet wg_table postrouting ip saddr {{ipv4Cidr}} oifname {{device}} masquerade; nft add rule inet wg_table postrouting ip6 saddr {{ipv6Cidr}} oifname {{device}} masquerade; nft add chain inet wg_table input { type filter hook input priority 0 \; policy accept \; }; nft add rule inet wg_table input udp dport {{port}} accept; nft add rule inet wg_table input tcp dport {{uiPort}} accept; nft add chain inet wg_table forward { type filter hook forward priority 0 \; policy accept \; }; nft add rule inet wg_table forward iifname "wg0" accept; nft add rule inet wg_table forward oifname "wg0" accept;
```
2. PostDown
```shell
nft delete table inet wg_table
```
If you don't have iptables loaded on your server, you could see many errors in the logs or in the UI. You can ignore them.
## Restart the Container
Restart the container to apply the new hooks:
```shell
sudo systemctl restart wg-easy
```

View File

@@ -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.

View File

@@ -0,0 +1,184 @@
---
title: Traefik
---
/// note | Opinionated
This guide is opinionated. If you use other conventions or folder layouts, feel free to change the commands and paths.
///
## Create docker compose project
```shell
sudo mkdir -p /etc/docker/containers/traefik
cd /etc/docker/containers/traefik
```
## Create docker compose file
File: `/etc/docker/containers/traefik/docker-compose.yml`
```yaml
services:
traefik:
image: traefik:3.3
container_name: traefik
restart: unless-stopped
ports:
- '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
- /etc/docker/volumes/traefik/traefik_dynamic.yml:/traefik_dynamic.yml:ro
- /etc/docker/volumes/traefik/acme.json:/acme.json
networks:
- traefik
networks:
traefik:
external: true
```
## Create traefik.yml
File: `/etc/docker/volumes/traefik/traefik.yml`
```yaml
log:
level: INFO
entryPoints:
web:
address: ':80/tcp'
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: ':443/tcp'
http:
middlewares:
- compress@file
- hsts@file
tls:
certResolver: letsencrypt
http3: {}
api:
dashboard: true
certificatesResolvers:
letsencrypt:
acme:
email: $mail@example.com$
storage: acme.json
httpChallenge:
entryPoint: web
providers:
docker:
watch: true
network: traefik
exposedByDefault: false
file:
filename: traefik_dynamic.yml
serversTransport:
insecureSkipVerify: true
```
## Create traefik_dynamic.yml
File: `/etc/docker/volumes/traefik/traefik_dynamic.yml`
```yaml
http:
middlewares:
services:
basicAuth:
users:
- '$username$:$password$'
compress:
compress: {}
hsts:
headers:
stsSeconds: 2592000
routers:
api:
rule: Host(`traefik.$example.com$`)
entrypoints:
- websecure
middlewares:
- services
service: api@internal
tls:
options:
default:
cipherSuites:
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
sniStrict: true
```
## Create acme.json
```shell
sudo touch /etc/docker/volumes/traefik/acme.json
sudo chmod 600 /etc/docker/volumes/traefik/acme.json
```
## Create network
```shell
sudo docker network create traefik
```
## Start traefik
```shell
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`.
## Add Labels to `wg-easy`
To add labels to your `wg-easy` service, you can add the following to your `docker-compose.yml` file:
File: `/etc/docker/containers/wg-easy/docker-compose.yml`
```yaml
services:
wg-easy:
...
container_name: wg-easy
networks:
...
traefik: {}
labels:
- "traefik.enable=true"
- "traefik.http.routers.wg-easy.rule=Host(`wg-easy.$example.com$`)"
- "traefik.http.routers.wg-easy.entrypoints=websecure"
- "traefik.http.routers.wg-easy.service=wg-easy"
- "traefik.http.services.wg-easy.loadbalancer.server.port=51821"
...
networks:
...
traefik:
external: true
```
## Restart `wg-easy`
```shell
cd /etc/docker/containers/wg-easy
sudo docker-compose up -d
```
You can now access `wg-easy` at `https://wg-easy.$example.com$` and start the setup.

97
docs/content/faq.md Normal file
View File

@@ -0,0 +1,97 @@
---
title: FAQ
hide:
- navigation
---
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.
## 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.
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:
```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:
```shell
echo "wireguard" | sudo tee -a /etc/modules
```
## can't initialize iptables table `nat': Table does not exist (do you need to insmod?)
This error indicates that the `nat` table in `iptables` does not exist. This can happen if the `iptables` kernel module is not loaded or if the `nat` table is not supported by your kernel.
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:
```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:
```shell
echo "iptable_nat" | sudo tee -a /etc/modules
```
## can't initialize ip6tables table `nat': Table does not exist (do you need to insmod?)
This error indicates that the `nat` table in `ip6tables` does not exist. This can happen if the `ip6tables` kernel module is not loaded or if the `nat` table is not supported by your kernel.
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:
```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:
```shell
echo "ip6table_nat" | sudo tee -a /etc/modules
```
## can't initialize iptables table `filter': Permission denied
This error indicates that the `filter` table in `iptables` cannot be initialized due to permission issues. This can happen if you are not running the command with sufficient privileges.
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:
```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:
```shell
echo "iptable_filter" | sudo tee -a /etc/modules
```
## can't initialize ip6tables table `filter': Permission denied
This error indicates that the `filter` table in `ip6tables` cannot be initialized due to permission issues. This can happen if you are not running the command with sufficient privileges.
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:
```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:
```shell
echo "ip6table_filter" | sudo tee -a /etc/modules
```

View File

@@ -0,0 +1,72 @@
---
title: Getting Started
hide:
- navigation
---
This page explains how to get started with `wg-easy`. The guide uses Docker Compose as a reference. In our examples, we mount the named volume `etc_wireguard` to `/etc/wireguard` inside the container.
## Preliminary Steps
Before you can get started with deploying your own VPN, there are some requirements to be met:
1. You need to have a host that you can manage
2. You need to have a domain name or a public IP address
3. You need a supported architecture (x86_64, arm64)
### Host Setup
There are a few requirements for a suitable host system:
1. You need to have a container runtime installed
/// note | About the Container Runtime
On the host, you need to have a suitable container runtime (like _Docker_ or _Podman_) installed. We assume [_Docker Compose_][docker-compose] is [installed][docker-compose-installation]. We have aligned file names and configuration conventions with the latest [Docker Compose specification][docker-compose-specification].
If you're using podman, make sure to read the related [documentation][docs-podman].
///
[docker-compose]: https://docs.docker.com/compose/
[docker-compose-installation]: https://docs.docker.com/compose/install/
[docker-compose-specification]: https://docs.docker.com/compose/compose-file/
[docs-podman]: ./examples/tutorials/podman-nft.md
## Deploying the Actual Image
### Tagging Convention
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])
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, recommended |
| `latest` | latest tag | `ghcr.io/wg-easy/wg-easy:latest` or `ghcr.io/wg-easy/wg-easy` | points to latest release, can include breaking changes |
| `15.0` | latest patch for that minor tag | `ghcr.io/wg-easy/wg-easy:15.0` | latest patches for specific minor version |
| `15.0.0` | specific tag | `ghcr.io/wg-easy/wg-easy:15.0.0` | specific release, no updates |
| `edge` | push to `master` | `ghcr.io/wg-easy/wg-easy:edge` | mostly unstable, gets frequent package and code updates |
| `development` | pull requests | `ghcr.io/wg-easy/wg-easy:development` | used for development, testing code from PRs |
<!-- ref: major version -->
When publishing a tag we follow the [Semantic Versioning][semver] specification. The `latest` tag is always pointing to the latest stable release. If you want to avoid breaking changes, use the major version tag (e.g. `15`).
[github-ci]: https://github.com/wg-easy/wg-easy/actions
[ghcr-image]: https://github.com/wg-easy/wg-easy/pkgs/container/wg-easy
[semver]: https://semver.org/
### Follow tutorials
- [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)
/// 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**.

View File

@@ -0,0 +1,26 @@
---
title: 2FA
---
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.

View File

@@ -0,0 +1,5 @@
---
title: Admin Panel
---
TODO

View File

@@ -0,0 +1,43 @@
---
title: CLI
---
If you want to use the CLI, you can run it with
### Docker Compose
```shell
cd /etc/docker/containers/wg-easy
docker compose exec -it wg-easy cli
```
### Docker Run
```shell
docker run --rm -it \
-v ~/.wg-easy:/etc/wireguard \
ghcr.io/wg-easy/wg-easy:15 \
cli
```
### Reset Password
If you want to reset the password for the admin user, you can run the following command:
#### By Prompt
```shell
cd /etc/docker/containers/wg-easy
docker compose exec -it wg-easy cli db:admin:reset
```
You are asked to provide the new password
#### By Argument
```shell
cd /etc/docker/containers/wg-easy
docker compose exec -it wg-easy cli db:admin:reset --password <new_password>
```
This will reset the password for the admin user to the new password you provided. If you include special characters in the password, make sure to escape them properly.

View File

@@ -0,0 +1,50 @@
---
title: Edit Client
---
## General
- **Name**: The name of the client.
- **Enabled**: Whether the client can connect to the VPN.
- **Expire Date**: The date the client will be disabled.
## Address
- **IPv4**: The IPv4 address of the client.
- **IPv6**: The IPv6 address of the client.
## Allowed IPs
Which IPs will be routed through the VPN.
This will not prevent the user from modifying it locally and accessing IP ranges that they should not be able to access.
Use firewall rules to prevent access to IP ranges that the user should not be able to access.
## Server Allowed IPs
Which IPs will be routed to the client.
## DNS
The DNS server that the client will use.
## Advanced
- **MTU**: The maximum transmission unit for the client.
- **Persistent Keepalive**: The interval for sending keepalive packets to the server.
## Hooks
This can only be used for clients that use `wg-quick`. Setting this will throw a error when importing the config on other clients.
- **PreUp**: Commands to run before the interface is brought up.
- **PostUp**: Commands to run after the interface is brought up.
- **PreDown**: Commands to run before the interface is brought down.
- **PostDown**: Commands to run after the interface is brought down.
## Actions
- **Save**: Save the changes made in the form.
- **Revert**: Revert the changes made in the form.
- **Delete**: Delete the client.

View File

@@ -0,0 +1,24 @@
---
title: Setup
---
## 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.

41
docs/content/index.md Normal file
View File

@@ -0,0 +1,41 @@
---
title: Home
hide:
- navigation
---
# Welcome to the Documentation for `wg-easy`
/// 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].
///
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.
[docs-tagging]: ./getting-started.md#tagging-convention
## About
`wg-easy` is the easiest way to run WireGuard VPN + Web-based Admin UI.
## Contents
### Getting Started
If you're new to wg-easy, make sure to read the [_Getting Started_ chapter][docs-getting-started] first. If you want to look at examples for Docker Run and Compose, we have an [_Examples_ page][docs-examples].
[docs-getting-started]: ./getting-started.md
[docs-examples]: ./examples/tutorials/basic-installation.md
### Contributing
We are always happy to welcome new contributors. For guidelines and entrypoints please have a look at the [Contributing section][docs-contributing].
[docs-contributing]: ./contributing/issues-and-pull-requests.md
### Migration
If you are migrating from an older version of `wg-easy`, please read the [_Migration_ chapter][docs-migration].
[docs-migration]: ./advanced/migrate/from-14-to-15.md

87
docs/mkdocs.yml Normal file
View File

@@ -0,0 +1,87 @@
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/>
<span>This project is licensed under AGPL-3.0-only.</span><br/>
<span>This project is not affiliated, associated, authorized, endorsed by, or in any way officially connected with Jason A. Donenfeld, ZX2C4 or Edge Security</span><br/>
<span>"WireGuard" and the "WireGuard" logo are registered trademarks of Jason A. Donenfeld</span>
</p>
repo_url: https://github.com/wg-easy/wg-easy
repo_name: wg-easy
edit_uri: 'edit/master/docs/content'
docs_dir: 'content/'
site_url: https://wg-easy.github.io/wg-easy
theme:
name: material
favicon: assets/logo/favicon.png
logo: assets/logo/logo.png
icon:
repo: fontawesome/brands/github
features:
- navigation.tabs
- navigation.top
- navigation.expand
- navigation.instant
- content.action.edit
- content.action.view
- content.code.annotate
palette:
# Light mode
- media: '(prefers-color-scheme: light)'
scheme: default
primary: grey
accent: red
toggle:
icon: material/weather-night
name: Switch to dark mode
# Dark mode
- media: '(prefers-color-scheme: dark)'
scheme: slate
primary: grey
accent: red
toggle:
icon: material/weather-sunny
name: Switch to light mode
extra:
version:
provider: mike
markdown_extensions:
- toc:
anchorlink: true
- abbr
- attr_list
- pymdownx.blocks.admonition:
types:
- danger
- note
- info
- question
- warning
- pymdownx.details
- pymdownx.superfences:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_code_format
- pymdownx.tabbed:
alternate_style: true
slugify: !!python/object/apply:pymdownx.slugs.slugify
kwds:
case: lower
- pymdownx.tasklist:
custom_checkbox: true
- pymdownx.magiclink
- pymdownx.inlinehilite
- pymdownx.tilde
- pymdownx.emoji:
emoji_index: !!python/name:material.extensions.emoji.twemoji
emoji_generator: !!python/name:material.extensions.emoji.to_svg

4
docs/requirements.txt Normal file
View File

@@ -0,0 +1,4 @@
mkdocs-material
pillow
cairosvg
mike

11
package-lock.json generated
View File

@@ -1,11 +0,0 @@
{
"name": "wg-easy",
"version": "1.0.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"version": "1.0.1"
}
}
}

View File

@@ -1,10 +1,16 @@
{
"version": "1.0.1",
"version": "1.0.0",
"private": true,
"scripts": {
"sudobuild": "DOCKER_BUILDKIT=1 sudo docker build --tag wg-easy .",
"build": "DOCKER_BUILDKIT=1 docker build --tag wg-easy .",
"serve": "docker compose -f docker-compose.yml -f docker-compose.dev.yml up",
"sudostart": "sudo docker run --env WG_HOST=0.0.0.0 --name wg-easy --cap-add=NET_ADMIN --cap-add=SYS_MODULE --sysctl=\"net.ipv4.conf.all.src_valid_mark=1\" --mount type=bind,source=\"$(pwd)\"/config,target=/etc/wireguard -p 51820:51820/udp -p 51821:51821/tcp wg-easy",
"start": "docker run --env WG_HOST=0.0.0.0 --name wg-easy --cap-add=NET_ADMIN --cap-add=SYS_MODULE --sysctl=\"net.ipv4.conf.all.src_valid_mark=1\" --mount type=bind,source=\"$(pwd)\"/config,target=/etc/wireguard -p 51820:51820/udp -p 51821:51821/tcp wg-easy"
}
}
"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",
"format:check:docs": "prettier --check docs"
},
"devDependencies": {
"prettier": "^3.5.3"
},
"packageManager": "pnpm@10.10.0"
}

24
pnpm-lock.yaml generated Normal file
View File

@@ -0,0 +1,24 @@
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.:
devDependencies:
prettier:
specifier: ^3.5.3
version: 3.5.3
packages:
prettier@3.5.3:
resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==}
engines: {node: '>=14'}
hasBin: true
snapshots:
prettier@3.5.3: {}

54
scripts/version.sh Normal file
View File

@@ -0,0 +1,54 @@
#!/bin/bash
package_json="src/package.json"
# Function to update the version in package.json
update_version() {
local new_version=$1
jq --arg new_version "$new_version" '.version = $new_version' $package_json > tmp.json && mv tmp.json $package_json
}
# Get the current version from package.json
current_version=$(jq -r '.version' $package_json)
echo "Current version: $current_version"
# Prompt the user for the new version
read -p "Enter the new version (following SemVer): " new_version
# Official SemVer regex for validation
semver_regex="^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$"
# Validate the new version
if ! echo "$new_version" | grep -Eq "$semver_regex"; then
echo "Invalid version format. Please use SemVer format (e.g., 1.0.0 or 1.0.0-alpha)."
exit 1
fi
# Update the version in package.json
update_version $new_version
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 "----"
echo "If you did everything press 'y' to commit the changes and create a new tag"
read -p "Do you want to continue? (y/n): " confirm
if [ "$confirm" != "y" ]; then
echo "Aborted."
exit 1
fi
# Commit the changes
git add $package_json
git commit -m "Bump version to $new_version"
echo "Committed the changes"
# Create a new Git tag
git tag -a "v$new_version" -m "Release version $new_version"
echo "Created Git tag v$new_version"
# Push the commit & tag to the remote repository
git push origin master --follow-tags
echo "Pushed Git commit and tag v$new_version to remote repository"

View File

@@ -1,11 +0,0 @@
{
"extends": "athom",
"ignorePatterns": [
"**/vendor/*.js"
],
"rules": {
"consistent-return": "off",
"no-shadow": "off",
"max-len": "off"
}
}

26
src/.gitignore vendored Normal file
View File

@@ -0,0 +1,26 @@
# Nuxt dev/build outputs
.output
.data
.nuxt
.nitro
.cache
dist
# Node dependencies
node_modules
# Logs
logs
*.log
# Misc
.DS_Store
.fleet
.idea
# Local env files
.env
.env.*
!.env.example
wg-easy.db

2
src/.prettierignore Normal file
View File

@@ -0,0 +1,2 @@
pnpm-lock.yaml
server/database/migrations/meta

7
src/.prettierrc.json Normal file
View File

@@ -0,0 +1,7 @@
{
"trailingComma": "es5",
"tabWidth": 2,
"semi": true,
"singleQuote": true,
"plugins": ["prettier-plugin-tailwindcss"]
}

57
src/app/app.vue Normal file
View File

@@ -0,0 +1,57 @@
<template>
<ToastProvider>
<NuxtLayout>
<NuxtPage />
<ToastViewport
class="fixed bottom-0 right-0 z-[2147483647] m-0 flex w-[390px] max-w-[100vw] list-none flex-col gap-[10px] p-[var(--viewport-padding)] outline-none [--viewport-padding:_25px]"
>
<BaseToast ref="toastRef" />
</ToastViewport>
</NuxtLayout>
</ToastProvider>
</template>
<script setup lang="ts">
const toast = useToast();
const toastRef = useTemplateRef('toastRef');
toast.setToast(toastRef);
// make sure to fetch release early
useGlobalStore();
useHead({
bodyAttrs: {
class: 'bg-gray-50 dark:bg-neutral-800',
},
link: [
{
rel: 'manifest',
href: '/manifest.json',
},
{
rel: 'icon',
type: 'image/png',
href: '/favicon.png',
},
{
rel: 'apple-touch-icon',
href: '/apple-touch-icon.png',
},
],
meta: [
{
name: 'mobile-web-app-capable',
content: 'yes',
},
{
name: 'apple-mobile-web-app-capable',
content: 'yes',
},
{
name: 'apple-mobile-web-app-status-bar-style',
content: 'black-translucent',
},
],
title: 'WireGuard',
});
</script>

View File

@@ -0,0 +1,34 @@
<template>
<BaseDialog :trigger-class="triggerClass">
<template #trigger><slot /></template>
<template #title>{{ $t('admin.interface.changeCidr') }}</template>
<template #description>
<FormGroup>
<FormTextField id="ipv4Cidr" v-model="ipv4Cidr" label="IPv4" />
<FormTextField id="ipv6Cidr" v-model="ipv6Cidr" label="IPv6" />
</FormGroup>
</template>
<template #actions>
<DialogClose as-child>
<BaseButton>{{ $t('dialog.cancel') }}</BaseButton>
</DialogClose>
<DialogClose as-child>
<BaseButton @click="$emit('change', ipv4Cidr, ipv6Cidr)">
{{ $t('dialog.change') }}
</BaseButton>
</DialogClose>
</template>
</BaseDialog>
</template>
<script lang="ts" setup>
defineEmits(['change']);
const props = defineProps<{
triggerClass?: string;
ipv4Cidr: string;
ipv6Cidr: string;
}>();
const ipv4Cidr = ref(props.ipv4Cidr);
const ipv6Cidr = ref(props.ipv6Cidr);
</script>

View File

@@ -0,0 +1,24 @@
<template>
<BaseDialog :trigger-class="triggerClass">
<template #trigger><slot /></template>
<template #title>{{ $t('admin.interface.restart') }}</template>
<template #description>
{{ $t('admin.interface.restartWarn') }}
</template>
<template #actions>
<DialogClose as-child>
<BaseButton>{{ $t('dialog.cancel') }}</BaseButton>
</DialogClose>
<DialogClose as-child>
<BaseButton @click="$emit('restart')">
{{ $t('admin.interface.restart') }}
</BaseButton>
</DialogClose>
</template>
</BaseDialog>
</template>
<script lang="ts" setup>
defineEmits(['restart']);
defineProps<{ triggerClass?: string }>();
</script>

View File

@@ -0,0 +1,39 @@
<template>
<BaseDialog :trigger-class="triggerClass">
<template #trigger><slot /></template>
<template #title>{{ $t('admin.config.suggest') }}</template>
<template #description>
<div class="flex flex-col items-start gap-2">
<p>{{ $t('admin.config.suggestDesc') }}</p>
<p v-if="!data">
{{ $t('general.loading') }}
</p>
<BaseSelect v-else v-model="selected" :options="data" />
</div>
</template>
<template #actions>
<DialogClose as-child>
<BaseButton>{{ $t('dialog.cancel') }}</BaseButton>
</DialogClose>
<DialogClose as-child>
<BaseButton @click="$emit('change', selected)">
{{ $t('dialog.change') }}
</BaseButton>
</DialogClose>
</template>
</BaseDialog>
</template>
<script lang="ts" setup>
defineEmits(['change']);
const props = defineProps<{
triggerClass?: string;
url: '/api/admin/ip-info' | '/api/setup/4';
}>();
const { data } = useFetch(props.url, {
method: 'get',
});
const selected = ref<string>();
</script>

View File

@@ -0,0 +1,20 @@
<template>
<AvatarRoot
class="mr-2 inline-flex select-none items-center justify-center overflow-hidden rounded-full align-middle"
>
<AvatarImage
class="h-full w-full rounded-[inherit] object-cover"
:src="img ?? ''"
/>
<AvatarFallback
class="leading-1 flex h-full w-full items-center justify-center bg-white text-sm font-medium"
:delay-ms="600"
>
<slot />
</AvatarFallback>
</AvatarRoot>
</template>
<script lang="ts" setup>
defineProps<{ img?: string }>();
</script>

View File

@@ -0,0 +1,26 @@
<template>
<component
:is="elementType"
role="button"
class="inline-flex items-center rounded border-2 border-gray-100 px-4 py-2 text-gray-700 transition hover:border-red-800 hover:bg-red-800 hover:text-white dark:border-neutral-600 dark:text-neutral-200"
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>

View File

@@ -0,0 +1,20 @@
<template>
<ClientOnly>
<apexchart
width="100%"
height="100%"
v-bind="$attrs"
:options="options"
:series="series"
/>
</ClientOnly>
</template>
<script setup lang="ts">
import type { VueApexChartsComponent } from 'vue3-apexcharts';
defineProps<{
options: VueApexChartsComponent['options'];
series: VueApexChartsComponent['series'];
}>();
</script>

View File

@@ -0,0 +1,31 @@
<template>
<DialogRoot :modal="true">
<DialogTrigger :class="triggerClass"><slot name="trigger" /></DialogTrigger>
<DialogPortal>
<DialogOverlay
class="fixed inset-0 z-30 bg-gray-500 opacity-75 dark:bg-black dark:opacity-50"
/>
<DialogContent
class="fixed left-1/2 top-1/2 z-[100] max-h-[85vh] w-[90vw] max-w-md -translate-x-1/2 -translate-y-1/2 rounded-md bg-white p-6 shadow-2xl focus:outline-none dark:bg-neutral-700"
>
<DialogTitle
class="m-0 text-lg font-semibold text-gray-900 dark:text-neutral-200"
>
<slot name="title" />
</DialogTitle>
<DialogDescription
class="mb-5 mt-2 text-sm leading-normal text-gray-500 dark:text-neutral-300"
>
<slot name="description" />
</DialogDescription>
<div class="mt-6 flex justify-end gap-2">
<slot name="actions" />
</div>
</DialogContent>
</DialogPortal>
</DialogRoot>
</template>
<script lang="ts" setup>
defineProps<{ triggerClass?: string }>();
</script>

View File

@@ -0,0 +1,10 @@
<template>
<input
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<unknown>();
</script>

View File

@@ -0,0 +1,37 @@
<template>
<SelectRoot v-model="selected">
<SelectTrigger
class="inline-flex h-8 items-center justify-around gap-2 rounded bg-gray-200 px-3 text-sm leading-none dark:bg-neutral-500 dark:text-neutral-200"
aria-label="Choose option"
>
<SelectValue placeholder="Select..." />
<IconsArrowDown class="size-3" />
</SelectTrigger>
<SelectPortal>
<SelectContent
class="z-[100] min-w-28 rounded bg-gray-300 dark:bg-neutral-500"
>
<SelectViewport class="p-2">
<SelectItem
v-for="(option, index) in options"
:key="index"
:value="option.value"
class="relative flex h-6 items-center rounded px-3 text-sm leading-none outline-none hover:bg-red-800 hover:text-white dark:text-white"
>
<SelectItemText>
{{ option.value }} - {{ option.label }}
</SelectItemText>
</SelectItem>
</SelectViewport>
</SelectContent>
</SelectPortal>
</SelectRoot>
</template>
<script lang="ts" setup>
defineProps<{
options: { label: string; value: string }[];
}>();
const selected = defineModel<string>();
</script>

View File

@@ -0,0 +1,17 @@
<template>
<SwitchRoot
:id="id"
v-model:checked="data"
:name="id"
class="relative flex h-6 w-10 cursor-default rounded-full bg-gray-200 shadow-sm focus-within:outline focus-within:outline-red-700 data-[state=checked]:bg-red-800 dark:bg-neutral-400"
>
<SwitchThumb
class="my-auto block h-4 w-4 translate-x-1 rounded-full bg-white shadow-sm transition-transform duration-100 will-change-transform data-[state=checked]:translate-x-[20px]"
/>
</SwitchRoot>
</template>
<script lang="ts" setup>
defineProps<{ id?: string }>();
const data = defineModel<boolean>();
</script>

View File

@@ -0,0 +1,46 @@
<template>
<ToastRoot
v-for="(e, i) in count"
:key="i"
:class="[
`grid grid-cols-[auto_max-content] items-center gap-x-3 rounded-md p-3 text-neutral-200 shadow-lg [grid-template-areas:_'title_action'_'description_action'] data-[swipe=cancel]:translate-x-0 data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)]`,
{
'bg-green-800': e.type === 'success',
'bg-red-800': e.type === 'error',
},
]"
>
<ToastTitle class="mb-1 text-sm font-medium [grid-area:_title]">
{{ e.title }}
</ToastTitle>
<ToastDescription class="m-0 text-sm [grid-area:_description]">{{
e.message
}}</ToastDescription>
<ToastAction as-child alt-text="toast" class="[grid-area:_action]">
<slot />
</ToastAction>
<ToastClose aria-label="Close">
<span aria-hidden>×</span>
</ToastClose>
</ToastRoot>
</template>
<script setup lang="ts">
import {
ToastAction,
ToastClose,
ToastDescription,
ToastRoot,
ToastTitle,
} from 'radix-vue';
defineExpose({
publish,
});
const count = reactive<ToastParams[]>([]);
function publish(e: ToastParams) {
count.push({ type: e.type, title: e.title, message: e.message });
}
</script>

View File

@@ -0,0 +1,29 @@
<template>
<TooltipProvider>
<TooltipRoot :open="open" @update:open="open = $event">
<TooltipTrigger
class="mx-2 inline-flex h-4 w-4 items-center justify-center rounded-full text-gray-400 outline-none focus:shadow-sm focus:shadow-black"
as-child
>
<button type="button" @click="open = !open">
<slot />
</button>
</TooltipTrigger>
<TooltipPortal>
<TooltipContent
class="select-none whitespace-pre-line rounded bg-gray-600 px-3 py-2 text-sm leading-none text-white shadow-lg will-change-[transform,opacity]"
:side-offset="5"
>
{{ text }}
<TooltipArrow class="fill-gray-600" :width="8" />
</TooltipContent>
</TooltipPortal>
</TooltipRoot>
</TooltipProvider>
</template>
<script lang="ts" setup>
defineProps<{ text: string }>();
const open = ref(false);
</script>

View File

@@ -0,0 +1,11 @@
<template>
<span class="inline-block">
{{ client.ipv4Address }}, {{ client.ipv6Address }}
</span>
</template>
<script setup lang="ts">
defineProps<{
client: LocalClient;
}>();
</script>

View File

@@ -0,0 +1,30 @@
<template>
<div class="relative mt-2 h-10 w-10 self-start rounded-full bg-gray-50">
<BaseAvatar :img="client.avatar" class="h-10 w-10">
<IconsAvatar class="h-6 w-6 text-gray-300" />
</BaseAvatar>
<div
v-if="
isPeerConnected({
latestHandshakeAt: client.latestHandshakeAt
? new Date(client.latestHandshakeAt)
: null,
})
"
>
<div
class="absolute -bottom-1 -right-1 h-4 w-4 animate-ping rounded-full bg-red-100 p-1 dark:bg-red-100"
/>
<div
class="absolute bottom-0 right-0 h-2 w-2 rounded-full bg-red-800 dark:bg-red-600"
/>
</div>
</div>
</template>
<script setup lang="ts">
defineProps<{
client: LocalClient;
}>();
</script>

View File

@@ -0,0 +1,136 @@
<template>
<div
:class="`absolute bottom-0 left-0 right-0 z-0 h-6 ${globalStore.uiChartType === 'line' && 'line-chart'}`"
>
<BaseChart :options="chartOptionsTX" :series="client.transferTxSeries" />
</div>
<div
:class="`absolute left-0 right-0 top-0 z-0 h-6 ${globalStore.uiChartType === 'line' && 'line-chart'}`"
>
<BaseChart
:options="chartOptionsRX"
:series="client.transferRxSeries"
style="transform: scaleY(-1)"
/>
</div>
</template>
<script setup lang="ts">
import type { ApexOptions } from 'apexcharts';
defineProps<{
client: LocalClient;
}>();
const globalStore = useGlobalStore();
const theme = useTheme();
const chartOptionsTX = computed(() => {
const opts = {
...chartOptions,
colors: [CHART_COLORS.tx[theme.value]],
};
opts.chart.type = globalStore.uiChartType;
opts.stroke.width = UI_CHART_PROPS[globalStore.uiChartType].strokeWidth;
return opts;
});
const chartOptionsRX = computed(() => {
const opts = {
...chartOptions,
colors: [CHART_COLORS.rx[theme.value]],
};
opts.chart.type = globalStore.uiChartType;
opts.stroke.width = UI_CHART_PROPS[globalStore.uiChartType].strokeWidth;
return opts;
});
const chartOptions = {
chart: {
type: undefined as ApexChart['type'],
background: 'transparent',
stacked: false,
toolbar: {
show: false,
},
animations: {
enabled: false,
},
parentHeightOffset: 0,
sparkline: {
enabled: true,
},
},
colors: [],
stroke: {
curve: 'smooth',
width: 0,
},
fill: {
type: 'gradient',
gradient: {
shade: 'dark',
type: 'vertical',
shadeIntensity: 0,
gradientToColors: CHART_COLORS.gradient[theme.value],
inverseColors: false,
opacityTo: 0,
stops: [0, 100],
},
},
dataLabels: {
enabled: false,
},
plotOptions: {
bar: {
horizontal: false,
},
},
xaxis: {
labels: {
show: false,
},
axisTicks: {
show: false,
},
axisBorder: {
show: false,
},
},
yaxis: {
labels: {
show: false,
},
min: 0,
},
tooltip: {
enabled: false,
},
legend: {
show: false,
},
grid: {
show: false,
padding: {
left: -10,
right: 0,
bottom: -15,
top: -15,
},
column: {
opacity: 0,
},
xaxis: {
lines: {
show: false,
},
},
},
} satisfies ApexOptions;
</script>
<style scoped lang="css">
.line-chart .apexcharts-svg {
transform: translateY(3px);
}
</style>

View File

@@ -0,0 +1,51 @@
<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"
>
<div class="flex w-full items-center gap-3 md:gap-4">
<ClientCardAvatar :client="client" />
<div class="flex w-full flex-col gap-2 xxs:flex-row">
<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"
>
<div>
<ClientCardAddress :client="client" />
</div>
<div>
<ClientCardLastSeen :client="client" />
</div>
</div>
<ClientCardOneTimeLink :client="client" />
<ClientCardExpireDate :client="client" />
</div>
<div
class="mt-px flex shrink-0 items-center justify-end gap-2 text-xs text-gray-400 dark:text-neutral-400"
>
<ClientCardTransfer :client="client" />
</div>
</div>
</div>
<div class="flex items-center justify-end">
<div
class="flex items-center justify-between gap-1 text-gray-400 dark:text-neutral-400"
>
<ClientCardSwitch :client="client" />
<ClientCardEdit :client="client" />
<ClientCardQRCode :client="client" />
<ClientCardConfig :client="client" />
<ClientCardOneTimeLinkBtn :client="client" />
</div>
</div>
</div>
</template>
<script setup lang="ts">
defineProps<{
client: LocalClient;
}>();
</script>

View File

@@ -0,0 +1,16 @@
<template>
<a
:href="'/api/client/' + client.id + '/configuration'"
download
class="inline-block rounded bg-gray-100 p-2 align-middle transition hover:bg-red-800 hover:text-white dark:bg-neutral-600 dark:text-neutral-300 dark:hover:bg-red-800 dark:hover:text-white"
:title="$t('client.downloadConfig')"
>
<IconsDownload class="w-5" />
</a>
</template>
<script setup lang="ts">
defineProps<{
client: LocalClient;
}>();
</script>

View File

@@ -0,0 +1,14 @@
<template>
<NuxtLink
class="rounded bg-gray-100 p-2 align-middle transition hover:bg-red-800 hover:text-white dark:bg-neutral-600 dark:text-neutral-300 dark:hover:bg-red-800 dark:hover:text-white"
:to="`/clients/${client.id}`"
>
<IconsEdit class="w-5" />
</NuxtLink>
</template>
<script setup lang="ts">
defineProps<{
client: LocalClient;
}>();
</script>

View File

@@ -0,0 +1,23 @@
<template>
<div
class="block pb-1 text-xs text-gray-500 md:inline-block md:pb-0 dark:text-neutral-400"
>
<span class="inline-block">{{ expiredDateFormat(client.expiresAt) }}</span>
</div>
</template>
<script setup lang="ts">
defineProps<{ client: LocalClient }>();
const { t, locale } = useI18n();
function expiredDateFormat(value: string | null) {
if (value === null) return t('client.permanent');
const dateTime = new Date(value);
return dateTime.toLocaleDateString(locale.value, {
year: 'numeric',
month: 'long',
day: 'numeric',
});
}
</script>

View File

@@ -0,0 +1,16 @@
<template>
<span
v-if="client.latestHandshakeAt"
:title="$t('client.lastSeen') + $d(new Date(client.latestHandshakeAt))"
>
{{ timeago(new Date(client.latestHandshakeAt)) }}
</span>
</template>
<script setup lang="ts">
import { format as timeago } from 'timeago.js';
defineProps<{
client: LocalClient;
}>();
</script>

View File

@@ -0,0 +1,16 @@
<template>
<div
class="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>
<script setup lang="ts">
defineProps<{
client: LocalClient;
}>();
</script>

View File

@@ -0,0 +1,51 @@
<template>
<div v-if="client.oneTimeLink !== null" class="text-xs text-gray-400">
<a :href="'./cnf/' + client.oneTimeLink.oneTimeLink">{{ path }}</a>
</div>
</template>
<script setup lang="ts">
const props = defineProps<{ client: LocalClient }>();
const path = ref('Loading...');
const timer = ref<NodeJS.Timeout | null>(null);
const { localeProperties } = useI18n();
onMounted(() => {
timer.value = setIntervalImmediately(() => {
if (props.client.oneTimeLink === null) {
return;
}
const timeLeft =
new Date(props.client.oneTimeLink.expiresAt).getTime() - Date.now();
if (timeLeft <= 0) {
path.value = `${document.location.protocol}//${document.location.host}/cnf/${props.client.oneTimeLink.oneTimeLink} (00:00)`;
return;
}
const formatter = new Intl.DateTimeFormat(localeProperties.value.language, {
minute: '2-digit',
second: '2-digit',
hourCycle: 'h23',
});
const minutes = Math.floor(timeLeft / 60000);
const seconds = Math.floor((timeLeft % 60000) / 1000);
const date = new Date(0);
date.setMinutes(minutes);
date.setSeconds(seconds);
path.value = `${document.location.protocol}//${document.location.host}/cnf/${props.client.oneTimeLink.oneTimeLink} (${formatter.format(date)})`;
}, 1000);
});
onUnmounted(() => {
if (timer.value) {
clearTimeout(timer.value);
}
});
</script>

View File

@@ -0,0 +1,32 @@
<template>
<button
class="inline-block rounded bg-gray-100 p-2 align-middle transition hover:bg-red-800 hover:text-white dark:bg-neutral-600 dark:text-neutral-300 dark:hover:bg-red-800 dark:hover:text-white"
:title="$t('client.otlDesc')"
@click="showOneTimeLink"
>
<IconsLink class="w-5" />
</button>
</template>
<script setup lang="ts">
const props = defineProps<{ client: LocalClient }>();
const clientsStore = useClientsStore();
const _showOneTimeLink = useSubmit(
`/api/client/${props.client.id}/generateOneTimeLink`,
{
method: 'post',
},
{
revert: async () => {
await clientsStore.refresh();
},
noSuccessToast: true,
}
);
function showOneTimeLink() {
return _showOneTimeLink(undefined);
}
</script>

View File

@@ -0,0 +1,16 @@
<template>
<ClientsQRCodeDialog :qr-code="`./api/client/${client.id}/qrcode.svg`">
<div
class="rounded bg-gray-100 p-2 align-middle transition hover:bg-red-800 hover:text-white dark:bg-neutral-600 dark:text-neutral-300 dark:hover:bg-red-800 dark:hover:text-white"
:title="$t('client.showQR')"
>
<IconsQRCode class="w-5" />
</div>
</ClientsQRCodeDialog>
</template>
<script setup lang="ts">
defineProps<{
client: LocalClient;
}>();
</script>

View File

@@ -0,0 +1,53 @@
<template>
<BaseSwitch
v-model="enabled"
:title="
client.enabled ? $t('client.disableClient') : $t('client.enableClient')
"
@click="toggleClient"
/>
</template>
<script setup lang="ts">
const props = defineProps<{
client: LocalClient;
}>();
const enabled = ref(props.client.enabled);
const clientsStore = useClientsStore();
const _disableClient = useSubmit(
`/api/client/${props.client.id}/disable`,
{
method: 'post',
},
{
revert: async () => {
await clientsStore.refresh();
},
noSuccessToast: true,
}
);
const _enableClient = useSubmit(
`/api/client/${props.client.id}/enable`,
{
method: 'post',
},
{
revert: async () => {
await clientsStore.refresh();
},
noSuccessToast: true,
}
);
async function toggleClient() {
if (props.client.enabled) {
await _disableClient(undefined);
} else {
await _enableClient(undefined);
}
}
</script>

View File

@@ -0,0 +1,45 @@
<template>
<!-- Transfer TX -->
<div v-if="client.transferTx" class="min-w-20 md:min-w-24">
<span
class="flex gap-1"
:title="$t('client.totalDownload') + bytes(client.transferTx)"
>
<IconsArrowDown class="mt-0.5 inline h-3 align-middle" />
<div>
<span class="text-gray-700 dark:text-neutral-200"
>{{ bytes(client.transferTxCurrent) }}/s</span
>
<!-- Total TX -->
<br /><span class="font-regular" style="font-size: 0.85em">{{
bytes(client.transferTx)
}}</span>
</div>
</span>
</div>
<!-- Transfer RX -->
<div v-if="client.transferRx" class="min-w-20 md:min-w-24">
<span
class="flex gap-1"
:title="$t('client.totalUpload') + bytes(client.transferRx)"
>
<IconsArrowUp class="mt-0.5 inline h-3 align-middle" />
<div>
<span class="text-gray-700 dark:text-neutral-200"
>{{ bytes(client.transferRxCurrent) }}/s</span
>
<!-- Total RX -->
<br /><span class="font-regular" style="font-size: 0.85em">{{
bytes(client.transferRx)
}}</span>
</div>
</span>
</div>
</template>
<script setup lang="ts">
defineProps<{
client: LocalClient;
}>();
</script>

View File

@@ -0,0 +1,53 @@
<template>
<BaseDialog :trigger-class="triggerClass">
<template #trigger>
<slot />
</template>
<template #title>
{{ $t('client.new') }}
</template>
<template #description>
<div class="flex flex-col">
<FormTextField id="name" v-model="name" :label="$t('client.name')" />
<FormDateField
id="expiresAt"
v-model="expiresAt"
:label="$t('client.expireDate')"
/>
</div>
</template>
<template #actions>
<DialogClose as-child>
<BaseButton>{{ $t('dialog.cancel') }}</BaseButton>
</DialogClose>
<DialogClose as-child>
<BaseButton @click="createClient">{{ $t('client.create') }}</BaseButton>
</DialogClose>
</template>
</BaseDialog>
</template>
<script lang="ts" setup>
const name = ref<string>('');
const expiresAt = ref<string | null>(null);
const clientsStore = useClientsStore();
const { t } = useI18n();
defineProps<{ triggerClass?: string }>();
function createClient() {
return _createClient({ name: name.value, expiresAt: expiresAt.value });
}
const _createClient = useSubmit(
'/api/client',
{
method: 'post',
},
{
revert: () => clientsStore.refresh(),
successMsg: t('client.created'),
}
);
</script>

View File

@@ -0,0 +1,26 @@
<template>
<BaseDialog :trigger-class="triggerClass">
<template #trigger><slot /></template>
<template #title>{{ $t('client.deleteClient') }}</template>
<template #description>
{{ $t('client.deleteDialog1') }}
<strong>{{ clientName }}</strong
>? {{ $t('client.deleteDialog2') }}
</template>
<template #actions>
<DialogClose as-child>
<BaseButton>{{ $t('dialog.cancel') }}</BaseButton>
</DialogClose>
<DialogClose as-child>
<BaseButton @click="$emit('delete')">{{
$t('client.deleteClient')
}}</BaseButton>
</DialogClose>
</template>
</BaseDialog>
</template>
<script lang="ts" setup>
defineEmits(['delete']);
defineProps<{ triggerClass?: string; clientName: string }>();
</script>

View File

@@ -0,0 +1,11 @@
<template>
<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" />
<span class="text-sm">{{ $t('client.new') }}</span>
</BaseButton>
</ClientsCreateDialog>
</p>
</template>

View File

@@ -0,0 +1,13 @@
<template>
<div
v-for="client in clientsStore.clients"
:key="client.id"
class="relative overflow-hidden border-b border-solid border-gray-100 last:border-b-0 dark:border-neutral-600"
>
<ClientCard :client="client" />
</div>
</template>
<script setup lang="ts">
const clientsStore = useClientsStore();
</script>

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