Compare commits

..

287 Commits

Author SHA1 Message Date
Donald Cheng Hong Zou
258564208b Finalizing everything. 2022-01-18 10:42:23 -05:00
Donald Cheng Hong Zou
e2e8222005 Upload manifest.json 2022-01-16 20:35:24 -05:00
Donald Cheng Hong Zou
75f2826805 Merge branch 'pr/93' into Migrate-to-SQLite 2022-01-13 09:44:47 -05:00
Donald Cheng Hong Zou
730af4f2ee Finishing documentation 2022-01-13 09:37:23 -05:00
Donald Zou
e5737ebd9d Merge branch 'main' into Migrate-to-SQLite 2022-01-12 21:41:41 -05:00
Donald Cheng Hong Zou
7f24904164 Bugs 2022-01-12 20:28:12 -05:00
Donald Cheng Hong Zou
9ff7198602 Still finalizing everything 2022-01-12 19:53:36 -05:00
Donald Cheng Hong Zou
6d56967a0f Getting ready for v3.0 2022-01-08 15:26:17 -05:00
Donald Cheng Hong Zou
584118805a Feature Added
Moved key generating to front-end, by using wireguard.js from WireGuard's official repository. Added "Add Peers by bulk" feature.
2022-01-06 15:17:43 -05:00
Donald Zou
68b3f01082 Merge pull request #127 from pgalonza/refactoring
Fixed misprint
2022-01-04 16:41:26 -05:00
Donald Cheng Hong Zou
77a82cb84b Finished adding available IP and related UI adjustment 2022-01-04 16:32:23 -05:00
Galonza Peter
844b19495d fixed misprint 2022-01-03 00:51:22 +03:00
Donald Cheng Hong Zou
b8b721f2bd Delete wgdashboard.db 2022-01-02 14:45:24 -05:00
Donald Cheng Hong Zou
4f93d48ac6 Successfully migrate to SQlite 2022-01-02 14:44:27 -05:00
Donald Zou
02d1bf5a2c Merge pull request #125 from pgalonza/refactoring
Refactoring
2022-01-02 14:41:24 -05:00
Galonza Peter
fb3606c2b7 Refactored
Added docstrings like sphinx style, small refactoring and added TODO
2022-01-02 16:35:39 +03:00
Galonza Peter
a5bc2cd48f wg_conf_path is global variable 2022-01-01 03:20:30 +03:00
Galonza Peter
72151ac6a3 db path is global variable 2022-01-01 02:57:59 +03:00
Donald Cheng Hong Zou
e2c8b3bf02 Removed Semaphore for configuration file 2021-12-31 13:29:37 -05:00
Donald Cheng Hong Zou
06dd641274 Minified all CSS and JS script 2021-12-30 15:21:25 -05:00
Donald Cheng Hong Zou
7d9bf01d27 Cleaned configuration.js
Removed unused functions and duplicated jQuery selector
2021-12-29 23:26:15 -05:00
Donald Cheng Hong Zou
b9553e0a05 Update dashboard.py
Finished refactoring some variable name
2021-12-29 16:15:00 -05:00
Donald Cheng Hong Zou
993dcaf235 Merge branch 'main' of https://github.com/donaldzou/WGDashboard 2021-12-29 15:30:52 -05:00
Donald Cheng Hong Zou
8d7c39bac4 Update dashboard.py
Wrapped all reading and writing to dashboard configuration into functions
2021-12-29 15:29:29 -05:00
Donald Zou
80933ab571 Merge pull request #124 from pgalonza/fix-variables
Fixed variables name
2021-12-29 14:57:53 -05:00
Donald Cheng Hong Zou
b5b30c8119 Update dashboard.py 2021-12-29 14:57:44 -05:00
Galonza Peter
bca06129d9 fixed variables name 2021-12-29 22:56:19 +03:00
Donald Zou
a136b86d81 Merge pull request #122 from pgalonza/change-path
Fixed variable name
2021-12-29 14:20:52 -05:00
Donald Zou
8d555d8ae7 Merge pull request #121 from pgalonza/refactoring
Refactoring
2021-12-29 13:57:21 -05:00
Donald Zou
c22e61add2 Merge branch 'main' into refactoring 2021-12-29 13:57:11 -05:00
Donald Zou
2c83f1ce57 Merge pull request #120 from pgalonza/fix-download
Fixed download configuration file for peer
2021-12-29 12:21:29 -05:00
Donald Cheng Hong Zou
f2750de153 Feature adding and UI adjustment
Added Preshared-Key to all configuration
2021-12-29 12:17:44 -05:00
Galonza Peter
e0934f11d6 Merge branch 'main' into fix-download 2021-12-29 02:00:36 +03:00
Galonza Peter
422924026a fixed variable name 2021-12-29 02:00:18 +03:00
Galonza Peter
332e22e534 Merge branch 'main' into refactoring
# Conflicts:
#	src/dashboard.py
2021-12-29 01:51:41 +03:00
Galonza Peter
c7bf30e18e pylint warnings 2021-12-29 00:59:10 +03:00
Galonza Peter
5ade5176c0 fixed download configuration file for peer 2021-12-28 22:55:52 +03:00
Donald Zou
f9e4fafe9f Merge pull request #96 from pgalonza/change-path
Added the ability to change the path for files to be saved
2021-12-27 21:05:34 -05:00
Donald Zou
201f2ef1f0 Merge branch 'main' into change-path 2021-12-27 21:05:25 -05:00
Donald Zou
32af4fdcca Merge pull request #119 from donaldzou/v3.0-beta
Finishing v3.0
2021-12-27 21:01:24 -05:00
Donald Cheng Hong Zou
d254b4cc39 Finishing v3.0 2021-12-27 21:01:02 -05:00
Galonza Peter
d0ad4e05bf general refactoring 2021-12-28 00:29:26 +03:00
Galonza Peter
c75854cb7b fixed after merge 2021-12-27 00:35:10 +03:00
Galonza Peter
8cc71913b3 fixed read configuration after merge 2021-12-26 23:17:19 +03:00
Galonza Peter
99abe6bcd2 Merge branch 'main' into change-path 2021-12-26 12:58:44 +03:00
Galonza Peter
f5ad7a49ce general refactoring 2021-12-26 12:57:57 +03:00
Donald Cheng Hong Zou
e42ecf3c99 Update wgd.sh 2021-12-25 17:45:47 -05:00
Donald Cheng Hong Zou
b58bf9dc17 Update wgd.sh 2021-12-25 17:40:53 -05:00
Donald Cheng Hong Zou
745c8aaf3e Update wgd.sh 2021-12-25 17:40:08 -05:00
Donald Cheng Hong Zou
0ca73bf80b Update wgd.sh 2021-12-25 17:38:13 -05:00
Donald Cheng Hong Zou
fd76cabd01 Update wgd.sh 2021-12-25 17:37:43 -05:00
Donald Cheng Hong Zou
fcf9300a49 Update wgd.sh 2021-12-25 17:35:36 -05:00
Donald Cheng Hong Zou
649b66ba1a Update wgd.sh 2021-12-25 17:20:52 -05:00
Donald Cheng Hong Zou
b5d73a0861 Delete hi.txt 2021-12-25 17:20:31 -05:00
Donald Cheng Hong Zou
1453996ccb Update wgd.sh 2021-12-25 17:16:50 -05:00
Donald Cheng Hong Zou
5bf9b97d70 Update wgd.sh 2021-12-25 17:15:33 -05:00
Donald Cheng Hong Zou
5c1e46f5a6 Update wgd.sh 2021-12-25 17:13:21 -05:00
Donald Cheng Hong Zou
c2665cbae8 Update wgd.sh 2021-12-25 17:13:08 -05:00
Donald Cheng Hong Zou
3934248a6b Update wgd.sh 2021-12-25 17:08:59 -05:00
Donald Cheng Hong Zou
7ae3bb5601 Testing update function 2021-12-25 17:07:33 -05:00
Donald Cheng Hong Zou
9890d4f3bc Removed using venv for now 2021-12-25 16:29:33 -05:00
Donald Cheng Hong Zou
5b47fc16d9 Update wgd.sh 2021-12-25 16:08:30 -05:00
Galonza Peter
344770a0a4 Merge branch 'main' into change-path
# Conflicts:
#	src/dashboard.py
2021-12-25 23:42:10 +03:00
Donald Cheng Hong Zou
fc11cacd24 Update wgd.sh 2021-12-25 15:20:56 -05:00
Donald Cheng Hong Zou
737ebdd0c6 Update wgd.sh 2021-12-25 15:20:04 -05:00
Galonza Peter
d06dddfa24 Merge branch 'main' into change-path 2021-12-25 23:17:40 +03:00
Donald Cheng Hong Zou
710d631ffe Update wgd.sh 2021-12-25 15:17:07 -05:00
Donald Cheng Hong Zou
7d9a845ca0 Update wgd.sh 2021-12-25 14:48:38 -05:00
Donald Cheng Hong Zou
cc6e5ffb97 Update wgd.sh 2021-12-25 14:46:47 -05:00
Donald Cheng Hong Zou
8d29cb7488 Testing Python venv 2021-12-25 14:44:14 -05:00
Donald Cheng Hong Zou
5c588ea01a Style adjustment
Adjusted style and the status light: Added box-shadow and border for connected peer
2021-12-24 00:20:44 -05:00
Donald Zou
dade9935da Merge pull request #117 from donaldzou/v3.0-Slow-Load-Fix
Slow Performance Fixed
2021-12-24 00:19:26 -05:00
Donald Cheng Hong Zou
34cd3aaaca Slow Performance Fixed 2021-12-23 21:26:24 -05:00
Donald Cheng Hong Zou
78d3f338fb Update dashboard.py 2021-12-22 11:36:29 -05:00
Donald Zou
d801c5911e Merge pull request #91 from pgalonza/todo-ipv6
Add IPv6
2021-12-22 11:35:17 -05:00
Galonza Peter
003743c074 Merge branch 'main' into gunicorn 2021-12-21 22:41:03 +03:00
Galonza Peter
4cef39d3b1 Merge branch 'main' into gunicorn-tmp
# Conflicts:
#	src/wgd.sh
2021-12-21 22:38:31 +03:00
Galonza Peter
83f0df32d7 added thread 2021-12-21 22:23:26 +03:00
Galonza Peter
24f269191c added Let’s Encrypt via certbot 2021-12-21 22:23:13 +03:00
Galonza Peter
94a0d5a0a4 refactored 2021-12-21 22:23:05 +03:00
Galonza Peter
c0fbf4dd0c optimized performance
# Conflicts:
#	src/gunicorn.conf.py
2021-12-21 22:22:37 +03:00
Galonza Peter
37b6161075 fixed check gunicorn process 2021-12-21 22:20:43 +03:00
Galonza Peter
c9b792c370 fixed update variable 2021-12-21 22:20:31 +03:00
Galonza Peter
2103d547a1 fixed initiation for gunicorn 2021-12-21 22:18:36 +03:00
Galonza Peter
3918e220d2 style 2021-12-21 22:17:32 +03:00
Galonza Peter
c3eaaed43b fixed stop 2021-12-21 22:17:07 +03:00
Galonza Peter
e0bf648076 added paths when command as root 2021-12-21 22:16:40 +03:00
Galonza Peter
bb298164e4 added gunicorn in requirements 2021-12-21 22:15:55 +03:00
Galonza Peter
898694b9be fixed gunicorn start and added stop 2021-12-21 22:15:41 +03:00
Galonza Peter
f0f9ac92e6 added gunicorn start 2021-12-21 22:15:23 +03:00
Galonza Peter
ea0229a8ab added wsgi for gunicorn 2021-12-21 22:15:08 +03:00
Donald Zou
66fc7cd91d Merge pull request #112 from reafian/main
Added MTU and PersistentKeepalive to QR code and download files
2021-12-21 14:12:42 -05:00
Donald Zou
c11d4f01b1 Merge pull request #114 from donaldzou/all-contributors/add-marneu
docs: add marneu as a contributor for code
2021-12-21 13:54:03 -05:00
Donald Zou
a8f498f292 Merge pull request #113 from donaldzou/marneu-main
Marneu main
2021-12-21 13:53:51 -05:00
allcontributors[bot]
6854e2275f docs: update .all-contributorsrc [skip ci] 2021-12-21 18:53:40 +00:00
allcontributors[bot]
6662e79ac5 docs: update README.md [skip ci] 2021-12-21 18:53:39 +00:00
Donald Cheng Hong Zou
9e77430634 Added Slash 2021-12-21 13:52:08 -05:00
Richard Newton
b8e070824a Update get_conf.html
Added MTU and PersistentKeepalive back into QR Code.

Update dashboard.py

Added MTU and PersistentKeepalive to downloaded file.
2021-12-21 16:48:11 +00:00
Donald Cheng Hong Zou
304d4293f3 Update wgd.sh 2021-12-20 19:54:53 -05:00
Donald Zou
a03e7f5b53 Merge pull request #111 from donaldzou/marneu-main
Marneu main
2021-12-19 18:18:50 -05:00
Donald Zou
ea57c8cad4 Merge branch 'main' into marneu-main 2021-12-19 18:18:45 -05:00
Donald Cheng Hong Zou
280802cf74 Update .gitignore 2021-12-19 18:16:45 -05:00
Donald Cheng Hong Zou
fbce01d846 Update .gitignore 2021-12-19 18:15:51 -05:00
Markus Neubauer
591b60c336 Update wgd.sh
a more automated approach to systemctl
2021-12-19 18:13:15 -05:00
Markus Neubauer
78ef870d91 Update wg-dashboard.service
A more automated approach
2021-12-19 18:13:15 -05:00
Donald Zou
bc19d82aed Merge pull request #105 from marneu/patch-1
An automated way to a systemctl unit
2021-12-19 18:07:35 -05:00
Donald Zou
79b41e1056 Update README.md 2021-12-06 14:15:22 -05:00
Markus Neubauer
805f03d231 Update .gitignore 2021-11-25 16:03:09 +01:00
Donald Zou
62acc1081f Update README.md 2021-11-19 14:48:46 -05:00
Donald Zou
257a2c2d9f Update README.md 2021-11-19 14:45:37 -05:00
Donald Zou
ed9b05cdf9 Update README.md 2021-11-19 14:44:40 -05:00
Donald Zou
2cc6fbd643 Update README.md 2021-11-10 11:40:46 -05:00
Galonza Peter
bdfc260dd2 fixed 2021-10-24 13:32:21 +03:00
Galonza Peter
7ad65956c9 fixed, if dir is exists 2021-10-24 12:45:08 +03:00
Galonza Peter
b3dc60b7ca fixed check ipv6 dns 2021-10-24 12:05:14 +03:00
Galonza Peter
bed08109fa not needed 2021-10-24 02:10:20 +03:00
Galonza Peter
06cedec090 mistake 2021-10-24 01:58:59 +03:00
Galonza Peter
1d2c468d03 fixed, when database does not create, if no directory 2021-10-24 01:51:04 +03:00
Galonza Peter
e84616f5f5 added the ability to change the path for files to be saved 2021-10-24 00:56:34 +03:00
Galonza Peter
caa9a90476 added dual-stack 2021-10-17 22:55:08 +03:00
Galonza Peter
62be683dd8 fixed search ipv6 in the string with comma 2021-10-17 21:13:18 +03:00
Galonza Peter
ffa63b3f82 completed todo. Added IPv6 2021-10-17 20:22:27 +03:00
Galonza Peter
c5a7788394 optimized pattern 2021-10-17 20:01:16 +03:00
Donald Zou
99c42ff2c1 Merge pull request #90 from pike00/pike00-update-readme
Updated README to include username/password
2021-10-14 10:14:38 -04:00
Will Pike
fd2b7bb917 Updated README to include username/password
Updated Installation instructions to include default username/password in step 6 on login. It is located in the 'Dashboard Configuration' section, however for readability/ease of setting up the server, I added the default login to the install instructions.
2021-10-14 10:10:17 -04:00
Donald Cheng Hong Zou
922a494df0 Finished 2021-09-09 12:50:38 -04:00
Donald Cheng Hong Zou
c478ec0095 Update README.md 2021-09-09 12:45:09 -04:00
Donald Cheng Hong Zou
3abbe17910 Naming changed 2021-09-09 12:43:49 -04:00
Donald Zou
349076599f Merge pull request #80 from ikidd/patch-1
Fixed service unit file spelling error
2021-09-09 12:35:44 -04:00
ikidd
92d299507f Fixed service unit file spelling error 2021-09-08 20:36:53 -06:00
Donald Cheng Hong Zou
6f2d61e918 Update README.md 2021-09-08 22:02:33 -04:00
Donald Cheng Hong Zou
ed13babf6a Merge branch 'main' of https://github.com/donaldzou/Wireguard-Dashboard into main 2021-09-08 22:01:01 -04:00
Donald Cheng Hong Zou
80db82eb93 Update dashboard.py 2021-09-08 22:00:53 -04:00
Donald Zou
8d20334d6e Update README.md 2021-09-08 21:59:15 -04:00
Donald Zou
03a95ad018 Merge pull request #79 from donaldzou/v2.3.1
Update README.md
2021-09-08 21:58:02 -04:00
Donald Cheng Hong Zou
aa59cc8af0 Update README.md 2021-09-08 21:57:50 -04:00
Donald Zou
80d63c2c89 Merge pull request #78 from donaldzou/v2.3.1
Changed dashboard naming to WGDashboard
2021-09-08 21:56:48 -04:00
Donald Cheng Hong Zou
03a4acd72b Changed dashboard naming to WGDashboard 2021-09-08 21:56:31 -04:00
Donald Cheng Hong Zou
53118cf660 Merge branch 'main' of https://github.com/donaldzou/Wireguard-Dashboard into main 2021-09-08 16:58:40 -04:00
Donald Cheng Hong Zou
16b1baf81e Update README.md 2021-09-08 16:58:36 -04:00
Donald Zou
960e0d0965 Update README.md 2021-09-08 14:50:26 -04:00
Donald Zou
8f41222e43 Merge pull request #77 from donaldzou/v2.3
Update README.md
2021-09-08 12:43:09 -04:00
Donald Cheng Hong Zou
e7bc447166 Update README.md 2021-09-08 12:42:50 -04:00
Donald Zou
1736359615 Merge pull request #76 from donaldzou/v2.3
V2.3
2021-09-08 12:42:11 -04:00
Donald Cheng Hong Zou
58784b1e22 v2.3 Final Commit 2021-09-08 12:39:25 -04:00
Donald Zou
07f2bfd279 Merge pull request #75 from donaldzou/all-contributors/add-davejlong
docs: add davejlong as a contributor for code
2021-09-08 11:24:04 -04:00
allcontributors[bot]
6d62db2890 docs: update .all-contributorsrc [skip ci] 2021-09-08 15:23:38 +00:00
allcontributors[bot]
a1b23c4aab docs: update README.md [skip ci] 2021-09-08 15:23:37 +00:00
Donald Zou
e272a096da Merge pull request #74 from donaldzou/all-contributors/add-reafian
docs: add reafian as a contributor for code
2021-09-05 16:38:57 -04:00
allcontributors[bot]
b8ca24c5bf docs: update .all-contributorsrc [skip ci] 2021-09-05 20:38:34 +00:00
allcontributors[bot]
a05d09f0de docs: update README.md [skip ci] 2021-09-05 20:38:33 +00:00
Donald Zou
1807a79c4f Create FUNDING.yml 2021-09-04 17:16:31 -04:00
Donald Cheng Hong Zou
77f6826af9 Adjusted UI and edit readme 2021-09-03 17:32:51 -04:00
Donald Cheng Hong Zou
7e39eedf35 Update wgd.sh 2021-09-03 14:40:57 -04:00
Donald Cheng Hong Zou
0b4b48ceae Update wgd.sh 2021-09-03 14:30:13 -04:00
Donald Cheng Hong Zou
dad931e898 Update wgd.sh 2021-09-03 14:26:27 -04:00
Donald Cheng Hong Zou
2e387730cb Update wgd.sh 2021-09-03 14:26:05 -04:00
Donald Cheng Hong Zou
c9b6775793 Update wgd.sh 2021-09-03 14:24:38 -04:00
Donald Cheng Hong Zou
a6eaf30c2b Update wgd.sh 2021-09-03 14:24:14 -04:00
Donald Cheng Hong Zou
6f91d199d8 Update wgd.sh 2021-09-03 14:23:08 -04:00
Donald Cheng Hong Zou
7ea486307a Update wgd.sh 2021-09-03 14:22:14 -04:00
Donald Cheng Hong Zou
37d254c827 Update wgd.sh 2021-09-03 13:55:41 -04:00
Donald Cheng Hong Zou
b4014eb317 Update wgd.sh 2021-09-03 13:55:03 -04:00
Donald Cheng Hong Zou
c7824fa184 Update wgd.sh 2021-09-03 13:52:23 -04:00
Donald Cheng Hong Zou
13c5bd45f9 Update wgd.sh 2021-09-02 22:52:22 -04:00
Donald Cheng Hong Zou
465d102bd0 Update wgd.sh 2021-09-02 22:52:01 -04:00
Donald Cheng Hong Zou
93007cd56c Update wgd.sh 2021-09-02 22:27:05 -04:00
Donald Cheng Hong Zou
8367576dc8 Update wgd.sh 2021-09-02 22:24:40 -04:00
Donald Cheng Hong Zou
dbc06145b3 Update wgd.sh 2021-09-02 22:24:07 -04:00
Donald Cheng Hong Zou
4efcf0e7c8 Update wgd.sh 2021-09-02 22:13:00 -04:00
Donald Cheng Hong Zou
0a4bbc1106 Update wgd.sh 2021-09-02 22:09:06 -04:00
Donald Cheng Hong Zou
b08c1f6699 Updated requirement.txt and wgd.sh 2021-09-02 21:56:50 -04:00
Donald Cheng Hong Zou
6828b905ef Added checking if peers removed directly from config file by not from dashboard, and fixed DNS did not get check from editing peers 2021-08-24 21:04:01 -04:00
Donald Zou
f72b405f6d Merge pull request #64 from Kofl/patch-1
Update typo3 fix
2021-08-24 20:19:22 -04:00
Donald Zou
27ae99864c Merge pull request #71 from donaldzou/v2.2.2
v2.2.2 Merge
2021-08-24 20:16:21 -04:00
Donald Cheng Hong Zou
82ba15ba5a Small fix on HTML files 2021-08-24 20:15:28 -04:00
Kofl
af01db7bc9 Update typo3 fix
TOTAL RECIEVED replaced with TOTAL RECEIVED
2021-08-18 21:46:06 +02:00
Donald Cheng Hong Zou
6ec0a65d0c Update README.md 2021-08-16 15:32:14 -04:00
Donald Zou
c32ccfc9ed Merge pull request #51 from donaldzou/v2.2.1
V2.2.1
2021-08-16 15:29:49 -04:00
Donald Cheng Hong Zou
ef818388c8 Update dashboard.py 2021-08-16 15:28:43 -04:00
Donald Cheng Hong Zou
f5aa45a3e1 Update README.md 2021-08-16 15:27:10 -04:00
Donald Cheng Hong Zou
b7a047a743 Bug fixed 2021-08-16 15:26:15 -04:00
Donald Zou
05ec985453 Update README.md 2021-08-16 12:23:56 -04:00
Donald Cheng Hong Zou
88ed9c3e79 Update README.md 2021-08-15 02:23:35 -04:00
Donald Zou
3fb03c8dfe Update README.md 2021-08-15 00:16:36 -04:00
Donald Zou
ff304027cd Merge pull request #46 from donaldzou/v2.2-beta
V2.2 pull request
2021-08-14 23:57:07 -04:00
Donald Cheng Hong Zou
3140d46c94 Finalizing the documentation 2021-08-14 23:43:30 -04:00
Donald Cheng Hong Zou
9d476af384 v2.2beta almost done!!!!! 2021-08-14 23:30:05 -04:00
Donald Cheng Hong Zou
0d380672f3 v2.2 beta 4 2021-08-14 17:13:16 -04:00
Donald Cheng Hong Zou
e2fb8dca5b Update README.md 2021-08-06 00:08:30 -04:00
Donald Cheng Hong Zou
eaba4c90c5 Edit Readme 2021-08-06 00:00:32 -04:00
Donald Cheng Hong Zou
b4ca7373f3 Update Readme 2021-08-05 23:47:18 -04:00
Donald Cheng Hong Zou
905c068ed5 v2.2-beta4
Finished testing the QR code function and finish debugging the peer file download function.
2021-08-05 23:15:50 -04:00
Donald Cheng Hong Zou
02886dd420 Remove public and private key txt 2021-08-05 01:39:32 -04:00
Donald Cheng Hong Zou
5bf7754d6c Update README.md 2021-08-05 01:21:44 -04:00
Donald Cheng Hong Zou
7a0a3055c4 Update README.md 2021-08-05 01:16:22 -04:00
Donald Cheng Hong Zou
bf73efb3b4 Update README.md 2021-08-05 01:15:17 -04:00
Donald Cheng Hong Zou
b4da212158 Update README.md 2021-08-05 01:07:10 -04:00
Donald Cheng Hong Zou
4a04d42600 v2.2-beta3
Finished QR code function and starting to test.
2021-08-05 00:45:15 -04:00
Donald Cheng Hong Zou
42bbfbe538 v2.2-beta update 2021-08-03 18:45:40 -04:00
Donald Cheng Hong Zou
31ec2430c4 Update README.md 2021-08-03 18:06:43 -04:00
Donald Cheng Hong Zou
f0b56c9f1d Update README.md 2021-08-03 18:06:22 -04:00
Donald Zou
6f9f01c991 Update README.md 2021-07-12 16:35:07 -04:00
Donald Cheng Hong Zou
b822ccb19d Update README.md 2021-07-11 13:07:27 -04:00
Donald Zou
dcb11671f3 Update README.md 2021-07-11 12:49:58 -04:00
Donald Zou
282599d36f Update README.md 2021-07-05 16:09:02 -04:00
Donald Zou
f11acdc68f Update README.md 2021-07-05 15:35:23 -04:00
Donald Zou
971e127cfd Update README.md 2021-07-03 10:30:54 -04:00
Donald Zou
9b2c59d529 Delete wg0.conf 2021-07-02 14:35:34 -04:00
Donald Zou
14dc049a66 Update README.md 2021-07-02 14:31:33 -04:00
Donald Zou
d3d0e7c615 Merge pull request #24 from donaldzou/v2.1-beta
v2.1 Merge
2021-07-02 13:28:36 -04:00
Donald Zou
89423ccb8b Merge branch 'main' into v2.1-beta 2021-07-02 13:28:29 -04:00
Donald Cheng Hong Zou
746f31f70c Update README.md 2021-07-02 13:25:35 -04:00
Donald Cheng Hong Zou
3aa9eab2bd Commit June 2 2021-07-02 13:23:04 -04:00
Donald Zou
1bcc8fed5e Update README.md 2021-07-02 12:00:12 -04:00
Donald Zou
1213a030e3 Update README.md 2021-05-13 19:54:37 -04:00
Donald Zou
3b2344b477 Delete .github/workflows directory 2021-05-13 19:51:54 -04:00
Donald Zou
c53b0fe2d4 Create main.yml 2021-05-13 19:38:22 -04:00
Donald Cheng Hong Zou
0a92269456 v2.0-beta-6 Commit 2021-05-13 18:47:59 -04:00
Donald Cheng Hong Zou
1cb8906893 v2.0-beta-6 Commit 2021-05-13 18:21:10 -04:00
Donald Cheng Hong Zou
f76579fe5e v2.0-beta-6 Commit 2021-05-13 18:00:40 -04:00
Donald Zou
db05ff5e98 Update README.md 2021-05-08 12:35:22 -04:00
Donald Zou
a05d6979ec Update README.md 2021-05-07 15:27:37 -04:00
Donald Zou
be40d17172 Update README.md 2021-05-07 15:25:23 -04:00
Donald Zou
9e11339979 Update README.md 2021-05-06 11:20:28 -04:00
Donald Zou
a56129afe0 Update README.md 2021-05-05 17:25:23 -04:00
Donald Zou
8933ea999c Update README.md 2021-05-05 15:52:13 -04:00
Donald Zou
ae3b8f6494 Merge pull request #19 from donaldzou/v2.0-beta-6
v2.0 merge to main
2021-05-05 15:29:41 -04:00
Donald Cheng Hong Zou
33487ee03f v2.0-beta-6 Commit 2021-05-05 15:16:32 -04:00
Donald Cheng Hong Zou
58ecae1162 v2.0-beta-6 Commit 2021-05-05 15:09:34 -04:00
Donald Cheng Hong Zou
8777da10e4 v2.0-beta-1 Commit 2021-05-05 14:59:00 -04:00
Donald Zou
4ea971c1b9 v2.0-beta-1 Commit 2021-05-05 14:52:28 -04:00
Donald Cheng Hong Zou
0fa4759c3a Update 2021-05-05 14:38:10 -04:00
Donald Cheng Hong Zou
fe7b9730d3 Update README.md 2021-05-04 21:41:13 -04:00
Donald Cheng Hong Zou
040f6a8393 Commit 2021-05-04 21:26:40 -04:00
Donald Cheng Hong Zou
b17575b197 Update requirements.txt 2021-05-04 15:55:52 -04:00
Donald Cheng Hong Zou
dee4f757e7 Upload README pictures 2021-05-04 15:42:04 -04:00
Donald Cheng Hong Zou
5ae08d02ab Merge branch 'main' of https://github.com/donaldzou/Wireguard-Dashboard into main 2021-05-04 02:10:32 -04:00
Donald Cheng Hong Zou
b83aec4965 v2.0-beta-2 Commit 2021-05-04 02:10:06 -04:00
Donald Zou
68c4837780 Merge pull request #18 from donaldzou/testing
Update .gitignore
2021-05-04 01:39:51 -04:00
Donald Cheng Hong Zou
46fe5e99fe Update .gitignore 2021-05-04 01:39:32 -04:00
Donald Zou
9ffb537cfb Delete wg-dashboard.ini 2021-05-04 01:39:14 -04:00
Donald Zou
cc7e6f852d Merge pull request #17 from donaldzou/testing
v2.0-beta1 Merge
2021-05-04 01:37:27 -04:00
Donald Cheng Hong Zou
f83e99c62c Merge branch 'testing' of https://github.com/donaldzou/Wireguard-Dashboard into testing 2021-05-04 01:36:32 -04:00
Donald Zou
bc8b8e7982 v2.0-beta-1 Commit 2021-05-04 01:32:34 -04:00
Donald Zou
3b761ca2a8 Update README.md 2021-05-04 01:20:20 -04:00
Donald Cheng Hong Zou
edc21f9830 Update README.md 2021-04-12 23:18:57 -04:00
Donald Cheng Hong Zou
d025ec4dff Push Logo 2021-04-12 23:16:12 -04:00
Donald Cheng Hong Zou
ab334a393f Logo 2021-04-12 23:15:53 -04:00
Donald Cheng Hong Zou
2a9044086f Push Logo 2021-04-12 23:14:53 -04:00
Donald Zou
c3038ec60c Merge pull request #14 from donaldzou/all-contributors/add-tonjo
docs: add tonjo as a contributor
2021-04-12 22:30:23 -04:00
allcontributors[bot]
0fa827ba00 docs: update .all-contributorsrc [skip ci] 2021-04-13 02:29:48 +00:00
allcontributors[bot]
26002fc372 docs: update README.md [skip ci] 2021-04-13 02:29:47 +00:00
Donald Zou
59acba8c08 Update README.md 2021-04-12 22:29:00 -04:00
Donald Zou
4c5ee45fdb Update README.md 2021-04-12 22:28:30 -04:00
Donald Zou
6062120d0d Merge pull request #13 from donaldzou/all-contributors/add-antonioag95
docs: add antonioag95 as a contributor
2021-04-12 22:27:54 -04:00
allcontributors[bot]
cd3ffb2126 docs: create .all-contributorsrc [skip ci] 2021-04-13 02:26:56 +00:00
allcontributors[bot]
a8c11cc445 docs: update README.md [skip ci] 2021-04-13 02:26:55 +00:00
Donald Zou
fcb89aac6b Update README.md 2021-04-12 11:05:19 -04:00
Donald Zou
3704d8f6e7 Merge pull request #11 from tonjo/patch-1
Avoid error when `AllowedIPs` is not defined for a peer.
2021-04-12 11:01:38 -04:00
Donald Zou
8c75d172c5 Update issue templates 2021-04-10 12:49:34 -04:00
Donald Zou
5687302081 Update issue templates 2021-04-10 12:47:18 -04:00
Donald Zou
9020b16a9a Update README.md 2021-04-10 00:49:32 -04:00
tonjo
d5462df4da Update dashboard.py 2021-04-09 11:15:25 +02:00
tonjo
9021ca31af Avoid error when AllowedIps is not defined 2021-04-09 11:08:41 +02:00
Donald Zou
83cb8b7f03 Update README.md 2021-04-09 00:42:42 -04:00
Donald Cheng Hong Zou
baf2658eb8 v20210409 2021-04-09 00:07:37 -04:00
Donald Zou
3f19b05d28 Update README.md 2021-04-07 17:25:21 -04:00
Donald Zou
5e6be1f830 Update README.md 2021-04-07 15:58:00 -04:00
Donald Zou
14d1a3038f Update README.md 2021-04-07 12:19:23 -04:00
Donald Zou
6a15e2e587 Update README.md 2021-04-07 12:15:45 -04:00
Donald Zou
0679805728 Update README.md 2021-04-07 12:01:35 -04:00
Donald Zou
62a03419a5 Update README.md 2021-04-07 11:52:24 -04:00
Donald Zou
81f6b0767e Update README.md 2021-04-07 11:52:03 -04:00
Donald Zou
c7ca5c749b Update README.md 2021-04-06 19:52:33 -04:00
Donald Zou
1daac09b7c Update README.md 2021-04-06 19:51:50 -04:00
Donald Zou
4d3dd68ac0 Merge pull request #5 from antonioag95/main
- Fixed a bug where a peer without last handshake date was showing wrong date in UI
- Hidden and temp `.conf` files will be excluded from interfaces list
2021-04-06 19:43:52 -04:00
Donald Zou
de9e4a6270 Update README.md 2021-04-06 16:51:37 -04:00
Donald Zou
bf8f347245 Update README.md 2021-04-06 15:03:49 -04:00
Donald Zou
a70d09ac7a Update README.md 2021-04-06 10:14:11 -04:00
Donald Zou
919b344da4 Update README.md 2021-04-05 22:41:55 -04:00
antonioag95
44f013a678 Update dashboard.py
- Fixed a bug where a peer without last handshake date was showing wrong date in webui
- Hidden and temp conf files will be excluded from interfaces list
2021-04-03 23:48:07 +02:00
Donald Zou
742191111b Merge pull request #4 from donaldzou/v20210403-alpha
v20210403 Bug Fixed
2021-04-03 14:31:11 -04:00
Donald Cheng Hong Zou
8dc0031475 Update dashboard.py 2021-04-03 14:30:02 -04:00
Donald Cheng Hong Zou
19fb90a300 Update dashboard.py 2021-04-03 14:10:07 -04:00
Donald Cheng Hong Zou
0c3960d8d9 Commit 2021-04-03 14:06:21 -04:00
Donald Cheng Hong Zou
7e770b9b5d v20210402.2 2021-04-02 22:22:41 -04:00
70 changed files with 12812 additions and 706 deletions

61
.all-contributorsrc Normal file
View File

@@ -0,0 +1,61 @@
{
"files": [
"README.md"
],
"imageSize": 100,
"commit": false,
"contributors": [
{
"login": "antonioag95",
"name": "antonioag95",
"avatar_url": "https://avatars.githubusercontent.com/u/30556866?v=4",
"profile": "https://github.com/antonioag95",
"contributions": [
"test",
"code"
]
},
{
"login": "tonjo",
"name": "tonjo",
"avatar_url": "https://avatars.githubusercontent.com/u/4726289?v=4",
"profile": "https://github.com/tonjo",
"contributions": [
"code"
]
},
{
"login": "reafian",
"name": "Richard Newton",
"avatar_url": "https://avatars.githubusercontent.com/u/11992416?v=4",
"profile": "https://github.com/reafian",
"contributions": [
"code"
]
},
{
"login": "davejlong",
"name": "David Long",
"avatar_url": "https://avatars.githubusercontent.com/u/175317?v=4",
"profile": "http://www.davejlong.com",
"contributions": [
"code"
]
},
{
"login": "marneu",
"name": "Markus Neubauer",
"avatar_url": "https://avatars.githubusercontent.com/u/5978293?v=4",
"profile": "http://www.std-soft.com",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7,
"projectName": "WGDashboard",
"projectOwner": "donaldzou",
"repoType": "github",
"repoHost": "https://github.com",
"skipCi": true
}

3
.github/FUNDING.yml vendored Normal file
View File

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

28
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,28 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
**Describe The Problem**
A clear and concise description of what the bug is.
**Expected Error / Traceback**
```
Please provide the error traceback here
```
**To Reproduce**
Please provide how you run the dashboard
**OS Information:**
- OS: [e.g. Ubuntu 18.02]
- Python Version: [e.g v3.7]
**Sample of your `.conf` file**
```
Please provide a sample of your configuration file that you are having problem with. You can replace your public key and private key to ABCD...
```

View File

@@ -0,0 +1,14 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.

13
.gitignore vendored
View File

@@ -5,3 +5,16 @@ wg.db
*.json
.idea
src/test.py
tmp
__pycache__
src/wg-dashboard.ini
src/static/pic.xd
*.conf
private_key.txt
public_key.txt
venv/**
log/**
release/*
*.db
src/db/wgdashboard.db
.jshintrc

6
.jshintrc Normal file
View File

@@ -0,0 +1,6 @@
{
"globals": {
"peers": true
},
"esversion": 7
}

518
README.md
View File

@@ -1,49 +1,499 @@
# Wireguard Dashboard
Monitoring Wireguard is not convinient, need to login into server and type `wg show`. That's why this platform is being created, to view all configurations in a more straight forward way.
<p align="center">
<img alt="WGDashboard" src="img/logo.png" width="128">
</p>
<h1 align="center">WGDashboard</h1>
## 💡Features
- Add peers in configuration
- Manage peer names
- Delete peers
- And many more coming up! Welcome to contribute to this project!
<p align="center">
<img src="http://ForTheBadge.com/images/badges/made-with-python.svg">
</p>
<p align="center">
<a href="https://github.com/donaldzou/wireguard-dashboard/releases/latest"><img src="https://img.shields.io/github/v/release/donaldzou/wireguard-dashboard"></a>
<a href="https://wakatime.com/badge/user/45f53c7c-9da9-4cb0-85d6-17bd38cc748b/project/5334ae20-e9a6-4c55-9fea-52d4eb9dfba6"><img src="https://wakatime.com/badge/user/45f53c7c-9da9-4cb0-85d6-17bd38cc748b/project/5334ae20-e9a6-4c55-9fea-52d4eb9dfba6.svg" alt="wakatime"></a>
</p>
<p align="center">Monitoring WireGuard is not convinient, need to login into server and type <code>wg show</code>. That's why this platform is being created, to view all configurations and manage them in a easier way.</p>
<p align="center"><small>Note: This project is not affiliate to the official WireGuard Project ;)</small></p>
## 📝Requirement
## 📣 What's New: v3.0
- Ubuntu 18.04.1 LTS, other OS might work, but haven't test yet.
- **Wireguard**
- Configuration files under **/etc/wireguard**
- Python 3.7
- 🎉 **New Features**
- **Moved from TinyDB to SQLite**: SQLite provide a better performance and loading speed when getting peers! Also avoided crashing the database due to **race condition**.
- **Added Gunicorn WSGI Server**: This could provide more stable on handling HTTP request, and more flexibility in the future (such as HTTPS support). **BIG THANKS to @pgalonza :heart: **
- **Add Peers by Bulk: ** User can add peers by bulk, just simply set the amount and click add.
- **Delete Peers by Bulk**: User can delete peers by bulk, without deleting peers one by one.
- **Download Peers in Zip**: User can download all *downloadable* peers in a zip.
- **Added Pre-shared Key to peers:** Now each peer can add with a pre-shared key to enhance security. Previously added peers can add the pre-shared key through the peer setting button.
- **Redirect Back to Previous Page:** The dashboard will now redirect you back to your previous page if the current session got timed out and you need to sign in again.
- 🪚 **Bug Fixed**
- [IP Sorting range issues #99](https://github.com/donaldzou/WGDashboard/issues/99) [❤️ @barryboom]
- [INvalid character written to tunnel json file #108](https://github.com/donaldzou/WGDashboard/issues/108) [❤️ @ ikidd]
- [Add IPv6 #91](https://github.com/donaldzou/WGDashboard/pull/91) [❤️ @ pgalonza]
- [Added MTU and PersistentKeepalive to QR code and download files #112](https://github.com/donaldzou/WGDashboard/pull/112) [:heart: @reafian]
- **And many other bugs provided by our beloved users** :heart:
- **🧐 Other Changes**
- **Key generating moved to front-end**: No longer need to use the server's WireGuard to generate keys, thanks to the `wireguard.js` from the [official repository](https://git.zx2c4.com/wireguard-tools/tree/contrib/keygen-html/wireguard.js)!
- **Peer transfer calculation**: each peer will now show all transfer amount (previously was only showing transfer amount from the last configuration start-up).
- **UI adjustment on running peers**: peers will have a new style indicating that it is running.
- **`wgd.sh` finally can update itself**: So now user could update the whole dashboard from `wgd.sh`, with the `update` command.
- **Minified JS and CSS files**: Although only a small changes on the file size, but I think is still a good practice to save a bit of bandwidth ;)
## 🛠Install
*And many other small changes for performance and bug fixes! :laughing:*
**1. Install Python Dependencies**
> If you have any other brilliant ideas for this project, please shout it in here [#129](https://github.com/donaldzou/WGDashboard/issues/129) :heart:
```
$ python3 -m pip install flask tinydb
<hr>
## Table of Content
- [💡 Features](#-features)
- [📝 Requirement](#-requirement)
- [🛠 Install](#-install)
- [🪜 Usage](#-usage)
- [Start/Stop/Restart WGDashboard](#startstoprestart-wgdashboard)
- [Autostart WGDashboard on boot](#autostart-wgdashboard-on-boot--v22)
- [✂️ Dashboard Configuration](#%EF%B8%8F-dashboard-configuration)
- [Dashboard Configuration file](#dashboard-configuration-file)
- [Generating QR code and peer configuration file (.conf)](#generating-qr-code-and-peer-configuration-file-conf)
- [❓ How to update the dashboard?](#-how-to-update-the-dashboard)
- [🔍 Screenshot](#-screenshot)
- [⏰ Changelog](#--changelog)
- [🛒 Dependencies](#-dependencies)
- [✨ Contributors](#-contributors)
## 💡 Features
- **No need to re-configure existing WireGuard configuration! It can search for existed configuration files.**
- Easy to use interface, provided username and password protection to the dashboard
- Add peers and edit (Allowed IPs, DNS, Private Key...)
- View peers and configuration real time details (Data Usage, Latest Handshakes...)
- Share your peer configuration with QR code or file download
- Testing tool: Ping and Traceroute to your peer's ip
- **And more functions are coming up!**
## 📝 Requirement
- Recommend the following OS, tested by our beloved users:
- [x] Ubuntu 18.04.1 LTS - 20.04.1 LTS [@Me]
- [x] Debian GNU/Linux 10 (buster) [❤️ @[robchez](https://github.com/robchez)]
- [x] AlmaLinux 8.4 (Electric Cheetah) [❤️ @[barry-smithjr](https://github.com/)]
- [x] CentOS 7 [❤️ @[PrzemekSkw](https://github.com/PrzemekSkw)]
> **If you have tested on other OS and it works perfectly please provide it to me in [#31](https://github.com/donaldzou/wireguard-dashboard/issues/31). Thank you!**
- **WireGuard** and **WireGuard-Tools (`wg-quick`)** are installed.
> Don't know how? Check this <a href="https://www.wireguard.com/install/">official documentation</a>
- Configuration files under **`/etc/wireguard`**, but please note the following sample
```ini
[Interface]
...
SaveConfig = true
# Need to include this line to allow WireGuard Tool to save your configuration,
# or if you just want it to monitor your WireGuard Interface and don't need to
# make any changes with the dashboard, you can set it to false.
[Peer]
PublicKey = abcd1234
AllowedIPs = 1.2.3.4/32
# Must have for each peer
```
- Python 3.7+ & Pip3
- Browser support CSS3 and ES6
## 🛠 Install
1. Download WGDashboard
```shell
git clone -b v3.0 https://github.com/donaldzou/WGDashboard.git wgdashboard
2. Open the WGDashboard folder
```shell
cd wgdashboard/src
```
3. Install WGDashboard
```shell
sudo chmod u+x wgd.sh
sudo ./wgd.sh install
```
4. Give read and execute permission to root of the WireGuard configuration folder, you can change the path if your configuration files are not stored in `/etc/wireguard`
```shell
sudo chmod -R 755 /etc/wireguard
```
5. Run WGDashboard
```shell
./wgd.sh start
```
**Note**:
> For [`pivpn`](https://github.com/pivpn/pivpn) user, please use `sudo ./wgd.sh start` to run if your current account does not have the permission to run `wg show` and `wg-quick`.
6. Access dashboard
Access your server with port `10086` (e.g. http://your_server_ip:10086), using username `admin` and password `admin`. See below how to change port and ip that the dashboard is running with.
## 🪜 Usage
#### Start/Stop/Restart WGDashboard
```shell
cd wgdashboard/src
-----------------------------
./wgd.sh start # Start the dashboard in background
-----------------------------
./wgd.sh debug # Start the dashboard in foreground (debug mode)
-----------------------------
./wgd.sh stop # Stop the dashboard
-----------------------------
./wgd.sh restart # Restart the dasboard
```
**2. Install Wireguard Dashboard**
#### Autostart WGDashboard on boot (>= v2.2)
```
$ git clone https://github.com/donaldzou/Wireguard-Dashboard.git
$ cd Wireguard-Dashboard/src
$ python3 dashboard.py
In the `src` folder, it contained a file called `wg-dashboard.service`, we can use this file to let our system to autostart the dashboard after reboot. The following guide has tested on **Ubuntu**, most **Debian** based OS might be the same, but some might not. Please don't hesitate to provide your system if you have tested the autostart on another system.
1. Changing the directory to the dashboard's directory
```shell
cd wgdashboard/src
```
2. Get the full path of the dashboard's directory
```shell
pwd
#Output: /root/wgdashboard/src
```
For this example, the output is `/root/wireguard-dashboard/src`, your path might be different since it depends on where you downloaded the dashboard in the first place. **Copy the the output to somewhere, we will need this in the next step.**
3. Edit the service file, the service file is located in `wireguard-dashboard/src`, you can use other editor you like, here will be using `nano`
```shell
nano wg-dashboard.service
```
You will see something like this:
```ini
[Unit]
After=network.service
[Service]
WorkingDirectory=<your dashboard directory full path here>
ExecStart=/usr/bin/python3 <your dashboard directory full path here>/dashboard.py
Restart=always
[Install]
WantedBy=default.target
```
Now, we need to replace both `<your dashboard directory full path here>` to the one you just copied from step 2. After doing this, the file will become something like this, your file might be different:
```ini
[Unit]
After=netword.service
[Service]
WorkingDirectory=/root/wgdashboard/src
ExecStart=/usr/bin/python3 /root/wgdashboard/src/dashboard.py
Restart=always
[Install]
WantedBy=default.target
```
**Be aware that after the value of `WorkingDirectory`, it does not have a `/` (slash).** And then save the file after you edited it
4. Copy the service file to systemd folder
```bash
$ cp wg-dashboard.service /etc/systemd/system/wg-dashboard.service
```
To make sure you copy the file successfully, you can use this command `cat /etc/systemd/system/wg-dashboard.service` to see if it will output the file you just edited.
5. Enable the service
```bash
$ sudo chmod 664 /etc/systemd/system/wg-dashboard.service
$ sudo systemctl daemon-reload
$ sudo systemctl enable wg-dashboard.service
$ sudo systemctl start wg-dashboard.service # <-- To start the service
```
6. Check if the service run correctly
```bash
$ sudo systemctl status wg-dashboard.service
```
And you should see something like this
```shell
● wg-dashboard.service
Loaded: loaded (/etc/systemd/system/wg-dashboard.service; enabled; vendor preset: enabled)
Active: active (running) since Tue 2021-08-03 22:31:26 UTC; 4s ago
Main PID: 6602 (python3)
Tasks: 1 (limit: 453)
Memory: 26.1M
CGroup: /system.slice/wg-dashboard.service
└─6602 /usr/bin/python3 /root/wgdashboard/src/dashboard.py
Aug 03 22:31:26 ubuntu-wg systemd[1]: Started wg-dashboard.service.
Aug 03 22:31:27 ubuntu-wg python3[6602]: * Serving Flask app "WGDashboard" (lazy loading)
Aug 03 22:31:27 ubuntu-wg python3[6602]: * Environment: production
Aug 03 22:31:27 ubuntu-wg python3[6602]: WARNING: This is a development server. Do not use it in a production deployment.
Aug 03 22:31:27 ubuntu-wg python3[6602]: Use a production WSGI server instead.
Aug 03 22:31:27 ubuntu-wg python3[6602]: * Debug mode: off
Aug 03 22:31:27 ubuntu-wg python3[6602]: * Running on all addresses.
Aug 03 22:31:27 ubuntu-wg python3[6602]: WARNING: This is a development server. Do not use it in a production deployment.
Aug 03 22:31:27 ubuntu-wg python3[6602]: * Running on http://0.0.0.0:10086/ (Press CTRL+C to quit)
```
If you see `Active:` followed by `active (running) since...` then it means it run correctly.
7. Stop/Start/Restart the service
```bash
sudo systemctl stop wg-dashboard.service # <-- To stop the service
sudo systemctl start wg-dashboard.service # <-- To start the service
sudo systemctl restart wg-dashboard.service # <-- To restart the service
```
8. **And now you can reboot your system, and use the command at step 6 to see if it will auto start after the reboot, or just simply access the dashboard through your browser. If you have any questions or problem, please report it in the issue page.**
## ✂️ Dashboard Configuration
#### Dashboard Configuration file
Since version 2.0, WGDashboard will be using a configuration file called `wg-dashboard.ini`, (It will generate automatically after first time running the dashboard). More options will include in future versions, and for now it included the following configurations:
| | Description | Default | Edit Available |
| ---------------------------- | ------------------------------------------------------------ | ---------------------------------------------------- | -------------- |
| **`[Account]`** | *Configuration on account* | | |
| `username` | Dashboard login username | `admin` | Yes |
| `password` | Password, will be hash with SHA256 | `admin` hashed in SHA256 | Yes |
| | | | |
| **`[Server]`** | *Configuration on dashboard* | | |
| `wg_conf_path` | The path of all the Wireguard configurations | `/etc/wireguard` | Yes |
| `app_ip` | IP address the dashboard will run with | `0.0.0.0` | Yes |
| `app_port` | Port the the dashboard will run with | `10086` | Yes |
| `auth_req` | Does the dashboard need authentication to access, if `auth_req = false` , user will not be access the **Setting** tab due to security consideration. **User can only edit the file directly in system**. | `true` | **No** |
| `version` | Dashboard Version | `v3.0` | **No** |
| `dashboard_refresh_interval` | How frequent the dashboard will refresh on the configuration page | `60000ms` | Yes |
| `dashboard_sort` | How configuration is sorting | `status` | Yes |
| | | | |
| **`[Peers]`** | *Default Settings on a new peer* | | |
| `peer_global_dns` | DNS Server | `1.1.1.1` | Yes |
| `peer_endpoint_allowed_ip` | Endpoint Allowed IP | `0.0.0.0/0` | Yes |
| `peer_display_mode` | How peer will display | `grid` | Yes |
| `remote_endpoint` | Remote Endpoint (i.e where your peers will connect to) | *depends on your server's default network interface* | Yes |
| `peer_mtu` | Maximum Transmit Unit | `1420` | |
| `peer_keep_alive` | Keep Alive | `21` | Yes |
#### Generating QR code and peer configuration file (.conf)
Starting version 2.2, dashboard can now generate QR code and configuration file for each peer. Here is a template of what each QR code encoded with and the same content will be inside the file:
```ini
[Interface]
PrivateKey = QWERTYUIOPO234567890YUSDAKFH10E1B12JE129U21=
Address = 0.0.0.0/32
DNS = 1.1.1.1
[Peer]
PublicKey = QWERTYUIOPO234567890YUSDAKFH10E1B12JE129U21=
AllowedIPs = 0.0.0.0/0
Endpoint = 0.0.0.0:51820
```
Access your server with port `10086` ! e.g (http://your_server_ip:10086)
| | Description | Default Value | Available in Peer setting |
| ----------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------- |
| **`[Interface]`** | | | |
| `PrivateKey` | The private key of this peer | Private key generated by WireGuard (`wg genkey`) or provided by user | Yes |
| `Address` | The `allowed_ips` of your peer | N/A | Yes |
| `DNS` | The DNS server your peer will use | `1.1.1.1` - Cloud flare DNS, you can change it when you adding the peer or in the peer setting. | Yes |
| **`[Peer]`** | | | |
| `PublicKey` | The public key of your server | N/A | No |
| `AllowedIPs` | IP ranges for which a peer will route traffic | `0.0.0.0/0` - Indicated a default route to send all internet and VPN traffic through that peer. | Yes |
| `Endpoint` | Your wireguard server ip and port, the dashboard will search for your server's default interface's ip. | `<your server default interface ip>:<listen port>` | Yes |
**3. Install with Production Mode (Optional)**
## ❓ How to update the dashboard?
```
$ cd Wireguard-Dashboard/src
$ export FLASK_APP=dashboard.py
$ export FLASK_RUN_HOST=0.0.0.0
$ export FLASK_ENV=development
$ export FLASK_DEBUG=0
$ flask run
```
#### **Please note for user who is using `v2.3.1` or below**
- For user who is using `v2.3.1` or below, please notice that all data that stored in the current database will **not** transfer to the new database. This is hard decision to move from TinyDB to SQLite. But SQLite does provide a thread-safe access and TinyDB doesn't. I couldn't find a safe way to transfer the data, so you need to do them manually... Sorry about that :pensive: . But I guess this would be a great start for future development :sunglasses:.
<hr>
1. Change your directory to `wgdashboard`
```shell
cd wgdashboard
```
2. Update the dashboard
```shell
git pull https://github.com/donaldzou/WGDashboard.git v3.0 --force
```
Starting with `v3.0`, you can simply do `./wgd.sh update` !! (I hope, lol)
## 🔍 Screenshot
![Sign In Page](img/SignIn.png)
![Index Image](img/HomePage.png)
![Configuration](img/Configuration.png)
![Add Peer](img/AddPeer.png)
![Edit Peer](img/EditPeer.png)
![Delete Peer](img/DeleteBulk.png)
![Dashboard Setting](img/DashboardSetting.png)
![Ping](img/Ping.png)
![Traceroute](img/Traceroute.png)
## ⏰ Changelog
#### v2.3.1 - Sep 8, 2021
- Updated dashboard's name to **WGDashboard**!!
#### v2.3 - Sep 8, 2021
- 🎉 **New Features**
- **Update directly from `wgd.sh`:** Now you can update WGDashboard directly from the bash script.
- **Displaying Peers:** You can switch the display mode between list and table in the configuration page.
- 🪚 **Bug Fixed**
- [Peer DNS Validation Fails #67](issues/67): Added DNS format check. [❤️ @realfian]
- [configparser.NoSectionError: No section: 'Interface' #66](issues/66): Changed permission requirement for `etc/wireguard` from `744` to `755`. [❤️ @ramalmaty]
- [Feature request: Interface not loading when information missing #73](issues/73): Fixed when Configuration Address and Listen Port is missing will crash the dashboard. [❤️ @js32]
- [Remote Peer, MTU and PersistentKeepalives added #70](pull/70): Added MTU, remote peer and Persistent Keepalive. [❤️ @realfian]
- [Fixes DNS check to support search domain #65](pull/65): Added allow input domain into DNS. [❤️@davejlong]
- **🧐 Other Changes**
- Moved Add Peer Button into the right bottom corner.
#### v2.2.1 - Aug 16, 2021
Bug Fixed:
- Added support for full subnet on Allowed IP
- Peer setting Save button
#### v2.2 - Aug 14, 2021
- 🎉 **New Features**
- **Add new peers**: Now you can add peers directly on dashboard, it will generate a pair of private key and public key. You can also set its DNS, endpoint allowed IPs. Both can set a default value in the setting page. [❤️ in [#44](https://github.com/donaldzou/wireguard-dashboard/issues/44)]
- **QR Code:** You can add the private key in peer setting of your existed peer to create a QR code. Or just create a new one, dashboard will now be able to auto generate a private key and public key ;) Don't worry, all keys will be generated on your machine, and **will delete all key files after they got generated**. [❤️ in [#29](https://github.com/donaldzou/wireguard-dashboard/issues/29)]
- **Peer configuration file download:** Same as QR code, you now can download the peer configuration file, so you don't need to manually input all the details on the peer machine! [❤️ in [#40](https://github.com/donaldzou/wireguard-dashboard/issues/40)]
- **Search peers**: You can now search peers by their name.
- **Autostart on boot:** Added a tutorial on how to start the dashboard to on boot! Please read the [tutorial below](#autostart-wireguard-dashboard-on-boot). [❤️ in [#29](https://github.com/donaldzou/wireguard-dashboard/issues/29)]
- **Click to copy**: You can now click and copy all peer's public key and configuration's public key.
- ....
- 🪚 **Bug Fixed**
- When there are comments in the wireguard config file, will cause the dashboard to crash.
- Used regex to search for config files.
- **🧐 Other Changes**
- Moved all external CSS and JavaScript file to local hosting (Except Bootstrap Icon, due to large amount of SVG files).
- Updated Python dependencies
- Flask: `v1.1.2 => v2.0.1`
- Jinja: `v2.10.1 => v3.0.1`
- icmplib: `v2.1.1 => v3.0.1`
- Updated CSS/JS dependencies
- Bootstrap: `v4.5.3 => v4.6.0`
- UI adjustment
- Adjusted how peers will display in larger screens, used to be 1 row per peer, now is 3 peers in 1 row.
#### v2.1 - Jul 2, 2021
- Added **Ping** and **Traceroute** tools!
- Adjusted the calculation of data usage on each peers
- Added refresh interval of the dashboard
- Bug fixed when no configuration on fresh install ([#23](https://github.com/donaldzou/wireguard-dashboard/issues/23))
- Fixed crash when too many peers ([#22](https://github.com/donaldzou/wireguard-dashboard/issues/22))
#### v2.0 - May 5, 2021
- Added login function to dashboard
- ***I'm not using the most ideal way to store the username and password, feel free to provide a better way to do this if you any good idea!***
- Added a config file to the dashboard
- Dashboard config can be change within the **Setting** tab on the side bar
- Adjusted UI
- And much more!
#### v1.1.2 - Apr 3, 2021
- Resolved issue [#3](https://github.com/donaldzou/wireguard-dashboard/issues/3).
#### v1.1.1 - Apr 2, 2021
- Able to add a friendly name to each peer. Thanks [#2](https://github.com/donaldzou/wireguard-dashboard/issues/2) !
#### v1.0 - Dec 27, 2020
- Added the function to remove peers
## 🛒 Dependencies
- CSS/JS
- [Bootstrap](https://getbootstrap.com/docs/4.6/getting-started/introduction/) `v4.6.0`
- [Bootstrap Icon](https://icons.getbootstrap.com) `v1.4.0`
- [jQuery](https://jquery.com) `v3.5.1`
- Python
- [Flask](https://pypi.org/project/Flask/) `v2.0.1`
- [TinyDB](https://pypi.org/project/tinydb/) `v4.3.0`
- [ifcfg](https://pypi.org/project/ifcfg/) `v0.21`
- [icmplib](https://pypi.org/project/icmplib/) `v2.1.1`
- [flask-qrcode](https://pypi.org/project/Flask-QRcode/) `v3.0.0`
## ✨ Contributors
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-5-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END -->
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tr>
<td align="center"><a href="https://github.com/antonioag95"><img src="https://avatars.githubusercontent.com/u/30556866?v=4?s=100" width="100px;" alt=""/><br /><sub><b>antonioag95</b></sub></a><br /><a href="https://github.com/donaldzou/WGDashboard/commits?author=antonioag95" title="Tests">⚠️</a> <a href="https://github.com/donaldzou/WGDashboard/commits?author=antonioag95" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/tonjo"><img src="https://avatars.githubusercontent.com/u/4726289?v=4?s=100" width="100px;" alt=""/><br /><sub><b>tonjo</b></sub></a><br /><a href="https://github.com/donaldzou/WGDashboard/commits?author=tonjo" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/reafian"><img src="https://avatars.githubusercontent.com/u/11992416?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Richard Newton</b></sub></a><br /><a href="https://github.com/donaldzou/WGDashboard/commits?author=reafian" title="Code">💻</a></td>
<td align="center"><a href="http://www.davejlong.com"><img src="https://avatars.githubusercontent.com/u/175317?v=4?s=100" width="100px;" alt=""/><br /><sub><b>David Long</b></sub></a><br /><a href="https://github.com/donaldzou/WGDashboard/commits?author=davejlong" title="Code">💻</a></td>
<td align="center"><a href="http://www.std-soft.com"><img src="https://avatars.githubusercontent.com/u/5978293?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Markus Neubauer</b></sub></a><br /><a href="https://github.com/donaldzou/WGDashboard/commits?author=marneu" title="Code">💻</a></td>
</tr>
</table>
<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
## 🔍Example
![Index Image](https://github.com/donaldzou/Wireguard-Dashboard/raw/main/src/static/index.png)
![Conf Image](https://github.com/donaldzou/Wireguard-Dashboard/raw/main/src/static/configuration.png)

BIN
img/AddPeer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

BIN
img/Configuration.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

BIN
img/DashboardSetting.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

BIN
img/DeleteBulk.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB

BIN
img/DeletePeer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 KiB

BIN
img/EditPeer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

BIN
img/HomePage.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

BIN
img/Ping.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 KiB

BIN
img/QRCode.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 KiB

BIN
img/SearchIP.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

BIN
img/SignIn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

BIN
img/Traceroute.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

BIN
img/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

View File

@@ -1,2 +0,0 @@
Flask==1.1.2
tinydb==4.3.0

4
src/certbot.ini Normal file
View File

@@ -0,0 +1,4 @@
authenticator = standalone
noninteractive = true
agree-tos = true
rsa-key-size = 2048

File diff suppressed because it is too large Load Diff

View File

@@ -1 +0,0 @@
You can delete this later ;)

26
src/db/wg1.conf.backup Normal file
View File

@@ -0,0 +1,26 @@
[Interface]
Address = 10.200.100.1/24
PostUp = iptables -A FORWARD -i wg1 -j ACCEPT; iptables -A FORWARD -o wg1 -j ACCEPT; iptables -t nat -A POSTROUTING -o ens3 -j MASQUERADE;tc qdisc add dev wg1 root tbf rate 50000kbit buffer 1600 limit 50000
PreDown = tc qdisc del dev wg1 root
PostDown = iptables -D FORWARD -i wg1 -j ACCEPT; iptables -D FORWARD -o wg1 -j ACCEPT; iptables -t nat -D POSTROUTING -o ens3 -j MASQUERADE;
ListenPort = 60945
PrivateKey = 8DsSMli3okgUx5frKbFQ0fMW5ZMyqyxOdOW7+g21L18=
[Peer]
PublicKey = aXsYmR73LEuwVOJeqUjlsWbWPyvJPm3lg3zh7WkruWg=
PresharedKey = GISQ6z6GMLocJQgdYOfi2XX7NQWkZBPPFiueRYLqnJE=
AllowedIPs = 10.200.100.2/32
[Peer]
PublicKey = FxXXNXoKZcBNyZbq0nFsmBC5YM+7up3a4bYGU6q900w=
PresharedKey = dqp44vullLVZQhIYE2VaY6WFQNfahnHum5kq3sWPsSc=
AllowedIPs = 10.200.100.3/32
[Peer]
PublicKey = FxXXNXoKZcBNyZbq0nFsmBC5YM+7up3a4bYGU6q900w=
PresharedKey = dqp44vullLVZQhIYE2VaY6WFQNfahnHum5kq3sWPsSc=
AllowedIPs = 10.200.100.3/32

11
src/gunicorn.conf.py Normal file
View File

@@ -0,0 +1,11 @@
import multiprocessing
import dashboard
app_host, app_port = dashboard.get_host_bind()
worker_class = 'gthread'
workers = multiprocessing.cpu_count() * 2 + 1
threads = 4
bind = f"{app_host}:{app_port}"
daemon = True
pidfile = './gunicorn.pid'

6
src/requirements.txt Normal file
View File

@@ -0,0 +1,6 @@
Flask
ifcfg
icmplib
flask-qrcode
gunicorn
certbot

BIN
src/static/.DS_Store vendored

Binary file not shown.

View File

@@ -1 +0,0 @@
!function(a){"use strict";function l(t,e){this.$element=a(t),this.options=a.extend({},this.defaults(),e),this.render()}l.VERSION="3.6.0",l.DEFAULTS={on:"On",off:"Off",onstyle:"primary",offstyle:"light",size:"normal",style:"",width:null,height:null},l.prototype.defaults=function(){return{on:this.$element.attr("data-on")||l.DEFAULTS.on,off:this.$element.attr("data-off")||l.DEFAULTS.off,onstyle:this.$element.attr("data-onstyle")||l.DEFAULTS.onstyle,offstyle:this.$element.attr("data-offstyle")||l.DEFAULTS.offstyle,size:this.$element.attr("data-size")||l.DEFAULTS.size,style:this.$element.attr("data-style")||l.DEFAULTS.style,width:this.$element.attr("data-width")||l.DEFAULTS.width,height:this.$element.attr("data-height")||l.DEFAULTS.height}},l.prototype.render=function(){this._onstyle="btn-"+this.options.onstyle,this._offstyle="btn-"+this.options.offstyle;var t="large"===this.options.size||"lg"===this.options.size?"btn-lg":"small"===this.options.size||"sm"===this.options.size?"btn-sm":"mini"===this.options.size||"xs"===this.options.size?"btn-xs":"",e=a('<label for="'+this.$element.prop("id")+'" class="btn">').html(this.options.on).addClass(this._onstyle+" "+t),s=a('<label for="'+this.$element.prop("id")+'" class="btn">').html(this.options.off).addClass(this._offstyle+" "+t),o=a('<span class="toggle-handle btn btn-light">').addClass(t),i=a('<div class="toggle-group">').append(e,s,o),l=a('<div class="toggle btn" data-toggle="toggle" role="button">').addClass(this.$element.prop("checked")?this._onstyle:this._offstyle+" off").addClass(t).addClass(this.options.style);this.$element.wrap(l),a.extend(this,{$toggle:this.$element.parent(),$toggleOn:e,$toggleOff:s,$toggleGroup:i}),this.$toggle.append(i);var n=this.options.width||Math.max(e.outerWidth(),s.outerWidth())+o.outerWidth()/2,h=this.options.height||Math.max(e.outerHeight(),s.outerHeight());e.addClass("toggle-on"),s.addClass("toggle-off"),this.$toggle.css({width:n,height:h}),this.options.height&&(e.css("line-height",e.height()+"px"),s.css("line-height",s.height()+"px")),this.update(!0),this.trigger(!0)},l.prototype.toggle=function(){this.$element.prop("checked")?this.off():this.on()},l.prototype.on=function(t){if(this.$element.prop("disabled"))return!1;this.$toggle.removeClass(this._offstyle+" off").addClass(this._onstyle),this.$element.prop("checked",!0),t||this.trigger()},l.prototype.off=function(t){if(this.$element.prop("disabled"))return!1;this.$toggle.removeClass(this._onstyle).addClass(this._offstyle+" off"),this.$element.prop("checked",!1),t||this.trigger()},l.prototype.enable=function(){this.$toggle.removeClass("disabled"),this.$toggle.removeAttr("disabled"),this.$element.prop("disabled",!1)},l.prototype.disable=function(){this.$toggle.addClass("disabled"),this.$toggle.attr("disabled","disabled"),this.$element.prop("disabled",!0)},l.prototype.update=function(t){this.$element.prop("disabled")?this.disable():this.enable(),this.$element.prop("checked")?this.on(t):this.off(t)},l.prototype.trigger=function(t){this.$element.off("change.bs.toggle"),t||this.$element.change(),this.$element.on("change.bs.toggle",a.proxy(function(){this.update()},this))},l.prototype.destroy=function(){this.$element.off("change.bs.toggle"),this.$toggleGroup.remove(),this.$element.removeData("bs.toggle"),this.$element.unwrap()};var t=a.fn.bootstrapToggle;a.fn.bootstrapToggle=function(o){var i=Array.prototype.slice.call(arguments,1)[0];return this.each(function(){var t=a(this),e=t.data("bs.toggle"),s="object"==typeof o&&o;e||t.data("bs.toggle",e=new l(this,s)),"string"==typeof o&&e[o]&&"boolean"==typeof i?e[o](i):"string"==typeof o&&e[o]&&e[o]()})},a.fn.bootstrapToggle.Constructor=l,a.fn.toggle.noConflict=function(){return a.fn.bootstrapToggle=t,this},a(function(){a("input[type=checkbox][data-toggle^=toggle]").bootstrapToggle()}),a(document).on("click.bs.toggle","div[data-toggle^=toggle]",function(t){a(this).find("input[type=checkbox]").bootstrapToggle("toggle"),t.preventDefault()})}(jQuery);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

7
src/static/css/bootstrap.min.css vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,532 @@
body {
font-size: .875rem;
}
.feather {
width: 16px;
height: 16px;
vertical-align: text-bottom;
}
/*
* Sidebar
*/
.sidebar {
position: fixed;
top: 0;
bottom: 0;
left: 0;
z-index: 100; /* Behind the navbar */
padding: 48px 0 0; /* Height of navbar */
box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1);
}
/*@media (max-width: 767.98px) {*/
/* .sidebar {*/
/* top: 5rem;*/
/* }*/
/*}*/
.sidebar-sticky {
position: relative;
top: 0;
height: calc(100vh - 48px);
padding-top: .5rem;
overflow-x: hidden;
overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
}
@supports ((position: -webkit-sticky) or (position: sticky)) {
.sidebar-sticky {
position: -webkit-sticky;
position: sticky;
}
}
.sidebar .nav-link {
font-weight: 500;
color: #333;
transition: 0.2s cubic-bezier(0.82, -0.07, 0, 1.01);
}
.nav-link:hover {
padding-left: 30px;
background-color: #dfdfdf;
}
.sidebar .nav-link .feather {
margin-right: 4px;
color: #999;
}
.sidebar .nav-link.active {
color: #007bff;
}
.sidebar .nav-link:hover .feather,
.sidebar .nav-link.active .feather {
color: inherit;
}
.sidebar-heading {
font-size: .75rem;
text-transform: uppercase;
}
/*
* Navbar
*/
.navbar-brand {
padding-top: .75rem;
padding-bottom: .75rem;
font-size: 1rem;
background-color: rgba(0, 0, 0, .25);
box-shadow: inset -1px 0 0 rgba(0, 0, 0, .25);
}
.navbar .navbar-toggler {
top: .25rem;
right: 1rem;
}
.form-control{
transition: all 0.2s ease-in-out;
}
.form-control:disabled{
cursor: not-allowed;
}
.navbar .form-control {
padding: .75rem 1rem;
border-width: 0;
border-radius: 0;
}
.form-control-dark {
color: #fff;
background-color: rgba(255, 255, 255, .1);
border-color: rgba(255, 255, 255, .1);
}
.form-control-dark:focus {
border-color: transparent;
box-shadow: 0 0 0 3px rgba(255, 255, 255, .25);
}
.dot{
width: 10px;
height: 10px;
border-radius: 50px;
display: inline-block;
margin-left: 10px;
}
.dot-running{
background-color: #28a745!important;
box-shadow: 0 0 0 0.2rem #28a74545;
}
.h6-dot-running{
margin-left: 0.3rem;
}
.dot-stopped{
background-color: #6c757d!important;
}
.card-running{
border-color: #28a745;
}
.info h6{
line-break: anywhere;
transition: 0.2s ease-in-out;
}
.info .row .col-sm{
display: flex;
flex-direction: column;
}
.info .row .col-sm small{
display: flex;
}
.info .row .col-sm small strong:last-child(1){
margin-left: auto !important;
}
.btn-control{
border: none !important;
padding: 0 1rem 0 0;
}
.btn-control:active, .btn-control:focus{
background-color: transparent !important;
border: none !important;
box-shadow: none;
}
.btn-qrcode-peer{
padding: 0 !important;
}
.btn-qrcode-peer:active, .btn-qrcode-peer:hover{
transform: scale(0.9) rotate(180deg);
border: 0 !important;
}
.btn-download-peer:active, .btn-download-peer:hover{
color: #17a2b8 !important;
transform: translateY(5px);
}
.share_peer_btn_group .btn-control{
margin: 0 0 0 1rem;
padding: 0 !important;
transition: all 0.4s cubic-bezier(1, -0.43, 0, 1.37);
}
.btn-control:hover{
background: white;
}
.btn-delete-peer:hover{
color: #dc3545;
}
.btn-setting-peer:hover{
color:#007bff
}
.btn-download-peer:hover{
color: #17a2b8;
}
.login-container{
padding: 2rem;
}
@media (max-width: 992px){
.card-col{
margin-bottom: 1rem;
}
}
.switch{
font-size: 2rem;
}
.switch:hover{
text-decoration: none
}
.btn-group-label:hover{
color: #007bff;
border-color: #007bff;
background: white;
}
/*.peer_data_group{*/
/* text-align: right;*/
/*}*/
@media (max-width: 768px) {
.peer_data_group{
text-align: left;
}
}
.index-switch{
text-align: right;
}
main{
margin-bottom: 3rem;
}
.peer_list{
margin-bottom: 7rem
}
@media (max-width: 768px) {
.add_btn{
bottom: 1.5rem !important;
}
.peer_list{
margin-bottom: 7rem !important;
}
}
.btn-manage-group{
z-index: 99;
position: fixed;
bottom: 3rem;
right: 2rem;
display: flex;
}
.btn-manage-group .setting_btn_menu{
position: absolute;
top: -124px;
background-color: white;
padding: 1rem 0;
right: 0;
box-shadow: 0 10px 20px rgb(0 0 0 / 19%), 0 6px 6px rgb(0 0 0 / 23%);
border-radius: 10px;
min-width: 200px;
display: none;
transform: translateY(-30px);
opacity: 0;
transition: all 0.3s cubic-bezier(0.58, 0.03, 0.05, 1.28);
}
.btn-manage-group .setting_btn_menu.show{
display: block;
}
.setting_btn_menu.showing{
transform: translateY(0px);
opacity: 1;
}
.setting_btn_menu a{
display: flex;
padding: 0.5rem 1rem;
transition: all 0.1s ease-in-out;
font-size: 1rem;
align-items: center;
cursor: pointer;
}
.setting_btn_menu a:hover{
background-color: #efefef;
text-decoration: none;
}
.setting_btn_menu a i{
margin-right: auto !important;
}
.add_btn{
height: 54px;
z-index: 99;
border-radius: 100px !important;
padding: 0 14px;
box-shadow: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23);
margin-right: 1rem;
font-size: 1.5rem;
}
.setting_btn{
height: 54px;
z-index: 99;
border-radius: 100px !important;
padding: 0 14px;
box-shadow: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23);
font-size: 1.5rem;
}
@-webkit-keyframes rotating /* Safari and Chrome */ {
from {
-webkit-transform: rotate(0deg);
-o-transform: rotate(0deg);
transform: rotate(0deg);
}
to {
-webkit-transform: rotate(360deg);
-o-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@keyframes rotating {
from {
-ms-transform: rotate(0deg);
-moz-transform: rotate(0deg);
-webkit-transform: rotate(0deg);
-o-transform: rotate(0deg);
transform: rotate(0deg);
}
to {
-ms-transform: rotate(360deg);
-moz-transform: rotate(360deg);
-webkit-transform: rotate(360deg);
-o-transform: rotate(360deg);
transform: rotate(360deg);
}
}
.rotating::before {
-webkit-animation: rotating 0.75s linear infinite;
-moz-animation: rotating 0.75s linear infinite;
-ms-animation: rotating 0.75s linear infinite;
-o-animation: rotating 0.75s linear infinite;
animation: rotating 0.75s linear infinite;
}
.peer_private_key_textbox_switch{
position: absolute;
right: 2rem;
transform: translateY(-28px);
font-size: 1.2rem;
cursor: pointer;
}
#peer_private_key_textbox, #private_key, #public_key{
font-family: SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;
}
.progress-bar{
transition: 0.3s ease-in-out;
}
.key{
transition: 0.2s ease-in-out;
cursor: pointer;
}
.key:hover{
color: #007bff;
}
.card{
border-radius: 10px;
}
.peer_list .card .button-group{
height: 22px;
}
.form-control{
border-radius: 10px;
}
.btn{
border-radius: 8px;
}
.modal-content{
border-radius: 10px;
}
.tooltip-inner{
font-size: 0.8rem;
}
@-webkit-keyframes loading {
0%{
background-color: #dfdfdf;
}
50%{
background-color: #adadad;
}
100%{
background-color: #dfdfdf;
}
}
@-moz-keyframes loading {
0%{
background-color: #dfdfdf;
}
50%{
background-color: #adadad;
}
100%{
background-color: #dfdfdf;
}
}
.conf_card{
transition: 0.2s ease-in-out;
}
.conf_card:hover{
border-color: #007bff;
cursor: pointer;
}
.info_loading{
animation: loading 2s infinite ease-in-out;
border-radius: 5px;
height: 19px;
transition: 0.3s ease-in-out;
}
#conf_status_btn{
transition: 0.2s ease-in-out;
}
#conf_status_btn.info_loading{
height: 38px;
border-radius: 5px;
animation: loading 3s infinite ease-in-out;
}
#qrcode_img img{
width: 100%;
}
#selected_ip_list .badge, #selected_peer_list .badge{
margin: 0.1rem
}
#add_modal.ip_modal_open{
transition: filter 0.2s ease-in-out;
filter: brightness(0.5);
}
#delete_bulk_modal .list-group a.active{
background-color: #dc3545;
border-color: #dc3545;
}
#selected_peer_list{
max-height: 80px;
overflow-y: scroll;
overflow-x: hidden;
}
.no-response{
width: 100%;
height: 100%;
position: fixed;
background: #000000ba;
z-index: 10000;
display: none;
flex-direction: column;
align-items: center;
justify-content: center;
opacity: 0;
transition: all 1s ease-in-out;
}
.no-response.active{
display: flex;
}
.no-response.active.show{
opacity: 100;
}
.no-response .container > *{
text-align: center;
}
.no-responding{
transition: all 1s ease-in-out;
filter: blur(10px);
}
pre.index-alert{
margin-bottom: 0;
padding: 1rem;
background-color: #343a40;
border: 1px solid rgba(0,0,0,.125);
border-radius: .25rem;
margin-top: 1rem;
color: white;
}

1
src/static/css/dashboard.min.css vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,142 +0,0 @@
body {
font-size: .875rem;
}
.feather {
width: 16px;
height: 16px;
vertical-align: text-bottom;
}
/*
* Sidebar
*/
.sidebar {
position: fixed;
top: 0;
bottom: 0;
left: 0;
z-index: 100; /* Behind the navbar */
padding: 48px 0 0; /* Height of navbar */
box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1);
}
@media (max-width: 767.98px) {
.sidebar {
top: 5rem;
}
}
.sidebar-sticky {
position: relative;
top: 0;
height: calc(100vh - 48px);
padding-top: .5rem;
overflow-x: hidden;
overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
}
@supports ((position: -webkit-sticky) or (position: sticky)) {
.sidebar-sticky {
position: -webkit-sticky;
position: sticky;
}
}
.sidebar .nav-link {
font-weight: 500;
color: #333;
}
.sidebar .nav-link .feather {
margin-right: 4px;
color: #999;
}
.sidebar .nav-link.active {
color: #007bff;
}
.sidebar .nav-link:hover .feather,
.sidebar .nav-link.active .feather {
color: inherit;
}
.sidebar-heading {
font-size: .75rem;
text-transform: uppercase;
}
/*
* Navbar
*/
.navbar-brand {
padding-top: .75rem;
padding-bottom: .75rem;
font-size: 1rem;
background-color: rgba(0, 0, 0, .25);
box-shadow: inset -1px 0 0 rgba(0, 0, 0, .25);
}
.navbar .navbar-toggler {
top: .25rem;
right: 1rem;
}
.navbar .form-control {
padding: .75rem 1rem;
border-width: 0;
border-radius: 0;
}
.form-control-dark {
color: #fff;
background-color: rgba(255, 255, 255, .1);
border-color: rgba(255, 255, 255, .1);
}
.form-control-dark:focus {
border-color: transparent;
box-shadow: 0 0 0 3px rgba(255, 255, 255, .25);
}
.dot{
width: 10px;
height: 10px;
border-radius: 50px;
display: inline-block;
margin-left: 10px;
}
.dot-running{
background-color: #28a745!important;
}
.dot-stopped{
background-color: #6c757d!important;
}
.info h6{
line-break: anywhere;
}
.btn-control{
border: none !important;
padding: 0;
padding-right: 0.5rem;
}
.btn-control:hover{
background: white;
}
.btn-delete-peer:hover{
color: #dc3545;
}
.btn-setting-peer:hover{
color:#007bff
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

BIN
src/static/img/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

7045
src/static/js/bootstrap.bundle.js vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

7
src/static/js/bootstrap.bundle.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

1
src/static/js/configuration.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

2
src/static/js/jquery.min.js vendored Normal file

File diff suppressed because one or more lines are too long

67
src/static/js/tools.js Normal file
View File

@@ -0,0 +1,67 @@
/**
* tools.js - Copyright(C) 2021 Donald Zou [https://github.com/donaldzou]
*/
$(".ip_dropdown").on("change",function (){
$(".modal.show .btn").removeAttr("disabled");
});
$(".conf_dropdown").on("change", function (){
$(".modal.show .ip_dropdown").html('<option value="none" selected="selected" disabled>Loading...');
$.ajax({
url: "/get_ping_ip",
method: "POST",
data: "config=" + $(this).children("option:selected").val(),
success: function (res){
$(".modal.show .ip_dropdown").html("");
$(".modal.show .ip_dropdown").append('<option value="none" selected="selected" disabled>Choose an IP');
$(".modal.show .ip_dropdown").append(res);
}
});
});
// Ping Tools
$(".send_ping").on("click", function (){
$(this).attr("disabled","disabled");
$(this).html("Pinging...");
$("#ping_modal .form-control").attr("disabled","disabled");
$.ajax({
method:"POST",
data: "ip="+ $(':selected', $("#ping_modal .ip_dropdown")).val() +
"&count=" + $("#ping_modal .ping_count").val(),
url: "/ping_ip",
success: function (res){
$(".ping_result tbody").html("");
let html = '<tr><th scope="row">Address</th><td>'+res.address+'</td></tr>' +
'<tr><th scope="row">Is Alive</th><td>'+res.is_alive+'</td></tr>' +
'<tr><th scope="row">Min RTT</th><td>'+res.min_rtt+'ms</td></tr>' +
'<tr><th scope="row">Average RTT </th><td>'+res.avg_rtt+'ms</td></tr>' +
'<tr><th scope="row">Max RTT</th><td>'+res.max_rtt+'ms</td></tr>' +
'<tr><th scope="row">Package Sent</th><td>'+res.package_sent+'</td></tr>' +
'<tr><th scope="row">Package Received</th><td>'+res.package_received+'</td></tr>' +
'<tr><th scope="row">Package Loss</th><td>'+res.package_loss+'</td></tr>';
$(".ping_result tbody").html(html);
$(".send_ping").removeAttr("disabled");
$(".send_ping").html("Ping");
$("#ping_modal .form-control").removeAttr("disabled");
}
});
});
// Traceroute Tools
$(".send_traceroute").on("click", function (){
$(this).attr("disabled","disabled");
$(this).html("Tracing...");
$("#traceroute_modal .form-control").attr("disabled","disabled");
$.ajax({
url: "/traceroute_ip",
method: "POST",
data: "ip=" + $(':selected', $("#traceroute_modal .ip_dropdown")).val(),
success: function (res){
$(".traceroute_result tbody").html("");
res.forEach((ele) =>
$(".traceroute_result tbody").append('<tr><th scope="row">'+ele.hop+'</th><td>'+ele.ip+'</td><td>'+ele.avg_rtt+'</td><td>'+ele.min_rtt+'</td><td>'+ele.max_rtt+'</td></tr>'));
$(".send_traceroute").removeAttr("disabled").html("Traceroute");
$("#traceroute_modal .form-control").removeAttr("disabled");
}
});
});

1
src/static/js/tools.min.js vendored Normal file
View File

@@ -0,0 +1 @@
$(".ip_dropdown").on("change",function(){$(".modal.show .btn").removeAttr("disabled")});$(".conf_dropdown").on("change",function(){$(".modal.show .ip_dropdown").html('<option value="none" selected="selected" disabled>Loading...');$.ajax({url:"/get_ping_ip",method:"POST",data:"config="+$(this).children("option:selected").val(),success:function(res){$(".modal.show .ip_dropdown").html("");$(".modal.show .ip_dropdown").append('<option value="none" selected="selected" disabled>Choose an IP');$(".modal.show .ip_dropdown").append(res)}})});$(".send_ping").on("click",function(){$(this).attr("disabled","disabled");$(this).html("Pinging...");$("#ping_modal .form-control").attr("disabled","disabled");$.ajax({method:"POST",data:"ip="+$(":selected",$("#ping_modal .ip_dropdown")).val()+"&count="+$("#ping_modal .ping_count").val(),url:"/ping_ip",success:function(res){$(".ping_result tbody").html("");let html='<tr><th scope="row">Address</th><td>'+res.address+"</td></tr>"+'<tr><th scope="row">Is Alive</th><td>'+res.is_alive+"</td></tr>"+'<tr><th scope="row">Min RTT</th><td>'+res.min_rtt+"ms</td></tr>"+'<tr><th scope="row">Average RTT </th><td>'+res.avg_rtt+"ms</td></tr>"+'<tr><th scope="row">Max RTT</th><td>'+res.max_rtt+"ms</td></tr>"+'<tr><th scope="row">Package Sent</th><td>'+res.package_sent+"</td></tr>"+'<tr><th scope="row">Package Received</th><td>'+res.package_received+"</td></tr>"+'<tr><th scope="row">Package Loss</th><td>'+res.package_loss+"</td></tr>";$(".ping_result tbody").html(html);$(".send_ping").removeAttr("disabled");$(".send_ping").html("Ping");$("#ping_modal .form-control").removeAttr("disabled")}})});$(".send_traceroute").on("click",function(){$(this).attr("disabled","disabled");$(this).html("Tracing...");$("#traceroute_modal .form-control").attr("disabled","disabled");$.ajax({url:"/traceroute_ip",method:"POST",data:"ip="+$(":selected",$("#traceroute_modal .ip_dropdown")).val(),success:function(res){$(".traceroute_result tbody").html("");res.forEach(ele=>$(".traceroute_result tbody").append('<tr><th scope="row">'+ele.hop+"</th><td>"+ele.ip+"</td><td>"+ele.avg_rtt+"</td><td>"+ele.min_rtt+"</td><td>"+ele.max_rtt+"</td></tr>"));$(".send_traceroute").removeAttr("disabled").html("Traceroute");$("#traceroute_modal .form-control").removeAttr("disabled")}})});

301
src/static/js/wireguard.js Normal file
View File

@@ -0,0 +1,301 @@
/*! SPDX-License-Identifier: GPL-2.0
*
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/
(function() {
function gf(init) {
var r = new Float64Array(16);
if (init) {
for (var i = 0; i < init.length; ++i)
r[i] = init[i];
}
return r;
}
function pack(o, n) {
var b, m = gf(), t = gf();
for (var i = 0; i < 16; ++i)
t[i] = n[i];
carry(t);
carry(t);
carry(t);
for (var j = 0; j < 2; ++j) {
m[0] = t[0] - 0xffed;
for (var i = 1; i < 15; ++i) {
m[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1);
m[i - 1] &= 0xffff;
}
m[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1);
b = (m[15] >> 16) & 1;
m[14] &= 0xffff;
cswap(t, m, 1 - b);
}
for (var i = 0; i < 16; ++i) {
o[2 * i] = t[i] & 0xff;
o[2 * i + 1] = t[i] >> 8;
}
}
function carry(o) {
var c;
for (var i = 0; i < 16; ++i) {
o[(i + 1) % 16] += (i < 15 ? 1 : 38) * Math.floor(o[i] / 65536);
o[i] &= 0xffff;
}
}
function cswap(p, q, b) {
var t, c = ~(b - 1);
for (var i = 0; i < 16; ++i) {
t = c & (p[i] ^ q[i]);
p[i] ^= t;
q[i] ^= t;
}
}
function add(o, a, b) {
for (var i = 0; i < 16; ++i)
o[i] = (a[i] + b[i]) | 0;
}
function subtract(o, a, b) {
for (var i = 0; i < 16; ++i)
o[i] = (a[i] - b[i]) | 0;
}
function multmod(o, a, b) {
var t = new Float64Array(31);
for (var i = 0; i < 16; ++i) {
for (var j = 0; j < 16; ++j)
t[i + j] += a[i] * b[j];
}
for (var i = 0; i < 15; ++i)
t[i] += 38 * t[i + 16];
for (var i = 0; i < 16; ++i)
o[i] = t[i];
carry(o);
carry(o);
}
function invert(o, i) {
var c = gf();
for (var a = 0; a < 16; ++a)
c[a] = i[a];
for (var a = 253; a >= 0; --a) {
multmod(c, c, c);
if (a !== 2 && a !== 4)
multmod(c, c, i);
}
for (var a = 0; a < 16; ++a)
o[a] = c[a];
}
function clamp(z) {
z[31] = (z[31] & 127) | 64;
z[0] &= 248;
}
function generatePublicKey(privateKey) {
var r, z = new Uint8Array(32);
var a = gf([1]),
b = gf([9]),
c = gf(),
d = gf([1]),
e = gf(),
f = gf(),
_121665 = gf([0xdb41, 1]),
_9 = gf([9]);
for (var i = 0; i < 32; ++i)
z[i] = privateKey[i];
clamp(z);
for (var i = 254; i >= 0; --i) {
r = (z[i >>> 3] >>> (i & 7)) & 1;
cswap(a, b, r);
cswap(c, d, r);
add(e, a, c);
subtract(a, a, c);
add(c, b, d);
subtract(b, b, d);
multmod(d, e, e);
multmod(f, a, a);
multmod(a, c, a);
multmod(c, b, e);
add(e, a, c);
subtract(a, a, c);
multmod(b, a, a);
subtract(c, d, f);
multmod(a, c, _121665);
add(a, a, d);
multmod(c, c, a);
multmod(a, d, f);
multmod(d, b, _9);
multmod(b, e, e);
cswap(a, b, r);
cswap(c, d, r);
}
invert(c, c);
multmod(a, a, c);
pack(z, a);
return z;
}
function generatePresharedKey() {
var privateKey = new Uint8Array(32);
window.crypto.getRandomValues(privateKey);
return privateKey;
}
function generatePrivateKey() {
var privateKey = generatePresharedKey();
clamp(privateKey);
return privateKey;
}
function encodeBase64(dest, src) {
var input = Uint8Array.from([(src[0] >> 2) & 63, ((src[0] << 4) | (src[1] >> 4)) & 63, ((src[1] << 2) | (src[2] >> 6)) & 63, src[2] & 63]);
for (var i = 0; i < 4; ++i)
dest[i] = input[i] + 65 +
(((25 - input[i]) >> 8) & 6) -
(((51 - input[i]) >> 8) & 75) -
(((61 - input[i]) >> 8) & 15) +
(((62 - input[i]) >> 8) & 3);
}
function keyToBase64(key) {
var i, base64 = new Uint8Array(44);
for (i = 0; i < 32 / 3; ++i)
encodeBase64(base64.subarray(i * 4), key.subarray(i * 3));
encodeBase64(base64.subarray(i * 4), Uint8Array.from([key[i * 3 + 0], key[i * 3 + 1], 0]));
base64[43] = 61;
return String.fromCharCode.apply(null, base64);
}
function putU32(b, n)
{
b.push(n & 0xff, (n >>> 8) & 0xff, (n >>> 16) & 0xff, (n >>> 24) & 0xff);
}
function putU16(b, n)
{
b.push(n & 0xff, (n >>> 8) & 0xff);
}
function putBytes(b, a)
{
for (var i = 0; i < a.length; ++i)
b.push(a[i] & 0xff);
}
function encodeString(s)
{
var utf8 = unescape(encodeURIComponent(s));
var b = new Uint8Array(utf8.length);
for (var i = 0; i < utf8.length; ++i)
b[i] = utf8.charCodeAt(i);
return b;
}
function crc32(b)
{
if (!crc32.table) {
crc32.table = [];
for (var c = 0, n = 0; n < 256; c = ++n) {
for (var k = 0; k < 8; ++k)
c = ((c & 1) ? (0xedb88320 ^ (c >>> 1)) : (c >>> 1));
crc32.table[n] = c;
}
}
var crc = -1;
for (var i = 0; i < b.length; ++i)
crc = (crc >>> 8) ^ crc32.table[(crc ^ b[i]) & 0xff];
return (crc ^ (-1)) >>> 0;
}
function createZipFile(files)
{
var b = [];
var cd = [];
var offset = 0;
for (var i = 0; i < files.length; ++i) {
var name = encodeString(files[i].filename);
var contents = encodeString(files[i].content);
var crc = crc32(contents);
putU32(b, 0x04034b50); /* signature */
putU16(b, 20); /* version needed */
putU16(b, 0); /* flags */
putU16(b, 0); /* compression method */
putU16(b, 0); /* mtime */
putU16(b, 0); /* mdate */
putU32(b, crc); /* crc32 */
putU32(b, contents.length); /* compressed size */
putU32(b, contents.length); /* uncompressed size */
putU16(b, name.length); /* file name length */
putU16(b, 0); /* extra field length */
putBytes(b, name);
putBytes(b, contents);
putU32(cd, 0x02014b50); /* signature */
putU16(cd, 0); /* version made */
putU16(cd, 20); /* version needed */
putU16(cd, 0); /* flags */
putU16(cd, 0); /* compression method */
putU16(cd, 0); /* mtime */
putU16(cd, 0); /* mdate */
putU32(cd, crc); /* crc32 */
putU32(cd, contents.length); /* compressed size */
putU32(cd, contents.length); /* uncompressed size */
putU16(cd, name.length); /* file name length */
putU16(cd, 0); /* extra field length */
putU16(cd, 0); /* file comment length */
putU16(cd, 0); /* disk number start */
putU16(cd, 0); /* internal file attributes */
putU32(cd, 32); /* external file attributes - 'archive' bit set (32) */
putU32(cd, offset); /* relative offset of local header */
putBytes(cd, name); /* file name */
offset += 30 + contents.length + name.length
}
putBytes(b, cd); /* central directory */
putU32(b, 0x06054b50); /* end of central directory signature */
putU16(b, 0); /* number of this disk */
putU16(b, 0); /* number of disk with central directory start */
putU16(b, files.length); /* number of entries on disk */
putU16(b, files.length); /* number of entries */
putU32(b, cd.length); /* length of central directory */
putU32(b, offset); /* offset to start of central directory */
putU16(b, 0); /* zip comment size */
return Uint8Array.from(b);
}
window.wireguard = {
generateKeypair: function() {
var privateKey = generatePrivateKey();
var publicKey = generatePublicKey(privateKey);
var presharedKey = generatePresharedKey();
return {
publicKey: keyToBase64(publicKey),
privateKey: keyToBase64(privateKey),
presharedKey: keyToBase64(presharedKey)
};
},
generatePublicKey: function (privateKey){
return keyToBase64(generatePublicKey(privateKey))
},
generateZipFiles: function(res){
var files = res.peers;
var zipFile = createZipFile(files);
var blob = new Blob([zipFile], { type: "application/zip" });
var a = document.createElement("a");
a.download = res.filename;
a.href = URL.createObjectURL(blob);
a.style.display = "none";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
};
})();

1
src/static/js/wireguard.min.js vendored Normal file
View File

@@ -0,0 +1 @@
(function(){function gf(init){var r=new Float64Array(16);if(init){for(var i=0;i<init.length;++i)r[i]=init[i]}return r}function pack(o,n){var b,m=gf(),t=gf();for(var i=0;i<16;++i)t[i]=n[i];carry(t);carry(t);carry(t);for(var j=0;j<2;++j){m[0]=t[0]-65517;for(var i=1;i<15;++i){m[i]=t[i]-65535-(m[i-1]>>16&1);m[i-1]&=65535}m[15]=t[15]-32767-(m[14]>>16&1);b=m[15]>>16&1;m[14]&=65535;cswap(t,m,1-b)}for(var i=0;i<16;++i){o[2*i]=t[i]&255;o[2*i+1]=t[i]>>8}}function carry(o){var c;for(var i=0;i<16;++i){o[(i+1)%16]+=(i<15?1:38)*Math.floor(o[i]/65536);o[i]&=65535}}function cswap(p,q,b){var t,c=~(b-1);for(var i=0;i<16;++i){t=c&(p[i]^q[i]);p[i]^=t;q[i]^=t}}function add(o,a,b){for(var i=0;i<16;++i)o[i]=a[i]+b[i]|0}function subtract(o,a,b){for(var i=0;i<16;++i)o[i]=a[i]-b[i]|0}function multmod(o,a,b){var t=new Float64Array(31);for(var i=0;i<16;++i){for(var j=0;j<16;++j)t[i+j]+=a[i]*b[j]}for(var i=0;i<15;++i)t[i]+=38*t[i+16];for(var i=0;i<16;++i)o[i]=t[i];carry(o);carry(o)}function invert(o,i){var c=gf();for(var a=0;a<16;++a)c[a]=i[a];for(var a=253;a>=0;--a){multmod(c,c,c);if(a!==2&&a!==4)multmod(c,c,i)}for(var a=0;a<16;++a)o[a]=c[a]}function clamp(z){z[31]=z[31]&127|64;z[0]&=248}function generatePublicKey(privateKey){var r,z=new Uint8Array(32);var a=gf([1]),b=gf([9]),c=gf(),d=gf([1]),e=gf(),f=gf(),_121665=gf([56129,1]),_9=gf([9]);for(var i=0;i<32;++i)z[i]=privateKey[i];clamp(z);for(var i=254;i>=0;--i){r=z[i>>>3]>>>(i&7)&1;cswap(a,b,r);cswap(c,d,r);add(e,a,c);subtract(a,a,c);add(c,b,d);subtract(b,b,d);multmod(d,e,e);multmod(f,a,a);multmod(a,c,a);multmod(c,b,e);add(e,a,c);subtract(a,a,c);multmod(b,a,a);subtract(c,d,f);multmod(a,c,_121665);add(a,a,d);multmod(c,c,a);multmod(a,d,f);multmod(d,b,_9);multmod(b,e,e);cswap(a,b,r);cswap(c,d,r)}invert(c,c);multmod(a,a,c);pack(z,a);return z}function generatePresharedKey(){var privateKey=new Uint8Array(32);window.crypto.getRandomValues(privateKey);return privateKey}function generatePrivateKey(){var privateKey=generatePresharedKey();clamp(privateKey);return privateKey}function encodeBase64(dest,src){var input=Uint8Array.from([src[0]>>2&63,(src[0]<<4|src[1]>>4)&63,(src[1]<<2|src[2]>>6)&63,src[2]&63]);for(var i=0;i<4;++i)dest[i]=input[i]+65+(25-input[i]>>8&6)-(51-input[i]>>8&75)-(61-input[i]>>8&15)+(62-input[i]>>8&3)}function keyToBase64(key){var i,base64=new Uint8Array(44);for(i=0;i<32/3;++i)encodeBase64(base64.subarray(i*4),key.subarray(i*3));encodeBase64(base64.subarray(i*4),Uint8Array.from([key[i*3+0],key[i*3+1],0]));base64[43]=61;return String.fromCharCode.apply(null,base64)}function putU32(b,n){b.push(n&255,n>>>8&255,n>>>16&255,n>>>24&255)}function putU16(b,n){b.push(n&255,n>>>8&255)}function putBytes(b,a){for(var i=0;i<a.length;++i)b.push(a[i]&255)}function encodeString(s){var utf8=unescape(encodeURIComponent(s));var b=new Uint8Array(utf8.length);for(var i=0;i<utf8.length;++i)b[i]=utf8.charCodeAt(i);return b}function crc32(b){if(!crc32.table){crc32.table=[];for(var c=0,n=0;n<256;c=++n){for(var k=0;k<8;++k)c=c&1?3988292384^c>>>1:c>>>1;crc32.table[n]=c}}var crc=-1;for(var i=0;i<b.length;++i)crc=crc>>>8^crc32.table[(crc^b[i])&255];return(crc^-1)>>>0}function createZipFile(files){var b=[];var cd=[];var offset=0;for(var i=0;i<files.length;++i){var name=encodeString(files[i].filename);var contents=encodeString(files[i].content);var crc=crc32(contents);putU32(b,67324752);putU16(b,20);putU16(b,0);putU16(b,0);putU16(b,0);putU16(b,0);putU32(b,crc);putU32(b,contents.length);putU32(b,contents.length);putU16(b,name.length);putU16(b,0);putBytes(b,name);putBytes(b,contents);putU32(cd,33639248);putU16(cd,0);putU16(cd,20);putU16(cd,0);putU16(cd,0);putU16(cd,0);putU16(cd,0);putU32(cd,crc);putU32(cd,contents.length);putU32(cd,contents.length);putU16(cd,name.length);putU16(cd,0);putU16(cd,0);putU16(cd,0);putU16(cd,0);putU32(cd,32);putU32(cd,offset);putBytes(cd,name);offset+=30+contents.length+name.length}putBytes(b,cd);putU32(b,101010256);putU16(b,0);putU16(b,0);putU16(b,files.length);putU16(b,files.length);putU32(b,cd.length);putU32(b,offset);putU16(b,0);return Uint8Array.from(b)}window.wireguard={generateKeypair:function(){var privateKey=generatePrivateKey();var publicKey=generatePublicKey(privateKey);var presharedKey=generatePresharedKey();return{publicKey:keyToBase64(publicKey),privateKey:keyToBase64(privateKey),presharedKey:keyToBase64(presharedKey)}},generatePublicKey:function(privateKey){return keyToBase64(generatePublicKey(privateKey))},generateZipFiles:function(res){var files=res.peers;var zipFile=createZipFile(files);var blob=new Blob([zipFile],{type:"application/zip"});var a=document.createElement("a");a.download=res.filename;a.href=URL.createObjectURL(blob);a.style.display="none";document.body.appendChild(a);a.click();document.body.removeChild(a)}}})();

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

View File

@@ -1,78 +1,231 @@
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Wireguard Dashboard</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<link rel= "stylesheet" type= "text/css" href= "{{ url_for('static',filename='dashboard.css') }}">
<link href="https://cdn.jsdelivr.net/gh/gitbrent/bootstrap4-toggle@3.6.1/css/bootstrap4-toggle.min.css" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.4.1/font/bootstrap-icons.css">
</head>
<!-- configuration.html - < WGDashboard > - Copyright(C) 2021 Donald Zou [https://github.com/donaldzou]-->
<html lang="en">
{% with title=title%}
{% include "header.html"%}
{% endwith %}
<body>
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow">
<a class="navbar-brand col-md-3 col-lg-2 mr-0 px-3" href="/">Wireguard Dashboard</a>
<button class="navbar-toggler position-absolute d-md-none collapsed" type="button" data-toggle="collapse"
data-target="#sidebarMenu" aria-controls="sidebarMenu" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
</nav>
<div class="container-fluid">
<div class="row">
<div class="row">
<nav id="sidebarMenu" class="col-md-3 col-lg-2 d-md-block bg-light sidebar collapse">
<div class="sidebar-sticky pt-3">
<ul class="nav flex-column">
<li class="nav-item"><a class="nav-link" href="/">Home</a></li>
</ul>
<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
<span>Configurations</span>
</h6>
<ul class="nav flex-column">
{% for i in conf%}
<li class="nav-item"><a class="nav-link" href="/configuration/{{i['conf']}}">{{i['conf']}}</a></li>
{%endfor%}
</ul>
</div>
</nav>
</div>
</div>
<div id="config_body">
<div class="no-response">
<div class="container">
<h1 class="text-white display-1"><i class="bi bi-emoji-frown-fill"></i></h1>
<h4 class="text-white">Oops!<br>I can't connect to the server.</h4>
</div>
</div>
{% include "navbar.html" %}
</div>
<div class="container-fluid" id="right_body">
{% include "sidebar.html" %}
<div class="col-md-9 ml-sm-auto col-lg-10 px-md-4 mt-4 mb-4">
<div class="form-group">
<input type="text" class="form-control" id="search_peer_textbox" placeholder="Search Peer..." value="">
</div>
</div>
<div id="config_body">
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4 mt-4 mb-4">
<div class="info mt-4">
<div id="config_info_alert"></div>
<div class="row">
<div class="col">
<small class="text-muted"><strong>CONFIGURATION</strong></small>
<h1 class="mb-3"><samp id="conf_name">{{ title }}</samp></h1>
</div>
<div class="col">
<small class="text-muted"><strong>SWITCH</strong></small><br>
<div id="conf_status_btn" class="info_loading"></div>
<div class="spinner-border text-primary" role="status" style="display: none; margin-top: 10px">
<span class="sr-only">Loading...</span>
</div>
</div>
<div class="w-100"></div>
<div class="col">
<small class="text-muted"><strong>STATUS</strong></small>
<h6 style="text-transform: uppercase;" id="conf_status" class="info_loading"></h6>
</div>
<div class="col">
<small class="text-muted"><strong>CONNECTED PEERS</strong></small>
<h6 style="text-transform: uppercase;" id="conf_connected_peers" class="info_loading"></h6>
</div>
<div class="col-sm">
<small class="text-muted"><strong>TOTAL DATA USAGE</strong></small>
<h6 style="text-transform: uppercase;" id="conf_total_data_usage" class="info_loading"></h6>
</div>
<div class="col-sm">
<small class="text-muted"><strong>TOTAL RECEIVED</strong></small>
<h6 style="text-transform: uppercase;" id="conf_total_data_received" class="info_loading"></h6>
</div>
<div class="col-sm">
<small class="text-muted"><strong>TOTAL SENT</strong></small>
<h6 style="text-transform: uppercase;" id="conf_total_data_sent" class="info_loading"></h6>
</div>
<div class="w-100"></div>
<div class="col-sm">
<small class="text-muted">
<strong>PUBLIC KEY</strong>
<strong style="margin-left: auto!important; opacity: 0; transition: 0.2s ease-in-out" class="text-primary">CLICK TO COPY</strong>
</small>
<h6 class="info_loading"><samp class="key" id="conf_public_key"></samp></h6>
</div>
<div class="col-sm">
<small class="text-muted"><strong>LISTEN PORT</strong></small>
<h6 style="text-transform: uppercase;" class="info_loading"><samp id="conf_listen_port"></samp></h6>
</div>
<div class="col-sm">
<small class="text-muted"><strong>ADDRESS</strong></small>
<h6 style="text-transform: uppercase;" class="info_loading"><samp id="conf_address"></samp></h6>
</div>
</div>
</div>
<hr>
<div class="button-div mb-3">
<div class="row">
<div class="col-sm">
<div class="form-group">
<label for="sort_by_dropdown"><small class="text-muted">Sort Peers By</small></label>
<select class="form-control" id="sort_by_dropdown">
<option value="status">Status</option>
<option value="name">Name</option>
<option value="allowed_ip">Allowed IP</option>
</select>
</div>
</div>
<div class="col-sm">
<div class="form-group">
<label><small class="text-muted">Refresh Interval</small></label><br>
<div class="btn-group interval-btn-group" role="group" style="width: 100%">
<button style="width: 20%" type="button" class="btn btn-outline-primary btn-group-label refresh" data-toggle="tooltip" data-placement="bottom" title="Refresh Peers"><i class="bi bi-arrow-repeat"></i></button>
<button style="width: 20%" type="button" class="btn btn-outline-primary update_interval" data-refresh-interval="5000">5s</button>
<button style="width: 20%" type="button" class="btn btn-outline-primary update_interval" data-refresh-interval="10000">10s</button>
<button style="width: 20%" type="button" class="btn btn-outline-primary update_interval" data-refresh-interval="30000">30s</button>
<button style="width: 20%" type="button" class="btn btn-outline-primary update_interval" data-refresh-interval="60000">1m</button>
</div>
</div>
</div>
<div class="col-sm">
<div class="form-group">
<label><small class="text-muted">Display Mode</small></label><br>
<div class="btn-group display-btn-group" role="group" style="width: 100%">
<button style="width: 20%" type="button" class="btn btn-outline-primary display_mode" data-display-mode="grid"><i class="bi bi-grid-fill" style="font-size: 1.5rem;"></i></button>
<button style="width: 20%" type="button" class="btn btn-outline-primary display_mode" data-display-mode="list"><i class="bi bi-list" style="font-size: 1.5rem;"></i></button>
</div>
</div>
</div>
<div class="btn-manage-group">
<button type="button" class="btn btn-primary add_btn"><i class="bi bi-plus-circle-fill" style=""></i></button>
<button type="button" class="btn btn-secondary setting_btn"><i class="bi bi-three-dots"></i></button>
<div class="setting_btn_menu">
<a class="text-danger" id="delete_peers_by_bulk_btn"><i class="bi bi-trash-fill"></i> Delete Peers</a>
<a class="text-info" id="download_all_peers" data-url="/download_all/{{conf_data['name']}}"><i class="bi bi-cloud-download-fill"></i> Download All Peers</a>
</div>
</div>
</div>
</div>
<div class="row peer_list"></div>
</main>
</div>
</div>
<div class="modal fade" id="add_modal" data-backdrop="static" data-keyboard="false" tabindex="-1"
aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">Add a new peer</h5>
<h5 class="modal-title" id="staticBackdropLabel">Add New Peer</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div>
<div class="custom-control custom-switch" style="margin-bottom: 1rem">
<input class="custom-control-input" type="checkbox" id="bulk_add">
<label class="custom-control-label" for="bulk_add"><strong>Add Peers by bulk</strong></label>
<i class="bi bi-question-circle-fill" style="cursor: pointer" data-container="body" data-toggle="popover" data-placement="right" data-trigger="click" data-content="By adding peers by bulk, each peer's name will be auto generated, and Allowed IP will be assign to the next available IP."></i>
</div>
<div class="form-group" style="margin: 0">
<input type="number" class="form-control" id="new_add_amount" min="1" placeholder="Amount" disabled>
<div id="bulk_amount_validation" class="invalid-feedback"></div>
</div>
</div>
<hr>
<div id="add_peer_alert" class="alert alert-danger alert-dismissible fade show d-none" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<form>
<div class="form-group">
<label for="public_key">Public Key</label>
<input type="text" class="form-control" id="public_key" aria-describedby="public_key">
</div>
<div class="form-group">
<label for="allowed_ips">Allowed IPs</label>
<input type="text" class="form-control" id="allowed_ips">
<form id="add_peer_form">
<div class="form-group non-bulk">
<div>
<label for="private_key">Private Key</label>
</div>
<div class="input-group">
<input type="text" class="form-control" id="private_key" aria-describedby="private_key">
<div class="input-group-append">
<button type="button" class="btn btn-danger" id="re_generate_key" data-toggle="tooltip" data-placement="top" title="Regenerate Key"><i class="bi bi-arrow-repeat"></i></button>
</div>
</div>
</div>
<div class="form-group non-bulk">
<label for="public_key">Public Key <code>(Required)</code></label>
<input type="text" class="form-control" id="public_key" aria-describedby="public_key" disabled>
</div>
<div class="row">
<div class="col-sm-6 non-bulk">
<div class="form-group">
<label for="new_add_name">Name</label>
<input type="text" class="form-control" id="new_add_name">
</div>
</div>
<div class="col-sm-6 non-bulk">
<div class="form-group">
<label for="allowed_ips">Allowed IPs <code>(Required)</code></label>
<div class="input-group">
<input type="text" class="form-control" id="allowed_ips">
<div class="input-group-append">
<button type="button" class="btn btn-primary" id="search_available_ip" data-toggle="tooltip" data-placement="top" title="Search Available IPs">
<i class="bi bi-search"></i>
</button>
</div>
</div>
<p style="position: absolute; top: 4px; right: 1rem;" class="text-success" id="allowed_ips_indicator"></p>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="new_add_DNS">DNS <code>(Required)</code></label>
<input type="text" class="form-control" id="new_add_DNS" value="{{ DNS }}">
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="new_add_endpoint_allowed_ip">Endpoint Allowed IPs <code>(Required)</code></label>
<input type="text" class="form-control" id="new_add_endpoint_allowed_ip" value="{{ endpoint_allowed_ip }}">
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="new_add_MTU">MTU</label>
<input type="text" class="form-control" id="new_add_MTU" value="{{ mtu }}">
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="new_add_keep_alive">Persistent keepalive</label>
<input type="text" class="form-control" id="new_add_keep_alive" value="{{ keep_alive }}">
</div>
</div>
<div class="col-sm">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="enable_preshare_key" name="enable_preshare_key" value="enable_psk">
<label class="form-check-label" for="enable_preshare_key">Use Pre-shared Key</label>
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="save_peer" conf_id={{conf_data['name']}}>Save</button>
<button type="button" class="btn btn-primary" id="save_peer" conf_id={{conf_data['name']}}>Add</button>
</div>
</div>
</div>
@@ -93,7 +246,7 @@
<span aria-hidden="true">&times;</span>
</button>
</div>
<h6>This action is not reversible.</h6>
<h6 style="margin: 0">This action is not reversible.</h6>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">No</button>
@@ -102,36 +255,150 @@
</div>
</div>
</div>
<div class="modal fade" id="setting_modal" data-backdrop="static" data-keyboard="false" tabindex="-1"
aria-labelledby="staticBackdropLabel" aria-hidden="true" conf_id={{conf_data['name']}} peer_id="">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="peer_name"></h5>
<h5 class="modal-title peer_name"></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div id="setting_peer_alert" class="alert alert-danger alert-dismissible fade show d-none" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="mb-3">
<label for="peer_private_key_textbox" class="form-label">Private Key <code>(Required for QR Code and download)</code></label>
<input type="password" class="form-control" id="peer_private_key_textbox" style="padding-right: 40px">
<a class="peer_private_key_textbox_switch"><i class="bi bi-eye-fill"></i></a>
</div>
<div>
<label for="peer_preshared_key_textbox" class="form-label">Pre-Shared Key</label>
<input type="text" class="form-control" id="peer_preshared_key_textbox">
</div>
<hr>
<div class="mb-3">
<label for="peer_name" class="form-label">Name</label>
<input type="text" class="form-control" id="peer_name_textbox" placeholder="">
<div class="row">
<div class="col-sm-6">
<div class="mb-3">
<label for="peer_name_textbox" class="form-label">Name</label>
<input type="text" class="form-control" id="peer_name_textbox" placeholder="">
</div>
</div>
<div class="col-sm-6">
<div class="mb-3">
<label for="peer_allowed_ip_textbox" class="form-label">Allowed IPs <code>(Required)</code></label>
<input type="text" class="form-control" id="peer_allowed_ip_textbox">
</div>
</div>
<div class="col-sm-6">
<div class="mb-3">
<label for="peer_DNS_textbox" class="form-label">DNS <code>(Required)</code></label>
<input type="text" class="form-control" id="peer_DNS_textbox">
</div>
</div>
<div class="col-sm-6">
<div class="mb-3">
<label for="peer_endpoint_allowed_ips" class="form-label">Endpoint Allowed IPs <code>(Required)</code></label>
<input type="text" class="form-control" id="peer_endpoint_allowed_ips">
</div>
</div>
<div class="col-sm-6">
<div class="mb-3">
<label for="peer_mtu" class="form-label">MTU</label>
<input type="text" class="form-control" id="peer_mtu">
</div>
</div>
<div class="col-sm-6">
<div class="mb-3">
<label for="peer_keep_alive" class="form-label">Persistent Keepalive</label>
<input type="text" class="form-control" id="peer_keep_alive">
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="save_peer_name" conf_id={{conf_data['name']}} peer_id="">Save</button>
<button type="button" class="btn btn-primary" id="save_peer_setting" conf_id={{conf_data['name']}} peer_id="">Save</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="available_ip_modal" data-backdrop="static" data-keyboard="false">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">Select available IP</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="selected_ip" style="padding: 1rem; border-bottom: 1px solid #dee2e6;">
<small class="text-muted"><strong>SELECTED IP (CLICK TO REMOVE)</strong></small>
<div id="selected_ip_list"></div>
</div>
<div class="modal-body" style="max-height: 400px; overflow-y: scroll;">
<div class="list-group"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="confirm_ip">Confirm</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="delete_bulk_modal" data-backdrop="static" data-keyboard="false">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">Select Peers to Delete</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div id="bulk_remove_peer_alert" class="alert alert-danger alert-dismissible fade show d-none" role="alert" style="margin: 1rem">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="selected_peers" style="padding: 1rem; border-bottom: 1px solid #dee2e6;">
<small class="text-muted"><strong>SELECTED PEERS (CLICK TO REMOVE)</strong></small>
<div id="selected_peer_list"></div>
</div>
<div class="modal-body" style="max-height: 400px; overflow-y: scroll;">
<div class="list-group"></div>
</div>
<div class="modal-footer">
<a class="text-danger" id="select_all_delete_bulk_peers" style="cursor: pointer; margin-right: auto;"><small><strong>SELECT ALL</strong></small></a>
<button type="button" class="btn btn-danger" id="confirm_delete_bulk_peers" disabled data-conf="{{conf_data['name']}}">Delete</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="qrcode_modal" data-backdrop="static" data-keyboard="false" tabindex="-1"
aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">QR Code</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<img id="qrcode_img" style="width: 100%">
</div>
</div>
</div>
</div>
<div class="position-fixed top-0 right-0 p-3" style="z-index: 5; right: 0; top: 50px;">
<div id="alertToast" class="toast hide" role="alert" aria-live="assertive" aria-atomic="true" data-delay="5000">
<div class="toast-header">
<strong class="mr-auto">Wireguard Dashboard</strong>
<strong class="mr-auto">WGDashboard</strong>
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
@@ -140,150 +407,20 @@
</div>
</div>
</div>
{% include "tools.html" %}
</body>
<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"
integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.min.js"
integrity="sha384-w1Q4orYjBQndcko6MimVbzY0tgp4pWB4lZ7lr30WKz0vr/aWKhXdBNmNb5D92v7s"
crossorigin="anonymous"></script>
{% include "footer.html" %}
<script src="{{ url_for('static',filename='js/wireguard.min.js') }}"></script>
<script src="{{ url_for('static',filename='js/configuration.min.js') }}"></script>
<script>
function load_data(){
$.ajax({
method: "GET",
url: "/get_config/"+"{{conf_data['name']}}",
headers:{
"Content-Type": "application/json"
},
async:false,
success: function (response){
$("#config_body").html(response);
$.ajax({
url: "{{ url_for('static',filename='bootstrap4-toggle.min.js') }}",
dataType: "script",
cache: true
});
}
})
}
$(document).ready(function(){
load_data();
setInterval(function(){
load_data();
}, 10000)
/* global peers */
let load_timeout;
let load_interval = 0;
let conf_name = "{{ conf_data['name'] }}"
let peers = [];
$(".sb-"+conf_name+"-url").addClass("active");
$(function(){
configurations.loadPeers($('#search_peer_textbox').val());
});
</script>
<script>
$('.switch').change(function() {
if ($(this).prop('checked') == false){
if (confirm('Are you sure you want to turn off this connection?')){
location.replace("/switch/"+$(this).attr('id'));
}
}
else{
location.replace("/switch/"+$(this).attr('id'));
}
});
$("#save_peer").click(function(){
if ($("#allowed_ips") != "" && $("#public_key") != ""){
var conf = $(this).attr('conf_id')
$.ajax({
method: "POST",
url: "/add_peer/"+conf,
headers:{
"Content-Type": "application/json"
},
data: JSON.stringify({"public_key":$("#public_key").val(), "allowed_ips": $("#allowed_ips").val()}),
success: function (response){
if(response != "true"){
$("#add_peer_alert").html(response+$("#add_peer_alert").html());
$("#add_peer_alert").removeClass("d-none");
}
else{
location.reload();
}
}
})
}
})
$("body").on("click", ".btn-delete-peer", function(){
var peer_id = $(this).attr("id");
$("#delete_peer").attr("peer_id", peer_id);
})
$(".btn-delete-peer").click(function(){
});
$("#delete_peer").click(function(){
var peer_id = $(this).attr("peer_id");
var config = $(this).attr("conf_id");
$.ajax({
method: "POST",
url: "/remove_peer/"+config,
headers:{
"Content-Type": "application/json"
},
data: JSON.stringify({"action": "delete", "peer_id": peer_id}),
success: function (response){
if(response != "true"){
$("#remove_peer_alert").html(response+$("#add_peer_alert").html());
$("#remove_peer_alert").removeClass("d-none");
}
else{
location.reload();
}
}
})
});
var myModal = new bootstrap.Modal(document.getElementById('setting_modal'), {
keyboard: false
})
$("body").on("click", ".btn-setting-peer", function(){
myModal.toggle();
var peer_id = $(this).attr("id");
$("#save_peer_name").attr("peer_id", peer_id);
$.ajax({
method: "POST",
url: "/get_peer_name/"+$("#setting_modal").attr("conf_id"),
headers:{
"Content-Type": "application/json"
},
data: JSON.stringify({"id": peer_id}),
success: function(response){
if (response == ""){
$("#setting_modal .peer_name").html("Untitled Peer");
}else{
$("#setting_modal .peer_name").html(response);
$("#peer_name_textbox").val(response)
}
}
})
});
$("#save_peer_name").click(function (){
var peer_id = $(this).attr("peer_id");
$.ajax({
method: "POST",
url: "/save_peer_name/"+"{{conf_data['name']}}",
headers:{
"Content-Type": "application/json"
},
data: JSON.stringify({id: peer_id, name: $("#peer_name_textbox").val()}),
success: function (response){
myModal.toggle();
load_data();
$('#alertToast').toast('show');
$('#alertToast .toast-body').html("Name Saved!");
}
})
})
</script>
</html>

View File

@@ -0,0 +1,3 @@
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-fQybjgWLrvvRgtW6bFlB7jaZrFsaBXjsOMm/tB9LTS58ONXgqbR9W8oWht/amnpF" crossorigin="anonymous"></script>
<script src="{{ url_for('static',filename='js/tools.min.js') }}"></script>

View File

@@ -1,20 +1,35 @@
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4 mt-4">
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4 mt-4 mb-4">
<div class="info mt-4">
{% if conf_data['listen_port'] == "" and conf_data['status'] == "stopped" %}
<div class="alert alert-warning" role="alert">Peer QR Code and configuration file download required a specified <strong>Listen Port</strong>.</div>
{% endif %}
{% if conf_data['conf_address'] == "N/A" %}
<div class="alert alert-warning" role="alert">
Configuration <strong>Address</strong> not be specified to have peer connect to it.
</div>
{% endif %}
<div class="row">
<div class="col-sm">
<div class="col">
<small class="text-muted"><strong>CONFIGURATION</strong></small>
<h1 class="mb-3">{{conf_data['name']}}</h1>
<h1 class="mb-3"><samp>{{conf_data['name']}}</samp></h1>
</div>
<div class="col-sm">
<div class="col">
<small class="text-muted"><strong>ACTION</strong></small><br>
<input class="mt-2 switch" id="{{conf_data['name']}}" type="checkbox" data-toggle="toggle" {{conf_data['checked']}} data-size="sm">
{% if conf_data['checked'] == "checked" %}
<a href="#" id="{{conf_data['name']}}" {{conf_data['checked']}} class="switch text-primary"><i class="bi bi-toggle2-on"></i> ON</a>
{% else %}
<a href="#" id="{{conf_data['name']}}" {{conf_data['checked']}} class="switch text-secondary"><i class="bi bi-toggle2-off"></i> OFF</a>
{% endif %}
<div class="spinner-border text-primary" role="status" style="display: none; margin-top: 10px">
<span class="sr-only">Loading...</span>
</div>
</div>
<div class="w-100"></div>
<div class="col-sm">
<div class="col">
<small class="text-muted"><strong>STATUS</strong></small>
<h6 style="text-transform: uppercase;">{{conf_data['status']}}<span class="dot dot-{{conf_data['status']}}"></span></h6>
</div>
<div class="col-sm">
<div class="col">
<small class="text-muted"><strong>CONNECTED PEERS</strong></small>
<h6 style="text-transform: uppercase;">{{conf_data['running_peer']}}</h6>
</div>
@@ -23,7 +38,7 @@
<h6 style="text-transform: uppercase;">{{conf_data['total_data_usage'][0]}} GB</h6>
</div>
<div class="col-sm">
<small class="text-muted"><strong>TOTAL RECIEVED</strong></small>
<small class="text-muted"><strong>TOTAL RECEIVED</strong></small>
<h6 style="text-transform: uppercase;">{{conf_data['total_data_usage'][1]}} GB</h6>
</div>
<div class="col-sm">
@@ -32,81 +47,138 @@
</div>
<div class="w-100"></div>
<div class="col-sm">
<small class="text-muted"><strong>PUBLIC KEY</strong></small>
<h6 style="text-transform: uppercase;"><samp>{{conf_data['public_key']}}</samp></h6>
<small class="text-muted">
<strong>PUBLIC KEY</strong>
<strong style="margin-left: auto!important; opacity: 0; transition: 0.2s ease-in-out" class="text-primary">CLICK TO COPY</strong>
</small>
<h6><samp class="key">{{conf_data['public_key']}}</samp></h6>
</div>
<div class="col-sm">
<small class="text-muted"><strong>LISTEN PORT</strong></small>
<h6 style="text-transform: uppercase;"><samp>{{conf_data['listen_port']}}</samp></h6>
<h6 style="text-transform: uppercase;"><samp>
{% if conf_data['listen_port'] == "" %}
N/A
{% else %}
{{conf_data['listen_port']}}
{% endif %}
</samp></h6>
</div>
<div class="col-sm">
<small class="text-muted"><strong>ADDRESS</strong></small>
<h6 style="text-transform: uppercase;"><samp>{{conf_data['conf_address']}}</samp></h6>
</div>
</div>
<hr>
<div class="button-div" style="text-align: right;">
<button type="button" class="btn btn-outline-primary btn-sm mb-3" data-toggle="modal" data-target="#add_modal">
<strong>ADD PEER</strong>
</button>
<div class="button-div mb-3">
<div class="row">
<div class="col-sm">
<div class="form-group">
<label for="sort_by_dropdown"><small class="text-muted">Sort Peers By</small></label>
<select class="form-control" id="sort_by_dropdown">
<option value="status" {% if sort_tag == "status" %} {{ "selected" }} {% endif %}>Status</option>
<option value="name" {% if sort_tag == "name" %} {{ "selected" }} {% endif %}>Name</option>
<option value="allowed_ip" {% if sort_tag == "allowed_ip" %} {{ "selected" }} {% endif %}>Allowed IP</option>
</select>
</div>
</div>
<div class="col-sm">
<div class="form-group">
<label><small class="text-muted">Refresh Interval</small></label><br>
<div class="btn-group" role="group" style="width: 100%">
<button style="width: 20%" type="button" class="btn btn-outline-primary btn-group-label refresh"><i class="bi bi-arrow-repeat"></i></button>
<button style="width: 20%" type="button" class="btn btn-outline-primary update_interval {% if dashboard_refresh_interval == 5000 %} {{ "active" }} {% endif %}" refresh-interval="5000">5s</button>
<button style="width: 20%" type="button" class="btn btn-outline-primary update_interval {% if dashboard_refresh_interval == 10000 %} {{ "active" }} {% endif %}" refresh-interval="10000">10s</button>
<button style="width: 20%" type="button" class="btn btn-outline-primary update_interval {% if dashboard_refresh_interval == 30000 %} {{ "active" }} {% endif %}" refresh-interval="30000">30s</button>
<button style="width: 20%" type="button" class="btn btn-outline-primary update_interval {% if dashboard_refresh_interval == 60000 %} {{ "active" }} {% endif %}" refresh-interval="60000">1m</button>
</div>
</div>
</div>
<div class="col-sm">
<div class="form-group">
<label><small class="text-muted">Display Mode</small></label><br>
<div class="btn-group" role="group" style="width: 100%">
<button style="width: 20%" type="button" class="btn btn-outline-primary display_mode {% if peer_display_mode == "grid" %} {{ "active" }} {% endif %}" display-mode="grid"><i class="bi bi-grid-fill" style="font-size: 1.5rem;"></i></button>
<button style="width: 20%" type="button" class="btn btn-outline-primary display_mode {% if peer_display_mode == "list" %} {{ "active" }} {% endif %}" display-mode="list"><i class="bi bi-list" style="font-size: 1.5rem;"></i></button>
</div>
</div>
</div>
<button type="button" class="btn btn-primary add_btn" data-toggle="modal" data-target="#add_modal">
<i class="bi bi-plus-circle-fill" style=""></i> Add Peer
</button>
</div>
</div>
</div>
{% for i in conf_data['peer_data']%}
<div class="card mb-3">
<div class="card-header">
{% if not i['name']%}
{{ "Untitled Peer" }}
{% else %}
{{i['name']}}
{% endif %}
</div>
<div class="card-body">
<div class="row">
<div class="col-sm">
<small class="text-muted"><strong>STATUS</strong></small>
<h6 style="text-transform: uppercase;">{{i['status']}}<span class="dot dot-{{i['status']}}"></span></h6>
</div>
<div class="col-sm">
<small class="text-muted"><strong>PEER</strong></small>
<h6><samp>{{i['id']}}</samp></h6>
</div>
<div class="w-100"></div>
<div class="col-sm">
<small class="text-muted"><strong>ALLOWED IP</strong></small>
<h6 style="text-transform: uppercase;">{{i['allowed_ip']}}</h6>
</div>
<div class="col-sm">
<small class="text-muted"><strong>LATEST HANDSHAKE</strong></small>
<h6 style="text-transform: uppercase;">{{i['latest_handshake']}}</h6>
</div>
<div class="col-sm">
<small class="text-muted"><strong>END POINT</strong></small>
<h6 style="text-transform: uppercase;">{{i['endpoint']}}</h6>
</div>
<div class="w-100"></div>
{# <div class="col-sm">#}
{# <small class="text-muted"><strong>TOTAL DATA USAGE</strong></small>#}
{# <h6 style="text-transform: uppercase;">{{i['total_data']}} GB</h6>#}
{# </div>#}
<div class="col-sm">
<small class="text-muted"><strong>TOTAL RECEIVED</strong></small>
<h6 style="text-transform: uppercase;"><i class="bi bi-arrow-down-right"></i> {{i['total_receive']}} GB</h6>
</div>
<div class="col-sm">
<small class="text-muted"><strong>TOTAL SENT</strong></small>
<h6 style="text-transform: uppercase;"><i class="bi bi-arrow-up-right"></i> {{i['total_sent']}} GB</h6>
</div>
<div class="w-100"></div>
<div class="col-sm">
<!-- <small class="text-muted"><strong>ACTION</strong></small> -->
<div class="button-group">
<hr>
<button type="button" class="btn btn-outline-primary btn-setting-peer btn-control" id="{{i['id']}}" data-toggle="modal"><i class="bi bi-gear-fill"></i></button>
<button type="button" class="btn btn-outline-danger btn-delete-peer btn-control" id="{{i['id']}}" data-toggle="modal" data-target="#delete_modal"><i class="bi bi-x-circle-fill"></i></button>
</div>
</div>
</div>
</div>
</div>
{%endfor%}
<div class="row peer_list">
{% if conf_data['peer_data']|length == 0 %}
<div class="col-12" style="text-align: center; margin-top: 1.5rem"><h3 class="text-muted">Oops! No peers found ‘︿’</h3></div>
{% endif %}
{% for i in conf_data['peer_data']%}
{% if peer_display_mode == "list" %}
<div class="col-12">
{% else %}
<div class="col-sm-6 col-lg-4">
{% endif %}
<div class="card mb-3 card-{{i['status']}}">
<div class="card-body">
<div class="row">
<div class="col-sm">
<h4>
{% if not i['name']%}
{{ "Untitled" }}
{% else %}
{{i['name']}}
{% endif %}
</h4>
</div>
<div class="w-100"></div>
<div class="col-6">
<small class="text-muted"><strong>STATUS</strong></small>
<h6 style="text-transform: uppercase;" class="mb-2 h6-dot-{{i['status']}}"><span class="dot dot-{{i['status']}}" style="margin-left: 0 !important;margin-top: 5px"></span></h6>
</div>
<div class="col-6 peer_data_group" style="text-align: right">
<small class="text-muted"><strong>TRANSFER</strong></small>
<p class="text-primary" style="text-transform: uppercase; margin-bottom: 0;"><small><i class="bi bi-arrow-down-right"></i> {{i['total_receive']}} GB</small></p>
<p class="text-success" style="text-transform: uppercase; margin-bottom: 0"><small><i class="bi bi-arrow-up-right"></i> {{i['total_sent']}} GB</small></p>
</div>
<div class="col-sm">
<small class="text-muted" style="display: flex">
<strong>PEER</strong>
<strong style="margin-left: auto!important; opacity: 0; transition: 0.2s ease-in-out" class="text-primary">CLICK TO COPY</strong></small>
<h6><samp class="ml-auto key">{{i['id']}}</samp></h6>
</div>
<div class="col-sm">
<small class="text-muted"><strong>ALLOWED IP</strong></small>
<h6 style="text-transform: uppercase;">{{i['allowed_ip']}}</h6>
</div>
<div class="col-sm">
<small class="text-muted"><strong>LATEST HANDSHAKE</strong></small>
<h6 style="text-transform: uppercase;">{{i['latest_handshake']}}</h6>
</div>
<div class="w-100"></div>
<div class="col-sm">
<small class="text-muted"><strong>END POINT</strong></small>
<h6 style="text-transform: uppercase;">{{i['endpoint']}}</h6>
</div>
<div class="w-100"></div>
<div class="col-sm"><hr><div class="button-group" style="display:flex"><button type="button" class="btn btn-outline-primary btn-setting-peer btn-control" id="{{i['id']}}" data-toggle="modal"><i class="bi bi-gear-fill"></i></button><button type="button" class="btn btn-outline-danger btn-delete-peer btn-control" id="{{i['id']}}" data-toggle="modal"><i class="bi bi-x-circle-fill"></i></button>
{% if i['private_key'] %}
<div class="share_peer_btn_group" style="margin-left: auto !important; display: inline">
<button type="button" class="btn btn-outline-success btn-qrcode-peer btn-control" img_src="/qrcode/{{ conf_data['name'] }}?id={{ i['id']|urlencode }}">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" style="width: 19px;" fill="#28a745"><path d="M3 11h8V3H3v8zm2-6h4v4H5V5zM3 21h8v-8H3v8zm2-6h4v4H5v-4zM13 3v8h8V3h-8zm6 6h-4V5h4v4zM13 13h2v2h-2zM15 15h2v2h-2zM13 17h2v2h-2zM17 17h2v2h-2zM19 19h2v2h-2zM15 19h2v2h-2zM17 13h2v2h-2zM19 15h2v2h-2z"/></svg>
</button>
<a href="/download/{{ conf_data['name'] }}?id={{ i['id']|urlencode }}" class="btn btn-outline-info btn-download-peer btn-control">
<i class="bi bi-download"></i>
</a>
</div>
{% endif %}
</div>
</div>
</div>
</div>
</div>
</div>
{%endfor%}
</div>
</main>

18
src/templates/header.html Normal file
View File

@@ -0,0 +1,18 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>{{ title }} | WGDashboard</title>
<link rel="manifest" href="{{ url_for('static',filename='json/manifest.json') }}">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="application-name" content="e">
<meta name="apple-mobile-web-app-title" content="e">
<meta name="msapplication-starturl" content="/">
<link rel="apple-touch-icon" sizes="192x192" href="{{ url_for('static',filename='img/192x192ios.png') }}">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="icon" href="{{ url_for('static',filename='img/logo.png') }}"/>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css" integrity="sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn" crossorigin="anonymous">
<link rel= "stylesheet" type= "text/css" href= "{{ url_for('static',filename='css/dashboard.css') }}">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.4.1/font/bootstrap-icons.css">
</head>

View File

@@ -1,96 +1,78 @@
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Wireguard Dashboard</title>
<link rel="icon" href="{{ url_for('static',filename='logo.png') }}"/>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<link rel= "stylesheet" type= "text/css" href= "{{ url_for('static',filename='dashboard.css') }}">
<link href="https://cdn.jsdelivr.net/gh/gitbrent/bootstrap4-toggle@3.6.1/css/bootstrap4-toggle.min.css" rel="stylesheet">
</head>
<!-- index.html - < WGDashboard > - Copyright(C) 2021 Donald Zou [https://github.com/donaldzou]-->
<html lang="en">
{% with %}
{% set title="Home" %}
{% include "header.html"%}
{% endwith %}
<body>
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow">
<a class="navbar-brand col-md-3 col-lg-2 mr-0 px-3" href="#">Wireguard Dashboard</a>
<button class="navbar-toggler position-absolute d-md-none collapsed" type="button" data-toggle="collapse"
data-target="#sidebarMenu" aria-controls="sidebarMenu" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
</nav>
{% include "navbar.html" %}
<div class="container-fluid">
<div class="row">
<div class="row">
<nav id="sidebarMenu" class="col-md-3 col-lg-2 d-md-block bg-light sidebar collapse">
<div class="sidebar-sticky pt-3">
<ul class="nav flex-column">
<li class="nav-item"><a class="nav-link" href="/">Home</a></li>
</ul>
<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
<span>Configurations</span>
</h6>
<ul class="nav flex-column">
{% for i in conf%}
<li class="nav-item"><a class="nav-link" href="/configuration/{{i['conf']}}">{{i['conf']}}</a></li>
{%endfor%}
</ul>
</div>
</nav>
</div>
</div>
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4">
{% include "sidebar.html" %}
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4 mb-4">
<div style="display: flex; flex-direction: row; align-items: center;">
<h1 class="pb-4 mt-4">Home</h1>
</div>
{% if msg != "" %}
<div class="alert alert-danger" role="alert">
Configuration toggle failed. Please check the following error message:
</div>
<pre class="index-alert"><code>{{ msg }}</code></pre>
{% endif %}
{% if conf == [] %}
<p class="text-muted">You don't have any WireGuard configurations yet. Please check the configuration folder or change it in "Settings". By default the folder is "/etc/wireguard".</p>
{% endif %}
{% for i in conf%}
<div class="card mt-3">
<div class="card mt-3 conf_card">
<div class="card-body">
<div class="row">
<div class="col">
<div class="col card-col">
<small class="text-muted"><strong>CONFIGURATION</strong></small>
<a href="/configuration/{{i['conf']}}">
<h5 class="card-title">{{i['conf']}}</h5>
<a href="/configuration/{{i['conf']}}" class="conf_link">
<h6 class="card-title" style="margin:0 !important;"><samp>{{i['conf']}}</samp></h6>
</a>
</div>
<div class="col">
<input class="mt-2 switch" id="{{i['conf']}}" type="checkbox" data-toggle="toggle" {{i['checked']}} data-size="sm">
</div>
<div class="w-100"></div>
<div class="col-sm">
<div class="col card-col">
<small class="text-muted"><strong>STATUS</strong></small>
<h6 style="text-transform: uppercase;">{{i['status']}}<span class="dot dot-{{i['status']}}"></span></h6>
<h6 style="text-transform: uppercase; margin:0 !important;">{{i['status']}}<span class="dot dot-{{i['status']}}"></span></h6>
</div>
<div class="col-sm">
<div class="col-md card-col">
<small class="text-muted"><strong>PUBLIC KEY</strong></small>
<h6 style="text-transform: uppercase;"><samp>{{i['public_key']}}</samp></h6>
<h6 style="margin:0 !important;"><samp>{{i['public_key']}}</samp></h6>
</div>
<div class="col-md index-switch">
{% if i['checked'] == "checked" %}
<a href="#" id="{{i['conf']}}" {{i['checked']}} class="switch text-primary tt"><i class="bi bi-toggle2-on"></i></a>
{% else %}
<a href="#" id="{{i['conf']}}" {{i['checked']}} class="switch text-secondary"><i class="bi bi-toggle2-off"></i></a>
{% endif %}
<div class="spinner-border text-primary" role="status" style="display: none">
<span class="sr-only">Loading...</span>
</div>
</div>
</div>
</div>
</div>
{%endfor%}
</main>
</div>
{% include "tools.html" %}
</body>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"
integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"
integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.min.js"
integrity="sha384-w1Q4orYjBQndcko6MimVbzY0tgp4pWB4lZ7lr30WKz0vr/aWKhXdBNmNb5D92v7s"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/gh/gitbrent/bootstrap4-toggle@3.6.1/js/bootstrap4-toggle.min.js"></script>
{% include "footer.html" %}
<script>
$('.switch').change(function() {
if ($(this).prop('checked') == false){
if (confirm('Are you sure you want to turn off this connection?')){
location.replace("/switch/"+$(this).attr('id'))
}
}
else{
location.replace("/switch/"+$(this).attr('id'))
}
$('.switch').on("click", function() {
$(this).siblings($(".spinner-border")).css("display", "inline-block")
$(this).remove()
location.replace("/switch/"+$(this).attr('id'))
});
$(".sb-home-url").addClass("active");
$(".card-body").on("click", function(handle){
if ($(handle.target).attr("class") !== "bi bi-toggle2-off" && $(handle.target).attr("class") !== "bi bi-toggle2-on") {
window.open($(this).find("a").attr("href"), "_self");
}
})
</script>
</html>

10
src/templates/navbar.html Normal file
View File

@@ -0,0 +1,10 @@
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow">
<a class="navbar-brand col-md-3 col-lg-2 mr-0 px-3" href="/">WGDashboard</a>
<button class="navbar-toggler position-absolute d-md-none collapsed" type="button" data-toggle="collapse"
data-target="#sidebarMenu" aria-controls="sidebarMenu" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
</nav>
<div class="progress" style="height: 3px; position: fixed; width: 100%; z-index: 10000; background-color: transparent">
<div class="progress-bar" role="progressbar" style="z-index: 10000; width: 0%"></div>
</div>

View File

@@ -0,0 +1 @@
{{ qrcode(i) }}

204
src/templates/settings.html Normal file
View File

@@ -0,0 +1,204 @@
<html>
{% with %}
{% set title="Settings" %}
{% include "header.html" %}
{% endwith %}
<body>
{% include "navbar.html" %}
<div class="container-fluid">
{% include "sidebar.html" %}
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4">
<div class="setting-container mt-4">
{% if message != "" %}
<div class="alert alert-{{ status }}" role="alert">
{{ message }}
</div>
{% endif %}
<h1 class="">Settings</h1>
<hr>
{% if required_auth == "true" %}
<div class="card mb-3">
<h6 class="card-header">Peer Default Settings</h6>
<div class="card-body">
<form action="/update_peer_default_config" method="post">
<div class="form-group">
<div class="row">
<div class="col-sm-6">
<label for="peer_global_DNS">DNS</label>
<input type="text" class="form-control mb-4" id="peer_global_DNS"
name="peer_global_DNS"
value="{{ peer_global_DNS }}" required>
</div>
<div class="col-sm-6">
<label for="peer_endpoint_allowed_ip">Peer Endpoint Allowed IPs</label>
<input type="text" class="form-control mb-4" id="peer_endpoint_allowed_ip"
name="peer_endpoint_allowed_ip"
value="{{ peer_endpoint_allowed_ip }}" required>
</div>
<div class="col-sm-6">
<label for="peer_mtu">MTU</label>
<input type="text" class="form-control mb-4" id="peer_mtu"
name="peer_mtu"
value="{{ peer_mtu }}">
</div>
<div class="col-sm-6">
<label for="peer_keep_alive">Persistent Keepalive</label>
<input type="text" class="form-control mb-4" id="peer_keep_alive"
name="peer_keep_alive"
value="{{ peer_keepalive }}">
</div>
<div class="col-sm-12">
<label for="peer_remote_endpoint"><strong>Peer Remote Endpoint (This will be change globally, and will be apply to all peer's QR code and configuration file.)</strong></label>
<input type="text" class="form-control mb-4" id="peer_remote_endpoint"
name="peer_remote_endpoint"
value="{{ peer_remote_endpoint }}" required>
</div>
</div>
<button class="btn btn-success" type="submit">Update Peer Default Settings</button>
</div>
</form>
</div>
</div>
<hr>
<div class="card mb-3">
<h6 class="card-header">WireGuard Configuration Path</h6>
<div class="card-body">
<form action="/update_wg_conf_path" method="post" class="update_wg_conf_path">
<div class="form-group">
<label for="wg_conf_path">Path</label>
<input type="text" class="form-control mb-2" id="wg_conf_path" name="wg_conf_path"
value="{{ wg_conf_path }}">
<p class="text-muted">Remember to remove <code>/</code> at the end of your path. e.g <code>/etc/wireguard</code>
</p>
<button class="btn btn-danger change_path">Update Path & Restart Dashboard</button>
</div>
</form>
</div>
</div>
<div class="card mb-3">
<h6 class="card-header">Account</h6>
<div class="card-body">
<form action="/update_acct" method="post">
<div class="form-group">
<label for="username">Username</label>
<input type="text" class="form-control mb-4" id="username" name="username"
value="{{ session['username'] }}" required>
<button type="submit" class="btn btn-danger">Update Account</button>
</div>
</form>
</div>
</div>
<div class="card mb-3">
<h6 class="card-header">Security</h6>
<div class="card-body">
<form action="/update_pwd" method="post">
<div class="form-group">
<label for="currentpass">Current Password</label>
<input type="password" class="form-control mb-2" id="currentpass" name="currentpass">
<label for="newpass">New Password</label>
<input type="password" class="form-control mb-2" id="newpass" name="newpass">
<label for="repnewpass">Repeat New Password</label>
<input type="password" class="form-control mb-4" id="repnewpass" name="repnewpass">
<button type="submit" class="btn btn-danger">Update Password</button>
</div>
</form>
</div>
</div>
{% endif %}
<div class="card">
<h6 class="card-header">Dashboard Configuration</h6>
<div class="card-body">
<form action="/update_app_ip_port" method="post" class="update_app_ip_port">
<div class="form-group">
<div class="row">
<div class="col-sm">
<label for="app_ip">Dashboard IP</label>
<input type="text" class="form-control mb-2" id="app_ip" name="app_ip" value="{{ app_ip }}">
<p><small class="text-danger mb-4">0.0.0.0 means it can be access by anyone with your server
IP Address.</small></p>
</div>
<div class="col-sm">
<label for="app_port">Dashboard Port</label>
<input type="text" class="form-control mb-4" id="app_port" name="app_port"
value="{{ app_port }}">
</div>
</div>
<button type="button" class="btn btn-danger confirm_modal" data-toggle="modal"
data-target="#confirmModal">Update Configuration & Restart Dashboard
</button>
</div>
</form>
</div>
</div>
</div>
</main>
<!-- Modal -->
<div class="modal fade" id="confirmModal" data-backdrop="static" data-keyboard="false" tabindex="-1"
aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">Confirm Dashboard Configuration</h5>
</div>
<div class="modal-body">
<small>Dashboard Original IP</small>
<p>{{ app_ip }}</p>
<small style="font-weight: bold" class="text-bold">Dashboard New IP</small>
<p class="app_new_ip text-bold text-danger" style="font-weight: bold"></p>
<small>Dashboard Original Port</small>
<p>{{ app_port }}</p>
<small style="font-weight: bold" class="text-bold">Dashboard New Port</small>
<p class="app_new_port text-bold text-danger" style="font-weight: bold"></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary cancel_restart" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger confirm_restart">Confirm & Restart Dashboard</button>
</div>
</div>
</div>
</div>
</div>
{% include "tools.html" %}
</body>
{% include "footer.html" %}
<script>
$(".sb-settings-url").addClass("active")
$(".confirm_modal").click(function () {
$(".app_new_ip").html($("#app_ip")[0].value)
$(".app_new_port").html($("#app_port")[0].value)
})
$(".confirm_restart").click(function () {
$(".cancel_restart").remove()
countdown = 7;
$.post('/update_app_ip_port', $('.update_app_ip_port').serialize())
url = $("#app_ip")[0].value + ":" + $("#app_port")[0].value;
$(".confirm_restart").attr("disabled", "disabled")
setInterval(function () {
if (countdown === 0) {
window.location.replace("http://" + url);
}
$(".confirm_restart").html("Redirecting you in " + countdown + " seconds.")
countdown--;
}, 1000)
});
$(".change_path").click(function () {
$(this).attr("disabled", "disabled");
countdown = 5;
setInterval(function () {
if (countdown === 0) {
location.reload()
}
$(".change_path").html("Redirecting you in " + countdown + " seconds.")
countdown--;
}, 1000)
$.post('/update_wg_conf_path', $('.update_wg_conf_path').serialize())
});
</script>
</html>

View File

@@ -0,0 +1,47 @@
<div class="row">
<div class="row">
<nav id="sidebarMenu" class="col-md-3 col-lg-2 d-md-block bg-light sidebar collapse">
<div class="sidebar-sticky pt-3">
<ul class="nav flex-column">
<li class="nav-item"><a class="nav-link sb-home-url" href="/">Home</a></li>
{% if "username" in session %}
<li class="nav-item"><a class="nav-link sb-settings-url" href="/settings">Settings</a></li>
{% endif %}
{% if session['update'] == "true" %}
<li class="nav-item sb-update-li">
<a class="nav-link sb-update-url" href="https://github.com/donaldzou/WGDashboard#-how-to-update-the-dashboard">New Update Available!<span class="dot dot-running"></span></a>
</li>
{% endif %}
</ul>
<hr>
<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
<span>Configurations</span>
</h6>
<ul class="nav flex-column">
{% for i in conf%}
<li class="nav-item"><a class="nav-link sb-{{i['conf']}}-url" href="/configuration/{{i['conf']}}"><samp>{{i['conf']}}</samp></a></li>
{%endfor%}
</ul>
<hr>
<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
<span>Tools</span>
</h6>
<ul class="nav flex-column">
<ul class="nav flex-column">
<li class="nav-item"><a class="nav-link" data-toggle="modal" data-target="#ping_modal" href="#">Ping</a></li>
<li class="nav-item"><a class="nav-link" data-toggle="modal" data-target="#traceroute_modal" href="#">Traceroute</a></li>
</ul>
</ul>
<hr>
{% if "username" in session %}
<ul class="nav flex-column">
<li class="nav-item"><a class="nav-link text-danger" href="/signout" style="font-weight: bold">Sign Out</a></li>
</ul>
{% endif %}
<ul class="nav flex-column">
<li class="nav-item"><a href="https://github.com/donaldzou/WGDashboard/releases/tag/{{ session['dashboard_version'] }}"><small class="nav-link text-muted">{{ session['dashboard_version'] }}</small></a></li>
</ul>
</div>
</nav>
</div>
</div>

78
src/templates/signin.html Normal file
View File

@@ -0,0 +1,78 @@
<html>
{% with title="Sign In"%}
{% include "header.html"%}
{% endwith %}
<style>
.login-container-fluid{
display: flex;
height: calc( 100% - 240px );
align-items: center;
}
</style>
<body>
{% include "navbar.html" %}
<div class="container-fluid login-container-fluid">
<main role="main" class="container login-container">
<div class="login-box" style="margin: auto !important;">
<h1 class="text-center">Sign In</h1>
<form style="margin-left: auto !important; margin-right: auto !important; max-width: 500px;" action="/auth" method="post">
{% if message != "" %}
<div class="alert alert-warning" role="alert">You need to sign in first</div>
{% endif %}
<div class="alert alert-danger d-none" role="alert"></div>
<div class="form-group">
<label for="username" class="text-left" style="font-size: 1rem"><i class="bi bi-person-circle"></i> Username</label>
<input type="text" class="form-control" id="username" name="username" required>
</div>
<div class="form-group">
<label for="password" class="text-left" style="font-size: 1rem"><i class="bi bi-key-fill"></i> Password</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<button type="submit" class="btn btn-dark" style="width: 100%;">Sign In</button>
</form>
</div>
</main>
</div>
</body>
{% include "footer.html" %}
<script>
$("button").on("click", function(e){
let req = $("input[required]");
let check = true
for (let i = 0; i < req.length; i++){
if ($(req[i]).val().length === 0){
$("button").html("Sign In");
check = false;
$("input[required]").addClass("is-invalid");
break;
}
}
if (check){
e.preventDefault();
$(this).html("Signing In...").attr("disabled", "disabled");
$.ajax({
url: "/auth",
method: "POST",
headers:{"Content-Type": "application/json"},
data: JSON.stringify({
"username": $("#username").val(),
"password": $("#password").val()
})
}).done(function(res){
if (res.status === true){
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.get("redirect")){
window.location.replace(urlParams.get("redirect"))
}else{
window.location.replace("/");
}
}else{
$(".alert").html(res.msg).removeClass("d-none");
$("button").html("Sign In").removeAttr("disabled");
$("input[required]").addClass("is-invalid");
}
});
}
});
</script>
</html>

121
src/templates/tools.html Normal file
View File

@@ -0,0 +1,121 @@
<div class="modal fade" id="ping_modal" data-backdrop="static" data-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">Ping</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="row">
<div class="col-sm">
<div class="mb-3">
<small>Configuration</small>
<select class="form-control mt-2 conf_dropdown">
<option value="none" selected="selected" disabled>Select Configuration</option>
{% for i in conf%}
<option value="{{ i['conf'] }}">{{ i['conf'] }}</option>
{%endfor%}
</select>
</div>
</div>
<div class="col-sm">
<div class="mb-3">
<small>IP</small>
<select class="form-control mt-2 ip_dropdown">
<option value="none" selected="selected" disabled>Choose an IP</option>
</select>
</div>
</div>
<div class="col-sm">
<div class="mb-3">
<small>Ping Count</small>
<input type="number" class="form-control mt-2 ping_count" min=1 value=4>
</div>
</div>
</div>
<hr>
<div class="ping_result">
<table class="table">
<tbody></tbody>
</table>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary send_ping" disabled>Ping</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="traceroute_modal" data-backdrop="static" data-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">Traceroute</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="row">
<div class="col-sm">
<div class="mb-3">
<small>Configuration</small>
<select class="form-control mt-2 conf_dropdown">
<option value="none" selected="selected" disabled>Select Configuration</option>
{% for i in conf%}
<option value="{{ i['conf'] }}">{{ i['conf'] }}</option>
{%endfor%}
</select>
</div>
</div>
<div class="col-sm">
<div class="mb-3">
<small>IP</small>
<select class="form-control mt-2 ip_dropdown">
<option value="none" selected="selected" disabled>Choose an IP</option>
</select>
</div>
</div>
</div>
<button class="btn btn-primary send_traceroute" disabled>Traceroute</button>
<hr>
<div class="traceroute_result">
<table class="table table-sm">
<thead>
<tr>
<th scope="col">Hop</th>
<th scope="col">IP</th>
<th scope="col">Avg RTT</th>
<th scope="col">Min RTT</th>
<th scope="col">Max RTT</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="modal fade" id="update_modal" data-backdrop="static" data-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">How to update dashboard</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<pre><code>$ sudo sh wgd.sh stop</code><br><code>$ sudo sh wgd.sh update</code><br><code>$ sudo sh wgd.sh start</code></pre>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>

81
src/util.py Normal file
View File

@@ -0,0 +1,81 @@
import re
"""
Helper Functions
"""
# Regex Match
def regex_match(regex, text):
pattern = re.compile(regex)
return pattern.search(text) is not None
# Check IP format
def check_IP(ip):
ip_patterns = (
r"((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}",
r"[0-9a-fA-F]{0,4}(:([0-9a-fA-F]{0,4})){1,7}$"
)
for match_pattern in ip_patterns:
match_result = regex_match(match_pattern, ip)
if match_result:
result = match_result
break
else:
result = None
return result
# Clean IP
def clean_IP(ip):
return ip.replace(' ', '')
# Clean IP with range
def clean_IP_with_range(ip):
return clean_IP(ip).split(',')
# Check IP with range
def check_IP_with_range(ip):
ip_patterns = (
r"((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|\/)){4}([0-9]{1,2})(,|$)",
r"[0-9a-fA-F]{0,4}(:([0-9a-fA-F]{0,4})){1,7}\/([0-9]{1,3})(,|$)"
)
for match_pattern in ip_patterns:
match_result = regex_match(match_pattern, ip)
if match_result:
result = match_result
break
else:
result = None
return result
# Check allowed ips list
def check_Allowed_IPs(ip):
ip = clean_IP_with_range(ip)
for i in ip:
if not check_IP_with_range(i): return False
return True
# Check DNS
def check_DNS(dns):
dns = dns.replace(' ', '').split(',')
status = True
for i in dns:
if not (check_IP(i) or regex_match("(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z][a-z]{0,61}[a-z]", i)):
return False
return True
# Check remote endpoint
def check_remote_endpoint(address):
return (check_IP(address) or regex_match("(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z][a-z]{0,61}[a-z]",
address))

14
src/wg-dashboard.service Normal file
View File

@@ -0,0 +1,14 @@
[Unit]
After=syslog.target network-online.target
ConditionPathIsDirectory=/etc/wireguard
[Service]
Environment="VIRTUAL_ENV={{VIRTUAL_ENV}}"
WorkingDirectory={{APP_ROOT}}
ExecStart={{VIRTUAL_ENV}}/bin/python3 {{APP_ROOT}}/dashboard.py
PrivateTmp=yes
Restart=always
[Install]
WantedBy=multi-user.target

209
src/wgd.sh Executable file
View File

@@ -0,0 +1,209 @@
#!/bin/bash
# wgd.sh - Copyright(C) 2021 Donald Zou [https://github.com/donaldzou]
# Under Apache-2.0 License
app_name="dashboard.py"
app_official_name="WGDashboard"
PID_FILE=./gunicorn.pid
environment=$(if [[ $ENVIRONMENT ]]; then echo $ENVIRONMENT; else echo 'develop'; fi)
if [[ $CONFIGURATION_PATH ]]; then
cb_work_dir=$CONFIGURATION_PATH/letsencrypt/work-dir
cb_config_dir=$CONFIGURATION_PATH/letsencrypt/config-dir
else
cb_work_dir=/etc/letsencrypt
cb_config_dir=/var/lib/letsencrypt
fi
dashes='------------------------------------------------------------'
equals='============================================================'
help () {
printf "=================================================================================\n"
printf "+ <WGDashboard> by Donald Zou - https://github.com/donaldzou +\n"
printf "=================================================================================\n"
printf "| Usage: ./wgd.sh <option> |\n"
printf "| |\n"
printf "| Available options: |\n"
printf "| start: To start WGDashboard. |\n"
printf "| stop: To stop WGDashboard. |\n"
printf "| debug: To start WGDashboard in debug mode (i.e run in foreground). |\n"
printf "| update: To update WGDashboard to the newest version from GitHub. |\n"
printf "| install: To install WGDashboard. |\n"
printf "| Thank you for using! Your support is my motivation ;) |\n"
printf "=================================================================================\n"
}
_check_and_set_venv(){
# deb/ubuntu users: might need a 'apt install python3.8-venv'
# set up the local environment
APP_ROOT=`pwd`
VIRTUAL_ENV="${APP_ROOT%/*}/venv"
if [ ! -d $VIRTUAL_ENV ]; then
python3 -m venv $VIRTUAL_ENV
fi
. ${VIRTUAL_ENV}/bin/activate
}
install_wgd(){
printf "| Starting to install WGDashboard |\n"
version_pass=$(python3 -c 'import sys; print("1") if (sys.version_info.major == 3 and sys.version_info.minor >= 7) else print("0");')
if [ $version_pass == "0" ]
then printf "| WGDashboard required Python 3.7 or above |\n"
printf "%s\n" "$dashes"
exit 1
fi
if [ ! -d "db" ]
then mkdir "db"
fi
if [ ! -d "log" ]
then mkdir "log"
fi
printf "| Upgrading pip |\n"
python3 -m pip install -U pip > /dev/null 2>&1
printf "| Installing latest Python dependencies |\n"
python3 -m pip install -U -r requirements.txt > /dev/null 2>&1
printf "| WGDashboard installed successfully! |\n"
printf "| Enter ./wgd start to start the dashboard |\n"
}
check_wgd_status(){
if test -f "$PID_FILE"; then
if ps aux | grep -v grep | grep $(cat ./gunicorn.pid) > /dev/null; then
return 0
else
return 1
fi
else
if ps aux | grep -v grep | grep '[p]ython3 '$app_name > /dev/null; then
return 0
else
return 1
fi
fi
}
certbot_create_ssl () {
certbot certonly --config ./certbot.ini --email "$EMAIL" --work-dir $cb_work_dir --config-dir $cb_config_dir --domain "$SERVERURL"
}
certbot_renew_ssl () {
certbot renew --work-dir $cb_work_dir --config-dir $cb_config_dir
}
gunicorn_start () {
printf "%s\n" "$dashes"
printf "| Starting WGDashboard with Gunicorn in the background. |\n"
if [ ! -d "log" ]; then
mkdir "log"
fi
d=$(date '+%Y%m%d%H%M%S')
if [[ $USER == root ]]; then
export PATH=$PATH:/usr/local/bin:$HOME/.local/bin
fi
gunicorn --access-logfile log/access_"$d".log \
--error-logfile log/error_"$d".log 'dashboard:run_dashboard()'
printf "| Log files is under log/ |\n"
printf "%s\n" "$dashes"
}
gunicorn_stop () {
kill $(cat ./gunicorn.pid)
}
start_wgd () {
gunicorn_start
}
stop_wgd() {
if test -f "$PID_FILE"; then
gunicorn_stop
else
kill "$(ps aux | grep "[p]ython3 $app_name" | awk '{print $2}')"
fi
}
start_wgd_debug() {
printf "%s\n" "$dashes"
printf "| Starting WGDashboard in the foreground. |\n"
python3 "$app_name"
printf "%s\n" "$dashes"
}
update_wgd() {
new_ver=$(python3 -c "import json; import urllib.request; data = urllib.request.urlopen('https://api.github.com/repos/donaldzou/WGDashboard/releases/latest').read(); output = json.loads(data);print(output['tag_name'])")
printf "%s\n" "$dashes"
printf "| Are you sure you want to update to the %s? (Y/N): " "$new_ver"
read up
if [ "$up" = "Y" ]; then
printf "| Shutting down WGDashboard... |\n"
if check_wgd_status; then
stop_wgd
fi
mv wgd.sh wgd.sh.old
printf "| Downloading %s from GitHub... |\n" "$new_ver"
git stash > /dev/null 2>&1
git pull https://github.com/donaldzou/WGDashboard.git $new_ver --force > /dev/null 2>&1
printf "| Upgrading pip |\n"
python3 -m pip install -U pip > /dev/null 2>&1
printf "| Installing latest Python dependencies |\n"
python3 -m pip install -U -r requirements.txt > /dev/null 2>&1
printf "| Update Successfully! |\n"
printf "%s\n" "$dashes"
rm wgd.sh.old
else
printf "%s\n" "$dashes"
printf "| Update Canceled. |\n"
printf "%s\n" "$dashes"
fi
}
if [ "$#" != 1 ];
then
help
else
if [ "$1" = "start" ]; then
if check_wgd_status; then
printf "%s\n" "$dashes"
printf "| WGDashboard is already running. |\n"
printf "%s\n" "$dashes"
else
start_wgd
fi
elif [ "$1" = "stop" ]; then
if check_wgd_status; then
printf "%s\n" "$dashes"
stop_wgd
printf "| WGDashboard is stopped. |\n"
printf "%s\n" "$dashes"
else
printf "%s\n" "$dashes"
printf "| WGDashboard is not running. |\n"
printf "%s\n" "$dashes"
fi
elif [ "$1" = "update" ]; then
update_wgd
elif [ "$1" = "install" ]; then
printf "%s\n" "$dashes"
install_wgd
printf "%s\n" "$dashes"
elif [ "$1" = "restart" ]; then
if check_wgd_status; then
printf "%s\n" "$dashes"
stop_wgd
printf "| WGDashboard is stopped. |\n"
sleep 2
start_wgd
else
start_wgd
fi
elif [ "$1" = "debug" ]; then
if check_wgd_status; then
printf "| WGDashboard is already running. |\n"
else
start_wgd_debug
fi
else
help
fi
fi