yeet
@ -1,49 +0,0 @@
|
||||
# Javascript Node CircleCI 2.0 configuration file
|
||||
#
|
||||
|
||||
jobs:
|
||||
test-job:
|
||||
docker:
|
||||
- image: "cimg/node:lts"
|
||||
|
||||
working_directory: ~/repo
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
# Download and cache dependencies
|
||||
- restore_cache:
|
||||
keys:
|
||||
- dependencies-{{ checksum "yarn.lock" }}
|
||||
# fallback to using the latest cache if no exact match is found
|
||||
- dependencies-
|
||||
|
||||
- run: yarn setup
|
||||
|
||||
- save_cache:
|
||||
paths:
|
||||
- node_modules
|
||||
- client/node_modules
|
||||
- server/node_modules
|
||||
key: dependencies-{{ checksum "yarn.lock" }}
|
||||
|
||||
- run:
|
||||
command: yarn lint
|
||||
|
||||
- run:
|
||||
command: yarn test
|
||||
environment:
|
||||
TZ: UTC
|
||||
VITE_COMMIT_SHA: some_sha
|
||||
|
||||
- store_artifacts: # For coverage report
|
||||
path: client/coverage
|
||||
|
||||
orbs: # declare what orbs we are going to use
|
||||
node: circleci/node@2.0.2 # the node orb provides common node-related configuration
|
||||
|
||||
version: 2.1
|
||||
|
||||
workflows:
|
||||
tests:
|
||||
jobs:
|
||||
- test-job
|
@ -1,8 +0,0 @@
|
||||
*
|
||||
!package.json
|
||||
!yarn.lock
|
||||
!build.sh
|
||||
!start.sh
|
||||
!server/*
|
||||
!client/*
|
||||
**/node_modules/*
|
@ -1,7 +0,0 @@
|
||||
{
|
||||
"trailingComma": "all",
|
||||
"tabWidth": 2,
|
||||
"singleQuote": true,
|
||||
"arrowParens": "avoid",
|
||||
"printWidth": 120
|
||||
}
|
19
Dockerfile
@ -1,19 +0,0 @@
|
||||
#FROM nginx:alpine3.18
|
||||
FROM node:20.9.0-alpine3.18
|
||||
|
||||
RUN apk update && apk add --no-cache bash nginx
|
||||
|
||||
USER node:node
|
||||
|
||||
WORKDIR /home/node
|
||||
|
||||
COPY --chown=node:node . .
|
||||
|
||||
RUN yarn && yarn build
|
||||
|
||||
STOPSIGNAL SIGINT
|
||||
|
||||
RUN chmod +x /home/node/start.sh
|
||||
|
||||
# Start the startup script
|
||||
CMD ["/home/node/start.sh"]
|
@ -1,12 +0,0 @@
|
||||
[Interface]
|
||||
PrivateKey = 4KX5qp4T8ANIoEk/eFJuYwi2g7/qD/6U9bzg2fUb60U=
|
||||
Address = 10.0.0.254/32
|
||||
DNS = 10.2.0.100,10.2.0.100
|
||||
MTU = 1420
|
||||
|
||||
[Peer]
|
||||
PublicKey = STk0MVroIsxKg0yMkorsQTiTFhk1R99yjP97Soe4MVA=
|
||||
AllowedIPs = 0.0.0.0/0
|
||||
Endpoint = 0.0.0.0:443
|
||||
PersistentKeepalive = 21
|
||||
PresharedKey = nD4vaypUdgiHFfxDU5Fb6pWuN3KDVgKMsfIpndaYHj8=
|
23
LICENSE
@ -1,23 +0,0 @@
|
||||
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2016-present darkwire.io
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@ -1,19 +0,0 @@
|
||||
.vscode
|
||||
.DS_Store
|
||||
.idea
|
||||
src/db
|
||||
__pycache__
|
||||
src/test.py
|
||||
*.db
|
||||
master-key/master.conf
|
||||
env/
|
||||
src/wg-dashboard.ini
|
||||
src/static/pic.xd
|
||||
*.conf
|
||||
private_key.txt
|
||||
public_key.txt
|
||||
venv/**
|
||||
log/**
|
||||
release/*
|
||||
src/db/wgdashboard.db
|
||||
.jshintrc
|
@ -1,21 +0,0 @@
|
||||
FROM alpine:latest
|
||||
|
||||
WORKDIR /home/app
|
||||
RUN apk update && \
|
||||
apk add --no-cache python3 py3-pip py3-bcrypt py3-pillow uwsgi-python3 && \
|
||||
apk add --no-cache build-base linux-headers wireguard-tools openssl nginx && \
|
||||
apk add --no-cache net-tools iproute2 iptables ip6tables openssl-dev && \
|
||||
apk add --no-cache inotify-tools procps openresolv libc-dev pcre-dev && \
|
||||
mkdir /home/app/master-key
|
||||
|
||||
COPY ./src /home/app
|
||||
|
||||
|
||||
RUN pip install --upgrade pip --no-cache-dir --break-system-packages && \
|
||||
python3 -m pip install -r /home/app/requirements.txt --no-cache-dir --break-system-packages && \
|
||||
chmod u+x /home/app/entrypoint.sh
|
||||
|
||||
ENTRYPOINT ["/home/app/entrypoint.sh"]
|
||||
|
||||
|
||||
|
201
WG-Dash/LICENSE
@ -1,201 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
@ -1,13 +0,0 @@
|
||||
#!/bin/bash
|
||||
WIREGUARD_INTERFACE=ADMINS
|
||||
WIREGUARD_LAN=10.0.0.1/24
|
||||
MASQUERADE_INTERFACE=eth0
|
||||
|
||||
CHAIN_NAME="WIREGUARD_$WIREGUARD_INTERFACE"
|
||||
|
||||
iptables -t nat -D POSTROUTING -o $MASQUERADE_INTERFACE -j MASQUERADE -s $WIREGUARD_LAN
|
||||
|
||||
# Remove and delete the WIREGUARD_wg0 chain
|
||||
iptables -D FORWARD -j $CHAIN_NAME
|
||||
iptables -F $CHAIN_NAME
|
||||
iptables -X $CHAIN_NAME
|
@ -1,26 +0,0 @@
|
||||
#!/bin/bash
|
||||
WIREGUARD_INTERFACE=ADMINS
|
||||
WIREGUARD_LAN=10.0.0.1/24
|
||||
MASQUERADE_INTERFACE=eth0
|
||||
|
||||
iptables -t nat -I POSTROUTING -o $MASQUERADE_INTERFACE -j MASQUERADE -s $WIREGUARD_LAN
|
||||
|
||||
# Add a WIREGUARD_wg0 chain to the FORWARD chain
|
||||
CHAIN_NAME="WIREGUARD_$WIREGUARD_INTERFACE"
|
||||
iptables -N $CHAIN_NAME
|
||||
iptables -A FORWARD -j $CHAIN_NAME
|
||||
|
||||
# Accept related or established traffic
|
||||
iptables -A $CHAIN_NAME -o $WIREGUARD_INTERFACE -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
|
||||
|
||||
# Accept traffic from any Wireguard IP address connected to the Wireguard server
|
||||
iptables -A $CHAIN_NAME -s $WIREGUARD_LAN -i $WIREGUARD_INTERFACE -j ACCEPT
|
||||
|
||||
# Allow traffic to the local loopback interface
|
||||
iptables -A $CHAIN_NAME -o lo -j ACCEPT
|
||||
|
||||
# Drop everything else coming through the Wireguard interface
|
||||
iptables -A $CHAIN_NAME -i $WIREGUARD_INTERFACE -j DROP
|
||||
|
||||
# Return to FORWARD chain
|
||||
iptables -A $CHAIN_NAME -j RETURN
|
@ -1,13 +0,0 @@
|
||||
#!/bin/bash
|
||||
WIREGUARD_INTERFACE=GUESTS
|
||||
WIREGUARD_LAN=192.168.20.1/24
|
||||
MASQUERADE_INTERFACE=eth0
|
||||
|
||||
CHAIN_NAME="WIREGUARD_$WIREGUARD_INTERFACE"
|
||||
|
||||
iptables -t nat -D POSTROUTING -o $MASQUERADE_INTERFACE -j MASQUERADE -s $WIREGUARD_LAN
|
||||
|
||||
# Remove and delete the WIREGUARD_wg0 chain
|
||||
iptables -D FORWARD -j $CHAIN_NAME
|
||||
iptables -F $CHAIN_NAME
|
||||
iptables -X $CHAIN_NAME
|
@ -1,30 +0,0 @@
|
||||
#!/bin/bash
|
||||
WIREGUARD_INTERFACE=GUESTS
|
||||
WIREGUARD_LAN=192.168.20.1/24
|
||||
MASQUERADE_INTERFACE=eth0
|
||||
|
||||
iptables -t nat -I POSTROUTING -o $MASQUERADE_INTERFACE -j MASQUERADE -s $WIREGUARD_LAN
|
||||
|
||||
# Add a WIREGUARD_wg0 chain to the FORWARD chain
|
||||
CHAIN_NAME="WIREGUARD_$WIREGUARD_INTERFACE"
|
||||
iptables -N $CHAIN_NAME
|
||||
iptables -A FORWARD -j $CHAIN_NAME
|
||||
|
||||
# Accept related or established traffic
|
||||
iptables -A $CHAIN_NAME -o $WIREGUARD_INTERFACE -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
|
||||
|
||||
# Drop incoming traffic from guests to wg-dashboard
|
||||
iptables -A INPUT -i $WIREGUARD_INTERFACE -j DROP
|
||||
|
||||
# DNS
|
||||
iptables -A $CHAIN_NAME -s $WIREGUARD_LAN -i $WIREGUARD_INTERFACE -d 10.2.0.100 -p udp --dport 53 -j ACCEPT
|
||||
# Drop traffic to your any private IP address
|
||||
iptables -A $CHAIN_NAME -s $WIREGUARD_LAN -i $WIREGUARD_INTERFACE -d 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 -j DROP
|
||||
# Accept outgoing connections to HTTP(S) ports to any IP address (public because of rule above)
|
||||
iptables -A $CHAIN_NAME -s $WIREGUARD_LAN -i $WIREGUARD_INTERFACE -d 0.0.0.0/0 -p tcp -m multiport --dports 80,443 -j ACCEPT
|
||||
|
||||
# Drop everything else coming through the Wireguard interface
|
||||
iptables -A $CHAIN_NAME -i $WIREGUARD_INTERFACE -j DROP
|
||||
|
||||
# Return to FORWARD chain
|
||||
iptables -A $CHAIN_NAME -j RETURN
|
@ -1,20 +0,0 @@
|
||||
#!/bin/bash
|
||||
WIREGUARD_INTERFACE=IPV6ADMINS
|
||||
WIREGUARD_LAN=192.168.30.1/24
|
||||
WIREGUARD_LAN_IPV6=2001:db8:30:1::/64
|
||||
MASQUERADE_INTERFACE=eth0
|
||||
|
||||
CHAIN_NAME="WIREGUARD_$WIREGUARD_INTERFACE"
|
||||
|
||||
iptables -t nat -D POSTROUTING -o $MASQUERADE_INTERFACE -j MASQUERADE -s $WIREGUARD_LAN
|
||||
ip6tables -t nat -D POSTROUTING -o $MASQUERADE_INTERFACE -j MASQUERADE -s $WIREGUARD_LAN_IPV6
|
||||
|
||||
# Remove and delete the WIREGUARD_wg0 chain
|
||||
iptables -D FORWARD -j $CHAIN_NAME
|
||||
iptables -F $CHAIN_NAME
|
||||
iptables -X $CHAIN_NAME
|
||||
|
||||
# Remove and delete the WIREGUARD_wg0 chain
|
||||
ip6tables -D FORWARD -j $CHAIN_NAME
|
||||
ip6tables -F $CHAIN_NAME
|
||||
ip6tables -X $CHAIN_NAME
|
@ -1,37 +0,0 @@
|
||||
#!/bin/bash
|
||||
WIREGUARD_INTERFACE=IPV6ADMINS
|
||||
WIREGUARD_LAN=192.168.30.1/24
|
||||
WIREGUARD_LAN_IPV6=2001:db8:30:1::/64
|
||||
MASQUERADE_INTERFACE=eth0
|
||||
|
||||
|
||||
iptables -t nat -I POSTROUTING -o $MASQUERADE_INTERFACE -j MASQUERADE -s $WIREGUARD_LAN
|
||||
ip6tables -t nat -I POSTROUTING -o $MASQUERADE_INTERFACE -j MASQUERADE -s $WIREGUARD_LAN_IPV6
|
||||
|
||||
# Add a WIREGUARD_wg0 chain to the FORWARD chain
|
||||
CHAIN_NAME="WIREGUARD_$WIREGUARD_INTERFACE"
|
||||
iptables -N $CHAIN_NAME
|
||||
iptables -A FORWARD -j $CHAIN_NAME
|
||||
ip6tables -N $CHAIN_NAME
|
||||
ip6tables -A FORWARD -j $CHAIN_NAME
|
||||
|
||||
# Accept related or established traffic
|
||||
iptables -A $CHAIN_NAME -o $WIREGUARD_INTERFACE -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
|
||||
ip6tables -A $CHAIN_NAME -o $WIREGUARD_INTERFACE -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
|
||||
|
||||
# Accept traffic from any Wireguard IP address connected to the Wireguard server
|
||||
iptables -A $CHAIN_NAME -s $WIREGUARD_LAN -i $WIREGUARD_INTERFACE -j ACCEPT
|
||||
ip6tables -A $CHAIN_NAME -s $WIREGUARD_LAN_IPV6 -i $WIREGUARD_INTERFACE -j ACCEPT
|
||||
|
||||
# Allow traffic to the local loopback interface
|
||||
iptables -A $CHAIN_NAME -o lo -j ACCEPT
|
||||
# Allow traffic to the local loopback interface
|
||||
ip6tables -A $CHAIN_NAME -o lo -j ACCEPT
|
||||
|
||||
# Drop everything else coming through the Wireguard interface
|
||||
iptables -A $CHAIN_NAME -i $WIREGUARD_INTERFACE -j DROP
|
||||
ip6tables -A $CHAIN_NAME -i $WIREGUARD_INTERFACE -j DROP
|
||||
|
||||
# Return to FORWARD chain
|
||||
iptables -A $CHAIN_NAME -j RETURN
|
||||
ip6tables -A $CHAIN_NAME -j RETURN
|
@ -1,20 +0,0 @@
|
||||
#!/bin/bash
|
||||
WIREGUARD_INTERFACE=IPV6MEMBERS
|
||||
WIREGUARD_LAN=192.168.40.1/24
|
||||
WIREGUARD_LAN_IPV6=2001:db8:40:1::/64
|
||||
MASQUERADE_INTERFACE=eth0
|
||||
|
||||
CHAIN_NAME="WIREGUARD_$WIREGUARD_INTERFACE"
|
||||
|
||||
iptables -t nat -D POSTROUTING -o $MASQUERADE_INTERFACE -j MASQUERADE -s $WIREGUARD_LAN
|
||||
ip6tables -t nat -D POSTROUTING -o $MASQUERADE_INTERFACE -j MASQUERADE -s $WIREGUARD_LAN_IPV6
|
||||
|
||||
# Remove and delete the WIREGUARD_wg0 chain
|
||||
iptables -D FORWARD -j $CHAIN_NAME
|
||||
iptables -F $CHAIN_NAME
|
||||
iptables -X $CHAIN_NAME
|
||||
|
||||
# Remove and delete the WIREGUARD_wg0 chain
|
||||
ip6tables -D FORWARD -j $CHAIN_NAME
|
||||
ip6tables -F $CHAIN_NAME
|
||||
ip6tables -X $CHAIN_NAME
|
@ -1,50 +0,0 @@
|
||||
#!/bin/bash
|
||||
WIREGUARD_INTERFACE=IPV6MEMBERS
|
||||
WIREGUARD_LAN=192.168.40.1/24
|
||||
WIREGUARD_LAN_IPV6=2001:db8:40:1::/64
|
||||
MASQUERADE_INTERFACE=eth0
|
||||
|
||||
iptables -t nat -I POSTROUTING -o $MASQUERADE_INTERFACE -j MASQUERADE -s $WIREGUARD_LAN
|
||||
ip6tables -t nat -I POSTROUTING -o $MASQUERADE_INTERFACE -j MASQUERADE -s $WIREGUARD_LAN_IPV6
|
||||
|
||||
# Add a WIREGUARD_wg0 chain to the FORWARD chain
|
||||
CHAIN_NAME="WIREGUARD_$WIREGUARD_INTERFACE"
|
||||
iptables -N $CHAIN_NAME
|
||||
iptables -A FORWARD -j $CHAIN_NAME
|
||||
ip6tables -N $CHAIN_NAME
|
||||
ip6tables -A FORWARD -j $CHAIN_NAME
|
||||
|
||||
# Accept related or established traffic
|
||||
iptables -A $CHAIN_NAME -o $WIREGUARD_INTERFACE -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
|
||||
ip6tables -A $CHAIN_NAME -o $WIREGUARD_INTERFACE -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
|
||||
|
||||
# Drop incoming traffic from wg1 to wg-dashboard
|
||||
iptables -A INPUT -i $WIREGUARD_INTERFACE -j DROP
|
||||
ip6tables -A INPUT -i $WIREGUARD_INTERFACE -j DROP
|
||||
|
||||
# Accept DNS from Adguard
|
||||
iptables -A $CHAIN_NAME -s $WIREGUARD_LAN -i $WIREGUARD_INTERFACE -d 10.2.0.100 -p udp --dport 53 -j ACCEPT
|
||||
# Accept Channels FEC
|
||||
iptables -A $CHAIN_NAME -s $WIREGUARD_LAN -i $WIREGUARD_INTERFACE -d 10.2.0.4 -p tcp --dport 80 -j ACCEPT
|
||||
# Drop Direct Forward traffic to Dockge
|
||||
iptables -A $CHAIN_NAME -s $WIREGUARD_LAN -i $WIREGUARD_INTERFACE -d 10.2.0.2 -j DROP
|
||||
# Drop Forward traffic to AdGuard Dashboard
|
||||
iptables -A $CHAIN_NAME -s $WIREGUARD_LAN -i $WIREGUARD_INTERFACE -d 10.2.0.100 -j DROP
|
||||
# Drop Forward traffic to Unbound (members should be restricted form accesing the means of modifying the network)
|
||||
iptables -A $CHAIN_NAME -s $WIREGUARD_LAN -i $WIREGUARD_INTERFACE -d 10.2.0.200 -j DROP
|
||||
# Drop Forward traffic to Channels Database
|
||||
iptables -A $CHAIN_NAME -s $WIREGUARD_LAN -i $WIREGUARD_INTERFACE -d 10.2.0.5 -j DROP
|
||||
iptables -A $CHAIN_NAME -s $WIREGUARD_LAN -i $WIREGUARD_INTERFACE -d 10.2.0.4 -j DROP
|
||||
|
||||
# Accept outgoing connections to HTTP(S) ports to any IP address (public because of rule above)
|
||||
iptables -A $CHAIN_NAME -s $WIREGUARD_LAN -i $WIREGUARD_INTERFACE -d 0.0.0.0/0 -p tcp -m multiport --dports 20,21,22,80,443,3389 -j ACCEPT
|
||||
# Accept outgoing connections to HTTP(S) ports to any IP address (public because of rule above)
|
||||
ip6tables -A $CHAIN_NAME -s $WIREGUARD_LAN_IPV6 -i $WIREGUARD_INTERFACE -d ::/0 -p tcp -m multiport --dports 20,21,22,80,443,3389 -j ACCEPT
|
||||
|
||||
# Drop everything else coming through the Wireguard interface
|
||||
iptables -A $CHAIN_NAME -i $WIREGUARD_INTERFACE -j DROP
|
||||
ip6tables -A $CHAIN_NAME -i $WIREGUARD_INTERFACE -j DROP
|
||||
|
||||
# Return to FORWARD chain
|
||||
iptables -A $CHAIN_NAME -j RETURN
|
||||
ip6tables -A $CHAIN_NAME -j RETURN
|
@ -1,13 +0,0 @@
|
||||
#!/bin/bash
|
||||
WIREGUARD_INTERFACE=LANP2P
|
||||
WIREGUARD_LAN=172.16.0.1/24
|
||||
MASQUERADE_INTERFACE=eth0
|
||||
|
||||
CHAIN_NAME="WIREGUARD_$WIREGUARD_INTERFACE"
|
||||
|
||||
iptables -t nat -D POSTROUTING -o $MASQUERADE_INTERFACE -j MASQUERADE -s $WIREGUARD_LAN
|
||||
|
||||
# Remove and delete the WIREGUARD_wg0 chain
|
||||
iptables -D FORWARD -j $CHAIN_NAME
|
||||
iptables -F $CHAIN_NAME
|
||||
iptables -X $CHAIN_NAME
|
@ -1,23 +0,0 @@
|
||||
#!/bin/bash
|
||||
WIREGUARD_INTERFACE=LANP2P
|
||||
WIREGUARD_LAN=172.16.0.1/24
|
||||
MASQUERADE_INTERFACE=eth0
|
||||
|
||||
iptables -t nat -I POSTROUTING -o $MASQUERADE_INTERFACE -j MASQUERADE -s $WIREGUARD_LAN
|
||||
|
||||
# Add a WIREGUARD_wg0 chain to the FORWARD chain
|
||||
CHAIN_NAME="WIREGUARD_$WIREGUARD_INTERFACE"
|
||||
iptables -N $CHAIN_NAME
|
||||
iptables -A FORWARD -j $CHAIN_NAME
|
||||
|
||||
# Accept related or established traffic
|
||||
iptables -A $CHAIN_NAME -o $WIREGUARD_INTERFACE -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
|
||||
|
||||
#Accept on connections to peers of LAN zone only
|
||||
iptables -A $CHAIN_NAME -s $WIREGUARD_LAN -i $WIREGUARD_INTERFACE -d 172.16.0.1/24 -j ACCEPT
|
||||
|
||||
# Drop everything else coming through the Wireguard interface
|
||||
iptables -A $CHAIN_NAME -i $WIREGUARD_INTERFACE -j DROP
|
||||
|
||||
# Return to FORWARD chain
|
||||
iptables -A $CHAIN_NAME -j RETURN
|
@ -1,13 +0,0 @@
|
||||
#!/bin/bash
|
||||
WIREGUARD_INTERFACE=MEMEBERS
|
||||
WIREGUARD_LAN=192.168.10.1/24
|
||||
MASQUERADE_INTERFACE=eth0
|
||||
|
||||
CHAIN_NAME="WIREGUARD_$WIREGUARD_INTERFACE"
|
||||
|
||||
iptables -t nat -D POSTROUTING -o $MASQUERADE_INTERFACE -j MASQUERADE -s $WIREGUARD_LAN
|
||||
|
||||
# Remove and delete the WIREGUARD_wg0 chain
|
||||
iptables -D FORWARD -j $CHAIN_NAME
|
||||
iptables -F $CHAIN_NAME
|
||||
iptables -X $CHAIN_NAME
|
@ -1,43 +0,0 @@
|
||||
#!/bin/bash
|
||||
WIREGUARD_INTERFACE=MEMEBERS
|
||||
WIREGUARD_LAN=192.168.10.1/24
|
||||
MASQUERADE_INTERFACE=eth0
|
||||
|
||||
iptables -t nat -I POSTROUTING -o $MASQUERADE_INTERFACE -j MASQUERADE -s $WIREGUARD_LAN
|
||||
|
||||
# Add a WIREGUARD_wg0 chain to the FORWARD chain
|
||||
CHAIN_NAME="WIREGUARD_$WIREGUARD_INTERFACE"
|
||||
iptables -N $CHAIN_NAME
|
||||
iptables -A FORWARD -j $CHAIN_NAME
|
||||
|
||||
# Accept related or established traffic
|
||||
iptables -A $CHAIN_NAME -o $WIREGUARD_INTERFACE -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
|
||||
|
||||
# Drop incoming traffic from wg1 to wg-dashboard
|
||||
iptables -A INPUT -i $WIREGUARD_INTERFACE -j DROP
|
||||
# Accept DNS from Adguard
|
||||
iptables -A $CHAIN_NAME -s $WIREGUARD_LAN -i $WIREGUARD_INTERFACE -d 10.2.0.100 -p udp --dport 53 -j ACCEPT
|
||||
|
||||
|
||||
# Accept Channels FEC
|
||||
iptables -A $CHAIN_NAME -s $WIREGUARD_LAN -i $WIREGUARD_INTERFACE -d 10.2.0.4 -p tcp --dport 80 -j ACCEPT
|
||||
|
||||
# Drop Direct Forward traffic to Dockge
|
||||
iptables -A $CHAIN_NAME -s $WIREGUARD_LAN -i $WIREGUARD_INTERFACE -d 10.2.0.2 -j DROP
|
||||
# Drop Direct Forward traffic to AdGuard Dashboard
|
||||
iptables -A $CHAIN_NAME -s $WIREGUARD_LAN -i $WIREGUARD_INTERFACE -d 10.2.0.100 -j DROP
|
||||
# Drop Direct Forward traffic to Unbound
|
||||
iptables -A $CHAIN_NAME -s $WIREGUARD_LAN -i $WIREGUARD_INTERFACE -d 10.2.0.200 -j DROP
|
||||
# Drop Forward traffic to Channels Database
|
||||
iptables -A $CHAIN_NAME -s $WIREGUARD_LAN -i $WIREGUARD_INTERFACE -d 10.2.0.5 -j DROP
|
||||
iptables -A $CHAIN_NAME -s $WIREGUARD_LAN -i $WIREGUARD_INTERFACE -d 10.2.0.4 -j DROP
|
||||
|
||||
|
||||
# Accept outgoing connections to HTTP(S) ports to any IP address (public because of rule above)
|
||||
iptables -A $CHAIN_NAME -s $WIREGUARD_LAN -i $WIREGUARD_INTERFACE -d 0.0.0.0/0 -p tcp -m multiport --dports 20,21,22,80,443,3389 -j ACCEPT
|
||||
|
||||
# Drop everything else coming through the Wireguard interface
|
||||
iptables -A $CHAIN_NAME -i $WIREGUARD_INTERFACE -j DROP
|
||||
|
||||
# Return to FORWARD chain
|
||||
iptables -A $CHAIN_NAME -j RETURN
|
@ -1,55 +0,0 @@
|
||||
import ipaddress, subprocess, datetime, os, util
|
||||
from util import *
|
||||
|
||||
notEnoughParameter = {"status": False, "reason": "Please provide all required parameters."}
|
||||
good = {"status": True, "reason": ""}
|
||||
|
||||
def ret(status=True, reason="", data=""):
|
||||
return {"status": status, "reason": reason, "data": data}
|
||||
|
||||
def togglePeerAccess(data, g):
|
||||
checkUnlock = g.cur.execute(f"SELECT * FROM {data['config']} WHERE id='{data['peerID']}'").fetchone()
|
||||
if checkUnlock:
|
||||
moveUnlockToLock = g.cur.execute(
|
||||
f"INSERT INTO {data['config']}_restrict_access SELECT * FROM {data['config']} WHERE id = '{data['peerID']}'")
|
||||
if g.cur.rowcount == 1:
|
||||
print(g.cur.rowcount)
|
||||
print(util.deletePeers(data['config'], [data['peerID']], g.cur, g.db))
|
||||
else:
|
||||
moveLockToUnlock = g.cur.execute(
|
||||
f"SELECT * FROM {data['config']}_restrict_access WHERE id = '{data['peerID']}'").fetchone()
|
||||
try:
|
||||
if len(moveLockToUnlock[-1]) == 0:
|
||||
status = subprocess.check_output(
|
||||
f"wg set {data['config']} peer {moveLockToUnlock[0]} allowed-ips {moveLockToUnlock[11]}",
|
||||
shell=True, stderr=subprocess.STDOUT)
|
||||
else:
|
||||
now = str(datetime.datetime.now().strftime("%m%d%Y%H%M%S"))
|
||||
f_name = now + "_tmp_psk.txt"
|
||||
f = open(f_name, "w+")
|
||||
f.write(moveLockToUnlock[-1])
|
||||
f.close()
|
||||
subprocess.check_output(
|
||||
f"wg set {data['config']} peer {moveLockToUnlock[0]} allowed-ips {moveLockToUnlock[11]} preshared-key {f_name}",
|
||||
shell=True, stderr=subprocess.STDOUT)
|
||||
os.remove(f_name)
|
||||
status = subprocess.check_output(f"wg-quick save {data['config']}", shell=True, stderr=subprocess.STDOUT)
|
||||
g.cur.execute(
|
||||
f"INSERT INTO {data['config']} SELECT * FROM {data['config']}_restrict_access WHERE id = '{data['peerID']}'")
|
||||
if g.cur.rowcount == 1:
|
||||
g.cur.execute(f"DELETE FROM {data['config']}_restrict_access WHERE id = '{data['peerID']}'")
|
||||
|
||||
except subprocess.CalledProcessError as exc:
|
||||
return {"status": False, "reason": str(exc.output.strip())}
|
||||
return good
|
||||
|
||||
|
||||
|
||||
class settings:
|
||||
def setTheme(self, theme, config, setConfig):
|
||||
themes = ['light', 'dark']
|
||||
if theme not in themes:
|
||||
return ret(status=False, reason="Theme does not exist")
|
||||
config['Server']['dashboard_theme'] = theme
|
||||
setConfig(config)
|
||||
return ret()
|
@ -1,99 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
chmod u+x /home/app/wgd.sh
|
||||
|
||||
chmod u+x /home/app/FIREWALLS/Admins/wg0-dwn.sh
|
||||
chmod u+x /home/app/FIREWALLS/Admins/wg0-nat.sh
|
||||
|
||||
chmod u+x /home/app/FIREWALLS/Members/wg1-dwn.sh
|
||||
chmod u+x /home/app/FIREWALLS/Members/wg1-nat.sh
|
||||
|
||||
chmod u+x /home/app/FIREWALLS/LAN-only-users/wg2-dwn.sh
|
||||
chmod u+x /home/app/FIREWALLS/LAN-only-users/wg2-nat.sh
|
||||
|
||||
chmod u+x /home/app/FIREWALLS/Guest/wg3-dwn.sh
|
||||
chmod u+x /home/app/FIREWALLS/Guest/wg3-nat.sh
|
||||
|
||||
chmod u+x /home/app/FIREWALLS/IPV6/wg0-dwn.sh
|
||||
chmod u+x /home/app/FIREWALLS/IPV6/wg0-nat.sh
|
||||
|
||||
chmod u+x /home/app/FIREWALLS/IPV6/wg1-dwn.sh
|
||||
chmod u+x /home/app/FIREWALLS/IPV6/wg1-nat.sh
|
||||
|
||||
|
||||
|
||||
if [ ! -f "/etc/wireguard/wg0.conf" ]; then
|
||||
/home/app/wgd.sh newconfig
|
||||
|
||||
fi
|
||||
run_wireguard_up() {
|
||||
config_files=$(find /etc/wireguard -type f -name "*.conf")
|
||||
|
||||
for file in $config_files; do
|
||||
config_name=$(basename "$file" ".conf")
|
||||
chmod 600 "/etc/wireguard/$config_name.conf"
|
||||
done
|
||||
|
||||
}
|
||||
|
||||
config_nginx () {
|
||||
rm /etc/nginx/http.d/default.conf
|
||||
cat <<EOF > "/etc/nginx/http.d/default.conf"
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
|
||||
location / {
|
||||
include uwsgi_params;
|
||||
uwsgi_pass 127.0.0.1:10086; # uWSGI service address and port
|
||||
}
|
||||
|
||||
# Set the location of the uWSGI static folder
|
||||
location /static/ {
|
||||
alias /home/app/static/;
|
||||
}
|
||||
|
||||
# Set security headers
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
add_header X-Frame-Options DENY;
|
||||
add_header X-XSS-Protection "1; mode=block";
|
||||
|
||||
# Disable server version information
|
||||
server_tokens off;
|
||||
|
||||
# Configure error pages
|
||||
error_page 400 401 402 403 404 /error.html;
|
||||
location = /error.html {
|
||||
root /path/to/error/pages;
|
||||
}
|
||||
|
||||
# Deny access to hidden files
|
||||
location ~ /\. {
|
||||
deny all;
|
||||
access_log off;
|
||||
log_not_found off;
|
||||
}
|
||||
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
run_wireguard_up #>/dev/null 2>&1 &&
|
||||
wg-quick up ADMINS
|
||||
config_nginx &&
|
||||
nginx &&
|
||||
/home/app/wgd.sh start
|
||||
|
@ -1,5 +0,0 @@
|
||||
Flask
|
||||
ifcfg
|
||||
configparser
|
||||
icmplib
|
||||
flask-qrcode
|
7
WG-Dash/src/static/css/bootstrap.min.css
vendored
@ -1,934 +0,0 @@
|
||||
body {
|
||||
font-size: .875rem;
|
||||
/*font-family: 'Poppins', sans-serif;*/
|
||||
}
|
||||
|
||||
.codeFont{
|
||||
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
}
|
||||
|
||||
.feather {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
.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, .bottomNavContainer .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, .bottomNavContainer .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: auto !important;
|
||||
}
|
||||
|
||||
.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: all 0.4s cubic-bezier(0.96, -0.07, 0.34, 1.01);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.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;
|
||||
margin: 0 1rem 0 0;
|
||||
}
|
||||
|
||||
.btn-control:hover{
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.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-lock-peer:hover {
|
||||
color: #28a745;
|
||||
}
|
||||
|
||||
.btn-lock-peer.lock{
|
||||
color: #6c757d
|
||||
}
|
||||
|
||||
.btn-lock-peer.lock:hover{
|
||||
color: #6c757d
|
||||
}
|
||||
|
||||
.btn-control.btn-outline-primary:hover{
|
||||
color: #007bff
|
||||
}
|
||||
|
||||
/* .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;
|
||||
display: flex;
|
||||
margin-bottom: 0.5rem
|
||||
}
|
||||
|
||||
.peer_data_group p {
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 0;
|
||||
margin-right: 1rem
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.peer_data_group {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.index-switch {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
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: 250px;
|
||||
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,
|
||||
#peer_preshared_key_textbox {
|
||||
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;
|
||||
/*padding: 0.6rem 0.9em;*/
|
||||
}
|
||||
|
||||
.login-box #username,
|
||||
.login-box #password {
|
||||
padding: 0.6rem calc( 0.9rem + 32px);
|
||||
height: inherit;
|
||||
}
|
||||
|
||||
.login-box label[for="username"],
|
||||
.login-box label[for="password"] {
|
||||
font-size: 1rem;
|
||||
margin: 0 !important;
|
||||
transform: translateY(30px) translateX(16px);
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
|
||||
/*label[for="password"]{*/
|
||||
|
||||
|
||||
/* transform: translateY(32px) translateX(16px);*/
|
||||
|
||||
|
||||
/*}*/
|
||||
|
||||
.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: 19.19px;
|
||||
/* transition: 0.3s ease-in-out; */
|
||||
|
||||
/* transform: translateX(40px); */
|
||||
opacity: 0 !important;
|
||||
}
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
.peerNameCol {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 0.2rem
|
||||
}
|
||||
|
||||
.peerName {
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.peerLightContainer {
|
||||
text-transform: uppercase;
|
||||
margin: 0;
|
||||
margin-left: auto !important;
|
||||
}
|
||||
|
||||
.conf_card .dot,
|
||||
.info .dot {
|
||||
transform: translateX(10px);
|
||||
}
|
||||
|
||||
#config_body {
|
||||
transition: 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
|
||||
#config_body.firstLoading {
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.chartTitle {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.chartControl {
|
||||
margin-bottom: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.chartTitle h6 {
|
||||
margin-bottom: 0;
|
||||
line-height: 1;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.chartContainer.fullScreen {
|
||||
position: fixed;
|
||||
z-index: 9999;
|
||||
background-color: white;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: calc( 100% + 15px);
|
||||
height: 100%;
|
||||
padding: 32px;
|
||||
}
|
||||
|
||||
.chartContainer.fullScreen .col-sm {
|
||||
padding-right: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.chartContainer.fullScreen .chartCanvasContainer {
|
||||
width: 100%;
|
||||
height: calc( 100% - 47px) !important;
|
||||
max-height: calc( 100% - 47px) !important;
|
||||
}
|
||||
|
||||
#switch{
|
||||
transition: all 200ms ease-in;
|
||||
}
|
||||
|
||||
.toggle--switch{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.toggleLabel{
|
||||
width: 64px;
|
||||
height: 32px;
|
||||
background-color: #6c757d17;
|
||||
display: flex;
|
||||
position: relative;
|
||||
border: 2px solid #6c757d8c;
|
||||
border-radius: 100px;
|
||||
transition: all 200ms ease-in;
|
||||
cursor: pointer;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.toggle--switch.waiting + .toggleLabel{
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.toggleLabel::before{
|
||||
background-color: #6c757d;
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
content: "";
|
||||
border-radius: 100px;
|
||||
margin: 1px;
|
||||
position: absolute;
|
||||
animation-name: off;
|
||||
animation-duration: 350ms;
|
||||
animation-fill-mode: forwards;
|
||||
transition: all 200ms ease-in;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.toggleLabel:hover::before{
|
||||
filter: brightness(1.2);
|
||||
}
|
||||
|
||||
|
||||
.toggle--switch:checked + .toggleLabel{
|
||||
background-color: #007bff17 !important;
|
||||
border: 2px solid #007bff8c;
|
||||
}
|
||||
|
||||
.toggle--switch:checked + .toggleLabel::before{
|
||||
background-color: #007bff;
|
||||
animation-name: on;
|
||||
animation-duration: 350ms;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
@keyframes on {
|
||||
0%{
|
||||
left: 0px;
|
||||
}
|
||||
60%{
|
||||
left: 0px;
|
||||
width: 40px;
|
||||
}
|
||||
100%{
|
||||
left: 32px;
|
||||
width: 26px;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes off {
|
||||
0%{
|
||||
left: 32px;
|
||||
}
|
||||
60%{
|
||||
left: 18px;
|
||||
width: 40px;
|
||||
}
|
||||
100%{
|
||||
left: 0px;
|
||||
width: 26px;
|
||||
}
|
||||
}
|
||||
|
||||
.toastContainer{
|
||||
z-index: 99999 !important;
|
||||
}
|
||||
|
||||
.toast{
|
||||
min-width: 300px;
|
||||
background-color: rgba(255,255,255,1);
|
||||
z-index: 99999;
|
||||
}
|
||||
|
||||
.toast-header{
|
||||
background-color: rgba(255,255,255);
|
||||
}
|
||||
|
||||
.toast-progressbar{
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
background-color: #007bff;
|
||||
border-bottom-left-radius: .25rem;
|
||||
}
|
||||
|
||||
.addConfigurationAvailableIPs{
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.input-feedback{
|
||||
display: none;
|
||||
}
|
||||
|
||||
#addConfigurationModal label{
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#addConfigurationModal label a{
|
||||
margin-left: auto !important;
|
||||
}
|
||||
|
||||
#reGeneratePrivateKey{
|
||||
border-top-right-radius: 10px;
|
||||
border-bottom-right-radius: 10px;
|
||||
}
|
||||
|
||||
.addConfigurationToggleStatus.waiting{
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/*.conf_card .card-body .row .card-col{*/
|
||||
/* margin-bottom: 0.5rem;*/
|
||||
/*}*/
|
||||
|
||||
.peerDataUsageChartContainer{
|
||||
min-height: 50vh;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.peerDataUsageChartControl{
|
||||
display: block !important;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.peerDataUsageChartControl .switchUnit{
|
||||
width: 33.3%;
|
||||
}
|
||||
|
||||
.peerDataUsageChartControl .switchTimePeriod{
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
@media (min-width: 1200px){
|
||||
#peerDataUsage .modal-xl {
|
||||
max-width: 95vw;
|
||||
}
|
||||
}
|
||||
|
||||
.bottom{
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
@media (max-width: 768px){
|
||||
.bottom{
|
||||
display: block;
|
||||
}
|
||||
|
||||
.btn-manage-group{
|
||||
bottom: calc( 3rem + 40px + env(safe-area-inset-bottom, 5px));
|
||||
}
|
||||
|
||||
main{
|
||||
padding-bottom: calc( 3rem + 40px + env(safe-area-inset-bottom, 5px));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.bottomNavContainer{
|
||||
display: flex;
|
||||
color: #333;
|
||||
padding-bottom: env(safe-area-inset-bottom, 5px);
|
||||
box-shadow: inset 0 1px 0 rgb(0 0 0 / 10%);
|
||||
}
|
||||
|
||||
.bottomNavButton{
|
||||
width: 25vw;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin: 0.7rem 0;
|
||||
color: rgba(51, 51, 51, 0.5);
|
||||
cursor: pointer;
|
||||
transition: all ease-in 0.2s;
|
||||
}
|
||||
|
||||
.bottomNavButton.active{
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.bottomNavButton i{
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.bottomNavButton .subNav{
|
||||
width: 100vw;
|
||||
position: absolute;
|
||||
z-index: 10000;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background-color: #272b30;
|
||||
display: none;
|
||||
animation-duration: 400ms;
|
||||
padding-bottom: env(safe-area-inset-bottom, 5px);
|
||||
}
|
||||
|
||||
.bottomNavButton .subNav.active{
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
.bottomNavButton .subNav .nav .nav-item .nav-link{
|
||||
padding: 0.7rem 1rem;
|
||||
}
|
||||
|
||||
.bottomNavWrapper{
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background-color: #000000a1;
|
||||
position: fixed;
|
||||
z-index: 1030;
|
||||
display: none;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.bottomNavWrapper.active{
|
||||
display: block;
|
||||
}
|
||||
|
||||
.sb-update-url .dot-running{
|
||||
transform: translateX(10px);
|
||||
}
|
||||
|
||||
.list-group-item{
|
||||
transition: all 0.1s ease-in;
|
||||
}
|
||||
|
||||
.theme-switch-btn{
|
||||
width: 100%;
|
||||
}
|
1
WG-Dash/src/static/css/dashboard.min.css
vendored
@ -1,391 +0,0 @@
|
||||
:root {
|
||||
--green: hsl(120deg, 30%, 50%) !important;
|
||||
--blue: hsl(235deg, 60%, 60%) !important;
|
||||
--red: hsl(0deg, 60%, 60%) !important;
|
||||
--magenta: hsl(315deg, 60%, 60%) !important;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #222222;
|
||||
color: hsl(0deg, 0%, 80%);
|
||||
}
|
||||
|
||||
a.text-primary:focus, a.text-primary:hover {
|
||||
color: hsl(235deg, 60%, 50%) !important;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
color: hsl(0deg, 0%, 90%) !important;
|
||||
border-color: hsl(235deg, 60%, 60%) !important;
|
||||
background: hsl(235deg, 60%, 60%) !important;
|
||||
}
|
||||
.btn-primary:hover {
|
||||
background-color: hsl(235deg, 60%, 50%) !important;
|
||||
border-color: hsl(235deg, 60%, 50%) !important;
|
||||
}
|
||||
|
||||
.btn-outline-primary {
|
||||
color: hsl(235deg, 60%, 60%) !important;
|
||||
border-color: hsl(235deg, 60%, 60%) !important;
|
||||
background: transparent !important;
|
||||
}
|
||||
.btn-outline-primary:hover, .btn-outline-primary.active {
|
||||
color: hsl(0deg, 0%, 90%) !important;
|
||||
}
|
||||
.btn-outline-primary.active {
|
||||
background-color: hsl(235deg, 60%, 60%) !important;
|
||||
border-color: hsl(235deg, 60%, 60%) !important;
|
||||
}
|
||||
.btn-outline-primary:hover {
|
||||
background-color: hsl(235deg, 60%, 50%) !important;
|
||||
border-color: hsl(235deg, 60%, 50%) !important;
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
color: hsl(0deg, 0%, 90%) !important;
|
||||
border-color: hsl(120deg, 30%, 50%) !important;
|
||||
background: hsl(120deg, 30%, 50%) !important;
|
||||
}
|
||||
.btn-success:hover {
|
||||
background-color: hsl(120deg, 30%, 40%) !important;
|
||||
border-color: hsl(120deg, 30%, 40%) !important;
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
background-color: #272727;
|
||||
border-color: #272727;
|
||||
color: white;
|
||||
}
|
||||
.list-group-item:hover {
|
||||
background-color: #333333;
|
||||
border-color: #333333;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.delete-peer-bulk-badge.badge-danger {
|
||||
background-color: hsl(0deg, 60%, 60%);
|
||||
}
|
||||
.delete-peer-bulk-badge.badge-danger:hover {
|
||||
background-color: hsl(0deg, 60%, 50%);
|
||||
}
|
||||
|
||||
#delete_bulk_modal .list-group a.active {
|
||||
background-color: hsl(0deg, 60%, 60%);
|
||||
border: hsl(0deg, 60%, 60%);
|
||||
}
|
||||
#delete_bulk_modal .list-group a.active:hover {
|
||||
background-color: hsl(0deg, 60%, 50%);
|
||||
border: hsl(0deg, 60%, 50%);
|
||||
}
|
||||
|
||||
#available_ip_modal .list-group a.active {
|
||||
background-color: hsl(235deg, 60%, 60%);
|
||||
border: hsl(235deg, 60%, 60%);
|
||||
}
|
||||
#available_ip_modal .list-group a.active:hover {
|
||||
background-color: hsl(235deg, 60%, 50%);
|
||||
border: hsl(235deg, 60%, 50%);
|
||||
}
|
||||
|
||||
.available-ip-badge.badge-primary {
|
||||
background-color: hsl(235deg, 60%, 60%);
|
||||
}
|
||||
.available-ip-badge.badge-primary:hover {
|
||||
background-color: hsl(235deg, 60%, 50%);
|
||||
}
|
||||
|
||||
.btn-outline-success {
|
||||
color: hsl(120deg, 30%, 50%) !important;
|
||||
border-color: hsl(120deg, 30%, 50%) !important;
|
||||
background: transparent !important;
|
||||
}
|
||||
.btn-outline-success:hover, .btn-outline-success.active {
|
||||
color: hsl(0deg, 0%, 90%) !important;
|
||||
}
|
||||
.btn-outline-success.active {
|
||||
background-color: hsl(120deg, 30%, 50%) !important;
|
||||
border-color: hsl(120deg, 30%, 50%) !important;
|
||||
}
|
||||
.btn-outline-success:hover {
|
||||
background-color: hsl(120deg, 30%, 40%) !important;
|
||||
border-color: hsl(120deg, 30%, 40%) !important;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
color: hsl(0deg, 0%, 90%) !important;
|
||||
border-color: hsl(0deg, 60%, 60%) !important;
|
||||
background: hsl(0deg, 60%, 60%) !important;
|
||||
}
|
||||
.btn-danger:hover {
|
||||
background-color: hsl(0deg, 60%, 50%) !important;
|
||||
border-color: hsl(0deg, 60%, 50%) !important;
|
||||
}
|
||||
|
||||
.btn-outline-danger {
|
||||
color: hsl(0deg, 60%, 60%) !important;
|
||||
border-color: hsl(0deg, 60%, 60%) !important;
|
||||
background: transparent !important;
|
||||
}
|
||||
.btn-outline-danger:hover, .btn-outline-danger.active {
|
||||
color: hsl(0deg, 0%, 90%) !important;
|
||||
}
|
||||
.btn-outline-danger.active {
|
||||
background-color: hsl(0deg, 60%, 60%) !important;
|
||||
border-color: hsl(0deg, 60%, 60%) !important;
|
||||
}
|
||||
.btn-outline-danger:hover {
|
||||
background-color: hsl(0deg, 60%, 50%) !important;
|
||||
border-color: hsl(0deg, 60%, 50%) !important;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
color: hsl(0deg, 0%, 90%) !important;
|
||||
border-color: #424242 !important;
|
||||
background: #424242 !important;
|
||||
}
|
||||
.btn-secondary:hover {
|
||||
background-color: #383838 !important;
|
||||
border-color: #383838 !important;
|
||||
}
|
||||
|
||||
.btn-outline-secondary {
|
||||
color: #424242 !important;
|
||||
border-color: #424242 !important;
|
||||
background: transparent !important;
|
||||
}
|
||||
.btn-outline-secondary:hover, .btn-outline-secondary.active {
|
||||
color: hsl(0deg, 0%, 90%) !important;
|
||||
}
|
||||
.btn-outline-secondary.active {
|
||||
background-color: #424242 !important;
|
||||
border-color: #424242 !important;
|
||||
}
|
||||
.btn-outline-secondary:hover {
|
||||
background-color: #383838 !important;
|
||||
border-color: #383838 !important;
|
||||
}
|
||||
|
||||
.btn-control.btn-lock-peer.lock {
|
||||
color: hsl(0deg, 60%, 60%) !important;
|
||||
}
|
||||
.btn-control.btn-lock-peer.lock:hover {
|
||||
color: hsl(0deg, 60%, 50%) !important;
|
||||
}
|
||||
.btn-control:hover {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
.btn-control:hover.btn-outline-primary {
|
||||
color: hsl(235deg, 60%, 50%) !important;
|
||||
}
|
||||
.btn-control:hover.btn-outline-success {
|
||||
color: hsl(120deg, 30%, 40%) !important;
|
||||
}
|
||||
.btn-control:hover.btn-outline-danger {
|
||||
color: hsl(0deg, 60%, 50%) !important;
|
||||
}
|
||||
.btn-control:hover.btn-outline-secondary {
|
||||
color: #383838 !important;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
background-color: #2c2c2c !important;
|
||||
border-color: transparent !important;
|
||||
color: hsl(0deg, 0%, 80%) !important;
|
||||
}
|
||||
|
||||
.form-control:disabled {
|
||||
color: #777777 !important;
|
||||
}
|
||||
|
||||
.card .form-control {
|
||||
background: #2c2c2c !important;
|
||||
}
|
||||
|
||||
.conf_card a {
|
||||
color: hsl(235deg, 60%, 60%);
|
||||
}
|
||||
.conf_card:hover {
|
||||
border-color: hsl(235deg, 60%, 60%);
|
||||
}
|
||||
|
||||
.sidebar .nav-link,
|
||||
.bottomNavContainer .nav-link {
|
||||
color: hsl(0deg, 0%, 80%);
|
||||
}
|
||||
.sidebar .nav-link:hover,
|
||||
.bottomNavContainer .nav-link:hover {
|
||||
background: #222222;
|
||||
}
|
||||
|
||||
nav#sidebarMenu.col-md-3.col-lg-2.d-md-block.bg-light.sidebar.collapse,
|
||||
.navbar-brand,
|
||||
.bg-dark {
|
||||
background-color: #1e1e1e !important;
|
||||
background: #1e1e1e !important;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: #272727;
|
||||
}
|
||||
|
||||
.text-muted {
|
||||
color: hsl(0deg, 0%, 50%) !important;
|
||||
}
|
||||
|
||||
.text-danger {
|
||||
color: hsl(0deg, 60%, 60%) !important;
|
||||
}
|
||||
|
||||
.text-success {
|
||||
color: hsl(120deg, 30%, 50%) !important;
|
||||
}
|
||||
|
||||
.text-primary {
|
||||
color: hsl(235deg, 60%, 60%) !important;
|
||||
}
|
||||
|
||||
.text-info {
|
||||
color: hsl(190deg, 60%, 60%) !important;
|
||||
}
|
||||
|
||||
a.text-success:focus,
|
||||
a.text-success:hover {
|
||||
color: hsl(120deg, 30%, 40%) !important;
|
||||
}
|
||||
|
||||
a.text-danger:focus,
|
||||
a.text-danger:hover {
|
||||
color: hsl(0deg, 60%, 50%) !important;
|
||||
}
|
||||
|
||||
a.text-info:focus,
|
||||
a.text-info:hover {
|
||||
color: hsl(190deg, 60%, 50%) !important;
|
||||
}
|
||||
|
||||
.dot-running {
|
||||
background-color: hsl(120deg, 30%, 50%) !important;
|
||||
}
|
||||
|
||||
.card-running {
|
||||
border-color: hsl(120deg, 30%, 50%);
|
||||
}
|
||||
|
||||
.toggle--switch:checked + .toggleLabel::before {
|
||||
background-color: hsl(235deg, 60%, 60%) !important;
|
||||
}
|
||||
|
||||
.toggle--switch:checked + .toggleLabel {
|
||||
background-color: #2e336b !important;
|
||||
border-color: hsl(235deg, 60%, 60%) !important;
|
||||
}
|
||||
|
||||
.sidebar .nav-link.active,
|
||||
.bottomNavContainer .nav-link.active {
|
||||
color: hsl(235deg, 60%, 60%) !important;
|
||||
}
|
||||
|
||||
hr {
|
||||
border-color: #2e2e2e;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background-color: #222222;
|
||||
}
|
||||
|
||||
.modal-header,
|
||||
.modal-footer {
|
||||
background-color: #1e1e1e;
|
||||
border-color: #2e2e2e;
|
||||
}
|
||||
|
||||
code {
|
||||
color: hsl(315deg, 60%, 60%);
|
||||
}
|
||||
|
||||
.close {
|
||||
color: hsl(0deg, 0%, 80%);
|
||||
text-shadow: none;
|
||||
}
|
||||
.close:hover {
|
||||
color: hsl(0deg, 0%, 70%);
|
||||
}
|
||||
|
||||
.chartContainer.fullScreen {
|
||||
background-color: #222222 !important;
|
||||
}
|
||||
|
||||
.popover {
|
||||
background-color: #333333 !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.popover-body {
|
||||
color: hsl(0deg, 0%, 80%) !important;
|
||||
}
|
||||
|
||||
div.toast {
|
||||
background-color: #424242 !important;
|
||||
}
|
||||
div.toast div.toast-header {
|
||||
background-color: #333333 !important;
|
||||
color: hsl(0deg, 0%, 80%) !important;
|
||||
border-bottom-color: #424242 !important;
|
||||
}
|
||||
div.toast div.toast-body {
|
||||
background-color: #383838 !important;
|
||||
color: hsl(0deg, 0%, 80%) !important;
|
||||
}
|
||||
div.toast div.toast-body.text-danger {
|
||||
color: hsl(0deg, 60%, 60%) !important;
|
||||
}
|
||||
div.toast div.toast-progressbar {
|
||||
background-color: hsl(235deg, 60%, 60%) !important;
|
||||
}
|
||||
div.toast div.toast-progressbar.bg-danger {
|
||||
background-color: hsl(0deg, 60%, 60%) !important;
|
||||
}
|
||||
|
||||
.bs-popover-auto[x-placement^=right] > .arrow::after,
|
||||
.bs-popover-right > .arrow::after {
|
||||
border-right-color: #333333 !important;
|
||||
}
|
||||
|
||||
.btn-manage-group .setting_btn_menu {
|
||||
background-color: #2c2c2c !important;
|
||||
}
|
||||
|
||||
.setting_btn_menu a:hover {
|
||||
background-color: #333333 !important;
|
||||
}
|
||||
|
||||
.table {
|
||||
color: hsl(0deg, 0%, 80%) !important;
|
||||
}
|
||||
.table th,
|
||||
.table td {
|
||||
border-color: #333333 !important;
|
||||
}
|
||||
|
||||
.btn-outline-primary.focus, .btn-outline-primary:focus, .btn-primary.focus, .btn-primary:focus {
|
||||
box-shadow: 0 0 0 0.2rem rgba(144, 153, 255, 0.29) !important;
|
||||
}
|
||||
|
||||
.bottomNav {
|
||||
background-color: #272727 !important;
|
||||
}
|
||||
.bottomNav .bottomNavButton {
|
||||
color: hsl(0deg, 0%, 60%);
|
||||
}
|
||||
.bottomNav .bottomNavButton.active {
|
||||
color: hsl(235deg, 60%, 60%) !important;
|
||||
}
|
||||
.bottomNav .subNav {
|
||||
background-color: #272727 !important;
|
||||
}
|
||||
|
||||
.key:hover {
|
||||
color: hsl(235deg, 60%, 60%);
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=dark.css.map */
|
@ -1 +0,0 @@
|
||||
{"version":3,"sourceRoot":"","sources":["dark.scss"],"names":[],"mappings":"AAgCA;EACE;EACA;EACA;EACA;;;AAGF;EACE,YAvCS;EAwCT,OAdS;;;AAiBX;EACE;;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;;;AAIJ;EACE;EACA;EACA;;AAEA;EAEE;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;;AAIJ;EACE;EACA;EACA;;AAEA;EACE;EACA;;;AAIJ;EACE,kBAzFS;EA0FT,cA1FS;EA2FT;;AAEA;EACE,kBA3FO;EA4FP,cA5FO;EA6FP;;;AAMJ;EACE,kBA3FQ;;AA4FR;EACE,kBA9FM;;;AAoGN;EACE,kBApGI;EAqGJ,QArGI;;AAuGJ;EACE,kBAzGE;EA0GF,QA1GE;;;AAkHN;EACE,kBA/GK;EAgHL,QAhHK;;AAkHL;EACE,kBApHG;EAqHH,QArHG;;;AA2HX;EACE,kBA3HS;;AA4HT;EACE,kBA9HO;;;AAkIX;EACE;EACA;EACA;;AAEA;EAEE;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;;AAIJ;EACE;EACA;EACA;;AAEA;EACE;EACA;;;AAIJ;EACE;EACA;EACA;;AAEA;EAEE;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;;AAIJ;EACE;EACA;EACA;;AAEA;EACE;EACA;;;AAIJ;EACE;EACA;EACA;;AAEA;EAEE;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;;AAKF;EACE;;AACA;EACE;;AAIJ;EACE;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;;AAKN;EACE;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAIA;EACE,OAnQO;;AAqQT;EACE,cAtQO;;;AA0QX;AAAA;EAEE,OAnQS;;AAqQT;AAAA;EACE,YAhSO;;;AAoSX;AAAA;AAAA;EAGE;EACA;;;AAGF;EACE,YA1SS;;;AA6SX;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;AAAA;EAEE;;;AAGF;AAAA;EAEE;;;AAGF;AAAA;EAEE;;;AAGF;EACE;;;AAGF;EACE,cA5UU;;;AA+UZ;EACE;;;AAEF;EACE;EACA;;;AAGF;AAAA;EAEE;;;AAGF;EACE,cApWS;;;AAuWX;EACE,kBA5WS;;;AA+WX;AAAA;EAEE,kBAlXS;EAmXT,cA9WS;;;AAiXX;EACE,OA/VY;;;AAkWd;EACE,OAhWS;EAiWT;;AAEA;EACE,OAnWO;;;AAuWX;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;AAEA;EACE;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;;AAIJ;AAAA;EAEE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;AAEA;AAAA;EAEE;;;AAKJ;EACE;;;AAIA;EACE;;AAIA;EACE,OAhbK;;AAobT;EACE;;AAGF;EACE;;;AAKJ;EACE,OA1cS","file":"dark.css"}
|
1
WG-Dash/src/static/css/theme/dark.min.css
vendored
@ -1 +0,0 @@
|
||||
{"version":3,"sourceRoot":"","sources":["dark.scss"],"names":[],"mappings":"AAgCA,MACE,0CACA,yCACA,sCACA,4CAGF,KACE,WAvCS,KAwCT,MAdS,KAiBX,0CACE,yBAGF,aACE,yBACA,gCACA,8BAEA,mBACE,oCACA,gCAIJ,qBACE,yBACA,gCACA,oCAEA,uDAEE,yBAGF,4BACE,oCACA,gCAGF,2BACE,oCACA,gCAIJ,aACE,yBACA,gCACA,8BAEA,mBACE,oCACA,gCAIJ,iBACE,iBAzFS,QA0FT,aA1FS,QA2FT,WAEA,uBACE,iBA3FO,KA4FP,aA5FO,KA6FP,WAMJ,qCACE,iBA3FQ,QA4FR,2CACE,iBA9FM,KAoGN,wCACE,iBApGI,QAqGJ,OArGI,QAuGJ,8CACE,iBAzGE,KA0GF,OA1GE,KAkHN,yCACE,iBA/GK,QAgHL,OAhHK,QAkHL,+CACE,iBApHG,QAqHH,OArHG,QA2HX,kCACE,iBA3HS,QA4HT,wCACE,iBA9HO,QAkIX,qBACE,yBACA,gCACA,oCAEA,uDAEE,yBAGF,4BACE,oCACA,gCAGF,2BACE,oCACA,gCAIJ,YACE,yBACA,gCACA,8BAEA,kBACE,iCACA,6BAIJ,oBACE,yBACA,gCACA,oCAEA,qDAEE,yBAGF,2BACE,oCACA,gCAGF,0BACE,iCACA,6BAIJ,eACE,yBACA,gCACA,8BAEA,qBACE,oCACA,gCAIJ,uBACE,yBACA,gCACA,oCAEA,2DAEE,yBAGF,8BACE,oCACA,gCAGF,6BACE,oCACA,gCAKF,gCACE,yBACA,sCACE,sBAIJ,mBACE,0CAEA,uCACE,yBAGF,uCACE,yBAGF,sCACE,sBAGF,yCACE,yBAKN,cACE,oCACA,sCACA,sBAGF,uBACE,sBAGF,oBACE,8BAIA,aACE,MAnQO,QAqQT,iBACE,aAtQO,QA0QX,iDAEE,MAnQS,KAqQT,6DACE,WAhSO,KAoSX,8FAGE,oCACA,8BAGF,MACE,WA1SS,QA6SX,YACE,sBAGF,aACE,yBAGF,cACE,yBAGF,cACE,yBAGF,WACE,yBAGF,0CAEE,yBAGF,wCAEE,sBAGF,oCAEE,yBAGF,aACE,oCAGF,cACE,aA5UU,QA+UZ,6CACE,oCAEF,qCACE,oCACA,gCAGF,+DAEE,yBAGF,GACE,aApWS,QAuWX,eACE,iBA5WS,KA+WX,4BAEE,iBAlXS,QAmXT,aA9WS,QAiXX,KACE,MA/VY,QAkWd,OACE,MAhWS,KAiWT,iBAEA,aACE,MAnWO,QAuWX,2BACE,iCAGF,SACE,iCACA,uBAGF,cACE,sBAGF,UACE,oCAEA,2BACE,iCACA,sBACA,uCAGF,yBACE,oCACA,sBAGF,qCACE,yBAGF,gCACE,oCAGF,0CACE,oCAIJ,mFAEE,mCAGF,oCACE,oCAGF,0BACE,iCAGF,OACE,sBAEA,oBAEE,6BAKJ,4FACE,wDAIA,WACE,oCAIA,4BACE,MAhbK,KAobT,mCACE,yBAGF,mBACE,oCAKJ,WACE,MA1cS","file":"dark.min.css"}
|
@ -1,480 +0,0 @@
|
||||
$grey-100: #1e1e1e;
|
||||
$grey-200: #222222;
|
||||
$grey-300: #242424;
|
||||
$grey-400: #272727;
|
||||
$grey-500: #2c2c2c;
|
||||
$grey-600: #2e2e2e;
|
||||
$grey-700: #333333;
|
||||
$grey-800: #383838;
|
||||
$grey-900: #424242;
|
||||
$grey-1000: #777777;
|
||||
|
||||
$green-400: hsl(120deg, 30%, 40%);
|
||||
$green-500: hsl(120deg, 30%, 50%);
|
||||
|
||||
$red-400: hsl(0deg, 60%, 50%);
|
||||
$red-500: hsl(0deg, 60%, 60%);
|
||||
|
||||
$blue-400: hsl(235deg, 60%, 50%);
|
||||
$blue-500: hsl(235deg, 60%, 60%);
|
||||
$blue-400-clear: rgba(51, 64, 204, 0.25);
|
||||
|
||||
$cyan-400: hsl(190deg, 60%, 50%);
|
||||
$cyan-500: hsl(190deg, 60%, 60%);
|
||||
|
||||
$magenta-500: hsl(315deg, 60%, 60%);
|
||||
|
||||
$text-100: hsl(0deg, 0%, 90%);
|
||||
$text-200: hsl(0deg, 0%, 80%);
|
||||
$text-300: hsl(0deg, 0%, 70%);
|
||||
$text-400: hsl(0deg, 0%, 60%);
|
||||
$text-500: hsl(0deg, 0%, 50%);
|
||||
|
||||
:root {
|
||||
--green: #{$green-500} !important;
|
||||
--blue: #{$blue-500} !important;
|
||||
--red: #{$red-500} !important;
|
||||
--magenta: #{$magenta-500} !important;
|
||||
}
|
||||
|
||||
body {
|
||||
background: $grey-200;
|
||||
color: $text-200;
|
||||
}
|
||||
|
||||
a.text-primary:focus, a.text-primary:hover{
|
||||
color: $blue-400 !important;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
color: $text-100 !important;
|
||||
border-color: $blue-500 !important;
|
||||
background: $blue-500 !important;
|
||||
|
||||
&:hover {
|
||||
background-color: $blue-400 !important;
|
||||
border-color: $blue-400 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-outline-primary {
|
||||
color: $blue-500 !important;
|
||||
border-color: $blue-500 !important;
|
||||
background: transparent !important;
|
||||
|
||||
&:hover,
|
||||
&.active {
|
||||
color: $text-100 !important;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: $blue-500 !important;
|
||||
border-color: $blue-500 !important;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: $blue-400 !important;
|
||||
border-color: $blue-400 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
color: $text-100 !important;
|
||||
border-color: $green-500 !important;
|
||||
background: $green-500 !important;
|
||||
|
||||
&:hover {
|
||||
background-color: $green-400 !important;
|
||||
border-color: $green-400 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.list-group-item{
|
||||
background-color: $grey-400;
|
||||
border-color: $grey-400;
|
||||
color: white;
|
||||
|
||||
&:hover{
|
||||
background-color: $grey-700;
|
||||
border-color: $grey-700;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.delete-peer-bulk-badge.badge-danger{
|
||||
background-color: $red-500;
|
||||
&:hover{
|
||||
background-color: $red-400;
|
||||
}
|
||||
}
|
||||
|
||||
#delete_bulk_modal{
|
||||
.list-group{
|
||||
a.active{
|
||||
background-color: $red-500;
|
||||
border: $red-500;
|
||||
|
||||
&:hover{
|
||||
background-color: $red-400;
|
||||
border: $red-400;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#available_ip_modal{
|
||||
.list-group{
|
||||
a.active{
|
||||
background-color: $blue-500;
|
||||
border: $blue-500;
|
||||
|
||||
&:hover{
|
||||
background-color: $blue-400;
|
||||
border: $blue-400;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.available-ip-badge.badge-primary{
|
||||
background-color: $blue-500;
|
||||
&:hover{
|
||||
background-color: $blue-400;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-outline-success {
|
||||
color: $green-500 !important;
|
||||
border-color: $green-500 !important;
|
||||
background: transparent !important;
|
||||
|
||||
&:hover,
|
||||
&.active {
|
||||
color: $text-100 !important;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: $green-500 !important;
|
||||
border-color: $green-500 !important;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: $green-400 !important;
|
||||
border-color: $green-400 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
color: $text-100 !important;
|
||||
border-color: $red-500 !important;
|
||||
background: $red-500 !important;
|
||||
|
||||
&:hover {
|
||||
background-color: $red-400 !important;
|
||||
border-color: $red-400 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-outline-danger {
|
||||
color: $red-500 !important;
|
||||
border-color: $red-500 !important;
|
||||
background: transparent !important;
|
||||
|
||||
&:hover,
|
||||
&.active {
|
||||
color: $text-100 !important;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: $red-500 !important;
|
||||
border-color: $red-500 !important;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: $red-400 !important;
|
||||
border-color: $red-400 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
color: $text-100 !important;
|
||||
border-color: $grey-900 !important;
|
||||
background: $grey-900 !important;
|
||||
|
||||
&:hover {
|
||||
background-color: $grey-800 !important;
|
||||
border-color: $grey-800 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-outline-secondary {
|
||||
color: $grey-900 !important;
|
||||
border-color: $grey-900 !important;
|
||||
background: transparent !important;
|
||||
|
||||
&:hover,
|
||||
&.active {
|
||||
color: $text-100 !important;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: $grey-900 !important;
|
||||
border-color: $grey-900 !important;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: $grey-800 !important;
|
||||
border-color: $grey-800 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-control {
|
||||
&.btn-lock-peer.lock {
|
||||
color: $red-500 !important;
|
||||
&:hover {
|
||||
color: $red-400 !important;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: transparent !important;
|
||||
|
||||
&.btn-outline-primary {
|
||||
color: $blue-400 !important;
|
||||
}
|
||||
|
||||
&.btn-outline-success {
|
||||
color: $green-400 !important;
|
||||
}
|
||||
|
||||
&.btn-outline-danger {
|
||||
color: $red-400 !important;
|
||||
}
|
||||
|
||||
&.btn-outline-secondary {
|
||||
color: $grey-800 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form-control {
|
||||
background-color: $grey-500 !important;
|
||||
border-color: transparent !important;
|
||||
color: $text-200 !important;
|
||||
}
|
||||
|
||||
.form-control:disabled{
|
||||
color: $grey-1000 !important;
|
||||
}
|
||||
|
||||
.card .form-control {
|
||||
background: $grey-500 !important;
|
||||
}
|
||||
|
||||
.conf_card{
|
||||
a{
|
||||
color: $blue-500;
|
||||
}
|
||||
&:hover{
|
||||
border-color: $blue-500;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar .nav-link,
|
||||
.bottomNavContainer .nav-link {
|
||||
color: $text-200;
|
||||
|
||||
&:hover {
|
||||
background: $grey-200;
|
||||
}
|
||||
}
|
||||
|
||||
nav#sidebarMenu.col-md-3.col-lg-2.d-md-block.bg-light.sidebar.collapse,
|
||||
.navbar-brand,
|
||||
.bg-dark {
|
||||
background-color: $grey-100 !important;
|
||||
background: $grey-100 !important;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: $grey-400;
|
||||
}
|
||||
|
||||
.text-muted {
|
||||
color: $text-500 !important;
|
||||
}
|
||||
|
||||
.text-danger {
|
||||
color: $red-500 !important;
|
||||
}
|
||||
|
||||
.text-success {
|
||||
color: $green-500 !important;
|
||||
}
|
||||
|
||||
.text-primary {
|
||||
color: $blue-500 !important;
|
||||
}
|
||||
|
||||
.text-info {
|
||||
color: $cyan-500 !important;
|
||||
}
|
||||
|
||||
a.text-success:focus,
|
||||
a.text-success:hover {
|
||||
color: $green-400 !important;
|
||||
}
|
||||
|
||||
a.text-danger:focus,
|
||||
a.text-danger:hover {
|
||||
color: $red-400 !important;
|
||||
}
|
||||
|
||||
a.text-info:focus,
|
||||
a.text-info:hover {
|
||||
color: $cyan-400 !important;
|
||||
}
|
||||
|
||||
.dot-running {
|
||||
background-color: $green-500 !important;
|
||||
}
|
||||
|
||||
.card-running {
|
||||
border-color: $green-500;
|
||||
}
|
||||
|
||||
.toggle--switch:checked + .toggleLabel::before {
|
||||
background-color: $blue-500 !important;
|
||||
}
|
||||
.toggle--switch:checked + .toggleLabel {
|
||||
background-color: mix($blue-500, #000f) !important;
|
||||
border-color: $blue-500 !important;
|
||||
}
|
||||
|
||||
.sidebar .nav-link.active,
|
||||
.bottomNavContainer .nav-link.active {
|
||||
color: $blue-500 !important;
|
||||
}
|
||||
|
||||
hr {
|
||||
border-color: $grey-600;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background-color: $grey-200;
|
||||
}
|
||||
|
||||
.modal-header,
|
||||
.modal-footer {
|
||||
background-color: $grey-100;
|
||||
border-color: $grey-600;
|
||||
}
|
||||
|
||||
code {
|
||||
color: $magenta-500;
|
||||
}
|
||||
|
||||
.close {
|
||||
color: $text-200;
|
||||
text-shadow: none;
|
||||
|
||||
&:hover {
|
||||
color: $text-300;
|
||||
}
|
||||
}
|
||||
|
||||
.chartContainer.fullScreen {
|
||||
background-color: $grey-200 !important;
|
||||
}
|
||||
|
||||
.popover {
|
||||
background-color: $grey-700 !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.popover-body {
|
||||
color: $text-200 !important;
|
||||
}
|
||||
|
||||
div.toast {
|
||||
background-color: $grey-900 !important;
|
||||
|
||||
div.toast-header {
|
||||
background-color: $grey-700 !important;
|
||||
color: $text-200 !important;
|
||||
border-bottom-color: $grey-900 !important;
|
||||
}
|
||||
|
||||
div.toast-body {
|
||||
background-color: $grey-800 !important;
|
||||
color: $text-200 !important;
|
||||
}
|
||||
|
||||
div.toast-body.text-danger{
|
||||
color: $red-500 !important;
|
||||
}
|
||||
|
||||
div.toast-progressbar {
|
||||
background-color: $blue-500 !important;
|
||||
}
|
||||
|
||||
div.toast-progressbar.bg-danger {
|
||||
background-color: $red-500 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.bs-popover-auto[x-placement^="right"] > .arrow::after,
|
||||
.bs-popover-right > .arrow::after {
|
||||
border-right-color: $grey-700 !important;
|
||||
}
|
||||
|
||||
.btn-manage-group .setting_btn_menu {
|
||||
background-color: $grey-500 !important;
|
||||
}
|
||||
|
||||
.setting_btn_menu a:hover {
|
||||
background-color: $grey-700 !important;
|
||||
}
|
||||
|
||||
.table {
|
||||
color: $text-200 !important;
|
||||
|
||||
th,
|
||||
td {
|
||||
border-color: $grey-700 !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.btn-outline-primary.focus, .btn-outline-primary:focus, .btn-primary.focus, .btn-primary:focus{
|
||||
box-shadow: 0 0 0 0.2rem rgb(144 153 255 / 29%) !important;
|
||||
}
|
||||
|
||||
.bottomNav{
|
||||
&{
|
||||
background-color: $grey-400 !important;
|
||||
}
|
||||
|
||||
.bottomNavButton{
|
||||
&{
|
||||
color: $text-400;
|
||||
}
|
||||
}
|
||||
|
||||
.bottomNavButton.active{
|
||||
color: $blue-500 !important;
|
||||
}
|
||||
|
||||
.subNav{
|
||||
background-color: $grey-400 !important;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.key:hover{
|
||||
color: $blue-500;
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 62 KiB |
@ -1,138 +0,0 @@
|
||||
let numberToast = 0,
|
||||
emptyInputFeedback = "Can't leave empty";
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
let $add_configuration = $("#add_configuration"),
|
||||
addConfigurationModal = $("#addConfigurationModal");
|
||||
|
||||
function showToast(a) {
|
||||
$(".toastContainer").append(`<div id="${numberToast}-toast" class="toast hide" role="alert" data-delay="500">
|
||||
<div class="toast-header">
|
||||
<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">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="toast-body">${a}</div>
|
||||
<div class="toast-progressbar"></div>
|
||||
</div>`), $(`#${numberToast}-toast`).toast("show"), $(`#${numberToast}-toast .toast-body`).html(a), $(`#${numberToast}-toast .toast-progressbar`).css("transition", `width ${$(`#${numberToast}-toast .toast-progressbar`).parent().data("delay")}ms cubic-bezier(0, 0, 0, 0)`), $(`#${numberToast}-toast .toast-progressbar`).css("width", "0px"), numberToast++
|
||||
}
|
||||
|
||||
function genKeyPair() {
|
||||
let a = window.wireguard.generateKeypair();
|
||||
$("#addConfigurationPrivateKey").val(a.privateKey).data("checked", !0)
|
||||
}
|
||||
|
||||
function ajaxPostJSON(a, t, e) {
|
||||
$.ajax({
|
||||
url: a,
|
||||
method: "POST",
|
||||
data: JSON.stringify(t),
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
}).done(function(a) {
|
||||
e(a)
|
||||
})
|
||||
}
|
||||
|
||||
function validInput(a) {
|
||||
a.removeClass("is-invalid").addClass("is-valid").removeAttr("disabled").data("checked", !0)
|
||||
}
|
||||
|
||||
function invalidInput(a, t, e) {
|
||||
a.removeClass("is-valid").addClass("is-invalid").removeAttr("disabled").data("checked", !1), t.addClass("invalid-feedback").text(e)
|
||||
}
|
||||
|
||||
function checkPort(a) {
|
||||
let t = a;
|
||||
t.attr("disabled", "disabled");
|
||||
let e = $("#addConfigurationListenPortFeedback");
|
||||
0 == t.val().length ? invalidInput(t, e, emptyInputFeedback) : ajaxPostJSON("/api/addConfigurationPortCheck", {
|
||||
port: t.val()
|
||||
}, function a(i) {
|
||||
i.status ? validInput(t) : invalidInput(t, e, i.reason)
|
||||
})
|
||||
}
|
||||
|
||||
function checkAddress(a) {
|
||||
let t = a;
|
||||
t.attr("disabled", "disabled");
|
||||
let e = $(".addConfigurationAvailableIPs"),
|
||||
i = $("#addConfigurationAddressFeedback");
|
||||
0 == t.val().length ? (invalidInput(t, i, emptyInputFeedback), e.html("N/A")) : ajaxPostJSON("/api/addConfigurationAddressCheck", {
|
||||
address: t.val()
|
||||
}, function a(n) {
|
||||
n.status ? (e.html(`<strong>${n.data}</strong>`), validInput(t)) : (invalidInput(t, i, n.reason), e.html("N/A"))
|
||||
})
|
||||
}
|
||||
|
||||
function checkName(a) {
|
||||
let t = a,
|
||||
e = $("#addConfigurationNameFeedback");
|
||||
t.val(t.val().replace(/\s/g, "")).attr("disabled", "disabled"), 0 === t.val().length ? invalidInput(t, e, emptyInputFeedback) : ajaxPostJSON("/api/addConfigurationNameCheck", {
|
||||
name: t.val()
|
||||
}, function a(i) {
|
||||
i.status ? validInput(t) : invalidInput(t, e, i.reason)
|
||||
})
|
||||
}
|
||||
addConfigurationModal.modal({
|
||||
keyboard: !1,
|
||||
backdrop: "static",
|
||||
show: !1
|
||||
}), addConfigurationModal.on("hidden.bs.modal", function() {
|
||||
$("#add_configuration_form").trigger("reset"), $("#add_configuration_form input").removeClass("is-valid").removeClass("is-invalid"), $(".addConfigurationAvailableIPs").text("N/A")
|
||||
}), $(".toggle--switch").on("change", function() {
|
||||
$(this).addClass("waiting").attr("disabled", "disabled");
|
||||
let a = $(this).data("conf-id"),
|
||||
t = $(this).prop("checked"),
|
||||
e = $(this);
|
||||
$(this).siblings("label"), $.ajax({
|
||||
url: `/switch/${a}`
|
||||
}).done(function(i) {
|
||||
let n = $(`div[data-conf-id="${a}"] .dot`);
|
||||
i.status ? t ? (n.removeClass("dot-stopped").addClass("dot-running"), n.siblings().text("Running"), showToast(`${a} is running.`)) : (n.removeClass("dot-running").addClass("dot-stopped"), showToast(`${a} is stopped.`)) : (e.parents().children(".card-message").html(`<pre class="index-alert">Configuration toggle failed. Please check the following error message:<br><code>${i.message}</code></pre>`), t ? e.prop("checked", !1) : e.prop("checked", !0)), e.removeClass("waiting").removeAttr("disabled")
|
||||
})
|
||||
}), $(".sb-home-url").addClass("active"), $(".card-body").on("click", function(a) {
|
||||
"toggleLabel" !== $(a.target).attr("class") && "toggle--switch" !== $(a.target).attr("class") && window.open($(this).find("a").attr("href"), "_self")
|
||||
}), $("#reGeneratePrivateKey").on("click", function() {
|
||||
genKeyPair()
|
||||
}), $("#toggleAddConfiguration").on("click", function() {
|
||||
addConfigurationModal.modal("toggle"), genKeyPair()
|
||||
}), $("#addConfigurationPrivateKey").on("change", function() {
|
||||
$privateKey = $(this), $privateKeyFeedback = $("#addConfigurationPrivateKeyFeedback"), 44 != $privateKey.val().length ? invalidInput($privateKey, $privateKeyFeedback, "Invalid length") : validInput($privateKey)
|
||||
}), $("#addConfigurationListenPort").on("change", function() {
|
||||
checkPort($(this))
|
||||
}), $("#addConfigurationAddress").on("change", function() {
|
||||
checkAddress($(this))
|
||||
}), $("#addConfigurationName").on("change", function() {
|
||||
checkName($(this))
|
||||
}), $("#addConfigurationBtn").on("click", function() {
|
||||
$(this);
|
||||
let a = $("#add_configuration_form input"),
|
||||
t = !0;
|
||||
for (let e = 0; e < a.length; e++) {
|
||||
let i = $(a[e]);
|
||||
void 0 == i.attr("required") || (0 == i.val().length && "addConfigurationPrivateKey" !== i.attr("name") && (invalidInput(i, i.siblings(".input-feedback"), emptyInputFeedback), t = !1), 44 != i.val().length && "addConfigurationPrivateKey" == i.attr("name") && (invalidInput(i, i.siblings(".input-feedback"), "Invalid length"), t = !1), i.data("checked") || (t = !1))
|
||||
}
|
||||
if (t) {
|
||||
$("#addConfigurationModal .modal-footer .btn").hide(), $(".addConfigurationStatus").removeClass("d-none");
|
||||
let n = {},
|
||||
o = [];
|
||||
for (let d = 0; d < a.length; d++) {
|
||||
let s = $(a[d]);
|
||||
n[s.attr("name")] = s.val(), o.push(s.attr("name"))
|
||||
}
|
||||
ajaxPostJSON("/api/addConfiguration", n, a => {
|
||||
let t = a.data;
|
||||
$(".addConfigurationAddStatus").removeClass("text-primary").addClass("text-success").html(`<i class="bi bi-check-circle-fill"></i> ${t} added successfully.`), a.status ? setTimeout(() => {
|
||||
$(".addConfigurationToggleStatus").removeClass("waiting").html('<div class="spinner-border spinner-border-sm" role="status"></div> Toggle Configuration'), $.ajax({
|
||||
url: `/switch/${t}`
|
||||
}).done(function(a) {
|
||||
a.status ? ($(".addConfigurationToggleStatus").removeClass("text-primary").addClass("text-success").html('<i class="bi bi-check-circle-fill"></i> Toggle Successfully. Refresh in 5 seconds.'), setTimeout(() => {
|
||||
$(".addConfigurationToggleStatus").text("Refeshing..."), location.reload()
|
||||
}, 5e3)) : ($(".addConfigurationToggleStatus").removeClass("text-primary").addClass("text-danger").html(`<i class="bi bi-x-circle-fill"></i> ${t} toggle failed.`), $("#addCconfigurationAlertMessage").removeClass("d-none").html(`${t} toggle failed. Please check the following error message:<br>${a.message}`))
|
||||
})
|
||||
}, 500) : ($(".addConfigurationStatus").removeClass("text-primary").addClass("text-danger").html(`<i class="bi bi-x-circle-fill"></i> ${t} adding failed.`), $("#addCconfigurationAlert").removeClass("d-none").children(".alert-body").text(a.reason))
|
||||
})
|
||||
}
|
||||
});
|
10
WG-Dash/src/static/js/index.min.js
vendored
@ -1,59 +0,0 @@
|
||||
$(".sb-settings-url").addClass("active");
|
||||
|
||||
$(".confirm_modal").click(function () {
|
||||
$(".app_new_ip").text($("#app_ip")[0].value);
|
||||
$(".app_new_port").text($("#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").text("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").text("Redirecting you in " + countdown + " seconds.");
|
||||
countdown--;
|
||||
}, 1000);
|
||||
$.post('/update_wg_conf_path', $('.update_wg_conf_path').serialize());
|
||||
});
|
||||
|
||||
$(".bottomNavSettings").addClass("active");
|
||||
|
||||
$(".theme-switch-btn").on("click", function(){
|
||||
if (!$(this).hasClass("active")){
|
||||
let theme = $(this).data("theme");
|
||||
$(".theme-switch-btn").removeClass("active");
|
||||
$(this).addClass("active");
|
||||
$.ajax({
|
||||
method: "POST",
|
||||
url: "/api/settings/setTheme",
|
||||
headers: {"Content-Type": "application/json"},
|
||||
data: JSON.stringify({"theme": theme})
|
||||
}).done(function(res){
|
||||
if (res.status == true){
|
||||
if (theme == "light"){
|
||||
$("#darkThemeCSS").remove();
|
||||
} else {
|
||||
$("head").append('<link rel="stylesheet" type="text/css" href="/static/css/theme/dark.min.css" id="darkThemeCSS">');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
1
WG-Dash/src/static/js/settings.min.js
vendored
@ -1 +0,0 @@
|
||||
$(".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(){0===countdown&&window.location.replace("http://"+url),$(".confirm_restart").html("Redirecting you in "+countdown+" seconds."),countdown--},1e3)}),$(".change_path").click(function(){$(this).attr("disabled","disabled"),countdown=5,setInterval(function(){0===countdown&&location.reload(),$(".change_path").html("Redirecting you in "+countdown+" seconds."),countdown--},1e3),$.post("/update_wg_conf_path",$(".update_wg_conf_path").serialize())}),$(".bottomNavSettings").addClass("active"),$(".theme-switch-btn").on("click",function(){if(!$(this).hasClass("active")){let t=$(this).data("theme");$(".theme-switch-btn").removeClass("active"),$(this).addClass("active"),$.ajax({method:"POST",url:"/api/settings/setTheme",headers:{"Content-Type":"application/json"},data:JSON.stringify({theme:t})}).done(function(e){!0==e.status&&("light"==t?$("#darkThemeCSS").remove():$("head").append('<link rel="stylesheet" type="text/css" href="/static/css/theme/dark.min.css" id="darkThemeCSS">'))})}});
|
@ -1,67 +0,0 @@
|
||||
/**
|
||||
* 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
WG-Dash/src/static/js/tools.min.js
vendored
@ -1 +0,0 @@
|
||||
$(".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(t){$(".modal.show .ip_dropdown").html(""),$(".modal.show .ip_dropdown").append('<option value="none" selected="selected" disabled>Choose an IP'),$(".modal.show .ip_dropdown").append(t)}})}),$(".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(t){$(".ping_result tbody").html("");let e='<tr><th scope="row">Address</th><td>'+t.address+'</td></tr><tr><th scope="row">Is Alive</th><td>'+t.is_alive+'</td></tr><tr><th scope="row">Min RTT</th><td>'+t.min_rtt+'ms</td></tr><tr><th scope="row">Average RTT </th><td>'+t.avg_rtt+'ms</td></tr><tr><th scope="row">Max RTT</th><td>'+t.max_rtt+'ms</td></tr><tr><th scope="row">Package Sent</th><td>'+t.package_sent+'</td></tr><tr><th scope="row">Package Received</th><td>'+t.package_received+'</td></tr><tr><th scope="row">Package Loss</th><td>'+t.package_loss+"</td></tr>";$(".ping_result tbody").html(e),$(".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(t){$(".traceroute_result tbody").html(""),t.forEach(t=>$(".traceroute_result tbody").append('<tr><th scope="row">'+t.hop+"</th><td>"+t.ip+"</td><td>"+t.avg_rtt+"</td><td>"+t.min_rtt+"</td><td>"+t.max_rtt+"</td></tr>")),$(".send_traceroute").removeAttr("disabled").html("Traceroute"),$("#traceroute_modal .form-control").removeAttr("disabled")}})});
|
@ -1,313 +0,0 @@
|
||||
/*! 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 base64ToKey(base64) {
|
||||
let binary_string = window.atob(base64);
|
||||
let len = binary_string.length;
|
||||
let bytes = new Uint8Array(len);
|
||||
for (let i = 0; i < len; i++) {
|
||||
bytes[i] = binary_string.charCodeAt(i);
|
||||
}
|
||||
let uint8 = new Uint8Array(bytes.buffer);
|
||||
return uint8;
|
||||
}
|
||||
|
||||
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){
|
||||
privateKey = base64ToKey(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
WG-Dash/src/static/js/wireguard.min.js
vendored
@ -1,31 +0,0 @@
|
||||
{
|
||||
"theme_color": "#343a40",
|
||||
"background_color": "#343a40",
|
||||
"display": "fullscreen",
|
||||
"scope": "/",
|
||||
"start_url": "/",
|
||||
"name": "WireGate",
|
||||
"short_name": "WireGate",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/static/img/icon-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/static/img/icon-256x256.png",
|
||||
"sizes": "256x256",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/static/img/icon-384x384.png",
|
||||
"sizes": "384x384",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/static/img/icon-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
]
|
||||
}
|
@ -1,467 +0,0 @@
|
||||
<!-- configuration.html - < WGDashboard > - Copyright(C) 2021 Donald Zou [https://github.com/donaldzou]-->
|
||||
<html lang="en">
|
||||
{% with title=title%}
|
||||
{% include "header.html"%}
|
||||
{% endwith %}
|
||||
<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 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>
|
||||
<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>
|
||||
<pre class="index-alert d-none" style="margin-bottom: 1rem;"></pre>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<small class="text-muted"><strong>ZONE</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 id="switch" class="info_loading">
|
||||
<input type="checkbox" class="toggle--switch" id="toggle--switch">
|
||||
<label for="toggle--switch" class="toggleLabel"></label>
|
||||
</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="row chartContainer">
|
||||
<div class="col-sm">
|
||||
<div class="chartTitle">
|
||||
<h6>Data Usage / Refresh Interval</h6>
|
||||
<div class="chartControl" style="margin-left: auto">
|
||||
<div class="btn-group" role="group">
|
||||
<button class="btn btn-outline-primary btn-sm switchUnit" data-unit="GB">GB</button>
|
||||
<button class="btn btn-outline-primary btn-sm switchUnit" data-unit="MB">MB</button>
|
||||
<button class="btn btn-outline-primary btn-sm switchUnit" data-unit="KB">KB</button>
|
||||
<button class="btn btn-outline-primary btn-sm fullScreen"><i class="bi bi-fullscreen"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chartCanvasContainer" style="width: 100%; height: 300px">
|
||||
<canvas id="totalDataUsageChartObj" width="100" height="100"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="button-div mb-3">
|
||||
<div class="row">
|
||||
<div class="col-sm">
|
||||
<div class="row">
|
||||
<div class="col-sm">
|
||||
<div class="form-group">
|
||||
<label for="search_peer_textbox"><small class="text-muted">Search Peers</small></label>
|
||||
<input type="text" class="form-control" id="search_peer_textbox" placeholder="Enter Peer's Name" value="">
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
|
||||
|
||||
|
||||
</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>
|
||||
<hr>
|
||||
<a class="text-primary" id="configuration_setting"><i class="bi bi-gear-fill"></i> Configration Settings</a>
|
||||
<a class="text-danger" id="configuration_delete"><i class="bi bi-trash3-fill"></i> Delete Configuration</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="row peer_list"></div>
|
||||
<small id="peer_loading_time" class="text-muted"></small>
|
||||
</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 modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<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">×</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">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form id="add_peer_form">
|
||||
<div class="form-group">
|
||||
<div>
|
||||
<label for="private_key">Private Key</label>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control non-bulk" id="private_key" aria-describedby="private_key">
|
||||
<div class="input-group-append">
|
||||
<button type="button" class="btn btn-danger non-bulk" 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">
|
||||
<label for="public_key">Public Key <code>(Required)</code></label>
|
||||
<input type="text" class="form-control non-bulk" id="public_key" aria-describedby="public_key" disabled>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label for="new_add_name">Name</label>
|
||||
<input type="text" class="form-control non-bulk" id="new_add_name">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label for="allowed_ips">Allowed IPs <code>(Required)</code></label>
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control non-bulk" id="allowed_ips">
|
||||
<div class="input-group-append">
|
||||
<button type="button" class="btn btn-primary non-bulk" 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']}}>Add</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" id="configuration_delete_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">Are you sure to delete this configuration?</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="remove_configuration_alert" class="alert alert-danger alert-dismissible fade show d-none" role="alert">
|
||||
|
||||
</div>
|
||||
<p style="margin: 0">This action is not reversible. The configuration will get toggle off, and delete from database and from the configuration folder.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">No</button>
|
||||
<button type="button" class="btn btn-danger" id="sure_delete_configuration">Yes</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" id="delete_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">Are you sure to delete this peer?</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="remove_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">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<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>
|
||||
<button type="button" class="btn btn-danger" id="delete_peer" conf_id={{conf_data['name']}} peer_id="">Yes</button>
|
||||
</div>
|
||||
</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 modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title peer_name"></h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</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">×</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="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_setting" data-conf-id="{{conf_data['name']}}" data-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">×</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">×</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">×</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">×</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 toastContainer" style="z-index: 5; right: 0; top: 50px;"></div>
|
||||
{% include "tools.html" %}
|
||||
</body>
|
||||
{% include "footer.html" %}
|
||||
<script src="{{ url_for('static',filename='js/configuration.js') }}"></script>
|
||||
<script src="{{ url_for('static',filename='js/wireguard.min.js') }}"></script>
|
||||
<script>
|
||||
configurations.setConfigurationName("{{ conf_data['name'] }}");
|
||||
configurations.setActiveConfigurationName();
|
||||
configurations.loadPeers($('#search_peer_textbox').val());
|
||||
</script>
|
||||
</html>
|
@ -1,2 +0,0 @@
|
||||
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" 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>
|
@ -1,184 +0,0 @@
|
||||
<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">
|
||||
<small class="text-muted"><strong>ZONE</strong></small>
|
||||
<h1 class="mb-3"><samp>{{conf_data['name']}}</samp></h1>
|
||||
</div>
|
||||
<div class="col">
|
||||
<small class="text-muted"><strong>ACTION</strong></small><br>
|
||||
{% 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">
|
||||
<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">
|
||||
<small class="text-muted"><strong>CONNECTED PEERS</strong></small>
|
||||
<h6 style="text-transform: uppercase;">{{conf_data['running_peer']}}</h6>
|
||||
</div>
|
||||
<div class="col-sm">
|
||||
<small class="text-muted"><strong>TOTAL DATA USAGE</strong></small>
|
||||
<h6 style="text-transform: uppercase;">{{conf_data['total_data_usage'][0]}} GB</h6>
|
||||
</div>
|
||||
<div class="col-sm">
|
||||
<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">
|
||||
<small class="text-muted"><strong>TOTAL SENT</strong></small>
|
||||
<h6 style="text-transform: uppercase;">{{conf_data['total_data_usage'][2]}} GB</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><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>
|
||||
{% 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 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>
|
||||
|
||||
<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>
|
@ -1,25 +0,0 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<title>{{ title }} | WireGate</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="WireGate">
|
||||
<meta name="apple-mobile-web-app-title" content="WireGate">
|
||||
<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.min.css') }}">
|
||||
<!-- THEME APPLY HERE -->
|
||||
{% if session["theme"] == "dark" %}
|
||||
<link rel= "stylesheet" type= "text/css" href="{{ url_for('static',filename='css/theme/dark.min.css') }}" id="darkThemeCSS">
|
||||
{% endif %}
|
||||
<!-- THEME APPLY HERE -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css">
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.min.js" integrity="sha512-QSkVNOCYLtj73J4hbmVoOV6KVZuMluZlioC+trLpewV8qMjsWqlIQvkn1KGX2StWvPMdWGBqim1xlC8krl1EKQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"/>
|
||||
</head>
|
||||
|
@ -1,77 +0,0 @@
|
||||
<!-- index.html - < WGDashboard > - Copyright(C) 2021 Donald Zou [https://github.com/donaldzou]-->
|
||||
<html lang="en">
|
||||
{% with %}
|
||||
{% set title="Home" %}
|
||||
{% 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 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 %} -->
|
||||
<div class="index-alert alert alert-danger d-none" role="alert">
|
||||
Configuration toggle failed. Please check the following error message:
|
||||
</div>
|
||||
<pre class="index-alert index-alert-full d-none"><code></code></pre>
|
||||
|
||||
{% 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 conf_card" data-conf-id="{{i['conf']}}">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col card-col">
|
||||
<small class="text-muted"><strong>ZONE</strong></small>
|
||||
<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 card-col">
|
||||
<small class="text-muted"><strong>STATUS</strong></small>
|
||||
<h6 style="text-transform: uppercase; margin:0 !important;"><span>{{i['status']}}</span><span class="dot dot-{{i['status']}}"></span></h6>
|
||||
</div>
|
||||
<div class="col-sm card-col">
|
||||
<small class="text-muted"><strong>PUBLIC KEY</strong></small>
|
||||
<h6 style="margin:0 !important;"><samp>{{i['public_key']}}</samp></h6>
|
||||
</div>
|
||||
<div class="col-sm index-switch">
|
||||
<div class="switch-test">
|
||||
<input type="checkbox" class="toggle--switch" id="{{i['conf']}}-switch" {{i['checked']}} data-conf-id="{{i['conf']}}">
|
||||
<label for="{{i['conf']}}-switch" class="toggleLabel"></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-message"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{%endfor%}
|
||||
</main>
|
||||
</div>
|
||||
<div class="position-fixed top-0 right-0 p-3 toastContainer" style="z-index: 5; right: 0; top: 50px;"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% include "tools.html" %}
|
||||
</body>
|
||||
{% include "footer.html" %}
|
||||
<script src="{{ url_for('static',filename='js/wireguard.min.js') }}"></script>
|
||||
<script src="{{ url_for('static',filename='js/index.min.js') }}"></script>
|
||||
|
||||
</html>
|
@ -1,10 +0,0 @@
|
||||
<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="/">WireGate</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>
|
@ -1 +0,0 @@
|
||||
{{ qrcode(i) }}
|
@ -1,179 +0,0 @@
|
||||
<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>
|
||||
<div class="card mb-3">
|
||||
<h6 class="card-header">Dashboard Theme</h6>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<button class='btn btn-outline-primary theme-switch-btn {% if session["theme"] == "light" %} {{ "active" }} {% endif %}' data-theme="light">
|
||||
<i class="bi bi-sun-fill"></i>
|
||||
Light
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<button class='btn btn-outline-primary theme-switch-btn {% if session["theme"] == "dark" %} {{ "active" }} {% endif %}' data-theme="dark">
|
||||
<i class="bi bi-moon-fill"></i>
|
||||
Dark
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
<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>
|
||||
<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" %}
|
||||
<div class="position-fixed top-0 right-0 p-3 toastContainer" style="z-index: 5; right: 0; top: 50px;"></div>
|
||||
</body>
|
||||
{% include "footer.html" %}
|
||||
<script src="{{ url_for('static',filename='js/settings.js') }}"></script>
|
||||
|
||||
</html>
|
@ -1,66 +0,0 @@
|
||||
<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>Zones</span>
|
||||
</h6>
|
||||
<ul class="nav flex-column">
|
||||
{% for i in conf%}
|
||||
<li class="nav-item"><a class="nav-link nav-conf-link sb-{{i['conf']}}-url" href="/configuration/{{i['conf']}}" data-conf-id="{{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://hub.docker.com/repository/docker/noxcis/wg-dashboard/general"><small class="nav-link text-muted">{{ session['dashboard_version'] }}</small></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
<script
|
||||
src="https://code.jquery.com/jquery-3.7.1.min.js"
|
||||
integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo="
|
||||
crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// Add click event listener to links in the "Zones" section
|
||||
$('.nav-conf-link').on('click', function(event) {
|
||||
event.preventDefault(); // Prevent default behavior (e.g., navigating to the href)
|
||||
|
||||
// Get the href attribute of the clicked link
|
||||
var url = $(this).attr('href');
|
||||
|
||||
// Redirect to the specified URL
|
||||
window.location.href = url;
|
||||
});
|
||||
});
|
||||
</script
|
@ -1,87 +0,0 @@
|
||||
<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>
|
||||
<h5 class="text-center">to WireGate</h5>
|
||||
<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" style="margin-top: 1rem; margin-bottom: 0rem;"></div>
|
||||
<div class="form-group">
|
||||
<label for="username" class="text-left" style="font-size: 1rem"><i class="bi bi-person-circle"></i></label>
|
||||
<input type="text" class="form-control" id="username" name="username" placeholder="Your username" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password" class="text-left" style="font-size: 1rem"><i class="bi bi-key-fill"></i></label>
|
||||
<input type="password" class="form-control" id="password" name="password" placeholder="Your password" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-dark" style="width: 100%">Sign In</button>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
<small class="text-muted" style="position: fixed; bottom: 0; width: 100%; text-align: center; margin-bottom: 2rem">Version: {{ version }}</small>
|
||||
</body>
|
||||
{% include "footer.html" %}
|
||||
<script>
|
||||
let loginButton = $('button[type="submit"]');
|
||||
loginButton.on("click", function(e){
|
||||
e.preventDefault();
|
||||
let $password = $("#password");
|
||||
let $username = $("#username");
|
||||
let req = [$password, $username];
|
||||
let check = true
|
||||
for (let i = 0; i < req.length; i++){
|
||||
if ($(req[i]).val().length === 0){
|
||||
loginButton.html("Sign In");
|
||||
check = false;
|
||||
$(req[i]).addClass("is-invalid");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (check){
|
||||
$(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")){
|
||||
if (document.URL.substring(0, 5) == "http:"){
|
||||
window.location.replace(`http://${urlParams.get("redirect")}`)
|
||||
}else if (document.URL.substring(0, 5) == "https"){
|
||||
window.location.replace(`https://${urlParams.get("redirect")}`)
|
||||
}
|
||||
}else{
|
||||
window.location.replace("/");
|
||||
}
|
||||
}else{
|
||||
$(".alert").html(res.msg).removeClass("d-none").fadeIn();
|
||||
loginButton.html("Sign In").removeAttr("disabled");
|
||||
$("input[required]").addClass("is-invalid");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</html>
|
@ -1,121 +0,0 @@
|
||||
<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">×</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">×</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">×</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>
|
@ -1,113 +0,0 @@
|
||||
import re
|
||||
import subprocess
|
||||
import dashboard
|
||||
"""
|
||||
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))
|
||||
|
||||
|
||||
def deletePeers(config_name, delete_keys, cur, db):
|
||||
sql_command = []
|
||||
wg_command = ["wg", "set", config_name]
|
||||
for delete_key in delete_keys:
|
||||
if delete_key not in dashboard.get_conf_peer_key(config_name):
|
||||
return "This key does not exist"
|
||||
sql_command.append("DELETE FROM " + config_name + " WHERE id = '" + delete_key + "';")
|
||||
wg_command.append("peer")
|
||||
wg_command.append(delete_key)
|
||||
wg_command.append("remove")
|
||||
try:
|
||||
print("deleting...")
|
||||
remove_wg = subprocess.check_output(" ".join(wg_command),
|
||||
shell=True, stderr=subprocess.STDOUT)
|
||||
save_wg = subprocess.check_output(f"wg-quick save {config_name}", shell=True, stderr=subprocess.STDOUT)
|
||||
cur.executescript(' '.join(sql_command))
|
||||
db.commit()
|
||||
except subprocess.CalledProcessError as exc:
|
||||
return exc.output.strip()
|
||||
return "true"
|
||||
|
||||
def checkJSONAllParameter(required, data):
|
||||
if len(data) == 0:
|
||||
return False
|
||||
for i in required:
|
||||
if i not in list(data.keys()) or len(data[i]) == 0:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
@ -1,220 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
|
||||
start_wgd () {
|
||||
echo -e "Start Dashboard--------------------------------------------------------------------------------\n"
|
||||
echo ""
|
||||
echo ""
|
||||
uwsgi --ini wiregate.ini
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
}
|
||||
|
||||
|
||||
|
||||
newconf_wgd () {
|
||||
newconf_wgd0
|
||||
newconf_wgd1
|
||||
newconf_wgd2
|
||||
newconf_wgd3
|
||||
newconf_wgd4
|
||||
newconf_wgd5
|
||||
}
|
||||
|
||||
|
||||
|
||||
newconf_wgd0() {
|
||||
local port_wg0=$WG_DASH_PORT_RANGE_STARTPORT
|
||||
private_key=$(wg genkey)
|
||||
public_key=$(echo "$private_key" | wg pubkey)
|
||||
|
||||
|
||||
cat <<EOF >"/etc/wireguard/ADMINS.conf"
|
||||
[Interface]
|
||||
PrivateKey = $private_key
|
||||
Address = 10.0.0.1/24
|
||||
ListenPort = $port_wg0
|
||||
SaveConfig = true
|
||||
PostUp = /home/app/FIREWALLS/Admins/wg0-nat.sh
|
||||
PreDown = /home/app/FIREWALLS/Admins/wg0-dwn.sh
|
||||
|
||||
EOF
|
||||
if [ ! -f "/master-key/master.conf" ]; then
|
||||
make_master_config # Only call make_master_config if master.conf doesn't exist
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
newconf_wgd1() {
|
||||
local port_wg1=$WG_DASH_PORT_RANGE_STARTPORT
|
||||
local port_wg1=$((port_wg1 + 1))
|
||||
private_key=$(wg genkey)
|
||||
public_key=$(echo "$private_key" | wg pubkey)
|
||||
|
||||
cat <<EOF >"/etc/wireguard/MEMEBERS.conf"
|
||||
[Interface]
|
||||
PrivateKey = $private_key
|
||||
Address = 192.168.10.1/24
|
||||
ListenPort = $port_wg1
|
||||
SaveConfig = true
|
||||
PostUp = /home/app/FIREWALLS/Members/wg1-nat.sh
|
||||
PreDown = /home/app/FIREWALLS/Members/wg1-dwn.sh
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
|
||||
|
||||
newconf_wgd2() {
|
||||
local port_wg2=$WG_DASH_PORT_RANGE_STARTPORT
|
||||
local port_wg2=$((port_wg2 + 2))
|
||||
private_key=$(wg genkey)
|
||||
public_key=$(echo "$private_key" | wg pubkey)
|
||||
|
||||
cat <<EOF >"/etc/wireguard/LANP2P.conf"
|
||||
[Interface]
|
||||
PrivateKey = $private_key
|
||||
Address = 172.16.0.1/24
|
||||
ListenPort = $port_wg2
|
||||
SaveConfig = true
|
||||
PostUp = /home/app/FIREWALLS/LAN-only-users/wg2-nat.sh
|
||||
PreDown = /home/app/FIREWALLS/LAN-only-users/wg2-dwn.sh
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
newconf_wgd3() {
|
||||
local port_wg3=$WG_DASH_PORT_RANGE_STARTPORT
|
||||
local port_wg3=$((port_wg3 + 3))
|
||||
private_key=$(wg genkey)
|
||||
public_key=$(echo "$private_key" | wg pubkey)
|
||||
|
||||
cat <<EOF >"/etc/wireguard/GUESTS.conf"
|
||||
[Interface]
|
||||
PrivateKey = $private_key
|
||||
Address = 192.168.20.1/24
|
||||
ListenPort = $port_wg3
|
||||
SaveConfig = true
|
||||
PostUp = /home/app/FIREWALLS/Guest/wg3-nat.sh
|
||||
PreDown = /home/app/FIREWALLS/Guest/wg3-dwn.sh
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
newconf_wgd4() {
|
||||
local port_wg4=$WG_DASH_PORT_RANGE_STARTPORT
|
||||
local port_wg4=$((port_wg4 + 4))
|
||||
private_key=$(wg genkey)
|
||||
public_key=$(echo "$private_key" | wg pubkey)
|
||||
|
||||
cat <<EOF >"/etc/wireguard/IPV6ADMINS.conf"
|
||||
[Interface]
|
||||
PrivateKey = $private_key
|
||||
Address = 192.168.30.1/24
|
||||
Address = 2001:db8:30:1::/64
|
||||
ListenPort = $port_wg4
|
||||
SaveConfig = true
|
||||
PostUp = /home/app/FIREWALLS/IPV6/wg0-nat.sh
|
||||
PreDown = /home/app/FIREWALLS/IPV6/wg0-dwn.sh
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
newconf_wgd5() {
|
||||
local port_wg5=$WG_DASH_PORT_RANGE_STARTPORT
|
||||
local port_wg5=$((port_wg5 + 5))
|
||||
private_key=$(wg genkey)
|
||||
public_key=$(echo "$private_key" | wg pubkey)
|
||||
|
||||
cat <<EOF >"/etc/wireguard/IPV6MEMBERS.conf"
|
||||
[Interface]
|
||||
PrivateKey = $private_key
|
||||
Address = 192.168.40.1/24
|
||||
Address = 2001:db8:40:1::/64
|
||||
ListenPort = $port_wg5
|
||||
SaveConfig = true
|
||||
PostUp = /home/app/FIREWALLS/IPV6/wg1-nat.sh
|
||||
PreDown = /home/app/FIREWALLS/IPV6/wg1-dwn.sh
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
make_master_config() {
|
||||
local svr_config="/etc/wireguard/ADMINS.conf"
|
||||
# Check if the specified config file exists
|
||||
if [ ! -f "$svr_config" ]; then
|
||||
echo "Error: Config file $svr_config not found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
#Function to generate a new peer's public key
|
||||
generate_public_key() {
|
||||
local private_key="$1"
|
||||
echo "$private_key" | wg pubkey
|
||||
}
|
||||
|
||||
# Function to generate a new preshared key
|
||||
generate_preshared_key() {
|
||||
wg genpsk
|
||||
}
|
||||
|
||||
|
||||
|
||||
# Generate the new peer's public key, preshared key, and allowed IP
|
||||
wg_private_key=$(wg genkey)
|
||||
peer_public_key=$(generate_public_key "$wg_private_key")
|
||||
preshared_key=$(generate_preshared_key)
|
||||
|
||||
# Add the peer to the WireGuard config file with the preshared key
|
||||
echo -e "\n[Peer]" >> "$svr_config"
|
||||
echo "PublicKey = $peer_public_key" >> "$svr_config"
|
||||
echo "PresharedKey = $preshared_key" >> "$svr_config"
|
||||
echo "AllowedIPs = 10.0.0.254/32" >> "$svr_config"
|
||||
|
||||
|
||||
server_public_key=$(grep -E '^PrivateKey' "$svr_config" | awk '{print $NF}')
|
||||
svrpublic_key=$(echo "$server_public_key" | wg pubkey)
|
||||
|
||||
|
||||
# Generate the client config file
|
||||
cat <<EOF >"/home/app/master-key/master.conf"
|
||||
[Interface]
|
||||
PrivateKey = $wg_private_key
|
||||
Address = 10.0.0.254/32
|
||||
DNS = 10.2.0.100,10.2.0.100
|
||||
MTU = 1420
|
||||
|
||||
[Peer]
|
||||
PublicKey = $svrpublic_key
|
||||
AllowedIPs = 0.0.0.0/0
|
||||
Endpoint = $WG_DASH_SERVER_IP:$WG_DASH_PORT_RANGE_STARTPORT
|
||||
PersistentKeepalive = 21
|
||||
PresharedKey = $preshared_key
|
||||
EOF
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if [ "$#" != 1 ];
|
||||
then
|
||||
help
|
||||
|
||||
elif [ "$1" = "install" ]; then
|
||||
install_wgd
|
||||
elif [ "$1" = "debug" ]; then
|
||||
start_wgd_debug
|
||||
elif [ "$1" = "start" ]; then
|
||||
start_wgd
|
||||
elif [ "$1" = "newconfig" ]; then
|
||||
newconf_wgd
|
||||
else
|
||||
help
|
||||
|
||||
fi
|
@ -1,44 +0,0 @@
|
||||
[uwsgi]
|
||||
plugin = python3
|
||||
module = dashboard:app
|
||||
pythonpath = /usr
|
||||
|
||||
|
||||
socket = :10086
|
||||
chmod-socket = 660
|
||||
master = true
|
||||
single-interpreter = true
|
||||
vacuum = true
|
||||
die-on-term = true
|
||||
need-app = true
|
||||
|
||||
|
||||
enable-threads = true
|
||||
processes = 2 # Set to 1 to limit the number of processes
|
||||
threads = 4 # Adjust based on the CPU and application requirements
|
||||
workers = 5 # Adjust based on the CPU and application requirements
|
||||
|
||||
|
||||
disable-logging = false
|
||||
log-4xx = true
|
||||
log-5xx = true
|
||||
|
||||
|
||||
max-requests = 500 # Adjust based on the available memory
|
||||
max-worker-lifetime = 1800 # Adjust based on the available memory
|
||||
reload-on-rss = 1024 # Adjust based on the available memory
|
||||
worker-reload-mercy = 5
|
||||
buffer-size = 6000 # Adjust based on response sizes
|
||||
|
||||
|
||||
cheaper = 2
|
||||
cheaper-initial = 2 # Adjust based on the available memory
|
||||
cheaper-overload = 30
|
||||
cheaper-step = 2
|
||||
cheaper-busyness-multiplier = 30
|
||||
cheaper-busyness-min = 20
|
||||
cheaper-busyness-max = 70
|
||||
cheaper-busyness-backlog-alert = 8
|
||||
cheaper-busyness-backlog-step = 2
|
||||
|
||||
#logto = /dev/null
|
54
app.json
@ -1,54 +0,0 @@
|
||||
{
|
||||
"name": "Darkwire",
|
||||
"description": "End-to-end encrypted web chat",
|
||||
"keywords": [
|
||||
"cryptography",
|
||||
"chat",
|
||||
"privacy"
|
||||
],
|
||||
"website": "https://darkwire.io",
|
||||
"repository": "https://github.com/darkwire/darkwire.io",
|
||||
"env": {
|
||||
"HEROKU_APP_NAME": {
|
||||
"required": false
|
||||
},
|
||||
"HEROKU_PARENT_APP_NAME": {
|
||||
"required": false
|
||||
},
|
||||
"MAILGUN_API_KEY": {
|
||||
"description": "Mailgun API Key (only required for abuse reporting)",
|
||||
"required": false
|
||||
},
|
||||
"ABUSE_TO_EMAIL_ADDRESS": {
|
||||
"description": "Where to send abuse reports (only required for abuse reporting)",
|
||||
"required": false
|
||||
},
|
||||
"MAILGUN_DOMAIN": {
|
||||
"description": "Mailgun domain (only required for abuse reporting)",
|
||||
"required": false
|
||||
},
|
||||
"ABUSE_FROM_EMAIL_ADDRESS": {
|
||||
"description": "From address on abuse emails (only required for abuse reporting)",
|
||||
"required": false
|
||||
},
|
||||
"API_HOST": {
|
||||
"description": "Example: 'api.your-darkwire-api.com'",
|
||||
"required": false
|
||||
},
|
||||
"API_PROTOCOL": {
|
||||
"description": "Example: 'https'",
|
||||
"required": false,
|
||||
"value": "https"
|
||||
},
|
||||
"API_PORT": {
|
||||
"description": "Example: 443",
|
||||
"required": false,
|
||||
"value": "443"
|
||||
},
|
||||
"SITE_URL": {
|
||||
"description": "Full URL of site. Example: https://darkwire.io",
|
||||
"required": false
|
||||
}
|
||||
},
|
||||
"image": "heroku/nodejs"
|
||||
}
|
16
build.sh
@ -1,16 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
api_host=$API_HOST
|
||||
|
||||
|
||||
|
||||
echo "building client..."
|
||||
cd client
|
||||
yarn --production=true
|
||||
yarn build
|
||||
cd ../
|
||||
|
||||
echo "building server..."
|
||||
cd server
|
||||
yarn --production=true
|
||||
yarn build
|
@ -1,14 +0,0 @@
|
||||
# Api settings
|
||||
TZ=UTC
|
||||
VITE_API_HOST=localhost
|
||||
VITE_API_PROTOCOL=http
|
||||
VITE_API_PORT=80
|
||||
VITE_COMMIT_SHA=some_sha
|
||||
MODE=production
|
||||
# To display darkwire version
|
||||
VITE_COMMIT_SHA=some_sha
|
||||
|
||||
# Set max transferable file size in MB
|
||||
VITE_MAX_FILE_SIZE=4
|
||||
|
||||
|
@ -1,10 +0,0 @@
|
||||
module.exports = {
|
||||
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['@typescript-eslint'],
|
||||
root: true,
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
},
|
||||
};
|
25
client/.gitignore
vendored
@ -1,25 +0,0 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist/
|
||||
dist-ssr
|
||||
*.local
|
||||
coverage
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016-present darkwire.io
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
@ -1,11 +0,0 @@
|
||||
# Darkwire Client
|
||||
|
||||
This is the client for [Darkwire](https://github.com/darkwire/darkwire.io). It requires [darkwire-server](../server) in order to run.
|
||||
|
||||
## Translations
|
||||
|
||||
Translation strings are pulled from JSON files in [this directory](https://github.com/darkwire/darkwire.io/tree/master/client/src/i18n). We welcome pull requests to add support for more lanuages. For native speakers, it should take only a few minutes to translate the site.
|
||||
|
||||
Please see [this PR](https://github.com/darkwire/darkwire.io/pull/95) for an example of what your translation PR should look like.
|
||||
|
||||
Thank you for your contributions!
|
@ -1,17 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="h-100">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="shortcut icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="robots" content="index,nofollow" />
|
||||
<meta name="googlebot" content="index,nofollow" />
|
||||
<meta name="description" content="darkwire.io is the simplest way to chat with encryption online." />
|
||||
<title>Darkwire.io - instant encrypted web chat</title>
|
||||
</head>
|
||||
<body class="h-100">
|
||||
<div id="root" class="h-100"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
@ -1,9 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": "src",
|
||||
"paths": {
|
||||
"@/*": ["src/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,77 +0,0 @@
|
||||
{
|
||||
"name": "darkwire-client",
|
||||
"version": "2.0.0-beta.12",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Daniel Seripap"
|
||||
},
|
||||
{
|
||||
"name": "Alan Friedman"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bootstrap": "^4.6.2",
|
||||
"classnames": "^2.3.2",
|
||||
"clipboard": "^2.0.11",
|
||||
"jquery": "3",
|
||||
"moment": "^2.29.4",
|
||||
"nanoid": "^4.0.0",
|
||||
"randomcolor": "^0.6.2",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-feather": "^2.0.10",
|
||||
"react-linkify": "^1.0.0-alpha",
|
||||
"react-modal": "^3.16.1",
|
||||
"react-redux": "^8.0.5",
|
||||
"react-router": "^6.4.4",
|
||||
"react-router-dom": "^6.4.4",
|
||||
"react-simple-dropdown": "^3.2.3",
|
||||
"redux": "^4.2.0",
|
||||
"redux-thunk": "^2.4.2",
|
||||
"sanitize-html": "^2.7.3",
|
||||
"socket.io-client": "^4.5.4",
|
||||
"tinycon": "^0.6.8"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"preview": "vite preview",
|
||||
"test": "TZ=UTC vitest",
|
||||
"lint": "eslint src",
|
||||
"coverage": "TZ=UTC vitest --coverage --watch=false"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^14.4.3",
|
||||
"@typescript-eslint/eslint-plugin": "^5.46.0",
|
||||
"@typescript-eslint/parser": "^5.46.0",
|
||||
"@vitejs/plugin-react": "^3.0.0",
|
||||
"@vitest/coverage-istanbul": "^0.25.7",
|
||||
"eslint": "^8.29.0",
|
||||
"eslint-config-prettier": "^6.11.0",
|
||||
"eslint-plugin-prettier": "^3.1.3",
|
||||
"jest-environment-jsdom-sixteen": "^1.0.3",
|
||||
"jest-fetch-mock": "^3.0.3",
|
||||
"prettier": "^2.0.5",
|
||||
"sass": "^1.56.2",
|
||||
"typescript": "^4.9.4",
|
||||
"vitest": "^0.25.7",
|
||||
"vitest-fetch-mock": "^0.2.1"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 7.3 KiB |
@ -1,15 +0,0 @@
|
||||
{
|
||||
"short_name": "Darkwire",
|
||||
"name": "Darkwire.io - encrypted web chat",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
User-agent: *
|
||||
Allow: /
|
@ -1,44 +0,0 @@
|
||||
export const openModal = payload => ({ type: 'OPEN_MODAL', payload });
|
||||
export const closeModal = () => ({ type: 'CLOSE_MODAL' });
|
||||
|
||||
export const setScrolledToBottom = payload => ({ type: 'SET_SCROLLED_TO_BOTTOM', payload });
|
||||
|
||||
export const showNotice = payload => async dispatch => {
|
||||
dispatch({ type: 'SHOW_NOTICE', payload });
|
||||
};
|
||||
|
||||
export const toggleWindowFocus = payload => async dispatch => {
|
||||
dispatch({ type: 'TOGGLE_WINDOW_FOCUS', payload });
|
||||
};
|
||||
|
||||
export const toggleSoundEnabled = payload => async dispatch => {
|
||||
dispatch({ type: 'TOGGLE_SOUND_ENABLED', payload });
|
||||
};
|
||||
|
||||
export const togglePersistenceEnabled = payload => async dispatch => {
|
||||
dispatch({ type: 'TOGGLE_PERSISTENCE_ENABLED', payload });
|
||||
};
|
||||
|
||||
export const toggleNotificationEnabled = payload => async dispatch => {
|
||||
dispatch({ type: 'TOGGLE_NOTIFICATION_ENABLED', payload });
|
||||
};
|
||||
|
||||
export const toggleNotificationAllowed = payload => async dispatch => {
|
||||
dispatch({ type: 'TOGGLE_NOTIFICATION_ALLOWED', payload });
|
||||
};
|
||||
|
||||
export const toggleSocketConnected = payload => async dispatch => {
|
||||
dispatch({ type: 'TOGGLE_SOCKET_CONNECTED', payload });
|
||||
};
|
||||
|
||||
export const createUser = payload => async dispatch => {
|
||||
dispatch({ type: 'CREATE_USER', payload });
|
||||
};
|
||||
|
||||
export const clearActivities = () => async dispatch => {
|
||||
dispatch({ type: 'CLEAR_ACTIVITIES' });
|
||||
};
|
||||
|
||||
export const setLanguage = payload => async dispatch => {
|
||||
dispatch({ type: 'CHANGE_LANGUAGE', payload });
|
||||
};
|
@ -1,55 +0,0 @@
|
||||
import * as actions from './app';
|
||||
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
|
||||
describe('App actions', () => {
|
||||
it('should create an action to scroll to bottom', () => {
|
||||
expect(actions.setScrolledToBottom('test')).toEqual({
|
||||
type: 'SET_SCROLLED_TO_BOTTOM',
|
||||
payload: 'test',
|
||||
});
|
||||
});
|
||||
|
||||
it('should create an action to close modal', () => {
|
||||
expect(actions.closeModal()).toEqual({
|
||||
type: 'CLOSE_MODAL',
|
||||
});
|
||||
});
|
||||
|
||||
it('should create an action to open modal', () => {
|
||||
expect(actions.openModal('test')).toEqual({
|
||||
type: 'OPEN_MODAL',
|
||||
payload: 'test',
|
||||
});
|
||||
});
|
||||
|
||||
it('should create an action to clear activities', () => {
|
||||
const mockDispatch = vi.fn();
|
||||
|
||||
actions.clearActivities()(mockDispatch);
|
||||
|
||||
expect(mockDispatch).toHaveBeenLastCalledWith({
|
||||
type: 'CLEAR_ACTIVITIES',
|
||||
});
|
||||
});
|
||||
it('should create all actions', () => {
|
||||
const mockDispatch = vi.fn();
|
||||
|
||||
const actionsResults = [
|
||||
[actions.toggleWindowFocus('test'), 'TOGGLE_WINDOW_FOCUS'],
|
||||
[actions.showNotice('test'), 'SHOW_NOTICE'],
|
||||
[actions.toggleSoundEnabled('test'), 'TOGGLE_SOUND_ENABLED'],
|
||||
[actions.toggleSocketConnected('test'), 'TOGGLE_SOCKET_CONNECTED'],
|
||||
[actions.createUser('test'), 'CREATE_USER'],
|
||||
[actions.setLanguage('test'), 'CHANGE_LANGUAGE'],
|
||||
];
|
||||
|
||||
actionsResults.forEach(([action, type]) => {
|
||||
action(mockDispatch);
|
||||
expect(mockDispatch).toHaveBeenLastCalledWith({
|
||||
type,
|
||||
payload: 'test',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -1,16 +0,0 @@
|
||||
import { getSocket } from '@/utils/socket';
|
||||
import { prepare as prepareMessage, process as processMessage } from '@/utils/message';
|
||||
|
||||
export const sendEncryptedMessage = payload => async (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const msg = await prepareMessage(payload, state);
|
||||
dispatch({ type: `SEND_ENCRYPTED_MESSAGE_${msg.original.type}`, payload: msg.original.payload });
|
||||
getSocket().emit('ENCRYPTED_MESSAGE', msg.toSend);
|
||||
};
|
||||
|
||||
export const receiveEncryptedMessage = payload => async (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const message = await processMessage(payload, state);
|
||||
// Pass current state to all RECEIVE_ENCRYPTED_MESSAGE reducers for convenience, since each may have different needs
|
||||
dispatch({ type: `RECEIVE_ENCRYPTED_MESSAGE_${message.type}`, payload: { payload: message.payload, state } });
|
||||
};
|
@ -1,51 +0,0 @@
|
||||
import * as actions from './encrypted_messages';
|
||||
import { getSocket } from '@/utils/socket';
|
||||
import { prepare as prepareMessage, process as processMessage } from '@/utils/message';
|
||||
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
|
||||
vi.mock('@/utils/message', () => {
|
||||
return {
|
||||
prepare: vi
|
||||
.fn()
|
||||
.mockResolvedValue({ original: { type: 'messageType', payload: 'test' }, toSend: 'encryptedpayload' }),
|
||||
process: vi.fn().mockResolvedValue({ type: 'messageType', payload: 'test' }),
|
||||
};
|
||||
});
|
||||
|
||||
const mockEmit = vi.fn();
|
||||
|
||||
vi.mock('@/utils/socket', () => {
|
||||
return {
|
||||
getSocket: vi.fn().mockImplementation(() => ({
|
||||
emit: mockEmit,
|
||||
})),
|
||||
};
|
||||
});
|
||||
|
||||
describe('Encrypted messages actions', () => {
|
||||
it('should create an action to send message', async () => {
|
||||
const mockDispatch = vi.fn();
|
||||
|
||||
await actions.sendEncryptedMessage({ payload: 'payload' })(mockDispatch, vi.fn().mockReturnValue({ state: {} }));
|
||||
|
||||
expect(prepareMessage).toHaveBeenLastCalledWith({ payload: 'payload' }, { state: {} });
|
||||
expect(mockDispatch).toHaveBeenLastCalledWith({ payload: 'test', type: 'SEND_ENCRYPTED_MESSAGE_messageType' });
|
||||
expect(getSocket().emit).toHaveBeenLastCalledWith('ENCRYPTED_MESSAGE', 'encryptedpayload');
|
||||
});
|
||||
|
||||
it('should create an action to receive message', async () => {
|
||||
const mockDispatch = vi.fn();
|
||||
|
||||
await actions.receiveEncryptedMessage({ payload: 'encrypted' })(
|
||||
mockDispatch,
|
||||
vi.fn().mockReturnValue({ state: {} }),
|
||||
);
|
||||
|
||||
expect(processMessage).toHaveBeenLastCalledWith({ payload: 'encrypted' }, { state: {} });
|
||||
expect(mockDispatch).toHaveBeenLastCalledWith({
|
||||
payload: { payload: 'test', state: { state: {} } },
|
||||
type: 'RECEIVE_ENCRYPTED_MESSAGE_messageType',
|
||||
});
|
||||
});
|
||||
});
|
@ -1,5 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
|
||||
export * from './app';
|
||||
export * from './unencrypted_messages';
|
||||
export * from './encrypted_messages';
|
@ -1,80 +0,0 @@
|
||||
import { getSocket } from '@/utils/socket';
|
||||
|
||||
const receiveUserEnter = (payload, dispatch) => {
|
||||
dispatch({ type: 'USER_ENTER', payload });
|
||||
};
|
||||
|
||||
const receiveToggleLockRoom = (payload, dispatch, getState) => {
|
||||
const state = getState();
|
||||
|
||||
const lockedByUser = state.room.members.find(m => m.publicKey.n === payload.publicKey.n);
|
||||
const lockedByUsername = lockedByUser.username;
|
||||
const lockedByUserId = lockedByUser.id;
|
||||
|
||||
dispatch({
|
||||
type: 'RECEIVE_TOGGLE_LOCK_ROOM',
|
||||
payload: {
|
||||
username: lockedByUsername,
|
||||
locked: payload.locked,
|
||||
id: lockedByUserId,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const receiveUserExit = (payload, dispatch, getState) => {
|
||||
const state = getState();
|
||||
const payloadPublicKeys = payload.map(member => member.publicKey.n);
|
||||
const exitingUser = state.room.members.find(m => !payloadPublicKeys.includes(m.publicKey.n));
|
||||
|
||||
if (!exitingUser) {
|
||||
return;
|
||||
}
|
||||
|
||||
const exitingUserId = exitingUser.id;
|
||||
const exitingUsername = exitingUser.username;
|
||||
|
||||
dispatch({
|
||||
type: 'USER_EXIT',
|
||||
payload: {
|
||||
members: payload,
|
||||
id: exitingUserId,
|
||||
username: exitingUsername,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const receiveUnencryptedMessage = (type, payload) => async (dispatch, getState) => {
|
||||
switch (type) {
|
||||
case 'USER_ENTER':
|
||||
return receiveUserEnter(payload, dispatch);
|
||||
case 'USER_EXIT':
|
||||
return receiveUserExit(payload, dispatch, getState);
|
||||
case 'TOGGLE_LOCK_ROOM':
|
||||
return receiveToggleLockRoom(payload, dispatch, getState);
|
||||
default:
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
const sendToggleLockRoom = (dispatch, getState) => {
|
||||
const state = getState();
|
||||
getSocket().emit('TOGGLE_LOCK_ROOM', null, res => {
|
||||
dispatch({
|
||||
type: 'TOGGLE_LOCK_ROOM',
|
||||
payload: {
|
||||
locked: res.isLocked,
|
||||
username: state.user.username,
|
||||
sender: state.user.id,
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const sendUnencryptedMessage = type => async (dispatch, getState) => {
|
||||
switch (type) {
|
||||
case 'TOGGLE_LOCK_ROOM':
|
||||
return sendToggleLockRoom(dispatch, getState);
|
||||
default:
|
||||
return;
|
||||
}
|
||||
};
|
@ -1,126 +0,0 @@
|
||||
import * as actions from './unencrypted_messages';
|
||||
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
|
||||
const mockEmit = vi.fn((_type, _null, callback) => {
|
||||
callback({ isLocked: true });
|
||||
});
|
||||
|
||||
vi.mock('@/utils/socket', () => {
|
||||
return {
|
||||
getSocket: vi.fn().mockImplementation(() => ({
|
||||
emit: mockEmit,
|
||||
})),
|
||||
};
|
||||
});
|
||||
|
||||
describe('Receive unencrypted message actions', () => {
|
||||
it('should create no action', () => {
|
||||
const mockDispatch = vi.fn();
|
||||
actions.receiveUnencryptedMessage('FAKE')(mockDispatch, vi.fn().mockReturnValue({}));
|
||||
expect(mockDispatch).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should create user enter action', () => {
|
||||
const mockDispatch = vi.fn();
|
||||
actions.receiveUnencryptedMessage('USER_ENTER', 'test')(mockDispatch, vi.fn().mockReturnValue({ state: {} }));
|
||||
expect(mockDispatch).toHaveBeenLastCalledWith({ type: 'USER_ENTER', payload: 'test' });
|
||||
});
|
||||
|
||||
it('should create user exit action', () => {
|
||||
const mockDispatch = vi.fn();
|
||||
const state = {
|
||||
room: {
|
||||
members: [
|
||||
{ publicKey: { n: 'alankey' }, id: 'alankey', username: 'alan' },
|
||||
{ publicKey: { n: 'dankey' }, id: 'dankey', username: 'dan' },
|
||||
{ publicKey: { n: 'alicekey' }, id: 'alicekey', username: 'dan' },
|
||||
],
|
||||
},
|
||||
};
|
||||
const mockGetState = vi.fn().mockReturnValue(state);
|
||||
const payload1 = [
|
||||
{ publicKey: { n: 'alankey' } },
|
||||
{ publicKey: { n: 'dankey' } },
|
||||
{ publicKey: { n: 'alicekey' } },
|
||||
];
|
||||
const payload2 = [{ publicKey: { n: 'dankey' } }, { publicKey: { n: 'alicekey' } }];
|
||||
|
||||
// Nobody left
|
||||
actions.receiveUnencryptedMessage('USER_EXIT', payload1)(mockDispatch, mockGetState);
|
||||
|
||||
expect(mockDispatch).not.toHaveBeenCalled();
|
||||
|
||||
actions.receiveUnencryptedMessage('USER_EXIT', payload2)(mockDispatch, mockGetState);
|
||||
expect(mockDispatch).toHaveBeenLastCalledWith({
|
||||
payload: {
|
||||
id: 'alankey',
|
||||
members: [{ publicKey: { n: 'dankey' } }, { publicKey: { n: 'alicekey' } }],
|
||||
username: 'alan',
|
||||
},
|
||||
type: 'USER_EXIT',
|
||||
});
|
||||
});
|
||||
|
||||
it('should create receive toggle lock room action', () => {
|
||||
const mockDispatch = vi.fn();
|
||||
const state = {
|
||||
room: {
|
||||
members: [
|
||||
{ publicKey: { n: 'alankey' }, id: 'idalan', username: 'alan' },
|
||||
{ publicKey: { n: 'dankey' }, id: 'iddan', username: 'dan' },
|
||||
],
|
||||
},
|
||||
};
|
||||
const mockGetState = vi.fn().mockReturnValue(state);
|
||||
const payload = { publicKey: { n: 'alankey' } };
|
||||
|
||||
actions.receiveUnencryptedMessage('TOGGLE_LOCK_ROOM', payload)(mockDispatch, mockGetState);
|
||||
expect(mockDispatch).toHaveBeenLastCalledWith({
|
||||
payload: { id: 'idalan', locked: undefined, username: 'alan' },
|
||||
type: 'RECEIVE_TOGGLE_LOCK_ROOM',
|
||||
});
|
||||
});
|
||||
|
||||
it('should create receive toggle lock room action', () => {
|
||||
const mockDispatch = vi.fn();
|
||||
const state = {
|
||||
user: {
|
||||
username: 'alan',
|
||||
id: 'idalan',
|
||||
},
|
||||
};
|
||||
const mockGetState = vi.fn().mockReturnValue(state);
|
||||
|
||||
actions.sendUnencryptedMessage('TOGGLE_LOCK_ROOM')(mockDispatch, mockGetState);
|
||||
expect(mockDispatch).toHaveBeenLastCalledWith({
|
||||
payload: { locked: true, sender: 'idalan', username: 'alan' },
|
||||
type: 'TOGGLE_LOCK_ROOM',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Send unencrypted message actions', () => {
|
||||
it('should create no action', () => {
|
||||
const mockDispatch = vi.fn();
|
||||
actions.sendUnencryptedMessage('FAKE')(mockDispatch, vi.fn().mockReturnValue({}));
|
||||
expect(mockDispatch).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should create toggle lock room action', () => {
|
||||
const mockDispatch = vi.fn();
|
||||
const state = {
|
||||
user: {
|
||||
username: 'alan',
|
||||
id: 'idalan',
|
||||
},
|
||||
};
|
||||
const mockGetState = vi.fn().mockReturnValue(state);
|
||||
|
||||
actions.sendUnencryptedMessage('TOGGLE_LOCK_ROOM')(mockDispatch, mockGetState);
|
||||
expect(mockDispatch).toHaveBeenLastCalledWith({
|
||||
payload: { locked: true, sender: 'idalan', username: 'alan' },
|
||||
type: 'TOGGLE_LOCK_ROOM',
|
||||
});
|
||||
});
|
||||
});
|
@ -1,27 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
let host;
|
||||
let protocol;
|
||||
let port;
|
||||
|
||||
switch (import.meta.env.MODE) {
|
||||
case 'staging':
|
||||
host = import.meta.env.VITE_API_HOST || 'localhost';
|
||||
protocol = import.meta.env.VITE_API_PROTOCOL || 'http';
|
||||
port = import.meta.env.VITE_API_PORT || 80;
|
||||
break;
|
||||
case 'production':
|
||||
host = import.meta.env.VITE_API_HOST || 'localhost';
|
||||
protocol = import.meta.env.VITE_API_PROTOCOL || 'http';
|
||||
port = import.meta.env.VITE_API_PORT || 80;
|
||||
break;
|
||||
default:
|
||||
host = import.meta.env.VITE_API_HOST || 'localhost';
|
||||
protocol = import.meta.env.VITE_API_PROTOCOL || 'http';
|
||||
port = import.meta.env.VITE_API_PORT || 80;
|
||||
}
|
||||
|
||||
export default {
|
||||
host,
|
||||
port,
|
||||
protocol,
|
||||
};
|
@ -1,14 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
import config from './config';
|
||||
|
||||
export default (resourceName = '') => {
|
||||
const { port, protocol, host } = config;
|
||||
|
||||
const resourcePath = resourceName;
|
||||
|
||||
if (!host) {
|
||||
return `/${resourcePath}`;
|
||||
}
|
||||
|
||||
return `${protocol}://${host}:${port}/${resourcePath}`;
|
||||
};
|
@ -1,54 +0,0 @@
|
||||
import React from 'react';
|
||||
import { render, fireEvent } from '@testing-library/react';
|
||||
import About from '.';
|
||||
import { describe, it, expect, vi, afterEach } from 'vitest';
|
||||
|
||||
vi.useFakeTimers();
|
||||
|
||||
// Mock Api generator
|
||||
|
||||
vi.mock('@/api/generator', () => {
|
||||
return {
|
||||
default: path => {
|
||||
return `http://fakedomain/${path}`;
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
describe('About component', () => {
|
||||
afterEach(() => {
|
||||
fetchMock.resetMocks();
|
||||
});
|
||||
|
||||
it('should display', async () => {
|
||||
const { asFragment } = render(<About roomId={'test'} />);
|
||||
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should report abuse', async () => {
|
||||
const { getByText, queryByText } = render(<About roomId={'test'} />);
|
||||
|
||||
expect(queryByText('Thank you!')).not.toBeInTheDocument();
|
||||
|
||||
fireEvent.click(getByText('Submit'));
|
||||
|
||||
expect(fetchMock).toHaveBeenCalledWith('http://fakedomain/abuse/test', { method: 'POST' });
|
||||
|
||||
expect(getByText('Thank you!')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should change room id', async () => {
|
||||
const { getByPlaceholderText, getByText, queryByText } = render(<About roomId={'test'} />);
|
||||
|
||||
expect(queryByText('Thank you!')).not.toBeInTheDocument();
|
||||
|
||||
fireEvent.change(getByPlaceholderText('Room ID'), { target: { value: 'newRoomName' } });
|
||||
|
||||
vi.runAllTimers();
|
||||
|
||||
fireEvent.click(getByText('Submit'));
|
||||
|
||||
expect(fetchMock).toHaveBeenLastCalledWith('http://fakedomain/abuse/newRoomName', { method: 'POST' });
|
||||
});
|
||||
});
|
@ -1,513 +0,0 @@
|
||||
// Vitest Snapshot v1
|
||||
|
||||
exports[`About component > should display 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="_base_4f26aa"
|
||||
>
|
||||
<div
|
||||
class="_links_4f26aa"
|
||||
>
|
||||
<div>
|
||||
<a
|
||||
href="#version"
|
||||
>
|
||||
Version
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a
|
||||
href="#software"
|
||||
>
|
||||
Software
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a
|
||||
href="#report-abuse"
|
||||
>
|
||||
Report Abuse
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a
|
||||
href="#acceptable-use"
|
||||
>
|
||||
Acceptable Use Policy
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a
|
||||
href="#disclaimer"
|
||||
>
|
||||
Disclaimer
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a
|
||||
href="#terms"
|
||||
>
|
||||
Terms of Service
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a
|
||||
href="#contact"
|
||||
>
|
||||
Contact
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a
|
||||
href="#donate"
|
||||
>
|
||||
Donate
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<section
|
||||
id="version"
|
||||
>
|
||||
<h4>
|
||||
Version
|
||||
</h4>
|
||||
<p>
|
||||
Commit SHA:
|
||||
<a
|
||||
href="https://github.com/darkwire/darkwire.io/commit/some_sha"
|
||||
target="_blank"
|
||||
>
|
||||
some_sha
|
||||
</a>
|
||||
</p>
|
||||
</section>
|
||||
<section
|
||||
id="software"
|
||||
>
|
||||
<h4>
|
||||
Software
|
||||
</h4>
|
||||
<p>
|
||||
This software uses the
|
||||
<a
|
||||
href="https://developer.mozilla.org/en-US/docs/Web/API/Crypto"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
Web Cryptography API
|
||||
</a>
|
||||
to encrypt data which is transferred using
|
||||
<a
|
||||
href="https://en.wikipedia.org/wiki/WebSocket"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
secure WebSockets
|
||||
</a>
|
||||
. Messages are never stored on a server or sent over the wire in plain-text.
|
||||
</p>
|
||||
<p>
|
||||
We believe in privacy and transparency.
|
||||
<a
|
||||
href="https://github.com/darkwire/darkwire.io"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
View the source code and documentation on GitHub.
|
||||
</a>
|
||||
</p>
|
||||
</section>
|
||||
<section
|
||||
id="report-abuse"
|
||||
>
|
||||
<h4>
|
||||
Report Abuse
|
||||
</h4>
|
||||
<p>
|
||||
We encourage you to report problematic content to us. Please keep in mind that to help ensure the safety, confidentiality and security of your messages, we do not have the contents of messages available to us, which limits our ability to verify the report and take action.
|
||||
</p>
|
||||
<p>
|
||||
When needed, you can take a screenshot of the content and share it, along with any available contact info, with appropriate law enforcement authorities.
|
||||
</p>
|
||||
<p>
|
||||
To report any content, email us at abuse[at]darkwire.io or submit the room ID below to report anonymously.
|
||||
</p>
|
||||
<form>
|
||||
<div>
|
||||
<div
|
||||
class="input-group"
|
||||
>
|
||||
<input
|
||||
class="form-control"
|
||||
placeholder="Room ID"
|
||||
type="text"
|
||||
value="test"
|
||||
/>
|
||||
<div
|
||||
class="input-group-append"
|
||||
>
|
||||
<button
|
||||
class="btn btn-secondary"
|
||||
type="submit"
|
||||
>
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<br />
|
||||
<p>
|
||||
If you feel you or anyone else is in immediate danger, please contact your local emergency services.
|
||||
</p>
|
||||
<p>
|
||||
If you receive content from someone who wishes to hurt themselves, and you're concerned for their safety, please contact your local emergency services or a
|
||||
<a
|
||||
href="https://faq.whatsapp.com/en/general/28030010"
|
||||
>
|
||||
suicide prevention hotline
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
<p>
|
||||
If you receive or encounter content indicating abuse or exploitation of a child, please contact the
|
||||
<a
|
||||
href="http://www.missingkids.com"
|
||||
>
|
||||
National Center for Missing and Exploited Children (NCMEC)
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
</section>
|
||||
<section
|
||||
id="acceptable-use"
|
||||
>
|
||||
<h4>
|
||||
Acceptable Use Policy
|
||||
</h4>
|
||||
<p>
|
||||
This Acceptable Use Policy (this “Policy”) describes prohibited uses of the web services offered by Darkwire and its affiliates (the “Services”) and the website located at https://darkwire.io (the “Darkwire Site”). The examples described in this Policy are not exhaustive. We may modify this Policy at any time by posting a revised version on the Darkwire Site. By using the Services or accessing the Darkwire Site, you agree to the latest version of this Policy. If you violate the Policy or authorize or help others to do so, we may suspend or terminate your use of the Services.
|
||||
</p>
|
||||
<strong>
|
||||
No Illegal, Harmful, or Offensive Use or Content
|
||||
</strong>
|
||||
<p>
|
||||
You may not use, or encourage, promote, facilitate or instruct others to use, the Services or Darkwire Site for any illegal, harmful, fraudulent, infringing or offensive use, or to transmit, store, display, distribute or otherwise make available content that is illegal, harmful, fraudulent, infringing or offensive. Prohibited activities or content include:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>
|
||||
Illegal, Harmful or Fraudulent Activities.
|
||||
</strong>
|
||||
Any activities that are illegal, that violate the rights of others, or that may be harmful to others, our operations or reputation, including disseminating, promoting or facilitating child pornography, offering or disseminating fraudulent goods, services, schemes, or promotions, make-money-fast schemes, ponzi and pyramid schemes, phishing, or pharming.
|
||||
</li>
|
||||
<li>
|
||||
<strong>
|
||||
Infringing Content.
|
||||
</strong>
|
||||
Content that infringes or misappropriates the intellectual property or proprietary rights of others.
|
||||
</li>
|
||||
<li>
|
||||
<strong>
|
||||
Offensive Content.
|
||||
</strong>
|
||||
Content that is defamatory, obscene, abusive, invasive of privacy, or otherwise objectionable, including content that constitutes child pornography, relates to bestiality, or depicts non-consensual sex acts.
|
||||
</li>
|
||||
<li>
|
||||
<strong>
|
||||
Harmful Content.
|
||||
</strong>
|
||||
Content or other computer technology that may damage, interfere with, surreptitiously intercept, or expropriate any system, program, or data, including viruses, Trojan horses, worms, time bombs, or cancelbots.
|
||||
</li>
|
||||
</ul>
|
||||
<strong>
|
||||
No Security Violations
|
||||
</strong>
|
||||
<br />
|
||||
You may not use the Services to violate the security or integrity of any network, computer or communications system, software application, or network or computing device (each, a “System”). Prohibited activities include:
|
||||
<ul>
|
||||
<li>
|
||||
<strong>
|
||||
Unauthorized Access.
|
||||
</strong>
|
||||
Accessing or using any System without permission, including attempting to probe, scan, or test the vulnerability of a System or to breach any security or authentication measures used by a System.
|
||||
</li>
|
||||
<li>
|
||||
<strong>
|
||||
Interception.
|
||||
</strong>
|
||||
Monitoring of data or traffic on a System without permission.
|
||||
</li>
|
||||
<li>
|
||||
<strong>
|
||||
Falsification of Origin.
|
||||
</strong>
|
||||
Forging TCP-IP packet headers, e-mail headers, or any part of a message describing its origin or route. The legitimate use of aliases and anonymous remailers is not prohibited by this provision.
|
||||
</li>
|
||||
</ul>
|
||||
<strong>
|
||||
No Network Abuse
|
||||
</strong>
|
||||
<br />
|
||||
You may not make network connections to any users, hosts, or networks unless you have permission to communicate with them. Prohibited activities include:
|
||||
<ul>
|
||||
<li>
|
||||
<strong>
|
||||
Monitoring or Crawling.
|
||||
</strong>
|
||||
Monitoring or crawling of a System that impairs or disrupts the System being monitored or crawled.
|
||||
</li>
|
||||
<li>
|
||||
<strong>
|
||||
Denial of Service (DoS).
|
||||
</strong>
|
||||
Inundating a target with communications requests so the target either cannot respond to legitimate traffic or responds so slowly that it becomes ineffective.
|
||||
</li>
|
||||
<li>
|
||||
<strong>
|
||||
Intentional Interference.
|
||||
</strong>
|
||||
Interfering with the proper functioning of any System, including any deliberate attempt to overload a system by mail bombing, news bombing, broadcast attacks, or flooding techniques.
|
||||
</li>
|
||||
<li>
|
||||
<strong>
|
||||
Operation of Certain Network Services.
|
||||
</strong>
|
||||
Operating network services like open proxies, open mail relays, or open recursive domain name servers.
|
||||
</li>
|
||||
<li>
|
||||
<strong>
|
||||
Avoiding System Restrictions.
|
||||
</strong>
|
||||
Using manual or electronic means to avoid any use limitations placed on a System, such as access and storage restrictions.
|
||||
</li>
|
||||
</ul>
|
||||
<strong>
|
||||
No E-Mail or Other Message Abuse
|
||||
</strong>
|
||||
<br />
|
||||
You will not distribute, publish, send, or facilitate the sending of unsolicited mass e-mail or other messages, promotions, advertising, or solicitations (like “spam”), including commercial advertising and informational announcements. You will not alter or obscure mail headers or assume a sender’s identity without the sender’s explicit permission. You will not collect replies to messages sent from another internet service provider if those messages violate this Policy or the acceptable use policy of that provider.
|
||||
<strong>
|
||||
Our Monitoring and Enforcement
|
||||
</strong>
|
||||
<br />
|
||||
We reserve the right, but do not assume the obligation, to investigate any violation of this Policy or misuse of the Services or Darkwire Site. We may:
|
||||
<ul>
|
||||
<li>
|
||||
investigate violations of this Policy or misuse of the Services or Darkwire Site; or
|
||||
</li>
|
||||
<li>
|
||||
remove, disable access to, or modify any content or resource that violates this Policy or any other agreement we have with you for use of the Services or the Darkwire Site.
|
||||
</li>
|
||||
<li>
|
||||
We may report any activity that we suspect violates any law or regulation to appropriate law enforcement officials, regulators, or other appropriate third parties. Our reporting may include disclosing appropriate customer information. We also may cooperate with appropriate law enforcement agencies, regulators, or other appropriate third parties to help with the investigation and prosecution of illegal conduct by providing network and systems information related to alleged violations of this Policy.
|
||||
</li>
|
||||
</ul>
|
||||
Reporting of Violations of this Policy
|
||||
<br />
|
||||
If you become aware of any violation of this Policy, you will immediately notify us and provide us with assistance, as requested, to stop or remedy the violation. To report any violation of this Policy, please follow our abuse reporting process.
|
||||
</section>
|
||||
<section
|
||||
id="terms"
|
||||
>
|
||||
<h4>
|
||||
Terms of Service ("Terms")
|
||||
</h4>
|
||||
<p>
|
||||
Last updated: December 11, 2017
|
||||
</p>
|
||||
<p>
|
||||
Please read these Terms of Service ("Terms", "Terms of Service") carefully before using the https://darkwire.io website (the "Service") operated by Darkwire ("us", "we", or "our").
|
||||
</p>
|
||||
<p>
|
||||
Your access to and use of the Service is conditioned on your acceptance of and compliance with these Terms. These Terms apply to all visitors, users and others who access or use the Service.
|
||||
</p>
|
||||
<p>
|
||||
By accessing or using the Service you agree to be bound by these Terms. If you disagree with any part of the terms then you may not access the Service.
|
||||
</p>
|
||||
<strong>
|
||||
Links To Other Web Sites
|
||||
</strong>
|
||||
<p>
|
||||
Our Service may contain links to third-party web sites or services that are not owned or controlled by Darkwire.
|
||||
</p>
|
||||
<p>
|
||||
Darkwire has no control over, and assumes no responsibility for, the content, privacy policies, or practices of any third party web sites or services. You further acknowledge and agree that Darkwire shall not be responsible or liable, directly or indirectly, for any damage or loss caused or alleged to be caused by or in connection with use of or reliance on any such content, goods or services available on or through any such web sites or services.
|
||||
</p>
|
||||
<p>
|
||||
We strongly advise you to read the terms and conditions and privacy policies of any third-party web sites or services that you visit.
|
||||
</p>
|
||||
<strong>
|
||||
Termination
|
||||
</strong>
|
||||
<p>
|
||||
We may terminate or suspend access to our Service immediately, without prior notice or liability, for any reason whatsoever, including without limitation if you breach the Terms.
|
||||
</p>
|
||||
<p>
|
||||
All provisions of the Terms which by their nature should survive termination shall survive termination, including, without limitation, ownership provisions, warranty disclaimers, indemnity and limitations of liability.
|
||||
</p>
|
||||
<strong>
|
||||
Governing Law
|
||||
</strong>
|
||||
<p>
|
||||
These Terms shall be governed and construed in accordance with the laws of New York, United States, without regard to its conflict of law provisions.
|
||||
</p>
|
||||
<p>
|
||||
Our failure to enforce any right or provision of these Terms will not be considered a waiver of those rights. If any provision of these Terms is held to be invalid or unenforceable by a court, the remaining provisions of these Terms will remain in effect. These Terms constitute the entire agreement between us regarding our Service, and supersede and replace any prior agreements we might have between us regarding the Service.
|
||||
</p>
|
||||
</section>
|
||||
<section
|
||||
id="disclaimer"
|
||||
>
|
||||
<h4>
|
||||
Disclaimer
|
||||
</h4>
|
||||
<p
|
||||
class="bold"
|
||||
>
|
||||
WARNING: Darkwire does not mask IP addresses nor can verify the integrity of parties recieving messages. Proceed with caution and always confirm recipients beforre starting a chat session.
|
||||
</p>
|
||||
<p>
|
||||
Please also note that
|
||||
<strong>
|
||||
ALL CHATROOMS
|
||||
</strong>
|
||||
are public. Anyone can guess your room URL. If you need a more-private room, use the lock feature or set the URL manually by entering a room ID after "darkwire.io/".
|
||||
</p>
|
||||
<br />
|
||||
<strong>
|
||||
No Warranties; Exclusion of Liability; Indemnification
|
||||
</strong>
|
||||
<p>
|
||||
<strong>
|
||||
OUR WEBSITE IS OPERATED BY Darkwire ON AN "AS IS," "AS AVAILABLE" BASIS, WITHOUT REPRESENTATIONS OR WARRANTIES OF ANY KIND. TO THE FULLEST EXTENT PERMITTED BY LAW, Darkwire SPECIFICALLY DISCLAIMS ALL WARRANTIES AND CONDITIONS OF ANY KIND, INCLUDING ALL IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NONINFRINGEMENT FOR OUR WEBSITE AND ANY CONTRACTS AND SERVICES YOU PURCHASE THROUGH IT. Darkwire SHALL NOT HAVE ANY LIABILITY OR RESPONSIBILITY FOR ANY ERRORS OR OMISSIONS IN THE CONTENT OF OUR WEBSITE, FOR CONTRACTS OR SERVICES SOLD THROUGH OUR WEBSITE, FOR YOUR ACTION OR INACTION IN CONNECTION WITH OUR WEBSITE OR FOR ANY DAMAGE TO YOUR COMPUTER OR DATA OR ANY OTHER DAMAGE YOU MAY INCUR IN CONNECTION WITH OUR WEBSITE. YOUR USE OF OUR WEBSITE AND ANY CONTRACTS OR SERVICES ARE AT YOUR OWN RISK. IN NO EVENT SHALL EITHER Darkwire OR THEIR AGENTS BE LIABLE FOR ANY DIRECT, INDIRECT, PUNITIVE, INCIDENTAL, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN ANY WAY CONNECTED WITH THE USE OF OUR WEBSITE, CONTRACTS AND SERVICES PURCHASED THROUGH OUR WEBSITE, THE DELAY OR INABILITY TO USE OUR WEBSITE OR OTHERWISE ARISING IN CONNECTION WITH OUR WEBSITE, CONTRACTS OR RELATED SERVICES, WHETHER BASED ON CONTRACT, TORT, STRICT LIABILITY OR OTHERWISE, EVEN IF ADVISED OF THE POSSIBILITY OF ANY SUCH DAMAGES. IN NO EVENT SHALL Darkwire’s LIABILITY FOR ANY DAMAGE CLAIM EXCEED THE AMOUNT PAID BY YOU TO Darkwire FOR THE TRANSACTION GIVING RISE TO SUCH DAMAGE CLAIM.
|
||||
</strong>
|
||||
</p>
|
||||
<p>
|
||||
<strong>
|
||||
SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THE ABOVE EXCLUSION MAY NOT APPLY TO YOU.
|
||||
</strong>
|
||||
</p>
|
||||
<p>
|
||||
<strong>
|
||||
WITHOUT LIMITING THE FOREGOING, Darkwire DO NOT REPRESENT OR WARRANT THAT THE INFORMATION ON THE WEBITE IS ACCURATE, COMPLETE, RELIABLE, USEFUL, TIMELY OR CURRENT OR THAT OUR WEBSITE WILL OPERATE WITHOUT INTERRUPTION OR ERROR.
|
||||
</strong>
|
||||
</p>
|
||||
<p>
|
||||
<strong>
|
||||
YOU AGREE THAT ALL TIMES, YOU WILL LOOK TO ATTORNEYS FROM WHOM YOU PURCHASE SERVICES FOR ANY CLAIMS OF ANY NATURE, INCLUDING LOSS, DAMAGE, OR WARRANTY. Darkwire AND THEIR RESPECTIVE AFFILIATES MAKE NO REPRESENTATION OR GUARANTEES ABOUT ANY CONTRACTS AND SERVICES OFFERED THROUGH OUR WEBSITE.
|
||||
</strong>
|
||||
</p>
|
||||
<p>
|
||||
<strong>
|
||||
Darkwire MAKES NO REPRESENTATION THAT CONTENT PROVIDED ON OUR WEBSITE, CONTRACTS, OR RELATED SERVICES ARE APPLICABLE OR APPROPRIATE FOR USE IN ALL JURISDICTIONS.
|
||||
</strong>
|
||||
</p>
|
||||
<strong>
|
||||
Indemnification
|
||||
</strong>
|
||||
<p>
|
||||
You agree to defend, indemnify and hold Darkwire harmless from and against any and all claims, damages, costs and expenses, including attorneys' fees, arising from or related to your use of our Website or any Contracts or Services you purchase through it.
|
||||
</p>
|
||||
<strong>
|
||||
Changes
|
||||
</strong>
|
||||
<p>
|
||||
We reserve the right, at our sole discretion, to modify or replace these Terms at any time. If a revision is material we will try to provide at least 30 days notice prior to any new terms taking effect. What constitutes a material change will be determined at our sole discretion.
|
||||
</p>
|
||||
<p>
|
||||
By continuing to access or use our Service after those revisions become effective, you agree to be bound by the revised terms. If you do not agree to the new terms, please stop using the Service.
|
||||
</p>
|
||||
<strong>
|
||||
Contact Us
|
||||
</strong>
|
||||
<p>
|
||||
If you have any questions about these Terms, please contact us at hello[at]darkwire.io.
|
||||
</p>
|
||||
</section>
|
||||
<section
|
||||
id="contact"
|
||||
>
|
||||
<h4>
|
||||
Contact
|
||||
</h4>
|
||||
<p>
|
||||
Questions/comments? Email us at hello[at]darkwire.io
|
||||
</p>
|
||||
<p>
|
||||
Found a bug or want a new feature?
|
||||
<a
|
||||
href="https://github.com/darkwire/darkwire.io/issues"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
Open a ticket on Github
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
</section>
|
||||
<section
|
||||
id="donate"
|
||||
>
|
||||
<h4>
|
||||
Donate
|
||||
</h4>
|
||||
<p>
|
||||
Darkwire is maintained and hosted by two developers with full-time jobs. If you get some value from this service we would appreciate any donation you can afford. We use these funds for server and DNS costs. Thank you!
|
||||
</p>
|
||||
<strong>
|
||||
Bitcoin
|
||||
</strong>
|
||||
<p>
|
||||
189sPnHGcjP5uteg2UuNgcJ5eoaRAP4Bw4
|
||||
</p>
|
||||
<strong>
|
||||
Ethereum
|
||||
</strong>
|
||||
<p>
|
||||
0x36dc407bB28aA1EE6AafBee0379Fe6Cff881758E
|
||||
</p>
|
||||
<strong>
|
||||
Litecoin
|
||||
</strong>
|
||||
<p>
|
||||
LUViQeSggBBtYoN2qNtXSuxYoRMzRY8CSX
|
||||
</p>
|
||||
<strong>
|
||||
PayPal:
|
||||
</strong>
|
||||
<br />
|
||||
<form
|
||||
action="https://www.paypal.com/cgi-bin/webscr"
|
||||
method="post"
|
||||
target="_blank"
|
||||
>
|
||||
<input
|
||||
name="cmd"
|
||||
type="hidden"
|
||||
value="_s-xclick"
|
||||
/>
|
||||
<input
|
||||
name="hosted_button_id"
|
||||
type="hidden"
|
||||
value="UAH5BCLA9Y8VW"
|
||||
/>
|
||||
<input
|
||||
alt="PayPal - The safer, easier way to pay online!"
|
||||
border="0"
|
||||
name="submit"
|
||||
src="https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif"
|
||||
type="image"
|
||||
/>
|
||||
<img
|
||||
alt=""
|
||||
border="0"
|
||||
height="1"
|
||||
src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif"
|
||||
width="1"
|
||||
/>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
@ -1,451 +0,0 @@
|
||||
/* eslint-disable */
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { COMMIT_SHA } from '@/config/env';
|
||||
import apiUrlGenerator from '@/api/generator';
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
class About extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
roomId: props.roomId,
|
||||
abuseReported: false,
|
||||
};
|
||||
}
|
||||
|
||||
handleUpdateRoomId(evt) {
|
||||
this.setState({
|
||||
roomId: evt.target.value,
|
||||
});
|
||||
}
|
||||
|
||||
handleReportAbuse(evt) {
|
||||
evt.preventDefault();
|
||||
fetch(`${apiUrlGenerator('abuse')}/${this.state.roomId}`, {
|
||||
method: 'POST',
|
||||
});
|
||||
this.setState({
|
||||
abuseReported: true,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className={styles.base}>
|
||||
<div className={styles.links}>
|
||||
<div>
|
||||
<a href="#version">Version</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="#software">Software</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="#report-abuse">Report Abuse</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="#acceptable-use">Acceptable Use Policy</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="#disclaimer">Disclaimer</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="#terms">Terms of Service</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="#contact">Contact</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="#donate">Donate</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section id="version">
|
||||
<h4>Version</h4>
|
||||
<p>
|
||||
Commit SHA:{' '}
|
||||
<a target="_blank" href={`https://github.com/darkwire/darkwire.io/commit/${COMMIT_SHA}`}>
|
||||
{COMMIT_SHA}
|
||||
</a>
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section id="software">
|
||||
<h4>Software</h4>
|
||||
<p>
|
||||
This software uses the{' '}
|
||||
<a href="https://developer.mozilla.org/en-US/docs/Web/API/Crypto" target="_blank" rel="noopener noreferrer">
|
||||
Web Cryptography API
|
||||
</a>{' '}
|
||||
to encrypt data which is transferred using{' '}
|
||||
<a href="https://en.wikipedia.org/wiki/WebSocket" target="_blank" rel="noopener noreferrer">
|
||||
secure WebSockets
|
||||
</a>
|
||||
. Messages are never stored on a server or sent over the wire in plain-text.
|
||||
</p>
|
||||
<p>
|
||||
We believe in privacy and transparency.
|
||||
<a href="https://github.com/darkwire/darkwire.io" target="_blank" rel="noopener noreferrer">
|
||||
View the source code and documentation on GitHub.
|
||||
</a>
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section id="report-abuse">
|
||||
<h4>Report Abuse</h4>
|
||||
<p>
|
||||
We encourage you to report problematic content to us. Please keep in mind that to help ensure the safety,
|
||||
confidentiality and security of your messages, we do not have the contents of messages available to us,
|
||||
which limits our ability to verify the report and take action.
|
||||
</p>
|
||||
<p>
|
||||
When needed, you can take a screenshot of the content and share it, along with any available contact info,
|
||||
with appropriate law enforcement authorities.
|
||||
</p>
|
||||
<p>
|
||||
To report any content, email us at abuse[at]darkwire.io or submit the room ID below to report anonymously.
|
||||
</p>
|
||||
<form onSubmit={this.handleReportAbuse.bind(this)}>
|
||||
{this.state.abuseReported && <div>Thank you!</div>}
|
||||
<div>
|
||||
<div className="input-group">
|
||||
<input
|
||||
className="form-control"
|
||||
placeholder="Room ID"
|
||||
onChange={this.handleUpdateRoomId.bind(this)}
|
||||
value={this.state.roomId}
|
||||
type="text"
|
||||
/>
|
||||
<div className="input-group-append">
|
||||
<button className="btn btn-secondary" type="submit">
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<br />
|
||||
<p>If you feel you or anyone else is in immediate danger, please contact your local emergency services.</p>
|
||||
<p>
|
||||
If you receive content from someone who wishes to hurt themselves, and you're concerned for their safety,
|
||||
please contact your local emergency services or a{' '}
|
||||
<a href="https://faq.whatsapp.com/en/general/28030010">suicide prevention hotline</a>.
|
||||
</p>
|
||||
<p>
|
||||
If you receive or encounter content indicating abuse or exploitation of a child, please contact the{' '}
|
||||
<a href="http://www.missingkids.com">National Center for Missing and Exploited Children (NCMEC)</a>.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section id="acceptable-use">
|
||||
<h4>Acceptable Use Policy</h4>
|
||||
<p>
|
||||
This Acceptable Use Policy (this “Policy”) describes prohibited uses of the web services offered by Darkwire
|
||||
and its affiliates (the “Services”) and the website located at https://darkwire.io (the “Darkwire Site”).
|
||||
The examples described in this Policy are not exhaustive. We may modify this Policy at any time by posting a
|
||||
revised version on the Darkwire Site. By using the Services or accessing the Darkwire Site, you agree to the
|
||||
latest version of this Policy. If you violate the Policy or authorize or help others to do so, we may
|
||||
suspend or terminate your use of the Services.
|
||||
</p>
|
||||
<strong>No Illegal, Harmful, or Offensive Use or Content</strong>
|
||||
<p>
|
||||
You may not use, or encourage, promote, facilitate or instruct others to use, the Services or Darkwire Site
|
||||
for any illegal, harmful, fraudulent, infringing or offensive use, or to transmit, store, display,
|
||||
distribute or otherwise make available content that is illegal, harmful, fraudulent, infringing or
|
||||
offensive. Prohibited activities or content include:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Illegal, Harmful or Fraudulent Activities.</strong> Any activities that are illegal, that violate
|
||||
the rights of others, or that may be harmful to others, our operations or reputation, including
|
||||
disseminating, promoting or facilitating child pornography, offering or disseminating fraudulent goods,
|
||||
services, schemes, or promotions, make-money-fast schemes, ponzi and pyramid schemes, phishing, or
|
||||
pharming.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<strong>Infringing Content.</strong> Content that infringes or misappropriates the intellectual property
|
||||
or proprietary rights of others.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<strong>Offensive Content.</strong> Content that is defamatory, obscene, abusive, invasive of privacy, or
|
||||
otherwise objectionable, including content that constitutes child pornography, relates to bestiality, or
|
||||
depicts non-consensual sex acts.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<strong>Harmful Content.</strong> Content or other computer technology that may damage, interfere with,
|
||||
surreptitiously intercept, or expropriate any system, program, or data, including viruses, Trojan horses,
|
||||
worms, time bombs, or cancelbots.
|
||||
</li>
|
||||
</ul>
|
||||
<strong>No Security Violations</strong>
|
||||
<br />
|
||||
You may not use the Services to violate the security or integrity of any network, computer or communications
|
||||
system, software application, or network or computing device (each, a “System”). Prohibited activities
|
||||
include:
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Unauthorized Access.</strong> Accessing or using any System without permission, including
|
||||
attempting to probe, scan, or test the vulnerability of a System or to breach any security or
|
||||
authentication measures used by a System.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<strong>Interception.</strong> Monitoring of data or traffic on a System without permission.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<strong>Falsification of Origin.</strong> Forging TCP-IP packet headers, e-mail headers, or any part of a
|
||||
message describing its origin or route. The legitimate use of aliases and anonymous remailers is not
|
||||
prohibited by this provision.
|
||||
</li>
|
||||
</ul>
|
||||
<strong>No Network Abuse</strong>
|
||||
<br />
|
||||
You may not make network connections to any users, hosts, or networks unless you have permission to
|
||||
communicate with them. Prohibited activities include:
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Monitoring or Crawling.</strong> Monitoring or crawling of a System that impairs or disrupts the
|
||||
System being monitored or crawled.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<strong>Denial of Service (DoS).</strong> Inundating a target with communications requests so the target
|
||||
either cannot respond to legitimate traffic or responds so slowly that it becomes ineffective.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<strong>Intentional Interference.</strong> Interfering with the proper functioning of any System,
|
||||
including any deliberate attempt to overload a system by mail bombing, news bombing, broadcast attacks, or
|
||||
flooding techniques.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<strong>Operation of Certain Network Services.</strong> Operating network services like open proxies, open
|
||||
mail relays, or open recursive domain name servers.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<strong>Avoiding System Restrictions.</strong> Using manual or electronic means to avoid any use
|
||||
limitations placed on a System, such as access and storage restrictions.
|
||||
</li>
|
||||
</ul>
|
||||
<strong>No E-Mail or Other Message Abuse</strong>
|
||||
<br />
|
||||
You will not distribute, publish, send, or facilitate the sending of unsolicited mass e-mail or other
|
||||
messages, promotions, advertising, or solicitations (like “spam”), including commercial advertising and
|
||||
informational announcements. You will not alter or obscure mail headers or assume a sender’s identity without
|
||||
the sender’s explicit permission. You will not collect replies to messages sent from another internet service
|
||||
provider if those messages violate this Policy or the acceptable use policy of that provider.
|
||||
<strong>Our Monitoring and Enforcement</strong>
|
||||
<br />
|
||||
We reserve the right, but do not assume the obligation, to investigate any violation of this Policy or misuse
|
||||
of the Services or Darkwire Site. We may:
|
||||
<ul>
|
||||
<li>investigate violations of this Policy or misuse of the Services or Darkwire Site; or</li>
|
||||
<li>
|
||||
remove, disable access to, or modify any content or resource that violates this Policy or any other
|
||||
agreement we have with you for use of the Services or the Darkwire Site.
|
||||
</li>
|
||||
<li>
|
||||
We may report any activity that we suspect violates any law or regulation to appropriate law enforcement
|
||||
officials, regulators, or other appropriate third parties. Our reporting may include disclosing
|
||||
appropriate customer information. We also may cooperate with appropriate law enforcement agencies,
|
||||
regulators, or other appropriate third parties to help with the investigation and prosecution of illegal
|
||||
conduct by providing network and systems information related to alleged violations of this Policy.
|
||||
</li>
|
||||
</ul>
|
||||
Reporting of Violations of this Policy
|
||||
<br />
|
||||
If you become aware of any violation of this Policy, you will immediately notify us and provide us with
|
||||
assistance, as requested, to stop or remedy the violation. To report any violation of this Policy, please
|
||||
follow our abuse reporting process.
|
||||
</section>
|
||||
|
||||
<section id="terms">
|
||||
<h4>Terms of Service ("Terms")</h4>
|
||||
<p>Last updated: December 11, 2017</p>
|
||||
<p>
|
||||
Please read these Terms of Service ("Terms", "Terms of Service") carefully before using the
|
||||
https://darkwire.io website (the "Service") operated by Darkwire ("us", "we", or "our").
|
||||
</p>
|
||||
<p>
|
||||
Your access to and use of the Service is conditioned on your acceptance of and compliance with these Terms.
|
||||
These Terms apply to all visitors, users and others who access or use the Service.
|
||||
</p>
|
||||
<p>
|
||||
By accessing or using the Service you agree to be bound by these Terms. If you disagree with any part of the
|
||||
terms then you may not access the Service.
|
||||
</p>
|
||||
<strong>Links To Other Web Sites</strong>
|
||||
<p>
|
||||
Our Service may contain links to third-party web sites or services that are not owned or controlled by
|
||||
Darkwire.
|
||||
</p>
|
||||
<p>
|
||||
Darkwire has no control over, and assumes no responsibility for, the content, privacy policies, or practices
|
||||
of any third party web sites or services. You further acknowledge and agree that Darkwire shall not be
|
||||
responsible or liable, directly or indirectly, for any damage or loss caused or alleged to be caused by or
|
||||
in connection with use of or reliance on any such content, goods or services available on or through any
|
||||
such web sites or services.
|
||||
</p>
|
||||
<p>
|
||||
We strongly advise you to read the terms and conditions and privacy policies of any third-party web sites or
|
||||
services that you visit.
|
||||
</p>
|
||||
<strong>Termination</strong>
|
||||
<p>
|
||||
We may terminate or suspend access to our Service immediately, without prior notice or liability, for any
|
||||
reason whatsoever, including without limitation if you breach the Terms.
|
||||
</p>
|
||||
<p>
|
||||
All provisions of the Terms which by their nature should survive termination shall survive termination,
|
||||
including, without limitation, ownership provisions, warranty disclaimers, indemnity and limitations of
|
||||
liability.
|
||||
</p>
|
||||
<strong>Governing Law</strong>
|
||||
<p>
|
||||
These Terms shall be governed and construed in accordance with the laws of New York, United States, without
|
||||
regard to its conflict of law provisions.
|
||||
</p>
|
||||
<p>
|
||||
Our failure to enforce any right or provision of these Terms will not be considered a waiver of those
|
||||
rights. If any provision of these Terms is held to be invalid or unenforceable by a court, the remaining
|
||||
provisions of these Terms will remain in effect. These Terms constitute the entire agreement between us
|
||||
regarding our Service, and supersede and replace any prior agreements we might have between us regarding the
|
||||
Service.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section id="disclaimer">
|
||||
<h4>Disclaimer</h4>
|
||||
<p className="bold">
|
||||
WARNING: Darkwire does not mask IP addresses nor can verify the integrity of parties recieving messages.
|
||||
Proceed with caution and always confirm recipients beforre starting a chat session.
|
||||
</p>
|
||||
<p>
|
||||
Please also note that <strong>ALL CHATROOMS</strong> are public. Anyone can guess your room URL. If
|
||||
you need a more-private room, use the lock feature or set the URL manually by entering a room ID after
|
||||
"darkwire.io/".
|
||||
</p>
|
||||
<br />
|
||||
<strong>No Warranties; Exclusion of Liability; Indemnification</strong>
|
||||
<p>
|
||||
<strong>
|
||||
OUR WEBSITE IS OPERATED BY Darkwire ON AN "AS IS," "AS AVAILABLE" BASIS, WITHOUT REPRESENTATIONS OR
|
||||
WARRANTIES OF ANY KIND. TO THE FULLEST EXTENT PERMITTED BY LAW, Darkwire SPECIFICALLY DISCLAIMS ALL
|
||||
WARRANTIES AND CONDITIONS OF ANY KIND, INCLUDING ALL IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NONINFRINGEMENT FOR OUR WEBSITE AND ANY CONTRACTS AND SERVICES
|
||||
YOU PURCHASE THROUGH IT. Darkwire SHALL NOT HAVE ANY LIABILITY OR RESPONSIBILITY FOR ANY ERRORS OR
|
||||
OMISSIONS IN THE CONTENT OF OUR WEBSITE, FOR CONTRACTS OR SERVICES SOLD THROUGH OUR WEBSITE, FOR YOUR
|
||||
ACTION OR INACTION IN CONNECTION WITH OUR WEBSITE OR FOR ANY DAMAGE TO YOUR COMPUTER OR DATA OR ANY OTHER
|
||||
DAMAGE YOU MAY INCUR IN CONNECTION WITH OUR WEBSITE. YOUR USE OF OUR WEBSITE AND ANY CONTRACTS OR SERVICES
|
||||
ARE AT YOUR OWN RISK. IN NO EVENT SHALL EITHER Darkwire OR THEIR AGENTS BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, PUNITIVE, INCIDENTAL, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN ANY WAY CONNECTED
|
||||
WITH THE USE OF OUR WEBSITE, CONTRACTS AND SERVICES PURCHASED THROUGH OUR WEBSITE, THE DELAY OR INABILITY
|
||||
TO USE OUR WEBSITE OR OTHERWISE ARISING IN CONNECTION WITH OUR WEBSITE, CONTRACTS OR RELATED SERVICES,
|
||||
WHETHER BASED ON CONTRACT, TORT, STRICT LIABILITY OR OTHERWISE, EVEN IF ADVISED OF THE POSSIBILITY OF ANY
|
||||
SUCH DAMAGES. IN NO EVENT SHALL Darkwire’s LIABILITY FOR ANY DAMAGE CLAIM EXCEED THE AMOUNT PAID BY YOU TO
|
||||
Darkwire FOR THE TRANSACTION GIVING RISE TO SUCH DAMAGE CLAIM.
|
||||
</strong>
|
||||
</p>
|
||||
<p>
|
||||
<strong>
|
||||
SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THE
|
||||
ABOVE EXCLUSION MAY NOT APPLY TO YOU.
|
||||
</strong>
|
||||
</p>
|
||||
<p>
|
||||
<strong>
|
||||
WITHOUT LIMITING THE FOREGOING, Darkwire DO NOT REPRESENT OR WARRANT THAT THE INFORMATION ON THE WEBITE IS
|
||||
ACCURATE, COMPLETE, RELIABLE, USEFUL, TIMELY OR CURRENT OR THAT OUR WEBSITE WILL OPERATE WITHOUT
|
||||
INTERRUPTION OR ERROR.
|
||||
</strong>
|
||||
</p>
|
||||
<p>
|
||||
<strong>
|
||||
YOU AGREE THAT ALL TIMES, YOU WILL LOOK TO ATTORNEYS FROM WHOM YOU PURCHASE SERVICES FOR ANY CLAIMS OF ANY
|
||||
NATURE, INCLUDING LOSS, DAMAGE, OR WARRANTY. Darkwire AND THEIR RESPECTIVE AFFILIATES MAKE NO
|
||||
REPRESENTATION OR GUARANTEES ABOUT ANY CONTRACTS AND SERVICES OFFERED THROUGH OUR WEBSITE.
|
||||
</strong>
|
||||
</p>
|
||||
<p>
|
||||
<strong>
|
||||
Darkwire MAKES NO REPRESENTATION THAT CONTENT PROVIDED ON OUR WEBSITE, CONTRACTS, OR RELATED SERVICES ARE
|
||||
APPLICABLE OR APPROPRIATE FOR USE IN ALL JURISDICTIONS.
|
||||
</strong>
|
||||
</p>
|
||||
<strong>Indemnification</strong>
|
||||
<p>
|
||||
You agree to defend, indemnify and hold Darkwire harmless from and against any and all claims, damages,
|
||||
costs and expenses, including attorneys' fees, arising from or related to your use of our Website or any
|
||||
Contracts or Services you purchase through it.
|
||||
</p>
|
||||
<strong>Changes</strong>
|
||||
<p>
|
||||
We reserve the right, at our sole discretion, to modify or replace these Terms at any time. If a revision is
|
||||
material we will try to provide at least 30 days notice prior to any new terms taking effect. What
|
||||
constitutes a material change will be determined at our sole discretion.
|
||||
</p>
|
||||
<p>
|
||||
By continuing to access or use our Service after those revisions become effective, you agree to be bound by
|
||||
the revised terms. If you do not agree to the new terms, please stop using the Service.
|
||||
</p>
|
||||
<strong>Contact Us</strong>
|
||||
<p>If you have any questions about these Terms, please contact us at hello[at]darkwire.io.</p>
|
||||
</section>
|
||||
|
||||
<section id="contact">
|
||||
<h4>Contact</h4>
|
||||
<p>Questions/comments? Email us at hello[at]darkwire.io</p>
|
||||
<p>
|
||||
Found a bug or want a new feature?{' '}
|
||||
<a href="https://github.com/darkwire/darkwire.io/issues" target="_blank" rel="noopener noreferrer">
|
||||
Open a ticket on Github
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section id="donate">
|
||||
<h4>Donate</h4>
|
||||
<p>
|
||||
Darkwire is maintained and hosted by two developers with full-time jobs. If you get some value from this
|
||||
service we would appreciate any donation you can afford. We use these funds for server and DNS costs. Thank
|
||||
you!
|
||||
</p>
|
||||
<strong>Bitcoin</strong>
|
||||
<p>189sPnHGcjP5uteg2UuNgcJ5eoaRAP4Bw4</p>
|
||||
<strong>Ethereum</strong>
|
||||
<p>0x36dc407bB28aA1EE6AafBee0379Fe6Cff881758E</p>
|
||||
<strong>Litecoin</strong>
|
||||
<p>LUViQeSggBBtYoN2qNtXSuxYoRMzRY8CSX</p>
|
||||
<strong>PayPal:</strong>
|
||||
<br />
|
||||
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_blank">
|
||||
<input type="hidden" name="cmd" value="_s-xclick" />
|
||||
<input type="hidden" name="hosted_button_id" value="UAH5BCLA9Y8VW" />
|
||||
<input
|
||||
type="image"
|
||||
src="https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif"
|
||||
border="0"
|
||||
name="submit"
|
||||
alt="PayPal - The safer, easier way to pay online!"
|
||||
/>
|
||||
<img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1" />
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
About.propTypes = {
|
||||
roomId: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default About;
|
@ -1,9 +0,0 @@
|
||||
.links {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.base {
|
||||
section {
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
}
|
@ -1,296 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import sanitizeHtml from 'sanitize-html';
|
||||
import { CornerDownRight } from 'react-feather';
|
||||
|
||||
import { getSelectedText, hasTouchSupport } from '@/utils/dom';
|
||||
|
||||
import FileTransfer from '@/components/FileTransfer';
|
||||
|
||||
export class Chat extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
message: '',
|
||||
touchSupport: hasTouchSupport,
|
||||
shiftKeyDown: false,
|
||||
};
|
||||
|
||||
this.commands = [
|
||||
{
|
||||
command: 'nick',
|
||||
description: 'Changes nickname.',
|
||||
parameters: ['{username}'],
|
||||
usage: '/nick {username}',
|
||||
scope: 'global',
|
||||
action: params => {
|
||||
// eslint-disable-line
|
||||
let newUsername = params.join(' ') || ''; // eslint-disable-line
|
||||
|
||||
// Remove things that aren't digits or chars
|
||||
newUsername = newUsername.replace(/[^A-Za-z0-9]/g, '-');
|
||||
|
||||
const errors = [];
|
||||
|
||||
if (!newUsername.trim().length) {
|
||||
errors.push('Username cannot be blank');
|
||||
}
|
||||
|
||||
if (newUsername.toString().length > 16) {
|
||||
errors.push('Username cannot be greater than 16 characters');
|
||||
}
|
||||
|
||||
if (!newUsername.match(/^[A-Z]/i)) {
|
||||
errors.push('Username must start with a letter');
|
||||
}
|
||||
|
||||
if (errors.length) {
|
||||
return this.props.showNotice({
|
||||
message: `${errors.join(', ')}`,
|
||||
level: 'error',
|
||||
});
|
||||
}
|
||||
|
||||
this.props.sendEncryptedMessage({
|
||||
type: 'CHANGE_USERNAME',
|
||||
payload: {
|
||||
id: this.props.userId,
|
||||
newUsername,
|
||||
currentUsername: this.props.username,
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
command: 'help',
|
||||
description: 'Shows a list of commands.',
|
||||
paramaters: [],
|
||||
usage: '/help',
|
||||
scope: 'local',
|
||||
action: params => {
|
||||
// eslint-disable-line
|
||||
const validCommands = this.commands.map(command => `/${command.command}`);
|
||||
this.props.showNotice({
|
||||
message: `Valid commands: ${validCommands.sort().join(', ')}`,
|
||||
level: 'info',
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
command: 'me',
|
||||
description: 'Invoke virtual action',
|
||||
paramaters: ['{action}'],
|
||||
usage: '/me {action}',
|
||||
scope: 'global',
|
||||
action: params => {
|
||||
// eslint-disable-line
|
||||
const actionMessage = params.join(' ');
|
||||
if (!actionMessage.trim().length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.props.sendEncryptedMessage({
|
||||
type: 'USER_ACTION',
|
||||
payload: {
|
||||
action: actionMessage,
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
command: 'clear',
|
||||
description: 'Clears the chat screen',
|
||||
paramaters: [],
|
||||
usage: '/clear',
|
||||
scope: 'local',
|
||||
action: (params = null) => {
|
||||
// eslint-disable-line
|
||||
this.props.clearActivities();
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (!hasTouchSupport) {
|
||||
// Disable for now due to vary issues:
|
||||
// Paste not working, shift+enter line breaks
|
||||
// autosize(this.textInput);
|
||||
this.textInput.addEventListener('autosize:resized', () => {
|
||||
this.props.scrollToBottom();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.focusChat) {
|
||||
if (!getSelectedText()) {
|
||||
// Don't focus for now, evaluate UX benfits
|
||||
// this.textInput.focus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(nextProps, nextState) {
|
||||
if (!nextState.message.trim().length) {
|
||||
// autosize.update(this.textInput)
|
||||
}
|
||||
}
|
||||
|
||||
handleKeyUp(e) {
|
||||
if (e.key === 'Shift') {
|
||||
this.setState({
|
||||
shiftKeyDown: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
handleKeyPress(e) {
|
||||
if (e.key === 'Shift') {
|
||||
this.setState({
|
||||
shiftKeyDown: true,
|
||||
});
|
||||
}
|
||||
// Fix when autosize is enabled - line breaks require shift+enter twice
|
||||
if (e.key === 'Enter' && !hasTouchSupport && !this.state.shiftKeyDown) {
|
||||
e.preventDefault();
|
||||
if (this.canSend()) {
|
||||
this.sendMessage();
|
||||
} else {
|
||||
this.setState({
|
||||
message: '',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
executeCommand(command) {
|
||||
const commandToExecute = this.commands.find(cmnd => cmnd.command === command.command);
|
||||
|
||||
if (commandToExecute) {
|
||||
const { params } = command;
|
||||
const commandResult = commandToExecute.action(params);
|
||||
|
||||
return commandResult;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
handleSendClick() {
|
||||
this.sendMessage.bind(this);
|
||||
this.textInput.focus();
|
||||
}
|
||||
|
||||
handleFormSubmit(evt) {
|
||||
evt.preventDefault();
|
||||
this.sendMessage();
|
||||
}
|
||||
|
||||
parseCommand(message) {
|
||||
const commandTrigger = {
|
||||
command: null,
|
||||
params: [],
|
||||
};
|
||||
|
||||
if (message.charAt(0) === '/') {
|
||||
const parsedCommand = message.replace('/', '').split(' ');
|
||||
commandTrigger.command = sanitizeHtml(parsedCommand[0]) || null;
|
||||
// Get params
|
||||
if (parsedCommand.length >= 2) {
|
||||
for (let i = 1; i < parsedCommand.length; i++) {
|
||||
commandTrigger.params.push(parsedCommand[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return commandTrigger;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
sendMessage() {
|
||||
if (!this.canSend()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { message } = this.state;
|
||||
const isCommand = this.parseCommand(message);
|
||||
|
||||
if (isCommand) {
|
||||
const res = this.executeCommand(isCommand);
|
||||
if (res === false) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
this.props.sendEncryptedMessage({
|
||||
type: 'TEXT_MESSAGE',
|
||||
payload: {
|
||||
text: message,
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({
|
||||
message: '',
|
||||
});
|
||||
}
|
||||
|
||||
handleInputChange(evt) {
|
||||
this.setState({
|
||||
message: evt.target.value,
|
||||
});
|
||||
}
|
||||
|
||||
canSend() {
|
||||
return this.state.message.trim().length;
|
||||
}
|
||||
|
||||
render() {
|
||||
const touchSupport = this.state.touchSupport;
|
||||
|
||||
return (
|
||||
<form onSubmit={this.handleFormSubmit.bind(this)} className="chat-preflight-container">
|
||||
<textarea
|
||||
rows="1"
|
||||
onKeyUp={this.handleKeyUp.bind(this)}
|
||||
onKeyDown={this.handleKeyPress.bind(this)}
|
||||
ref={input => {
|
||||
this.textInput = input;
|
||||
}}
|
||||
autoFocus
|
||||
className="chat"
|
||||
value={this.state.message}
|
||||
placeholder={this.props.translations.typePlaceholder}
|
||||
onChange={this.handleInputChange.bind(this)}
|
||||
/>
|
||||
<div className="input-controls">
|
||||
<FileTransfer sendEncryptedMessage={this.props.sendEncryptedMessage} />
|
||||
{touchSupport && (
|
||||
<button
|
||||
onClick={this.handleSendClick.bind(this)}
|
||||
className={`icon is-right send btn btn-link ${this.canSend() ? 'active' : ''}`}
|
||||
title="Send"
|
||||
>
|
||||
<CornerDownRight className={this.canSend() ? '' : 'disabled'} />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Chat.propTypes = {
|
||||
sendEncryptedMessage: PropTypes.func.isRequired,
|
||||
showNotice: PropTypes.func.isRequired,
|
||||
userId: PropTypes.string.isRequired,
|
||||
username: PropTypes.string.isRequired,
|
||||
clearActivities: PropTypes.func.isRequired,
|
||||
focusChat: PropTypes.bool.isRequired,
|
||||
scrollToBottom: PropTypes.func.isRequired,
|
||||
translations: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default Chat;
|
@ -1,254 +0,0 @@
|
||||
import React from 'react';
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import { describe, it, expect, vi, afterEach } from 'vitest';
|
||||
|
||||
import { Chat } from '@/components/Chat/Chat';
|
||||
|
||||
import * as dom from '@/utils/dom';
|
||||
|
||||
const translations = {
|
||||
typePlaceholder: 'inputplaceholder',
|
||||
};
|
||||
|
||||
// Fake date
|
||||
vi.spyOn(global.Date, 'now').mockImplementation(() => new Date('2020-03-14T11:01:58.135Z').valueOf());
|
||||
|
||||
// To change touch support
|
||||
vi.mock('@/utils/dom');
|
||||
|
||||
describe('Chat component', () => {
|
||||
afterEach(() => {
|
||||
// Reset touch support
|
||||
dom.hasTouchSupport = false;
|
||||
});
|
||||
|
||||
it('should display', () => {
|
||||
const { asFragment } = render(
|
||||
<Chat
|
||||
scrollToBottom={() => {}}
|
||||
focusChat={false}
|
||||
userId="foo"
|
||||
username="user"
|
||||
showNotice={() => {}}
|
||||
clearActivities={() => {}}
|
||||
sendEncryptedMessage={() => {}}
|
||||
translations={{}}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('can send message', () => {
|
||||
const sendEncryptedMessage = vi.fn();
|
||||
|
||||
render(
|
||||
<Chat
|
||||
scrollToBottom={() => {}}
|
||||
focusChat={false}
|
||||
userId="foo"
|
||||
username="user"
|
||||
showNotice={() => {}}
|
||||
clearActivities={() => {}}
|
||||
sendEncryptedMessage={sendEncryptedMessage}
|
||||
translations={translations}
|
||||
/>,
|
||||
);
|
||||
|
||||
const textarea = screen.getByPlaceholderText(translations.typePlaceholder);
|
||||
|
||||
// Validate but without text
|
||||
fireEvent.keyDown(textarea, { key: 'Enter' });
|
||||
|
||||
expect(sendEncryptedMessage).not.toHaveBeenCalled();
|
||||
|
||||
// Type test
|
||||
fireEvent.change(textarea, { target: { value: 'test' } });
|
||||
// Validate
|
||||
fireEvent.keyDown(textarea, { key: 'Enter' });
|
||||
|
||||
expect(sendEncryptedMessage).toHaveBeenLastCalledWith({
|
||||
payload: { text: 'test', timestamp: 1584183718135 },
|
||||
type: 'TEXT_MESSAGE',
|
||||
});
|
||||
|
||||
// Validate (textarea should be empty)
|
||||
fireEvent.keyDown(textarea, { key: 'Enter' });
|
||||
|
||||
expect(sendEncryptedMessage).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("shouldn't send message with Shift+enter", () => {
|
||||
const sendEncryptedMessage = vi.fn();
|
||||
|
||||
render(
|
||||
<Chat
|
||||
scrollToBottom={() => {}}
|
||||
focusChat={false}
|
||||
userId="foo"
|
||||
username="user"
|
||||
showNotice={() => {}}
|
||||
clearActivities={() => {}}
|
||||
sendEncryptedMessage={sendEncryptedMessage}
|
||||
translations={translations}
|
||||
/>,
|
||||
);
|
||||
|
||||
const textarea = screen.getByPlaceholderText(translations.typePlaceholder);
|
||||
|
||||
// Test shift effect
|
||||
fireEvent.change(textarea, { target: { value: 'test2' } });
|
||||
fireEvent.keyDown(textarea, { key: 'Shift' });
|
||||
fireEvent.keyDown(textarea, { key: 'Enter' });
|
||||
fireEvent.keyUp(textarea, { key: 'Shift' });
|
||||
|
||||
expect(sendEncryptedMessage).toHaveBeenCalledTimes(0);
|
||||
|
||||
// Now we want to send the message
|
||||
fireEvent.keyDown(textarea, { key: 'Enter' });
|
||||
|
||||
expect(sendEncryptedMessage).toHaveBeenCalledTimes(1);
|
||||
|
||||
expect(sendEncryptedMessage).toHaveBeenLastCalledWith({
|
||||
payload: { text: 'test2', timestamp: 1584183718135 },
|
||||
type: 'TEXT_MESSAGE',
|
||||
});
|
||||
});
|
||||
|
||||
it('should send commands', () => {
|
||||
const sendEncryptedMessage = vi.fn();
|
||||
const showNotice = vi.fn();
|
||||
const clearActivities = vi.fn();
|
||||
|
||||
render(
|
||||
<Chat
|
||||
scrollToBottom={() => {}}
|
||||
focusChat={false}
|
||||
userId="foo"
|
||||
username="user"
|
||||
showNotice={showNotice}
|
||||
clearActivities={clearActivities}
|
||||
sendEncryptedMessage={sendEncryptedMessage}
|
||||
translations={translations}
|
||||
/>,
|
||||
);
|
||||
|
||||
const textarea = screen.getByPlaceholderText(translations.typePlaceholder);
|
||||
|
||||
// Test /help
|
||||
fireEvent.change(textarea, { target: { value: '/help' } });
|
||||
fireEvent.keyDown(textarea, { key: 'Enter' });
|
||||
|
||||
expect(showNotice).toHaveBeenLastCalledWith({
|
||||
level: 'info',
|
||||
message: 'Valid commands: /clear, /help, /me, /nick',
|
||||
});
|
||||
|
||||
// Test /me
|
||||
fireEvent.change(textarea, { target: { value: '/me' } });
|
||||
fireEvent.keyDown(textarea, { key: 'Enter' });
|
||||
|
||||
expect(sendEncryptedMessage).not.toHaveBeenCalled();
|
||||
|
||||
fireEvent.change(textarea, { target: { value: '/me action' } });
|
||||
fireEvent.keyDown(textarea, { key: 'Enter' });
|
||||
|
||||
expect(sendEncryptedMessage).toHaveBeenLastCalledWith({
|
||||
payload: { action: 'action' },
|
||||
type: 'USER_ACTION',
|
||||
});
|
||||
|
||||
// Test /clear
|
||||
fireEvent.change(textarea, { target: { value: '/clear' } });
|
||||
fireEvent.keyDown(textarea, { key: 'Enter' });
|
||||
|
||||
expect(clearActivities).toHaveBeenLastCalledWith();
|
||||
|
||||
// Test /nick/clear
|
||||
fireEvent.change(textarea, { target: { value: '/nick john!Th3Ripp&3r' } });
|
||||
fireEvent.keyDown(textarea, { key: 'Enter' });
|
||||
|
||||
expect(sendEncryptedMessage).toHaveBeenLastCalledWith({
|
||||
payload: { currentUsername: 'user', id: 'foo', newUsername: 'john-Th3Ripp-3r' },
|
||||
type: 'CHANGE_USERNAME',
|
||||
});
|
||||
|
||||
// Test /nick
|
||||
fireEvent.change(textarea, { target: { value: '/nick' } });
|
||||
fireEvent.keyDown(textarea, { key: 'Enter' });
|
||||
|
||||
expect(showNotice).toHaveBeenLastCalledWith({
|
||||
level: 'error',
|
||||
message: 'Username cannot be blank, Username must start with a letter',
|
||||
});
|
||||
|
||||
// Test /nick
|
||||
fireEvent.change(textarea, { target: { value: '/nick 3po' } });
|
||||
fireEvent.keyDown(textarea, { key: 'Enter' });
|
||||
|
||||
expect(showNotice).toHaveBeenLastCalledWith({
|
||||
level: 'error',
|
||||
message: 'Username must start with a letter',
|
||||
});
|
||||
|
||||
// Test /nick
|
||||
fireEvent.change(textarea, {
|
||||
target: { value: '/nick 3po3ralotsofcrapscharactersforyourpleasureandnotmine' },
|
||||
});
|
||||
fireEvent.keyDown(textarea, { key: 'Enter' });
|
||||
|
||||
expect(sendEncryptedMessage).toHaveBeenLastCalledWith({
|
||||
payload: { currentUsername: 'user', id: 'foo', newUsername: 'john-Th3Ripp-3r' },
|
||||
type: 'CHANGE_USERNAME',
|
||||
});
|
||||
|
||||
// Test badcommand
|
||||
fireEvent.change(textarea, { target: { value: '/void' } });
|
||||
fireEvent.keyDown(textarea, { key: 'Enter' });
|
||||
});
|
||||
|
||||
it('should work with touch support', () => {
|
||||
// Enable touch support
|
||||
dom.hasTouchSupport = true;
|
||||
|
||||
vi.mock('@/utils/dom', () => {
|
||||
return {
|
||||
getSelectedText: vi.fn(),
|
||||
hasTouchSupport: true,
|
||||
};
|
||||
});
|
||||
|
||||
const sendEncryptedMessage = vi.fn();
|
||||
|
||||
const { getByTitle } = render(
|
||||
<Chat
|
||||
scrollToBottom={() => {}}
|
||||
focusChat={false}
|
||||
userId="foo"
|
||||
username="user"
|
||||
showNotice={() => {}}
|
||||
clearActivities={() => {}}
|
||||
sendEncryptedMessage={sendEncryptedMessage}
|
||||
translations={translations}
|
||||
/>,
|
||||
);
|
||||
|
||||
const textarea = screen.getByPlaceholderText(translations.typePlaceholder);
|
||||
|
||||
// Type test
|
||||
fireEvent.change(textarea, { target: { value: 'test' } });
|
||||
|
||||
// Touch send button
|
||||
fireEvent.click(getByTitle('Send'));
|
||||
|
||||
expect(sendEncryptedMessage).toHaveBeenLastCalledWith({
|
||||
payload: { text: 'test', timestamp: 1584183718135 },
|
||||
type: 'TEXT_MESSAGE',
|
||||
});
|
||||
|
||||
// Should not send message
|
||||
fireEvent.click(getByTitle('Send'));
|
||||
|
||||
expect(sendEncryptedMessage).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
@ -1,50 +0,0 @@
|
||||
// Vitest Snapshot v1
|
||||
|
||||
exports[`Chat component > should display 1`] = `
|
||||
<DocumentFragment>
|
||||
<form
|
||||
class="chat-preflight-container"
|
||||
>
|
||||
<textarea
|
||||
class="chat"
|
||||
rows="1"
|
||||
/>
|
||||
<div
|
||||
class="input-controls"
|
||||
>
|
||||
<div
|
||||
class="_styles_374fdd icon file-transfer btn btn-link"
|
||||
>
|
||||
<input
|
||||
id="fileInput"
|
||||
name="fileUploader"
|
||||
placeholder="Choose a file..."
|
||||
type="file"
|
||||
/>
|
||||
<label
|
||||
for="fileInput"
|
||||
>
|
||||
<svg
|
||||
fill="none"
|
||||
height="24"
|
||||
stroke="#fff"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"
|
||||
/>
|
||||
<polyline
|
||||
points="13 2 13 9 20 9"
|
||||
/>
|
||||
</svg>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</DocumentFragment>
|
||||
`;
|
@ -1,17 +0,0 @@
|
||||
import Chat from './Chat';
|
||||
import { connect } from 'react-redux';
|
||||
import { clearActivities, showNotice, sendEncryptedMessage } from '@/actions';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
username: state.user.username,
|
||||
userId: state.user.id,
|
||||
translations: state.app.translations,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = {
|
||||
clearActivities,
|
||||
showNotice,
|
||||
sendEncryptedMessage,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Chat);
|
@ -1,10 +0,0 @@
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import Connecting from '.';
|
||||
import { test, expect } from 'vitest';
|
||||
|
||||
test('Connecting component is displaying', async () => {
|
||||
const { asFragment } = render(<Connecting />);
|
||||
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
@ -1,9 +0,0 @@
|
||||
// Vitest Snapshot v1
|
||||
|
||||
exports[`Connecting component is displaying 1`] = `
|
||||
<DocumentFragment>
|
||||
<div>
|
||||
Please wait while we secure a connection to Darkwire...
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|