Compare commits

...

75 Commits

Author SHA1 Message Date
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 a998f6be8a.

* 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 a7376abcf1.

* Revert "Fix port in Readme"

This reverts commit 9760bde2f2.

* Revert "Separate port for prometheus metrics"

This reverts commit 58f5b6806e.

* Revert "Separate port for prometheus metrics"

This reverts commit 6d246ea4bd.

* 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
277 changed files with 21212 additions and 10798 deletions
+3 -1
View File
@@ -1 +1,3 @@
/src/node_modules
/src/node_modules
/src/.nuxt
/src/.output
+23
View File
@@ -0,0 +1,23 @@
# http://editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
# The JSON files contain newlines inconsistently
[*.json]
insert_final_newline = ignore
# Minified JavaScript files shouldn't be changed
[**.min.js]
indent_style = ignore
insert_final_newline = ignore
[*.md]
trim_trailing_whitespace = false
+3 -2
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
+1 -1
View File
@@ -1,3 +1,3 @@
# These are supported funding model platforms
github: weejewel
github: [weejewel, kaaax0815]
+37
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
@@ -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
-38
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
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
+10
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"
+18 -16
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}}"
+56 -22
View File
@@ -1,37 +1,71 @@
name: Build & Publish Development
name: Development
on:
workflow_dispatch:
jobs:
deploy:
name: Build & Deploy
docker:
name: Build & Deploy Docker
runs-on: ubuntu-latest
if: github.repository_owner == 'wg-easy'
permissions:
packages: write
contents: read
steps:
- uses: actions/checkout@v4
with:
ref: production
- uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- 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: 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:development
- name: Build & Publish Docker Image
uses: docker/build-push-action@v6
with:
context: .
push: true
platforms: linux/amd64,linux/arm64
tags: ghcr.io/wg-easy/wg-easy:development
cache-from: type=gha
cache-to: type=gha,mode=min
docs:
name: Build & Deploy Docs
runs-on: ubuntu-latest
if: github.repository_owner == 'wg-easy'
permissions:
contents: write
needs: docker
steps:
- uses: actions/checkout@v4
- 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
+60 -22
View File
@@ -1,4 +1,4 @@
name: Build & Publish Nightly
name: Nightly
on:
workflow_dispatch:
@@ -6,34 +6,72 @@ on:
- cron: "0 0 * * *"
jobs:
deploy:
name: Build & Deploy
docker:
name: Build & Deploy Docker
runs-on: ubuntu-latest
if: github.repository_owner == 'wg-easy'
permissions:
packages: write
contents: read
steps:
- uses: actions/checkout@v4
with:
ref: production
- uses: actions/checkout@v4
with:
ref: master
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- 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: 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
- name: Build & Publish Docker Image
uses: docker/build-push-action@v6
with:
context: .
push: true
platforms: linux/amd64,linux/arm64
tags: ghcr.io/wg-easy/wg-easy:nightly
cache-from: type=gha
cache-to: type=gha,mode=min
docs:
name: Build & Deploy Docs
runs-on: ubuntu-latest
if: github.repository_owner == 'wg-easy'
permissions:
contents: write
needs: docker
steps:
- uses: actions/checkout@v4
with:
ref: master
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: 3.11.9
cache: "pip"
cache-dependency-path: docs/requirements.txt
- name: Install Dependencies
run: |
pip install -r docs/requirements.txt
- name: Setup Git User
run: |
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
- name: Build Docs Website
run: |
cd docs
git fetch origin gh-pages --depth=1 || true
mike deploy --push --update-aliases nightly
+27 -22
View File
@@ -1,38 +1,43 @@
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
docker:
name: Build Docker
runs-on: ubuntu-latest
if: github.repository_owner == 'wg-easy'
permissions:
packages: write
contents: read
steps:
- uses: actions/checkout@v4
with:
ref: production
- uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- 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: 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:
push: false
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8
tags: ghcr.io/wg-easy/wg-easy:pr
- name: Build Docker Image
uses: docker/build-push-action@v6
with:
context: .
push: false
platforms: linux/amd64,linux/arm64
tags: ghcr.io/wg-easy/wg-easy:pr
cache-from: type=gha
cache-to: type=gha,mode=min
+87 -27
View File
@@ -1,43 +1,103 @@
name: Build & Publish Latest
name: Production
on:
workflow_dispatch:
push:
branches:
- production
tags:
- "v*"
jobs:
deploy:
name: Build & Deploy
docker:
name: Build & Deploy Docker
runs-on: ubuntu-latest
if: github.repository_owner == 'wg-easy'
if: |
github.repository_owner == 'wg-easy' &&
startsWith(github.ref, 'refs/tags/v')
permissions:
packages: write
contents: read
steps:
- uses: actions/checkout@v4
with:
ref: production
- uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- 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
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}
type=semver,pattern={{major}}.{{minor}}
- name: Set environment variables
run: echo RELEASE=$(cat ./src/package.json | jq -r .release | jq -r .version) >> $GITHUB_ENV
- 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:latest, ghcr.io/wg-easy/wg-easy:${{ env.RELEASE }}
- name: Build & Publish Docker Image
uses: docker/build-push-action@v6
with:
context: .
push: true
platforms: linux/amd64,linux/arm64
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=min
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
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
# latest will point to old docs if old tag is pushed
# 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
+18 -6
View File
@@ -4,7 +4,6 @@ on:
push:
branches:
- master
- production
pull_request:
jobs:
@@ -12,18 +11,31 @@ jobs:
name: Lint
runs-on: ubuntu-latest
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: '20'
node-version: "lts/*"
check-latest: true
cache: 'npm'
cache: "pnpm"
- name: npm run lint
- name: pnpm ${{ matrix.command }}
run: |
cd src
npm ci
npm run lint
pnpm install
pnpm ${{ matrix.command }}
-40
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
+3 -4
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"
-4
View File
@@ -1,6 +1,2 @@
/config
/wg0.conf
/wg0.json
/src/node_modules
.DS_Store
*.swp
+14
View File
@@ -0,0 +1,14 @@
{
"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"
]
}
+25
View File
@@ -0,0 +1,25 @@
{
"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",
"[vue]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"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"
}
+32
View File
@@ -0,0 +1,32 @@
# 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.
## 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
## [14.0.0] - 2024-09-04
### Major changes
- `PASSWORD` has been replaced by `PASSWORD_HASH`
+32 -27
View File
@@ -1,49 +1,54 @@
# 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
RUN npm install --no-save libsql
# 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 /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
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"]
+37
View File
@@ -0,0 +1,37 @@
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=false
# Install Dependencies
COPY src/package.json src/pnpm-lock.yaml ./
RUN pnpm install
# Copy Project
COPY src ./
-28
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
```
+661 -437
View File
File diff suppressed because it is too large Load Diff
+114 -73
View File
@@ -1,56 +1,78 @@
# 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-11M-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)
- 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
> [!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)
- [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/)
## Requirements
* A host with a kernel that supports WireGuard (all modern kernels).
* A host with Docker installed.
- A host with a kernel that supports WireGuard (all modern kernels).
- A host with Docker installed.
## Versions
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!
> 💡 We follow semantic versioning (semver)
| 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`. |
We offer multiple Docker image tags to suit your needs. The table below is in a particular order, with the first tag being the most recommended:
| tag | Branch | Example | Description |
| ------------- | ---------------------------------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `15` | latest minor for that major tag | `ghcr.io/wg-easy/wg-easy:15` | latest features for specific major versions, no breaking changes |
| `latest` | latest tag | `ghcr.io/wg-easy/wg-easy:latest` or `ghcr.io/wg-easy/wg-easy` | stable as possible get bug fixes quickly when needed, see Releases for more information. |
| `15.0` | latest patch for that minor tag | `ghcr.io/wg-easy/wg-easy:15.0` | latest patches for specific minor version |
| `15.0.0` | specific tag | `ghcr.io/wg-easy/wg-easy:15.0.0` | specific release, don't use this as this will not get updated |
| `nightly` | [`master`](https://github.com/wg-easy/wg-easy/tree/master) | `ghcr.io/wg-easy/wg-easy:nightly` | mostly unstable gets frequent package and code updates, deployed against [`master`](https://github.com/wg-easy/wg-easy/tree/master). |
| `development` | pull requests | `ghcr.io/wg-easy/wg-easy:development` | used for development, testing code from PRs before landing into [`master`](https://github.com/wg-easy/wg-easy/tree/master). |
## Installation
### 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,76 +80,85 @@ And log in again.
### 2. Run WireGuard Easy
The easiest way to run WireGuard Easy is with Docker Compose.
Just download [`docker-compose.yml`](docker-compose.yml), make necessary adjustments and
execute `sudo docker compose up -d`.
Now setup a reverse proxy to be able to access the Web UI 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
<!-- TOOD: add to docs: Grafana dashboard [21733](https://grafana.com/grafana/dashboards/21733-wireguard/) -->
<!-- TOOD: add to docs
To setup the IPv6 Network, simply run once:
```bash
docker network create \
-d bridge --ipv6 \
-d default \
--subnet 10.42.42.0/24 \
--subnet fdcc:ad94:bacf:61a3::/64 wg \
```
To automatically install & run wg-easy, simply run:
```
```bash
docker run -d \
--name=wg-easy \
-e LANG=de \
-e WG_HOST=<🚨YOUR_SERVER_IP> \
-e PASSWORD_HASH=<🚨YOUR_ADMIN_PASSWORD_HASH> \
--net wg \
-e PORT=51821 \
-e WG_PORT=51820 \
--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.conf.all.src_valid_mark=1" \
--sysctl="net.ipv4.ip_forward=1" \
--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
```
> 💡 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.
The Web UI will now be available on `http://0.0.0.0:51821`.
The Prometheus metrics will now be available on `http://0.0.0.0:51821/api/metrics`. Grafana dashboard [21733](https://grafana.com/grafana/dashboards/21733-wireguard/)
> 💡 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) 🍻
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) ☕
<!-- TOOD: add to docs
## 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.
| 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 |
## Updating
To update to the latest version, simply run:
```bash
```shell
docker stop wg-easy
docker rm wg-easy
docker pull ghcr.io/wg-easy/wg-easy
@@ -140,12 +171,22 @@ With Docker Compose WireGuard Easy can be updated with a single command:
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
The WireGuard Easy container will be automatically recreated if a newer image
was pulled.
## Common Use Cases
* [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)
- [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)
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).
-->
## License
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
+12 -5
View File
@@ -1,10 +1,13 @@
services:
wg-easy:
build:
dockerfile: ./Dockerfile
command: npm run serve
dockerfile: ./Dockerfile.dev
command: pnpm run dev
volumes:
- ./src/:/app/
- temp:/app/.nuxt/
- temp1:/app/node_modules/
- /lib/modules:/lib/modules:ro
# - ./data/:/etc/wireguard
ports:
- "51820:51820/udp"
@@ -12,6 +15,10 @@ services:
cap_add:
- NET_ADMIN
- SYS_MODULE
environment:
# - PASSWORD=p
- WG_HOST=192.168.1.233
# folders should be generated inside container
volumes:
temp:
driver: local
temp1:
driver: local
+24 -25
View File
@@ -3,35 +3,21 @@ volumes:
services:
wg-easy:
environment:
# Change Language:
# (Supports: en, ua, ru, tr, no, pl, fr, de, ca, es, ko, vi, nl, is, pt, chs, cht, it, th, hi)
- LANG=de
# ⚠️ Required:
# Change this to your host's public address
- 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)
# - PORT=51821
# - WG_PORT=51820
# - WG_CONFIG_PORT=92820
# - WG_DEFAULT_ADDRESS=10.8.0.x
# - WG_DEFAULT_DNS=1.1.1.1
# - WG_MTU=1420
# - WG_ALLOWED_IPS=192.168.15.0/24, 10.0.1.0/24
# - WG_PERSISTENT_KEEPALIVE=25
# - WG_PRE_UP=echo "Pre Up" > /etc/wireguard/pre-up.txt
# - WG_POST_UP=echo "Post Up" > /etc/wireguard/post-up.txt
# - WG_PRE_DOWN=echo "Pre Down" > /etc/wireguard/pre-down.txt
# - WG_POST_DOWN=echo "Post Down" > /etc/wireguard/post-down.txt
# - UI_TRAFFIC_STATS=true
# - UI_CHART_TYPE=0 # (0 Charts disabled, 1 # Line chart, 2 # Area chart, 3 # Bar chart)
#environment:
# Optional:
# - PORT=51821
# - HOST=0.0.0.0
# - INSECURE=false
image: ghcr.io/wg-easy/wg-easy
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"
@@ -39,7 +25,20 @@ 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
- 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
+5
View File
@@ -0,0 +1,5 @@
---
title: API
---
TODO
@@ -0,0 +1,5 @@
---
title: Optional Configuration
---
TODO
@@ -0,0 +1,47 @@
---
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.
## 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](../../usage.md) or [Basic Installation](../../examples/tutorials/basic-installation.md) 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.
### Done
You have now successfully migrated to `v15` of `wg-easy`.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

+23
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 `nightly` in the dropdown menu at the top. Navigate to the page you would like to edit and click the edit button in the top right. This allows you to make changes and create a pull-request.
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.
@@ -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 (_via assignment of the label `meta/no template - no 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 `:nightly` image every night and your changes will be includes 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
@@ -0,0 +1,56 @@
---
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
DIR=/docker/wg-easy
sudo mkdir -p $DIR
```
2. Download docker compose file
```shell
sudo curl -o $URL/docker-compose.yml https://raw.githubusercontent.com/wg-easy/wg-easy/master/docker-compose.yml
```
3. Start `wg-easy`
```shell
sudo docker-compose -f $DIR/docker-compose.yml up -d
```
## Setup Firewall
If you are using a firewall, you need to open the following ports:
- UDP 51820 (WireGuard)
- TCP 51821 (Web UI)
These ports can be changed, so if you change them you have to update your firewall rules accordingly.
## Setup Reverse Proxy
TODO
## Access the Web UI
Open your browser and navigate to `https://<your-domain>:51821` or `https://<your-ip>:51821`.
Follow the instructions to set up your WireGuard VPN.
@@ -0,0 +1,5 @@
---
title: Without Docker
---
TODO
+96
View File
@@ -0,0 +1,96 @@
---
title: Podman
---
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:
```ini
[Container]
ContainerName=wg-easy
Image=ghcr.io/wg-easy/wg-easy:latest
Volume=/etc/containers/volumes/wg-easy:/etc/wireguard:Z
Network=wg-easy.network
PublishPort=51820:51820/udp
PublishPort=51821:51821/tcp
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
apk add nftables; nft add table inet wg_table; 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 drop \; }; nft add rule inet wg_table input udp dport {{port}} accept; nft add chain inet wg_table forward { type filter hook forward priority 0 \; policy drop \; }; 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
```
<!--
TODO: improve docs after better nftables support
TODO: fix accept web ui port
-->
+92
View File
@@ -0,0 +1,92 @@
---
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.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.
| Event | Image Tags |
| ----------------------- | ----------------------------- |
| `cron` on `master` | `nightly` |
| `push` a tag (`v1.2.3`) | `1.2.3`, `1.2`, `1`, `latest` |
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/
### Get All Files
Issue the following command to acquire the necessary file:
```shell
wget "https://raw.githubusercontent.com/wg-easy/wg-easy/master/docker-compose.yml"
```
### Start the Container
To start the container, issue the following command:
```shell
sudo docker compose up -d
```
### Configuration Steps
Now follow the setup process in your web browser
### Stopping the Container
To stop the container, issue the following command:
```shell
sudo docker compose down
```
/// danger | Using the Correct Commands For Stopping and Starting wg-easy
**Use `sudo docker compose up / down`, not `sudo docker compose start / stop`**. Otherwise, the container is not properly destroyed and you may experience problems during startup because of inconsistent state.
///
**That's it! It really is that easy**.
If you need more help you can read the [Basic Installation Tutorial][basic-installation].
[basic-installation]: ./examples/tutorials/basic-installation.md
+35
View File
@@ -0,0 +1,35 @@
---
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]: ./usage.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 [_Usage_ chapter][docs-usage] first. If you want to look at examples for Docker Run and Compose, we have an [_Examples_ page][docs-examples].
[docs-usage]: ./usage.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
+81
View File
@@ -0,0 +1,81 @@
site_name: "wg-easy"
site_description: "The easiest way to run WireGuard VPN + Web-based Admin UI."
site_author: "wg-easy (Github Organization)"
copyright: '<p>&copy <a href="https://github.com/wg-easy"><em>Wireguard Easy Organization</em></a><br/><span>This project is licensed under the GNU Affero General Public License v3.0 or later.</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
View File
@@ -0,0 +1,4 @@
mkdocs-material
pillow
cairosvg
mike
-11
View File
@@ -1,11 +0,0 @@
{
"name": "wg-easy",
"version": "1.0.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"version": "1.0.1"
}
}
}
+8 -8
View File
@@ -1,10 +1,10 @@
{
"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 --build",
"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"
},
"packageManager": "pnpm@10.5.2"
}
+9
View File
@@ -0,0 +1,9 @@
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.: {}
-11
View File
@@ -1,11 +0,0 @@
{
"extends": "athom",
"ignorePatterns": [
"**/vendor/*.js"
],
"rules": {
"consistent-return": "off",
"no-shadow": "off",
"max-len": "off"
}
}
+26
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
+1
View File
@@ -0,0 +1 @@
public-hoist-pattern[]=@libsql/linux*
+2
View File
@@ -0,0 +1,2 @@
pnpm-lock.yaml
server/database/migrations/meta
+7
View File
@@ -0,0 +1,7 @@
{
"trailingComma": "es5",
"tabWidth": 2,
"semi": true,
"singleQuote": true,
"plugins": ["prettier-plugin-tailwindcss"]
}
+57
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>
+34
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>
+20
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>
+26
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>
+20
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>
+31
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 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>
+10
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>
+17
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>
+46
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>
+24
View File
@@ -0,0 +1,24 @@
<template>
<TooltipProvider>
<TooltipRoot>
<TooltipTrigger
class="inline-flex h-8 w-8 items-center justify-center rounded-full text-gray-400 outline-none focus:shadow-sm focus:shadow-black"
>
<slot />
</TooltipTrigger>
<TooltipPortal>
<TooltipContent
class="select-none 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 }>();
</script>
+11
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>
+30
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>
+136
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>
@@ -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>
+16
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>
+14
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>
@@ -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>
@@ -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>
+16
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>
@@ -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>
@@ -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>
+16
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>
+53
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>
@@ -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>
@@ -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>
@@ -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>
+11
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>
+13
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>
+8
View File
@@ -0,0 +1,8 @@
<template>
<ClientsCreateDialog>
<BaseButton as="span">
<IconsPlus class="w-4 md:mr-2" />
<span class="text-sm max-md:hidden">{{ $t('client.newShort') }}</span>
</BaseButton>
</ClientsCreateDialog>
</template>
@@ -0,0 +1,19 @@
<template>
<BaseDialog>
<template #trigger>
<slot />
</template>
<template #description>
<img :src="qrCode" />
</template>
<template #actions>
<DialogClose>
<BaseButton>{{ $t('dialog.cancel') }}</BaseButton>
</DialogClose>
</template>
</BaseDialog>
</template>
<script setup lang="ts">
defineProps<{ qrCode: string }>();
</script>
+20
View File
@@ -0,0 +1,20 @@
<template>
<BaseButton @click="toggleSort">
<IconsArrowDown
v-if="globalStore.sortClient === true"
class="w-4 md:mr-2"
/>
<IconsArrowUp v-else class="w-4 md:mr-2" />
<span class="text-sm max-md:hidden"> {{ $t('client.sort') }}</span>
</BaseButton>
</template>
<script setup lang="ts">
const globalStore = useGlobalStore();
const clientsStore = useClientsStore();
function toggleSort() {
globalStore.sortClient = !globalStore.sortClient;
clientsStore.refresh().catch(console.error);
}
</script>
+16
View File
@@ -0,0 +1,16 @@
<template>
<input
:value="label"
:type="type ?? 'button'"
class="col-span-2 rounded-lg border-2 border-gray-100 py-2 text-gray-500 hover:border-red-800 hover:bg-red-800 hover:text-white 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>
import type { InputTypeHTMLAttribute } from 'vue';
defineProps<{
label: string;
type?: InputTypeHTMLAttribute;
}>();
</script>
+51
View File
@@ -0,0 +1,51 @@
<template>
<div v-if="data?.length === 0">
{{ emptyText || $t('form.noItems') }}
</div>
<div v-else class="flex flex-col gap-2">
<div v-for="(item, i) in data" :key="i">
<div class="flex flex-row gap-1">
<input
:value="item"
:name="name"
type="text"
class="rounded-lg border-2 border-gray-100 text-gray-500 focus:border-red-800 focus:outline-0 focus:ring-0 dark:border-neutral-800 dark:bg-neutral-700 dark:text-neutral-200 dark:placeholder:text-neutral-400"
@input="update($event, i)"
/>
<BaseButton as="input" type="button" value="-" @click="del(i)" />
</div>
</div>
<div class="mt-2">
<BaseButton
as="input"
type="button"
:value="$t('form.add')"
@click="add"
/>
</div>
</div>
</template>
<script lang="ts" setup>
const data = defineModel<string[]>();
defineProps<{ emptyText?: string[]; name: string }>();
function update(e: Event, i: number) {
const v = (e.target as HTMLInputElement).value;
if (!data.value) {
return;
}
data.value[i] = v;
}
function add() {
data.value?.push('');
}
function del(i: number) {
if (!data.value) {
return;
}
data.value.splice(i, 1);
}
</script>
+44
View File
@@ -0,0 +1,44 @@
<template>
<div class="flex items-center">
<FormLabel :for="id">
{{ label }}
</FormLabel>
<BaseTooltip v-if="description" :text="description">
<IconsInfo class="size-4" />
</BaseTooltip>
</div>
<BaseInput
:id="id"
:model-value="formattedDate"
:name="id"
type="date"
max="9999-12-31"
@update:model-value="updateDate"
/>
</template>
<script lang="ts" setup>
defineProps<{ id: string; label: string; description?: string }>();
const data = defineModel<string | null>();
const date = ref(data);
const formattedDate = computed(() => {
return date.value ? date.value.split('T')[0] : '';
});
const updateDate = (value: unknown) => {
if (typeof value !== 'string' && value !== null) {
return;
}
const temp = value?.trim() ?? null;
if (temp === '' || temp === null) {
date.value = null;
} else {
date.value = new Date(temp).toISOString();
}
};
</script>
+5
View File
@@ -0,0 +1,5 @@
<template>
<form>
<slot />
</form>
</template>
+9
View File
@@ -0,0 +1,9 @@
<template>
<section class="grid grid-cols-1 gap-4 md:grid-cols-2">
<slot />
<Separator
decorative
class="col-span-2 h-px w-full bg-gray-100 dark:bg-neutral-600"
/>
</section>
</template>
+12
View File
@@ -0,0 +1,12 @@
<template>
<h4 class="col-span-full flex items-center py-6 text-2xl">
<slot />
<BaseTooltip v-if="description" :text="description">
<IconsInfo class="size-4" />
</BaseTooltip>
</h4>
</template>
<script lang="ts" setup>
defineProps<{ description?: string }>();
</script>
+11
View File
@@ -0,0 +1,11 @@
<template>
<RLabel :for="props.for" class="md:align-middle md:leading-10"
><slot
/></RLabel>
</template>
<script lang="ts" setup>
import { Label as RLabel } from 'radix-vue';
const props = defineProps<{ for: string }>();
</script>
+38
View File
@@ -0,0 +1,38 @@
<template>
<div class="flex items-center">
<FormLabel :for="id">
{{ label }}
</FormLabel>
<BaseTooltip v-if="description" :text="description">
<IconsInfo class="size-4" />
</BaseTooltip>
</div>
<BaseInput
:id="id"
v-model.trim="data"
:name="id"
type="text"
:autcomplete="autocomplete"
:placeholder="placeholder"
/>
</template>
<script lang="ts" setup>
defineProps<{
id: string;
label: string;
description?: string;
autocomplete?: string;
placeholder?: string;
}>();
const data = defineModel<string | null>({
set(value) {
const temp = value?.trim() ?? null;
if (temp === '') {
return null;
}
return temp;
},
});
</script>
+17
View File
@@ -0,0 +1,17 @@
<template>
<div class="flex items-center">
<FormLabel :for="id">
{{ label }}
</FormLabel>
<BaseTooltip v-if="description" :text="description">
<IconsInfo class="size-4" />
</BaseTooltip>
</div>
<BaseInput :id="id" v-model.number="data" :name="id" type="number" />
</template>
<script lang="ts" setup>
defineProps<{ id: string; label: string; description?: string }>();
const data = defineModel<number>();
</script>
+18
View File
@@ -0,0 +1,18 @@
<template>
<FormLabel :for="id">
{{ label }}
</FormLabel>
<BaseInput
:id="id"
v-model.trim="data"
:name="id"
type="password"
:autocomplete="autocomplete"
/>
</template>
<script lang="ts" setup>
defineProps<{ id: string; label: string; autocomplete: string }>();
const data = defineModel<string>();
</script>
+16
View File
@@ -0,0 +1,16 @@
<template>
<div class="flex items-center">
<FormLabel :for="id">
{{ label }}
</FormLabel>
<BaseTooltip v-if="description" :text="description">
<IconsInfo class="size-4" />
</BaseTooltip>
</div>
<BaseSwitch :id="id" v-model="data" />
</template>
<script lang="ts" setup>
defineProps<{ id: string; label: string; description?: string }>();
const data = defineModel<boolean>();
</script>
+28
View File
@@ -0,0 +1,28 @@
<template>
<div class="flex items-center">
<FormLabel :for="id">
{{ label }}
</FormLabel>
<BaseTooltip v-if="description" :text="description">
<IconsInfo class="size-4" />
</BaseTooltip>
</div>
<BaseInput
:id="id"
v-model.trim="data"
:name="id"
type="text"
:autcomplete="autocomplete"
/>
</template>
<script lang="ts" setup>
defineProps<{
id: string;
label: string;
description?: string;
autocomplete?: string;
}>();
const data = defineModel<string>();
</script>
+16
View File
@@ -0,0 +1,16 @@
<template>
<Toggle
:pressed="globalStore.uiShowCharts"
class="group inline-flex h-8 w-8 cursor-pointer items-center justify-center whitespace-nowrap rounded-full bg-gray-200 transition hover:bg-gray-300 dark:bg-neutral-700 dark:hover:bg-neutral-600"
:title="$t('layout.toggleCharts')"
@update:pressed="globalStore.toggleCharts"
>
<IconsChart
class="h-5 w-5 fill-gray-400 transition group-data-[state=on]:fill-gray-600 dark:fill-neutral-600 dark:group-data-[state=on]:fill-neutral-400"
/>
</Toggle>
</template>
<script lang="ts" setup>
const globalStore = useGlobalStore();
</script>
@@ -0,0 +1,44 @@
<template>
<SelectRoot v-model="langProxy" :default-value="locale">
<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-700 dark:text-neutral-400"
aria-label="Select language"
>
<IconsLanguage class="size-3" />
<SelectValue />
<IconsArrowDown class="size-3" />
</SelectTrigger>
<SelectPortal>
<SelectContent
class="min-w-28 rounded bg-gray-300 dark:bg-neutral-500"
position="popper"
>
<SelectViewport class="p-2">
<SelectItem
v-for="(option, index) in langs"
:key="index"
:value="option.code"
class="relative flex h-6 items-center rounded px-3 text-sm leading-none outline-none hover:bg-red-800 hover:text-white data-[state=checked]:underline dark:text-white"
>
<SelectItemText>
{{ option.name }}
</SelectItemText>
</SelectItem>
</SelectViewport>
</SelectContent>
</SelectPortal>
</SelectRoot>
</template>
<script setup lang="ts">
const { locales, locale, setLocale } = useI18n();
const langProxy = ref(locale);
watchEffect(() => {
setLocale(langProxy.value);
});
const langs = locales.value.sort((a, b) => a.code.localeCompare(b.code));
</script>
+11
View File
@@ -0,0 +1,11 @@
<template>
<NuxtLink to="/" class="mb-4 flex-grow self-start">
<h1 class="text-4xl font-medium dark:text-neutral-200">
<img
src="/logo.png"
width="32"
class="dark:bg mr-2 inline align-middle"
/><span class="align-middle">WireGuard</span>
</h1>
</NuxtLink>
</template>
+28
View File
@@ -0,0 +1,28 @@
<template>
<button
class="flex h-8 w-8 items-center justify-center rounded-full bg-gray-200 transition hover:bg-gray-300 dark:bg-neutral-700 dark:hover:bg-neutral-600"
:title="$t(`theme.${theme.preference}`)"
@click="toggleTheme"
>
<IconsSun v-if="theme.preference === 'light'" class="h-5 w-5" />
<IconsMoon
v-else-if="theme.preference === 'dark'"
class="h-5 w-5 text-neutral-400"
/>
<IconsHalfMoon v-else class="h-5 w-5 fill-gray-600 dark:fill-neutral-400" />
</button>
</template>
<script lang="ts" setup>
const theme = useTheme();
function toggleTheme() {
const themeCycle = {
system: 'light',
light: 'dark',
dark: 'system',
} as const;
theme.preference = themeCycle[theme.preference];
}
</script>
+28
View File
@@ -0,0 +1,28 @@
<template>
<div
v-if="globalStore.release?.updateAvailable"
class="font-small mb-10 rounded-md bg-red-800 p-4 text-sm text-white shadow-lg dark:bg-red-100 dark:text-red-600"
:title="`v${globalStore.release.currentRelease} → v${globalStore.release.latestRelease.version}`"
>
<div class="container mx-auto flex flex-auto flex-row items-center">
<div class="flex-grow">
<p class="font-bold">{{ $t('update.updateAvailable') }}</p>
<p>{{ globalStore.release.latestRelease.changelog }}</p>
</div>
<a
:href="`https://github.com/wg-easy/wg-easy/releases/tag/${globalStore.release.latestRelease.version}`"
target="_blank"
class="font-sm float-right flex-shrink-0 rounded-md border-2 border-red-800 bg-white p-3 font-semibold text-red-800 transition-all hover:border-white hover:bg-red-800 hover:text-white dark:border-red-600 dark:bg-red-100 dark:text-red-600 dark:hover:border-red-600 dark:hover:bg-red-600 dark:hover:text-red-100"
>
{{ $t('update.update') }}
</a>
</div>
</div>
</template>
<script lang="ts" setup>
const globalStore = useGlobalStore();
// TODO: only show this to admins
</script>

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